Merge "Remove annotations from MmTelFeature.MmTelCapabilities.toString"
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 d61a6a7..9b6dcc7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9824,6 +9824,7 @@
field public static final String CARRIER_CONFIG_SERVICE = "carrier_config";
field public static final String CLIPBOARD_SERVICE = "clipboard";
field public static final String COMPANION_DEVICE_SERVICE = "companiondevice";
+ field public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics";
field public static final String CONNECTIVITY_SERVICE = "connectivity";
field public static final String CONSUMER_IR_SERVICE = "consumer_ir";
field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
@@ -9882,6 +9883,7 @@
field public static final String USB_SERVICE = "usb";
field public static final String USER_SERVICE = "user";
field public static final String VIBRATOR_SERVICE = "vibrator";
+ field public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
field public static final String WALLPAPER_SERVICE = "wallpaper";
field public static final String WIFI_AWARE_SERVICE = "wifiaware";
field public static final String WIFI_P2P_SERVICE = "wifip2p";
@@ -16637,7 +16639,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 +17579,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 +18293,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 +18322,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 +18373,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 +22546,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;
@@ -28683,8 +28693,6 @@
public class ConnectivityDiagnosticsManager {
method public void registerConnectivityDiagnosticsCallback(@NonNull android.net.NetworkRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
method public void unregisterConnectivityDiagnosticsCallback(@NonNull android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback);
- field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
- field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
}
public abstract static class ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback {
@@ -28694,21 +28702,44 @@
method public void onNetworkConnectivityReported(@NonNull android.net.Network, boolean);
}
- public static class ConnectivityDiagnosticsManager.ConnectivityReport {
+ public static final class ConnectivityDiagnosticsManager.ConnectivityReport implements android.os.Parcelable {
ctor public ConnectivityDiagnosticsManager.ConnectivityReport(@NonNull android.net.Network, long, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
- field @NonNull public final android.os.PersistableBundle additionalInfo;
- field @NonNull public final android.net.LinkProperties linkProperties;
- field @NonNull public final android.net.Network network;
- field @NonNull public final android.net.NetworkCapabilities networkCapabilities;
- field public final long reportTimestamp;
+ method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getAdditionalInfo();
+ method @NonNull public android.net.LinkProperties getLinkProperties();
+ method @NonNull public android.net.Network getNetwork();
+ method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+ method public long getReportTimestamp();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR;
+ field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttemped";
+ field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded";
+ field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+ field public static final int NETWORK_PROBE_DNS = 4; // 0x4
+ field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20
+ field public static final int NETWORK_PROBE_HTTP = 8; // 0x8
+ field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10
+ field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40
+ field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0
+ field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2
+ field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3
+ field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1
}
- public static class ConnectivityDiagnosticsManager.DataStallReport {
+ public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle);
- field public final int detectionMethod;
- field @NonNull public final android.net.Network network;
- field public final long reportTimestamp;
- field @NonNull public final android.os.PersistableBundle stallDetails;
+ method public int describeContents();
+ method public int getDetectionMethod();
+ method @NonNull public android.net.Network getNetwork();
+ method public long getReportTimestamp();
+ method @NonNull public android.os.PersistableBundle getStallDetails();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR;
+ field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1
+ field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2
+ field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+ field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis";
+ field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
}
public class ConnectivityManager {
@@ -28851,6 +28882,35 @@
field public final int code;
}
+ public final class Ikev2VpnProfile extends android.net.PlatformVpnProfile {
+ method @NonNull public java.util.List<java.lang.String> getAllowedAlgorithms();
+ method public int getMaxMtu();
+ method @Nullable public String getPassword();
+ method @Nullable public byte[] getPresharedKey();
+ method @Nullable public android.net.ProxyInfo getProxyInfo();
+ method @Nullable public java.security.PrivateKey getRsaPrivateKey();
+ method @NonNull public String getServerAddr();
+ method @Nullable public java.security.cert.X509Certificate getServerRootCaCert();
+ method @Nullable public java.security.cert.X509Certificate getUserCert();
+ method @NonNull public String getUserIdentity();
+ method @Nullable public String getUsername();
+ method public boolean isBypassable();
+ method public boolean isMetered();
+ }
+
+ public static final class Ikev2VpnProfile.Builder {
+ ctor public Ikev2VpnProfile.Builder(@NonNull String, @NonNull String);
+ method @NonNull public android.net.Ikev2VpnProfile build();
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setAllowedAlgorithms(@NonNull java.util.List<java.lang.String>);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey, @Nullable java.security.cert.X509Certificate);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthPsk(@NonNull byte[]);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthUsernamePassword(@NonNull String, @NonNull String, @Nullable java.security.cert.X509Certificate);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setBypassable(boolean);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setMaxMtu(int);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setMetered(boolean);
+ method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo);
+ }
+
public class InetAddresses {
method public static boolean isNumericAddress(@NonNull String);
method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String);
@@ -29197,6 +29257,14 @@
field public String response;
}
+ public abstract class PlatformVpnProfile {
+ method public final int getType();
+ method @NonNull public final String getTypeString();
+ field public static final int TYPE_IKEV2_IPSEC_PSK = 7; // 0x7
+ field public static final int TYPE_IKEV2_IPSEC_RSA = 8; // 0x8
+ field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6
+ }
+
public final class Proxy {
ctor public Proxy();
method @Deprecated public static String getDefaultHost();
@@ -29477,6 +29545,13 @@
method public String sanitize(String);
}
+ public class VpnManager {
+ method public void deleteProvisionedVpnProfile();
+ method @Nullable public android.content.Intent provisionVpnProfile(@NonNull android.net.PlatformVpnProfile);
+ method public void startProvisionedVpnProfile();
+ method public void stopProvisionedVpnProfile();
+ }
+
public class VpnService extends android.app.Service {
ctor public VpnService();
method public final boolean isAlwaysOn();
@@ -41059,6 +41134,138 @@
}
+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 {
+ 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 {
@@ -43468,6 +43675,7 @@
field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
+ field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 8192; // 0x2000
field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 2048; // 0x800
field public static final int PROPERTY_RTT = 1024; // 0x400
@@ -43545,6 +43753,7 @@
public abstract class Conference extends android.telecom.Conferenceable {
ctor public Conference(android.telecom.PhoneAccountHandle);
method public final boolean addConnection(android.telecom.Connection);
+ method @NonNull public static android.telecom.Conference createFailedConference(@NonNull android.telecom.DisconnectCause, @NonNull android.telecom.PhoneAccountHandle);
method public final void destroy();
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
@@ -43559,6 +43768,8 @@
method public final android.telecom.StatusHints getStatusHints();
method public android.telecom.Connection.VideoProvider getVideoProvider();
method public int getVideoState();
+ method public final boolean isRingbackRequested();
+ method public void onAnswer(int);
method public void onCallAudioStateChanged(android.telecom.CallAudioState);
method public void onConnectionAdded(android.telecom.Connection);
method public void onDisconnect();
@@ -43567,6 +43778,7 @@
method public void onMerge(android.telecom.Connection);
method public void onMerge();
method public void onPlayDtmfTone(char);
+ method public void onReject();
method public void onSeparate(android.telecom.Connection);
method public void onStopDtmfTone();
method public void onSwap();
@@ -43586,6 +43798,8 @@
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final void setExtras(@Nullable android.os.Bundle);
method public final void setOnHold();
+ method public final void setRingbackRequested(boolean);
+ method public final void setRinging();
method public final void setStatusHints(android.telecom.StatusHints);
method public final void setVideoProvider(android.telecom.Connection, android.telecom.Connection.VideoProvider);
method public final void setVideoState(android.telecom.Connection, int);
@@ -43743,6 +43957,7 @@
field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
+ field public static final int PROPERTY_IS_ADHOC_CONFERENCE = 4096; // 0x1000
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
field public static final int PROPERTY_IS_RTT = 256; // 0x100
field public static final int PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL = 1024; // 0x400
@@ -43815,8 +44030,10 @@
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public android.net.Uri getAddress();
method public android.os.Bundle getExtras();
+ method @Nullable public java.util.List<android.net.Uri> getParticipants();
method public android.telecom.Connection.RttTextStream getRttTextStream();
method public int getVideoState();
+ method public boolean isAdhocConferenceCall();
method public boolean isRequestingRtt();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
@@ -43836,9 +44053,13 @@
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public void onConnectionServiceFocusGained();
method public void onConnectionServiceFocusLost();
+ method @Nullable public android.telecom.Conference onCreateIncomingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateIncomingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method @Nullable public android.telecom.Conference onCreateOutgoingConference(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConferenceFailed(@Nullable android.telecom.PhoneAccountHandle, @Nullable android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public void onCreateOutgoingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingHandoverConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
@@ -43953,6 +44174,7 @@
method public boolean supportsUriScheme(String);
method public android.telecom.PhoneAccount.Builder toBuilder();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 16384; // 0x4000
field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
@@ -44148,6 +44370,7 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int);
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
+ method public void addNewIncomingConference(@NonNull android.telecom.PhoneAccountHandle, @NonNull android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification();
method public android.content.Intent createManageBlockedNumbersIntent();
method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
@@ -44175,6 +44398,7 @@
method public void registerPhoneAccount(android.telecom.PhoneAccount);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
+ method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void startConference(@NonNull java.util.List<android.net.Uri>, @NonNull android.os.Bundle);
method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
field public static final String ACTION_CHANGE_DEFAULT_DIALER = "android.telecom.action.CHANGE_DEFAULT_DIALER";
field public static final String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
@@ -44653,6 +44877,7 @@
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
+ field public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL = "support_adhoc_conference_calls_bool";
field public static final String KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL = "support_clir_network_default_bool";
field public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final String KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL = "support_emergency_sms_over_ims_bool";
@@ -45183,7 +45408,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
@@ -45610,6 +45835,7 @@
method public String getNetworkCountryIso();
method public String getNetworkOperator();
method public String getNetworkOperatorName();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
method public String getNetworkSpecifier();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
method @Deprecated public int getPhoneCount();
@@ -45742,6 +45968,9 @@
field public static final int MULTISIM_ALLOWED = 0; // 0x0
field public static final int MULTISIM_NOT_SUPPORTED_BY_CARRIER = 2; // 0x2
field public static final int MULTISIM_NOT_SUPPORTED_BY_HARDWARE = 1; // 0x1
+ field public static final int NETWORK_SELECTION_MODE_AUTO = 1; // 0x1
+ field public static final int NETWORK_SELECTION_MODE_MANUAL = 2; // 0x2
+ field public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0; // 0x0
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -49424,6 +49653,71 @@
}
+package android.util.proto {
+
+ public final class ProtoOutputStream {
+ ctor public ProtoOutputStream();
+ ctor public ProtoOutputStream(int);
+ ctor public ProtoOutputStream(@NonNull java.io.OutputStream);
+ method public static int checkFieldId(long, long);
+ method public void dump(@NonNull String);
+ method public void end(long);
+ method public void flush();
+ method @NonNull public byte[] getBytes();
+ method @Nullable public static String getFieldCountString(long);
+ method @NonNull public static String getFieldIdString(long);
+ method @Nullable public static String getFieldTypeString(long);
+ method public int getRawSize();
+ method @Nullable public static String getWireTypeString(int);
+ method public static long makeFieldId(int, long);
+ method public static long makeToken(int, boolean, int, int, int);
+ method public long start(long);
+ method @NonNull public static String token2String(long);
+ method public void write(long, double);
+ method public void write(long, float);
+ method public void write(long, int);
+ method public void write(long, long);
+ method public void write(long, boolean);
+ method public void write(long, @Nullable String);
+ method public void write(long, @Nullable byte[]);
+ method public void writeTag(int, int);
+ field public static final long FIELD_COUNT_MASK = 16492674416640L; // 0xf0000000000L
+ field public static final long FIELD_COUNT_PACKED = 5497558138880L; // 0x50000000000L
+ field public static final long FIELD_COUNT_REPEATED = 2199023255552L; // 0x20000000000L
+ field public static final int FIELD_COUNT_SHIFT = 40; // 0x28
+ field public static final long FIELD_COUNT_SINGLE = 1099511627776L; // 0x10000000000L
+ field public static final long FIELD_COUNT_UNKNOWN = 0L; // 0x0L
+ field public static final int FIELD_ID_SHIFT = 3; // 0x3
+ field public static final long FIELD_TYPE_BOOL = 34359738368L; // 0x800000000L
+ field public static final long FIELD_TYPE_BYTES = 51539607552L; // 0xc00000000L
+ field public static final long FIELD_TYPE_DOUBLE = 4294967296L; // 0x100000000L
+ field public static final long FIELD_TYPE_ENUM = 60129542144L; // 0xe00000000L
+ field public static final long FIELD_TYPE_FIXED32 = 30064771072L; // 0x700000000L
+ field public static final long FIELD_TYPE_FIXED64 = 25769803776L; // 0x600000000L
+ field public static final long FIELD_TYPE_FLOAT = 8589934592L; // 0x200000000L
+ field public static final long FIELD_TYPE_INT32 = 21474836480L; // 0x500000000L
+ field public static final long FIELD_TYPE_INT64 = 12884901888L; // 0x300000000L
+ field public static final long FIELD_TYPE_MASK = 1095216660480L; // 0xff00000000L
+ field public static final long FIELD_TYPE_MESSAGE = 47244640256L; // 0xb00000000L
+ field public static final long FIELD_TYPE_SFIXED32 = 64424509440L; // 0xf00000000L
+ field public static final long FIELD_TYPE_SFIXED64 = 68719476736L; // 0x1000000000L
+ field public static final int FIELD_TYPE_SHIFT = 32; // 0x20
+ field public static final long FIELD_TYPE_SINT32 = 73014444032L; // 0x1100000000L
+ field public static final long FIELD_TYPE_SINT64 = 77309411328L; // 0x1200000000L
+ field public static final long FIELD_TYPE_STRING = 38654705664L; // 0x900000000L
+ field public static final long FIELD_TYPE_UINT32 = 55834574848L; // 0xd00000000L
+ field public static final long FIELD_TYPE_UINT64 = 17179869184L; // 0x400000000L
+ field public static final int WIRE_TYPE_END_GROUP = 4; // 0x4
+ field public static final int WIRE_TYPE_FIXED32 = 5; // 0x5
+ field public static final int WIRE_TYPE_FIXED64 = 1; // 0x1
+ field public static final int WIRE_TYPE_LENGTH_DELIMITED = 2; // 0x2
+ field public static final int WIRE_TYPE_MASK = 7; // 0x7
+ field public static final int WIRE_TYPE_START_GROUP = 3; // 0x3
+ field public static final int WIRE_TYPE_VARINT = 0; // 0x0
+ }
+
+}
+
package android.view {
public abstract class AbsSavedState implements android.os.Parcelable {
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/module-lib-current.txt b/api/module-lib-current.txt
index c8253a0..c657e00 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -118,9 +118,31 @@
}
public final class TimeZoneFinder {
+ method @Nullable public String getIanaVersion();
method @NonNull public static android.timezone.TimeZoneFinder getInstance();
method @Nullable public android.timezone.CountryTimeZones lookupCountryTimeZones(@NonNull String);
}
+ public final class TzDataSetVersion {
+ method public static int currentFormatMajorVersion();
+ method public static int currentFormatMinorVersion();
+ method public int getFormatMajorVersion();
+ method public int getFormatMinorVersion();
+ method public int getRevision();
+ method @NonNull public String getRulesVersion();
+ method public static boolean isCompatibleWithThisDevice(android.timezone.TzDataSetVersion);
+ method @NonNull public static android.timezone.TzDataSetVersion read() throws java.io.IOException, android.timezone.TzDataSetVersion.TzDataSetException;
+ }
+
+ public static class TzDataSetVersion.TzDataSetException extends java.lang.Exception {
+ ctor public TzDataSetVersion.TzDataSetException(String);
+ ctor public TzDataSetVersion.TzDataSetException(String, Throwable);
+ }
+
+ public final class ZoneInfoDb {
+ method @NonNull public static android.timezone.ZoneInfoDb getInstance();
+ method @NonNull public String getVersion();
+ }
+
}
diff --git a/api/system-current.txt b/api/system-current.txt
index e2fb99e..9acd67f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1440,12 +1440,13 @@
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
}
- public final class BluetoothPan implements android.bluetooth.BluetoothProfile {
- method protected void finalize();
+ public final class BluetoothPan implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public void close();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) protected void finalize();
method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
- method public boolean isTetheringOn();
- method public void setBluetoothTethering(boolean);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isTetheringOn();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public void setBluetoothTethering(boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
field public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
@@ -1463,10 +1464,14 @@
}
public interface BluetoothProfile {
+ field public static final int A2DP_SINK = 11; // 0xb
+ field public static final int AVRCP_CONTROLLER = 12; // 0xc
field public static final int CONNECTION_POLICY_ALLOWED = 100; // 0x64
field public static final int CONNECTION_POLICY_FORBIDDEN = 0; // 0x0
field public static final int CONNECTION_POLICY_UNKNOWN = -1; // 0xffffffff
+ field public static final int HEADSET_CLIENT = 16; // 0x10
field public static final int PAN = 5; // 0x5
+ field public static final int PBAP_CLIENT = 17; // 0x11
field @Deprecated public static final int PRIORITY_OFF = 0; // 0x0
field @Deprecated public static final int PRIORITY_ON = 100; // 0x64
}
@@ -1636,6 +1641,7 @@
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
+ field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -1940,6 +1946,7 @@
field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ANY_USER = 4194304; // 0x400000
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
+ field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000
field public static final int MATCH_INSTANT = 8388608; // 0x800000
field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1
field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2
@@ -2088,6 +2095,15 @@
}
+package android.debug {
+
+ public class AdbManager {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public boolean isAdbWifiQrSupported();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING) public boolean isAdbWifiSupported();
+ }
+
+}
+
package android.hardware {
public final class Sensor {
@@ -4038,8 +4054,8 @@
public final class DvbDeviceInfo implements android.os.Parcelable {
ctor public DvbDeviceInfo(int, int);
method public int describeContents();
- method public int getAdapterId();
- method public int getDeviceId();
+ method @IntRange(from=0) public int getAdapterId();
+ method @IntRange(from=0) public int getDeviceId();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR;
}
@@ -4294,6 +4310,32 @@
field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
}
+ public final class CaptivePortalData implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getByteLimit();
+ method public long getExpiryTimeMillis();
+ method public long getRefreshTimeMillis();
+ method @Nullable public android.net.Uri getUserPortalUrl();
+ method @Nullable public android.net.Uri getVenueInfoUrl();
+ method public boolean isCaptive();
+ method public boolean isSessionExtendable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
+ }
+
+ public static class CaptivePortalData.Builder {
+ ctor public CaptivePortalData.Builder();
+ ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
+ method @NonNull public android.net.CaptivePortalData build();
+ method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
+ method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
+ method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
+ method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
+ method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
+ method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+ method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
+ }
+
public class ConnectivityManager {
method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
@@ -4425,6 +4467,8 @@
method @NonNull public java.util.List<java.lang.String> getAllInterfaceNames();
method @NonNull public java.util.List<android.net.LinkAddress> getAllLinkAddresses();
method @NonNull public java.util.List<android.net.RouteInfo> getAllRoutes();
+ method @Nullable public android.net.Uri getCaptivePortalApiUrl();
+ method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
method @Nullable public String getTcpBufferSizes();
method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
@@ -4438,9 +4482,12 @@
method public boolean isIpv6Provisioned();
method public boolean isProvisioned();
method public boolean isReachable(@NonNull java.net.InetAddress);
+ method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy();
method public boolean removeDnsServer(@NonNull java.net.InetAddress);
method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
method public boolean removeRoute(@NonNull android.net.RouteInfo);
+ method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
+ method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
method public void setPrivateDnsServerName(@Nullable String);
method public void setTcpBufferSizes(@Nullable String);
@@ -4469,10 +4516,30 @@
field public final int netId;
}
+ public final class NetworkAgentConfig implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public String getSubscriberId();
+ method public boolean isNat64DetectionEnabled();
+ method public boolean isProvisioningNotificationEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkAgentConfig> CREATOR;
+ }
+
+ public static class NetworkAgentConfig.Builder {
+ ctor public NetworkAgentConfig.Builder();
+ method @NonNull public android.net.NetworkAgentConfig build();
+ method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection();
+ method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification();
+ method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
+ }
+
public final class NetworkCapabilities implements android.os.Parcelable {
method public boolean deduceRestrictedCapability();
+ method @NonNull public java.util.List<java.lang.Integer> getAdministratorUids();
+ method @Nullable public String getSSID();
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
+ method public void setAdministratorUids(@NonNull java.util.List<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String);
method @NonNull public android.net.NetworkCapabilities setTransportInfo(@NonNull android.net.TransportInfo);
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
@@ -4507,6 +4574,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);
}
@@ -4641,15 +4712,6 @@
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);
}
@@ -4843,7 +4905,7 @@
}
public final class IkeSessionConfiguration {
- ctor public IkeSessionConfiguration();
+ method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
method @NonNull public String getRemoteApplicationVersion();
method @NonNull public java.util.List<byte[]> getRemoteVendorIDs();
method public boolean isIkeExtensionEnabled(int);
@@ -4852,6 +4914,7 @@
}
public final class IkeSessionParams {
+ method @NonNull public java.util.List<android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest> getConfigurationRequests();
method public long getHardLifetime();
method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig();
method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification();
@@ -4865,6 +4928,8 @@
public static final class IkeSessionParams.Builder {
ctor public IkeSessionParams.Builder();
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(@NonNull java.net.InetAddress);
+ method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(int);
method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
method @NonNull public android.net.ipsec.ike.IkeSessionParams build();
method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
@@ -4878,6 +4943,14 @@
method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
}
+ public static interface IkeSessionParams.ConfigRequestIpv4PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
+ method @Nullable public java.net.Inet4Address getAddress();
+ }
+
+ public static interface IkeSessionParams.ConfigRequestIpv6PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
+ method @Nullable public java.net.Inet6Address getAddress();
+ }
+
public abstract static class IkeSessionParams.IkeAuthConfig {
}
@@ -4899,6 +4972,9 @@
method @NonNull public byte[] getPsk();
}
+ public static interface IkeSessionParams.IkeConfigRequest {
+ }
+
public final class IkeTrafficSelector {
ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress);
field public final int endPort;
@@ -4946,7 +5022,7 @@
}
public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
- method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest> getConfigurationRequests();
+ method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest> getConfigurationRequests();
}
public static final class TunnelModeChildSessionParams.Builder {
@@ -4963,33 +5039,33 @@
method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder setLifetime(long, long);
}
- public static interface TunnelModeChildSessionParams.ConfigRequest {
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet4Address getAddress();
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet4Address getAddress();
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet4Address getAddress();
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet6Address getAddress();
method public int getPrefixLength();
}
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest {
+ public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
method @Nullable public java.net.Inet6Address getAddress();
}
+ public static interface TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
+ }
+
}
package android.net.ipsec.ike.exceptions {
@@ -5473,7 +5549,7 @@
method public boolean isPortableHotspotSupported();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
- method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
+ method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
@@ -6890,6 +6966,7 @@
field public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
+ field public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category.";
field public static final String DOZE_ALWAYS_ON = "doze_always_on";
field public static final String HUSH_GESTURE_USED = "hush_gesture_used";
@@ -7034,6 +7111,14 @@
}
+package android.se.omapi {
+
+ public final class Reader {
+ method @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED) public boolean reset();
+ }
+
+}
+
package android.security.keystore {
public abstract class AttestationUtils {
@@ -8835,7 +8920,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
@@ -9116,6 +9201,7 @@
}
public final class SmsManager {
+ method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean copyMessageToIcc(@Nullable byte[], @NonNull byte[], int);
method @RequiresPermission(android.Manifest.permission.ACCESS_MESSAGES_ON_ICC) public boolean deleteMessageFromIcc(int);
method public boolean disableCellBroadcastRange(int, int, int);
method public boolean enableCellBroadcastRange(int, int, int);
@@ -9127,6 +9213,7 @@
public class SmsMessage {
method @Nullable public static android.telephony.SmsMessage createFromNativeSmsSubmitPdu(@NonNull byte[], boolean);
+ method @Nullable public static android.telephony.SmsMessage.SubmitPdu getSmsPdu(int, int, @Nullable String, @NonNull String, @NonNull String, long);
method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public static byte[] getSubmitPduEncodedMessage(boolean, @NonNull String, @NonNull String, int, int, int, int, int, int);
}
@@ -9791,7 +9878,8 @@
field public static final int DIALSTRING_USSD = 2; // 0x2
field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
- field public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+ field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE";
+ field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
field public static final String EXTRA_CNA = "cna";
field public static final String EXTRA_CNAP = "cnap";
@@ -9823,8 +9911,8 @@
method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
- method public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
- method public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
+ method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
+ method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
@@ -9832,7 +9920,7 @@
method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionInviteParticipantsRequestDelivered();
method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
- method public void callSessionMayHandover(int, int);
+ method @Deprecated public void callSessionMayHandover(int, int);
method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
@@ -9854,6 +9942,9 @@
method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
method public void callSessionUpdated(android.telephony.ims.ImsCallProfile);
method public void callSessionUssdMessageReceived(int, String);
+ method public void onHandover(int, int, @Nullable android.telephony.ims.ImsReasonInfo);
+ method public void onHandoverFailed(int, int, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public void onMayHandover(int, int);
}
public final class ImsConferenceState implements android.os.Parcelable {
@@ -9907,6 +9998,9 @@
method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+ field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
}
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
@@ -10189,7 +10283,27 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+ field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
+ field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
+ field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
+ field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
+ field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
+ field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
+ field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
+ field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
+ field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
+ field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
+ field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
+ field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
+ field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
+ field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
+ field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
+ field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
+ field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
+ field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
+ field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
+ field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
@@ -10199,16 +10313,52 @@
field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
+ field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
+ field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
+ field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
+ field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
+ field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
+ field public static final int KEY_RTT_ENABLED = 66; // 0x42
+ field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
+ field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
+ field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
+ field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
+ field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
+ field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
+ field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
+ field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
+ field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
+ field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
+ field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
+ field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
+ field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
+ field public static final int KEY_SMS_FORMAT = 13; // 0xd
+ field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
+ field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
+ field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
+ field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
+ field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
+ field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
+ field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
+ field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
+ field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
+ field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
+ field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
+ field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
+ field public static final int SMS_FORMAT_3GPP = 1; // 0x1
+ field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
+ field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
+ field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
}
public static class ProvisioningManager.Callback {
@@ -10526,6 +10676,7 @@
method public int transact(android.os.Bundle);
method public int updateCallBarring(int, int, String[]);
method public int updateCallBarringForServiceClass(int, int, String[], int);
+ method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
method public int updateCallForward(int, int, String, int, int);
method public int updateCallWaiting(boolean, int);
method public int updateClip(boolean);
diff --git a/api/test-current.txt b/api/test-current.txt
index e4997b7..98b224d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1361,6 +1361,32 @@
field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
}
+ public final class CaptivePortalData implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getByteLimit();
+ method public long getExpiryTimeMillis();
+ method public long getRefreshTimeMillis();
+ method @Nullable public android.net.Uri getUserPortalUrl();
+ method @Nullable public android.net.Uri getVenueInfoUrl();
+ method public boolean isCaptive();
+ method public boolean isSessionExtendable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR;
+ }
+
+ public static class CaptivePortalData.Builder {
+ ctor public CaptivePortalData.Builder();
+ ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData);
+ method @NonNull public android.net.CaptivePortalData build();
+ method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long);
+ method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean);
+ method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long);
+ method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long);
+ method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean);
+ method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri);
+ method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri);
+ }
+
public class ConnectivityManager {
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
@@ -1391,6 +1417,8 @@
ctor public LinkProperties(@Nullable android.net.LinkProperties);
method public boolean addDnsServer(@NonNull java.net.InetAddress);
method public boolean addLinkAddress(@NonNull android.net.LinkAddress);
+ method @Nullable public android.net.Uri getCaptivePortalApiUrl();
+ method @Nullable public android.net.CaptivePortalData getCaptivePortalData();
method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
method @Nullable public String getTcpBufferSizes();
method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers();
@@ -1401,9 +1429,12 @@
method public boolean isIpv6Provisioned();
method public boolean isProvisioned();
method public boolean isReachable(@NonNull java.net.InetAddress);
+ method @NonNull public android.net.LinkProperties makeSensitiveFieldsParcelingCopy();
method public boolean removeDnsServer(@NonNull java.net.InetAddress);
method public boolean removeLinkAddress(@NonNull android.net.LinkAddress);
method public boolean removeRoute(@NonNull android.net.RouteInfo);
+ method public void setCaptivePortalApiUrl(@Nullable android.net.Uri);
+ method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData);
method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>);
method public void setPrivateDnsServerName(@Nullable String);
method public void setTcpBufferSizes(@Nullable String);
@@ -2853,6 +2884,8 @@
method @NonNull public android.telecom.ConnectionRequest.Builder setAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
method @NonNull public android.telecom.ConnectionRequest.Builder setAddress(@NonNull android.net.Uri);
method @NonNull public android.telecom.ConnectionRequest.Builder setExtras(@NonNull android.os.Bundle);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setIsAdhocConferenceCall(boolean);
+ method @NonNull public android.telecom.ConnectionRequest.Builder setParticipants(@Nullable java.util.List<android.net.Uri>);
method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeFromInCall(@NonNull android.os.ParcelFileDescriptor);
method @NonNull public android.telecom.ConnectionRequest.Builder setRttPipeToInCall(@NonNull android.os.ParcelFileDescriptor);
method @NonNull public android.telecom.ConnectionRequest.Builder setShouldShowIncomingCallUi(boolean);
@@ -3153,7 +3186,8 @@
field public static final int DIALSTRING_USSD = 2; // 0x2
field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS";
- field public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
+ field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE";
+ field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
field public static final String EXTRA_CHILD_NUMBER = "ChildNum";
field public static final String EXTRA_CNA = "cna";
field public static final String EXTRA_CNAP = "cnap";
@@ -3186,8 +3220,8 @@
method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState);
- method public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
- method public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
+ method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo);
+ method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo);
method public void callSessionHeld(android.telephony.ims.ImsCallProfile);
method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
@@ -3195,7 +3229,7 @@
method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionInviteParticipantsRequestDelivered();
method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
- method public void callSessionMayHandover(int, int);
+ method @Deprecated public void callSessionMayHandover(int, int);
method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase);
method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo);
method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile);
@@ -3217,6 +3251,9 @@
method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile);
method public void callSessionUpdated(android.telephony.ims.ImsCallProfile);
method public void callSessionUssdMessageReceived(int, String);
+ method public void onHandover(int, int, @Nullable android.telephony.ims.ImsReasonInfo);
+ method public void onHandoverFailed(int, int, @NonNull android.telephony.ims.ImsReasonInfo);
+ method public void onMayHandover(int, int);
}
public final class ImsConferenceState implements android.os.Parcelable {
@@ -3270,6 +3307,9 @@
method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int);
method @NonNull public android.telephony.ims.ImsRcsManager getImsRcsManager(int);
field public static final String ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION = "com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
+ field public static final String ACTION_WFC_IMS_REGISTRATION_ERROR = "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+ field public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE = "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
}
public class ImsMmTelManager implements android.telephony.ims.RegistrationManager {
@@ -3548,7 +3588,27 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback);
+ field public static final int KEY_1X_EPDG_TIMER_SEC = 64; // 0x40
+ field public static final int KEY_1X_THRESHOLD = 59; // 0x3b
+ field public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50; // 0x32
+ field public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0; // 0x0
+ field public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53; // 0x35
+ field public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49; // 0x31
+ field public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48; // 0x30
+ field public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1; // 0x1
+ field public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47; // 0x2f
+ field public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52; // 0x34
+ field public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51; // 0x33
field public static final int KEY_EAB_PROVISIONING_STATUS = 25; // 0x19
+ field public static final int KEY_ENABLE_SILENT_REDIAL = 6; // 0x6
+ field public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31; // 0x1f
+ field public static final int KEY_LTE_EPDG_TIMER_SEC = 62; // 0x3e
+ field public static final int KEY_LTE_THRESHOLD_1 = 56; // 0x38
+ field public static final int KEY_LTE_THRESHOLD_2 = 57; // 0x39
+ field public static final int KEY_LTE_THRESHOLD_3 = 58; // 0x3a
+ field public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3; // 0x3
+ field public static final int KEY_MOBILE_DATA_ENABLED = 29; // 0x1d
+ field public static final int KEY_MULTIENDPOINT_ENABLED = 65; // 0x41
field public static final int KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC = 19; // 0x13
field public static final int KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC = 18; // 0x12
field public static final int KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC = 20; // 0x14
@@ -3558,16 +3618,52 @@
field public static final int KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS = 21; // 0x15
field public static final int KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC = 16; // 0x10
field public static final int KEY_RCS_PUBLISH_TIMER_SEC = 15; // 0xf
+ field public static final int KEY_REGISTRATION_DOMAIN_NAME = 12; // 0xc
+ field public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33; // 0x21
+ field public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34; // 0x22
+ field public static final int KEY_RTP_SPEECH_END_PORT = 36; // 0x24
+ field public static final int KEY_RTP_SPEECH_START_PORT = 35; // 0x23
+ field public static final int KEY_RTT_ENABLED = 66; // 0x42
+ field public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43; // 0x2b
+ field public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44; // 0x2c
+ field public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38; // 0x26
+ field public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4; // 0x4
+ field public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37; // 0x25
+ field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42; // 0x2a
+ field public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39; // 0x27
+ field public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32; // 0x20
+ field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45; // 0x2d
+ field public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40; // 0x28
+ field public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46; // 0x2e
+ field public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41; // 0x29
+ field public static final int KEY_SIP_SESSION_TIMER_SEC = 2; // 0x2
+ field public static final int KEY_SMS_FORMAT = 13; // 0xd
+ field public static final int KEY_SMS_OVER_IP_ENABLED = 14; // 0xe
+ field public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54; // 0x36
field public static final int KEY_T1_TIMER_VALUE_MS = 7; // 0x7
+ field public static final int KEY_T2_TIMER_VALUE_MS = 8; // 0x8
+ field public static final int KEY_TF_TIMER_VALUE_MS = 9; // 0x9
+ field public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5; // 0x5
+ field public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24; // 0x18
+ field public static final int KEY_VIDEO_QUALITY = 55; // 0x37
+ field public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28; // 0x1c
field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b
field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a
field public static final int KEY_VOLTE_PROVISIONING_STATUS = 10; // 0xa
+ field public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30; // 0x1e
field public static final int KEY_VT_PROVISIONING_STATUS = 11; // 0xb
+ field public static final int KEY_WIFI_EPDG_TIMER_SEC = 63; // 0x3f
+ field public static final int KEY_WIFI_THRESHOLD_A = 60; // 0x3c
+ field public static final int KEY_WIFI_THRESHOLD_B = 61; // 0x3d
field public static final int PROVISIONING_RESULT_UNKNOWN = -1; // 0xffffffff
field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0
field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1
+ field public static final int SMS_FORMAT_3GPP = 1; // 0x1
+ field public static final int SMS_FORMAT_3GPP2 = 0; // 0x0
field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC";
field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY";
+ field public static final int VIDEO_QUALITY_HIGH = 1; // 0x1
+ field public static final int VIDEO_QUALITY_LOW = 0; // 0x0
}
public static class ProvisioningManager.Callback {
@@ -3837,6 +3933,7 @@
method public int transact(android.os.Bundle);
method public int updateCallBarring(int, int, String[]);
method public int updateCallBarringForServiceClass(int, int, String[], int);
+ method public int updateCallBarringWithPassword(int, int, @Nullable String[], int, @NonNull String);
method public int updateCallForward(int, int, String, int, int);
method public int updateCallWaiting(boolean, int);
method public int updateClip(boolean);
@@ -4038,138 +4135,10 @@
method public void writeRawZigZag64(long);
}
- public final class ProtoOutputStream extends android.util.proto.ProtoStream {
- ctor public ProtoOutputStream();
- ctor public ProtoOutputStream(int);
- ctor public ProtoOutputStream(java.io.OutputStream);
- ctor public ProtoOutputStream(java.io.FileDescriptor);
- method public static int checkFieldId(long, long);
- method public void dump(String);
- method public void end(long);
- method @Deprecated public void endObject(long);
- method @Deprecated public void endRepeatedObject(long);
- method public void flush();
- method public byte[] getBytes();
- method public int getRawSize();
- method public static long makeFieldId(int, long);
- method public long start(long);
- method @Deprecated public long startObject(long);
- method @Deprecated public long startRepeatedObject(long);
- method public void write(long, double);
- method public void write(long, float);
- method public void write(long, int);
- method public void write(long, long);
- method public void write(long, boolean);
- method public void write(long, String);
- method public void write(long, byte[]);
- method @Deprecated public void writeBool(long, boolean);
- method @Deprecated public void writeBytes(long, byte[]);
- method @Deprecated public void writeDouble(long, double);
- method @Deprecated public void writeEnum(long, int);
- method @Deprecated public void writeFixed32(long, int);
- method @Deprecated public void writeFixed64(long, long);
- method @Deprecated public void writeFloat(long, float);
- method @Deprecated public void writeInt32(long, int);
- method @Deprecated public void writeInt64(long, long);
- method @Deprecated public void writeObject(long, byte[]);
- method @Deprecated public void writePackedBool(long, boolean[]);
- method @Deprecated public void writePackedDouble(long, double[]);
- method @Deprecated public void writePackedEnum(long, int[]);
- method @Deprecated public void writePackedFixed32(long, int[]);
- method @Deprecated public void writePackedFixed64(long, long[]);
- method @Deprecated public void writePackedFloat(long, float[]);
- method @Deprecated public void writePackedInt32(long, int[]);
- method @Deprecated public void writePackedInt64(long, long[]);
- method @Deprecated public void writePackedSFixed32(long, int[]);
- method @Deprecated public void writePackedSFixed64(long, long[]);
- method @Deprecated public void writePackedSInt32(long, int[]);
- method @Deprecated public void writePackedSInt64(long, long[]);
- method @Deprecated public void writePackedUInt32(long, int[]);
- method @Deprecated public void writePackedUInt64(long, long[]);
- method @Deprecated public void writeRepeatedBool(long, boolean);
- method @Deprecated public void writeRepeatedBytes(long, byte[]);
- method @Deprecated public void writeRepeatedDouble(long, double);
- method @Deprecated public void writeRepeatedEnum(long, int);
- method @Deprecated public void writeRepeatedFixed32(long, int);
- method @Deprecated public void writeRepeatedFixed64(long, long);
- method @Deprecated public void writeRepeatedFloat(long, float);
- method @Deprecated public void writeRepeatedInt32(long, int);
- method @Deprecated public void writeRepeatedInt64(long, long);
- method @Deprecated public void writeRepeatedObject(long, byte[]);
- method @Deprecated public void writeRepeatedSFixed32(long, int);
- method @Deprecated public void writeRepeatedSFixed64(long, long);
- method @Deprecated public void writeRepeatedSInt32(long, int);
- method @Deprecated public void writeRepeatedSInt64(long, long);
- method @Deprecated public void writeRepeatedString(long, String);
- method @Deprecated public void writeRepeatedUInt32(long, int);
- method @Deprecated public void writeRepeatedUInt64(long, long);
- method @Deprecated public void writeSFixed32(long, int);
- method @Deprecated public void writeSFixed64(long, long);
- method @Deprecated public void writeSInt32(long, int);
- method @Deprecated public void writeSInt64(long, long);
- method @Deprecated public void writeString(long, String);
- method public void writeTag(int, int);
- method @Deprecated public void writeUInt32(long, int);
- method @Deprecated public void writeUInt64(long, long);
- }
-
public class ProtoParseException extends java.lang.RuntimeException {
ctor public ProtoParseException(String);
}
- public abstract class ProtoStream {
- ctor public ProtoStream();
- method public static int convertObjectIdToOrdinal(int);
- method public static int getDepthFromToken(long);
- method public static String getFieldCountString(long);
- method public static String getFieldIdString(long);
- method public static String getFieldTypeString(long);
- method public static int getObjectIdFromToken(long);
- method public static int getOffsetFromToken(long);
- method public static boolean getRepeatedFromToken(long);
- method public static int getTagSizeFromToken(long);
- method public static String getWireTypeString(int);
- method public static long makeFieldId(int, long);
- method public static long makeToken(int, boolean, int, int, int);
- method public static String token2String(long);
- field public static final long FIELD_COUNT_MASK = 16492674416640L; // 0xf0000000000L
- field public static final long FIELD_COUNT_PACKED = 5497558138880L; // 0x50000000000L
- field public static final long FIELD_COUNT_REPEATED = 2199023255552L; // 0x20000000000L
- field public static final int FIELD_COUNT_SHIFT = 40; // 0x28
- field public static final long FIELD_COUNT_SINGLE = 1099511627776L; // 0x10000000000L
- field public static final long FIELD_COUNT_UNKNOWN = 0L; // 0x0L
- field public static final int FIELD_ID_MASK = -8; // 0xfffffff8
- field public static final int FIELD_ID_SHIFT = 3; // 0x3
- field public static final long FIELD_TYPE_BOOL = 34359738368L; // 0x800000000L
- field public static final long FIELD_TYPE_BYTES = 51539607552L; // 0xc00000000L
- field public static final long FIELD_TYPE_DOUBLE = 4294967296L; // 0x100000000L
- field public static final long FIELD_TYPE_ENUM = 60129542144L; // 0xe00000000L
- field public static final long FIELD_TYPE_FIXED32 = 30064771072L; // 0x700000000L
- field public static final long FIELD_TYPE_FIXED64 = 25769803776L; // 0x600000000L
- field public static final long FIELD_TYPE_FLOAT = 8589934592L; // 0x200000000L
- field public static final long FIELD_TYPE_INT32 = 21474836480L; // 0x500000000L
- field public static final long FIELD_TYPE_INT64 = 12884901888L; // 0x300000000L
- field public static final long FIELD_TYPE_MASK = 1095216660480L; // 0xff00000000L
- field public static final long FIELD_TYPE_MESSAGE = 47244640256L; // 0xb00000000L
- field protected static final String[] FIELD_TYPE_NAMES;
- field public static final long FIELD_TYPE_SFIXED32 = 64424509440L; // 0xf00000000L
- field public static final long FIELD_TYPE_SFIXED64 = 68719476736L; // 0x1000000000L
- field public static final int FIELD_TYPE_SHIFT = 32; // 0x20
- field public static final long FIELD_TYPE_SINT32 = 73014444032L; // 0x1100000000L
- field public static final long FIELD_TYPE_SINT64 = 77309411328L; // 0x1200000000L
- field public static final long FIELD_TYPE_STRING = 38654705664L; // 0x900000000L
- field public static final long FIELD_TYPE_UINT32 = 55834574848L; // 0xd00000000L
- field public static final long FIELD_TYPE_UINT64 = 17179869184L; // 0x400000000L
- field public static final long FIELD_TYPE_UNKNOWN = 0L; // 0x0L
- field public static final int WIRE_TYPE_END_GROUP = 4; // 0x4
- field public static final int WIRE_TYPE_FIXED32 = 5; // 0x5
- field public static final int WIRE_TYPE_FIXED64 = 1; // 0x1
- field public static final int WIRE_TYPE_LENGTH_DELIMITED = 2; // 0x2
- field public static final int WIRE_TYPE_MASK = 7; // 0x7
- field public static final int WIRE_TYPE_START_GROUP = 3; // 0x3
- field public static final int WIRE_TYPE_VARINT = 0; // 0x0
- }
-
public class WireTypeMismatchException extends android.util.proto.ProtoParseException {
ctor public WireTypeMismatchException(String);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 7d3cd28..6ff48ea 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -720,7 +720,18 @@
/** @hide Capture the device's display contents and/or audio */
@UnsupportedAppUsage
public static final int OP_PROJECT_MEDIA = 46;
- /** @hide Activate a VPN connection without user intervention. */
+
+ /**
+ * Start (without additional user intervention) a VPN connection, as used by {@link
+ * android.net.VpnService} along with as Platform VPN connections, as used by {@link
+ * android.net.VpnManager}
+ *
+ * <p>This appop is granted to apps that have already been given user consent to start
+ * VpnService based VPN connections. As this is a superset of OP_ACTIVATE_PLATFORM_VPN, this
+ * appop also allows the starting of Platform VPNs.
+ *
+ * @hide
+ */
@UnsupportedAppUsage
public static final int OP_ACTIVATE_VPN = 47;
/** @hide Access the WallpaperManagerAPI to write wallpapers. */
@@ -840,10 +851,21 @@
public static final int OP_READ_DEVICE_IDENTIFIERS = 89;
/** @hide Read location metadata from media */
public static final int OP_ACCESS_MEDIA_LOCATION = 90;
+ /**
+ * Start (without additional user intervention) a Platform VPN connection, as used by {@link
+ * android.net.VpnManager}
+ *
+ * <p>This appop is granted to apps that have already been given user consent to start Platform
+ * VPN connections. This appop is insufficient to start VpnService based VPNs (but the opposite
+ * is true).
+ *
+ * @hide
+ */
+ public static final int OP_ACTIVATE_PLATFORM_VPN = 91;
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 91;
+ public static final int _NUM_OP = 92;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1122,6 +1144,8 @@
public static final String OPSTR_ACCESS_ACCESSIBILITY = "android:access_accessibility";
/** @hide Read device identifiers */
public static final String OPSTR_READ_DEVICE_IDENTIFIERS = "android:read_device_identifiers";
+ /** @hide Start Platform VPN without user intervention */
+ public static final String OPSTR_ACTIVATE_PLATFORM_VPN = "android:activate_platform_vpn";
// Warning: If an permission is added here it also has to be added to
// com.android.packageinstaller.permission.utils.EventLogger
@@ -1285,6 +1309,7 @@
OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY
OP_READ_DEVICE_IDENTIFIERS, // READ_DEVICE_IDENTIFIERS
OP_ACCESS_MEDIA_LOCATION, // ACCESS_MEDIA_LOCATION
+ OP_ACTIVATE_PLATFORM_VPN, // ACTIVATE_PLATFORM_VPN
};
/**
@@ -1382,6 +1407,7 @@
OPSTR_ACCESS_ACCESSIBILITY,
OPSTR_READ_DEVICE_IDENTIFIERS,
OPSTR_ACCESS_MEDIA_LOCATION,
+ OPSTR_ACTIVATE_PLATFORM_VPN,
};
/**
@@ -1480,6 +1506,7 @@
"ACCESS_ACCESSIBILITY",
"READ_DEVICE_IDENTIFIERS",
"ACCESS_MEDIA_LOCATION",
+ "ACTIVATE_PLATFORM_VPN"
};
/**
@@ -1579,6 +1606,7 @@
null, // no permission for OP_ACCESS_ACCESSIBILITY
null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS
Manifest.permission.ACCESS_MEDIA_LOCATION,
+ null, // no permission for OP_ACTIVATE_PLATFORM_VPN
};
/**
@@ -1678,6 +1706,7 @@
null, // ACCESS_ACCESSIBILITY
null, // READ_DEVICE_IDENTIFIERS
null, // ACCESS_MEDIA_LOCATION
+ null, // ACTIVATE_PLATFORM_VPN
};
/**
@@ -1776,6 +1805,7 @@
false, // ACCESS_ACCESSIBILITY
false, // READ_DEVICE_IDENTIFIERS
false, // ACCESS_MEDIA_LOCATION
+ false, // ACTIVATE_PLATFORM_VPN
};
/**
@@ -1873,6 +1903,7 @@
AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY
AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS
AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION
+ AppOpsManager.MODE_IGNORED, // ACTIVATE_PLATFORM_VPN
};
/**
@@ -1974,6 +2005,7 @@
false, // ACCESS_ACCESSIBILITY
false, // READ_DEVICE_IDENTIFIERS
false, // ACCESS_MEDIA_LOCATION
+ false, // ACTIVATE_PLATFORM_VPN
};
/**
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 88976e1..81f6d28 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -98,6 +98,7 @@
import android.media.soundtrigger.SoundTriggerManager;
import android.media.tv.ITvInputManager;
import android.media.tv.TvInputManager;
+import android.net.ConnectivityDiagnosticsManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityThread;
import android.net.EthernetManager;
@@ -112,6 +113,7 @@
import android.net.NetworkWatchlistManager;
import android.net.TestNetworkManager;
import android.net.TetheringManager;
+import android.net.VpnManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
@@ -369,6 +371,27 @@
return new IpSecManager(ctx, service);
}});
+ registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class,
+ new CachedServiceFetcher<VpnManager>() {
+ @Override
+ public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+ IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ return new VpnManager(ctx, service);
+ }});
+
+ registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE,
+ ConnectivityDiagnosticsManager.class,
+ new CachedServiceFetcher<ConnectivityDiagnosticsManager>() {
+ @Override
+ public ConnectivityDiagnosticsManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ // ConnectivityDiagnosticsManager is backed by ConnectivityService
+ IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE);
+ IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
+ return new ConnectivityDiagnosticsManager(ctx, service);
+ }});
+
registerService(
Context.TEST_NETWORK_SERVICE,
TestNetworkManager.class,
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index 8993de0..ee2cc6d 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -297,7 +297,8 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -345,7 +346,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(@Nullable BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothA2dpSink service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 1869c6f..bd0a39c 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1222,6 +1222,7 @@
if (mService != null) {
return mService.factoryReset();
}
+ Log.e(TAG, "factoryReset(): IBluetooth Service is null");
SystemProperties.set("persist.bluetooth.factoryreset", "true");
} catch (RemoteException e) {
Log.e(TAG, "", e);
@@ -1239,7 +1240,7 @@
*/
@UnsupportedAppUsage
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public @NonNull ParcelUuid[] getUuids() {
+ public @Nullable ParcelUuid[] getUuids() {
if (getState() != STATE_ON) {
return null;
}
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 6de1ffb..fbda9e9 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -584,7 +584,8 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothHeadsetClient service =
getService();
@@ -633,7 +634,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHeadsetClient service =
getService();
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index c1233b8..26e3e27 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -416,7 +416,8 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -464,7 +465,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(@Nullable BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 4674706..1c62faa 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -340,7 +340,8 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -388,7 +389,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(@Nullable BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothMap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 0aa5aac..8d2aadd 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -271,7 +271,8 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -319,7 +320,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")");
final IBluetoothMapClient service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 024bb06..ec63fd0 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -30,6 +30,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.CloseGuard;
import android.util.Log;
import java.lang.annotation.Retention;
@@ -50,10 +51,11 @@
* @hide
*/
@SystemApi
-public final class BluetoothPan implements BluetoothProfile {
+public final class BluetoothPan implements BluetoothProfile, AutoCloseable {
private static final String TAG = "BluetoothPan";
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ private CloseGuard mCloseGuard;
/**
* Intent used to broadcast the change in connection state of the Pan
@@ -166,10 +168,15 @@
mAdapter = BluetoothAdapter.getDefaultAdapter();
mContext = context;
mProfileConnector.connect(context, listener);
+ mCloseGuard = new CloseGuard();
+ mCloseGuard.open("close");
}
- @UnsupportedAppUsage
- /*package*/ void close() {
+ /**
+ * Closes the connection to the service and unregisters callbacks
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
+ public void close() {
if (VDBG) log("close()");
mProfileConnector.disconnect();
}
@@ -178,8 +185,11 @@
return mProfileConnector.getService();
}
-
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
protected void finalize() {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
close();
}
@@ -316,6 +326,7 @@
* @hide
*/
@Override
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
final IBluetoothPan service = getService();
@@ -335,6 +346,7 @@
* {@inheritDoc}
*/
@Override
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
public int getConnectionState(@Nullable BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
final IBluetoothPan service = getService();
@@ -355,6 +367,7 @@
*
* @param value is whether to enable or disable bluetooth tethering
*/
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public void setBluetoothTethering(boolean value) {
String pkgName = mContext.getOpPackageName();
if (DBG) log("setBluetoothTethering(" + value + "), calling package:" + pkgName);
@@ -373,6 +386,7 @@
*
* @return true if tethering is on, false if not or some error occurred
*/
+ @RequiresPermission(Manifest.permission.BLUETOOTH)
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
final IBluetoothPan service = getService();
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index 9618ba0..9563c68 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -271,7 +271,8 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) {
log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
}
@@ -323,7 +324,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) {
log("getConnectionPolicy(" + device + ")");
}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 638e6de..7538df8 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -146,7 +146,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
int A2DP_SINK = 11;
/**
@@ -154,7 +154,7 @@
*
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
int AVRCP_CONTROLLER = 12;
/**
@@ -169,6 +169,7 @@
*
* @hide
*/
+ @SystemApi
int HEADSET_CLIENT = 16;
/**
@@ -176,6 +177,7 @@
*
* @hide
*/
+ @SystemApi
int PBAP_CLIENT = 17;
/**
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 8bf1b58..bfc3a4d 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -330,7 +330,8 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
+ public boolean setConnectionPolicy(BluetoothDevice device,
+ @ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
@@ -378,7 +379,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public int getConnectionPolicy(BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
final IBluetoothSap service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index cd4af96..472d956 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3296,6 +3296,7 @@
CONNECTIVITY_SERVICE,
//@hide: IP_MEMORY_STORE_SERVICE,
IPSEC_SERVICE,
+ VPN_MANAGEMENT_SERVICE,
TEST_NETWORK_SERVICE,
//@hide: UPDATE_LOCK_SERVICE,
//@hide: NETWORKMANAGEMENT_SERVICE,
@@ -3880,6 +3881,24 @@
public static final String IPSEC_SERVICE = "ipsec";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.VpnManager} to
+ * manage profiles for the platform built-in VPN.
+ *
+ * @see #getSystemService(String)
+ */
+ public static final String VPN_MANAGEMENT_SERVICE = "vpn_management";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a {@link
+ * android.net.ConnectivityDiagnosticsManager} for performing network connectivity diagnostics
+ * as well as receiving network connectivity information from the system.
+ *
+ * @see #getSystemService(String)
+ * @see android.net.ConnectivityDiagnosticsManager
+ */
+ public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.net.TestNetworkManager} for building TUNs and limited-use Networks
*
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9cba7aa..d859a3af 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3619,7 +3619,9 @@
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide
*/
- @UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @SystemApi
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0ae00fb..93b90b4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -545,9 +545,10 @@
public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
/**
- * Internal flag used to indicate that a package is a hidden system app.
+ * Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app.
* @hide
*/
+ @SystemApi
public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000;
/**
diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java
index ae3d794..0a76bed 100644
--- a/core/java/android/debug/AdbManager.java
+++ b/core/java/android/debug/AdbManager.java
@@ -16,15 +16,17 @@
package android.debug;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
+import android.os.RemoteException;
/**
- * This class allows the control of ADB-related functions. Currently only ADB over USB is
- * supported, and none of the API is public.
- *
+ * This class allows the control of ADB-related functions.
* @hide
*/
+@SystemApi
@SystemService(Context.ADB_SERVICE)
public class AdbManager {
private static final String TAG = "AdbManager";
@@ -39,4 +41,33 @@
mContext = context;
mService = service;
}
+
+ /**
+ * @return true if the device supports secure ADB over Wi-Fi.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
+ public boolean isAdbWifiSupported() {
+ try {
+ return mService.isAdbWifiSupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @return true if the device supports secure ADB over Wi-Fi and device pairing by
+ * QR code.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_DEBUGGING)
+ public boolean isAdbWifiQrSupported() {
+ try {
+ return mService.isAdbWifiQrSupported();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/core/java/android/debug/IAdbManager.aidl b/core/java/android/debug/IAdbManager.aidl
index 79e0794..c48fc07 100644
--- a/core/java/android/debug/IAdbManager.aidl
+++ b/core/java/android/debug/IAdbManager.aidl
@@ -41,4 +41,15 @@
* Clear all public keys installed for secure ADB debugging.
*/
void clearDebuggingKeys();
+
+ /**
+ * Returns true if device supports secure Adb over Wi-Fi.
+ */
+ boolean isAdbWifiSupported();
+
+ /**
+ * Returns true if device supports secure Adb over Wi-Fi and device pairing by
+ * QR code.
+ */
+ boolean isAdbWifiQrSupported();
}
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/NetworkMisc.aidl b/core/java/android/net/CaptivePortalData.aidl
similarity index 84%
copy from core/java/android/net/NetworkMisc.aidl
copy to core/java/android/net/CaptivePortalData.aidl
index c65583f..1d57ee7 100644
--- a/core/java/android/net/NetworkMisc.aidl
+++ b/core/java/android/net/CaptivePortalData.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 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.
@@ -16,4 +16,4 @@
package android.net;
-parcelable NetworkMisc;
+@JavaOnlyStableParcelable parcelable CaptivePortalData;
diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java
new file mode 100644
index 0000000..1357803
--- /dev/null
+++ b/core/java/android/net/CaptivePortalData.java
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 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.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Metadata sent by captive portals, see https://www.ietf.org/id/draft-ietf-capport-api-03.txt.
+ * @hide
+ */
+@SystemApi
+@TestApi
+public final class CaptivePortalData implements Parcelable {
+ private final long mRefreshTimeMillis;
+ @Nullable
+ private final Uri mUserPortalUrl;
+ @Nullable
+ private final Uri mVenueInfoUrl;
+ private final boolean mIsSessionExtendable;
+ private final long mByteLimit;
+ private final long mExpiryTimeMillis;
+ private final boolean mCaptive;
+
+ private CaptivePortalData(long refreshTimeMillis, Uri userPortalUrl, Uri venueInfoUrl,
+ boolean isSessionExtendable, long byteLimit, long expiryTimeMillis, boolean captive) {
+ mRefreshTimeMillis = refreshTimeMillis;
+ mUserPortalUrl = userPortalUrl;
+ mVenueInfoUrl = venueInfoUrl;
+ mIsSessionExtendable = isSessionExtendable;
+ mByteLimit = byteLimit;
+ mExpiryTimeMillis = expiryTimeMillis;
+ mCaptive = captive;
+ }
+
+ private CaptivePortalData(Parcel p) {
+ this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
+ p.readLong(), p.readLong(), p.readBoolean());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeLong(mRefreshTimeMillis);
+ dest.writeParcelable(mUserPortalUrl, 0);
+ dest.writeParcelable(mVenueInfoUrl, 0);
+ dest.writeBoolean(mIsSessionExtendable);
+ dest.writeLong(mByteLimit);
+ dest.writeLong(mExpiryTimeMillis);
+ dest.writeBoolean(mCaptive);
+ }
+
+ /**
+ * A builder to create new {@link CaptivePortalData}.
+ */
+ public static class Builder {
+ private long mRefreshTime;
+ private Uri mUserPortalUrl;
+ private Uri mVenueInfoUrl;
+ private boolean mIsSessionExtendable;
+ private long mBytesRemaining = -1;
+ private long mExpiryTime = -1;
+ private boolean mCaptive;
+
+ /**
+ * Create an empty builder.
+ */
+ public Builder() {}
+
+ /**
+ * Create a builder copying all data from existing {@link CaptivePortalData}.
+ */
+ public Builder(@Nullable CaptivePortalData data) {
+ if (data == null) return;
+ setRefreshTime(data.mRefreshTimeMillis)
+ .setUserPortalUrl(data.mUserPortalUrl)
+ .setVenueInfoUrl(data.mVenueInfoUrl)
+ .setSessionExtendable(data.mIsSessionExtendable)
+ .setBytesRemaining(data.mByteLimit)
+ .setExpiryTime(data.mExpiryTimeMillis)
+ .setCaptive(data.mCaptive);
+ }
+
+ /**
+ * Set the time at which data was last refreshed, as per {@link System#currentTimeMillis()}.
+ */
+ @NonNull
+ public Builder setRefreshTime(long refreshTime) {
+ mRefreshTime = refreshTime;
+ return this;
+ }
+
+ /**
+ * Set the URL to be used for users to login to the portal, if captive.
+ */
+ @NonNull
+ public Builder setUserPortalUrl(@Nullable Uri userPortalUrl) {
+ mUserPortalUrl = userPortalUrl;
+ return this;
+ }
+
+ /**
+ * Set the URL that can be used by users to view information about the network venue.
+ */
+ @NonNull
+ public Builder setVenueInfoUrl(@Nullable Uri venueInfoUrl) {
+ mVenueInfoUrl = venueInfoUrl;
+ return this;
+ }
+
+ /**
+ * Set whether the portal supports extending a user session on the portal URL page.
+ */
+ @NonNull
+ public Builder setSessionExtendable(boolean sessionExtendable) {
+ mIsSessionExtendable = sessionExtendable;
+ return this;
+ }
+
+ /**
+ * Set the number of bytes remaining on the network before the portal closes.
+ */
+ @NonNull
+ public Builder setBytesRemaining(long bytesRemaining) {
+ mBytesRemaining = bytesRemaining;
+ return this;
+ }
+
+ /**
+ * Set the time at the session will expire, as per {@link System#currentTimeMillis()}.
+ */
+ @NonNull
+ public Builder setExpiryTime(long expiryTime) {
+ mExpiryTime = expiryTime;
+ return this;
+ }
+
+ /**
+ * Set whether the network is captive (portal closed).
+ */
+ @NonNull
+ public Builder setCaptive(boolean captive) {
+ mCaptive = captive;
+ return this;
+ }
+
+ /**
+ * Create a new {@link CaptivePortalData}.
+ */
+ @NonNull
+ public CaptivePortalData build() {
+ return new CaptivePortalData(mRefreshTime, mUserPortalUrl, mVenueInfoUrl,
+ mIsSessionExtendable, mBytesRemaining, mExpiryTime, mCaptive);
+ }
+ }
+
+ /**
+ * Get the time at which data was last refreshed, as per {@link System#currentTimeMillis()}.
+ */
+ public long getRefreshTimeMillis() {
+ return mRefreshTimeMillis;
+ }
+
+ /**
+ * Get the URL to be used for users to login to the portal, or extend their session if
+ * {@link #isSessionExtendable()} is true.
+ */
+ @Nullable
+ public Uri getUserPortalUrl() {
+ return mUserPortalUrl;
+ }
+
+ /**
+ * Get the URL that can be used by users to view information about the network venue.
+ */
+ @Nullable
+ public Uri getVenueInfoUrl() {
+ return mVenueInfoUrl;
+ }
+
+ /**
+ * Indicates whether the user portal URL can be used to extend sessions, when the user is logged
+ * in and the session has a time or byte limit.
+ */
+ public boolean isSessionExtendable() {
+ return mIsSessionExtendable;
+ }
+
+ /**
+ * Get the remaining bytes on the captive portal session, at the time {@link CaptivePortalData}
+ * was refreshed. This may be different from the limit currently enforced by the portal.
+ * @return The byte limit, or -1 if not set.
+ */
+ public long getByteLimit() {
+ return mByteLimit;
+ }
+
+ /**
+ * Get the time at the session will expire, as per {@link System#currentTimeMillis()}.
+ * @return The expiry time, or -1 if unset.
+ */
+ public long getExpiryTimeMillis() {
+ return mExpiryTimeMillis;
+ }
+
+ /**
+ * Get whether the network is captive (portal closed).
+ */
+ public boolean isCaptive() {
+ return mCaptive;
+ }
+
+ @NonNull
+ public static final Creator<CaptivePortalData> CREATOR = new Creator<CaptivePortalData>() {
+ @Override
+ public CaptivePortalData createFromParcel(Parcel source) {
+ return new CaptivePortalData(source);
+ }
+
+ @Override
+ public CaptivePortalData[] newArray(int size) {
+ return new CaptivePortalData[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRefreshTimeMillis, mUserPortalUrl, mVenueInfoUrl,
+ mIsSessionExtendable, mByteLimit, mExpiryTimeMillis, mCaptive);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof CaptivePortalData)) return false;
+ final CaptivePortalData other = (CaptivePortalData) obj;
+ return mRefreshTimeMillis == other.mRefreshTimeMillis
+ && Objects.equals(mUserPortalUrl, other.mUserPortalUrl)
+ && Objects.equals(mVenueInfoUrl, other.mVenueInfoUrl)
+ && mIsSessionExtendable == other.mIsSessionExtendable
+ && mByteLimit == other.mByteLimit
+ && mExpiryTimeMillis == other.mExpiryTimeMillis
+ && mCaptive == other.mCaptive;
+ }
+
+ @Override
+ public String toString() {
+ return "CaptivePortalData {"
+ + "refreshTime: " + mRefreshTimeMillis
+ + ", userPortalUrl: " + mUserPortalUrl
+ + ", venueInfoUrl: " + mVenueInfoUrl
+ + ", isSessionExtendable: " + mIsSessionExtendable
+ + ", byteLimit: " + mByteLimit
+ + ", expiryTime: " + mExpiryTimeMillis
+ + ", captive: " + mCaptive
+ + "}";
+ }
+}
diff --git a/core/java/android/net/NetworkMisc.aidl b/core/java/android/net/ConnectivityDiagnosticsManager.aidl
similarity index 69%
copy from core/java/android/net/NetworkMisc.aidl
copy to core/java/android/net/ConnectivityDiagnosticsManager.aidl
index c65583f..82ba0ca 100644
--- a/core/java/android/net/NetworkMisc.aidl
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.aidl
@@ -1,11 +1,12 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
+/**
+ *
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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,
@@ -16,4 +17,5 @@
package android.net;
-parcelable NetworkMisc;
+parcelable ConnectivityDiagnosticsManager.ConnectivityReport;
+parcelable ConnectivityDiagnosticsManager.DataStallReport;
\ No newline at end of file
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 6afdb5e..4564813 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -18,10 +18,19 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StringDef;
+import android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.PersistableBundle;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -47,38 +56,169 @@
* </ul>
*/
public class ConnectivityDiagnosticsManager {
- public static final int DETECTION_METHOD_DNS_EVENTS = 1;
- public static final int DETECTION_METHOD_TCP_METRICS = 2;
+ private final Context mContext;
+ private final IConnectivityManager mService;
/** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(
- prefix = {"DETECTION_METHOD_"},
- value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
- public @interface DetectionMethod {}
+ public ConnectivityDiagnosticsManager(Context context, IConnectivityManager service) {
+ mContext = Preconditions.checkNotNull(context, "missing context");
+ mService = Preconditions.checkNotNull(service, "missing IConnectivityManager");
+ }
/** @hide */
- public ConnectivityDiagnosticsManager() {}
+ @VisibleForTesting
+ public static boolean persistableBundleEquals(
+ @Nullable PersistableBundle a, @Nullable PersistableBundle b) {
+ if (a == b) return true;
+ if (a == null || b == null) return false;
+ if (!Objects.equals(a.keySet(), b.keySet())) return false;
+ for (String key : a.keySet()) {
+ if (!Objects.equals(a.get(key), b.get(key))) return false;
+ }
+ return true;
+ }
/** Class that includes connectivity information for a specific Network at a specific time. */
- public static class ConnectivityReport {
+ public static final class ConnectivityReport implements Parcelable {
+ /**
+ * The overall status of the network is that it is invalid; it neither provides
+ * connectivity nor has been exempted from validation.
+ */
+ public static final int NETWORK_VALIDATION_RESULT_INVALID = 0;
+
+ /**
+ * The overall status of the network is that it is valid, this may be because it provides
+ * full Internet access (all probes succeeded), or because other properties of the network
+ * caused probes not to be run.
+ */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID
+ public static final int NETWORK_VALIDATION_RESULT_VALID = 1;
+
+ /**
+ * The overall status of the network is that it provides partial connectivity; some
+ * probed services succeeded but others failed.
+ */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL;
+ public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2;
+
+ /**
+ * Due to the properties of the network, validation was not performed.
+ */
+ public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"NETWORK_VALIDATION_RESULT_"},
+ value = {
+ NETWORK_VALIDATION_RESULT_INVALID,
+ NETWORK_VALIDATION_RESULT_VALID,
+ NETWORK_VALIDATION_RESULT_PARTIALLY_VALID,
+ NETWORK_VALIDATION_RESULT_SKIPPED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkValidationResult {}
+
+ /**
+ * The overall validation result for the Network being reported on.
+ *
+ * <p>The possible values for this key are:
+ * {@link #NETWORK_VALIDATION_RESULT_INVALID},
+ * {@link #NETWORK_VALIDATION_RESULT_VALID},
+ * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID},
+ * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}.
+ *
+ * @see android.net.NetworkCapabilities#CAPABILITY_VALIDATED
+ */
+ @NetworkValidationResult
+ public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult";
+
+ /** DNS probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS
+ public static final int NETWORK_PROBE_DNS = 0x04;
+
+ /** HTTP probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP
+ public static final int NETWORK_PROBE_HTTP = 0x08;
+
+ /** HTTPS probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS;
+ public static final int NETWORK_PROBE_HTTPS = 0x10;
+
+ /** Captive portal fallback probe. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_FALLBACK
+ public static final int NETWORK_PROBE_FALLBACK = 0x20;
+
+ /** Private DNS (DNS over TLS) probd. */
+ // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS
+ public static final int NETWORK_PROBE_PRIVATE_DNS = 0x40;
+
+ /** @hide */
+ @IntDef(
+ prefix = {"NETWORK_PROBE_"},
+ value = {
+ NETWORK_PROBE_DNS,
+ NETWORK_PROBE_HTTP,
+ NETWORK_PROBE_HTTPS,
+ NETWORK_PROBE_FALLBACK,
+ NETWORK_PROBE_PRIVATE_DNS
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface NetworkProbe {}
+
+ /**
+ * A bitmask of network validation probes that succeeded.
+ *
+ * <p>The possible bits values reported by this key are:
+ * {@link #NETWORK_PROBE_DNS},
+ * {@link #NETWORK_PROBE_HTTP},
+ * {@link #NETWORK_PROBE_HTTPS},
+ * {@link #NETWORK_PROBE_FALLBACK},
+ * {@link #NETWORK_PROBE_PRIVATE_DNS}.
+ */
+ @NetworkProbe
+ public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK =
+ "networkProbesSucceeded";
+
+ /**
+ * A bitmask of network validation probes that were attempted.
+ *
+ * <p>These probes may have failed or may be incomplete at the time of this report.
+ *
+ * <p>The possible bits values reported by this key are:
+ * {@link #NETWORK_PROBE_DNS},
+ * {@link #NETWORK_PROBE_HTTP},
+ * {@link #NETWORK_PROBE_HTTPS},
+ * {@link #NETWORK_PROBE_FALLBACK},
+ * {@link #NETWORK_PROBE_PRIVATE_DNS}.
+ */
+ @NetworkProbe
+ public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK =
+ "networkProbesAttemped";
+
+ /** @hide */
+ @StringDef(prefix = {"KEY_"}, value = {
+ KEY_NETWORK_VALIDATION_RESULT, KEY_NETWORK_PROBES_SUCCEEDED_BITMASK,
+ KEY_NETWORK_PROBES_ATTEMPTED_BITMASK})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ConnectivityReportBundleKeys {}
+
/** The Network for which this ConnectivityReport applied */
- @NonNull public final Network network;
+ @NonNull private final Network mNetwork;
/**
* The timestamp for the report. The timestamp is taken from {@link
* System#currentTimeMillis}.
*/
- public final long reportTimestamp;
+ private final long mReportTimestamp;
/** LinkProperties available on the Network at the reported timestamp */
- @NonNull public final LinkProperties linkProperties;
+ @NonNull private final LinkProperties mLinkProperties;
/** NetworkCapabilities available on the Network at the reported timestamp */
- @NonNull public final NetworkCapabilities networkCapabilities;
+ @NonNull private final NetworkCapabilities mNetworkCapabilities;
/** PersistableBundle that may contain additional info about the report */
- @NonNull public final PersistableBundle additionalInfo;
+ @NonNull private final PersistableBundle mAdditionalInfo;
/**
* Constructor for ConnectivityReport.
@@ -101,30 +241,191 @@
@NonNull LinkProperties linkProperties,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull PersistableBundle additionalInfo) {
- this.network = network;
- this.reportTimestamp = reportTimestamp;
- this.linkProperties = linkProperties;
- this.networkCapabilities = networkCapabilities;
- this.additionalInfo = additionalInfo;
+ mNetwork = network;
+ mReportTimestamp = reportTimestamp;
+ mLinkProperties = linkProperties;
+ mNetworkCapabilities = networkCapabilities;
+ mAdditionalInfo = additionalInfo;
}
+
+ /**
+ * Returns the Network for this ConnectivityReport.
+ *
+ * @return The Network for which this ConnectivityReport applied
+ */
+ @NonNull
+ public Network getNetwork() {
+ return mNetwork;
+ }
+
+ /**
+ * Returns the epoch timestamp (milliseconds) for when this report was taken.
+ *
+ * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
+ */
+ public long getReportTimestamp() {
+ return mReportTimestamp;
+ }
+
+ /**
+ * Returns the LinkProperties available when this report was taken.
+ *
+ * @return LinkProperties available on the Network at the reported timestamp
+ */
+ @NonNull
+ public LinkProperties getLinkProperties() {
+ return new LinkProperties(mLinkProperties);
+ }
+
+ /**
+ * Returns the NetworkCapabilities when this report was taken.
+ *
+ * @return NetworkCapabilities available on the Network at the reported timestamp
+ */
+ @NonNull
+ public NetworkCapabilities getNetworkCapabilities() {
+ return new NetworkCapabilities(mNetworkCapabilities);
+ }
+
+ /**
+ * Returns a PersistableBundle with additional info for this report.
+ *
+ * @return PersistableBundle that may contain additional info about the report
+ */
+ @NonNull
+ public PersistableBundle getAdditionalInfo() {
+ return new PersistableBundle(mAdditionalInfo);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ConnectivityReport)) return false;
+ final ConnectivityReport that = (ConnectivityReport) o;
+
+ // PersistableBundle is optimized to avoid unparcelling data unless fields are
+ // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
+ // {@link PersistableBundle#kindofEquals}.
+ return mReportTimestamp == that.mReportTimestamp
+ && mNetwork.equals(that.mNetwork)
+ && mLinkProperties.equals(that.mLinkProperties)
+ && mNetworkCapabilities.equals(that.mNetworkCapabilities)
+ && persistableBundleEquals(mAdditionalInfo, that.mAdditionalInfo);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mNetwork,
+ mReportTimestamp,
+ mLinkProperties,
+ mNetworkCapabilities,
+ mAdditionalInfo);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mNetwork, flags);
+ dest.writeLong(mReportTimestamp);
+ dest.writeParcelable(mLinkProperties, flags);
+ dest.writeParcelable(mNetworkCapabilities, flags);
+ dest.writeParcelable(mAdditionalInfo, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<ConnectivityReport> CREATOR =
+ new Creator<ConnectivityReport>() {
+ public ConnectivityReport createFromParcel(Parcel in) {
+ return new ConnectivityReport(
+ in.readParcelable(null),
+ in.readLong(),
+ in.readParcelable(null),
+ in.readParcelable(null),
+ in.readParcelable(null));
+ }
+
+ public ConnectivityReport[] newArray(int size) {
+ return new ConnectivityReport[size];
+ }
+ };
}
/** Class that includes information for a suspected data stall on a specific Network */
- public static class DataStallReport {
+ public static final class DataStallReport implements Parcelable {
+ public static final int DETECTION_METHOD_DNS_EVENTS = 1;
+ public static final int DETECTION_METHOD_TCP_METRICS = 2;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"DETECTION_METHOD_"},
+ value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS})
+ public @interface DetectionMethod {}
+
+ /**
+ * This key represents the period in milliseconds over which other included TCP metrics
+ * were measured.
+ *
+ * <p>This key will be included if the data stall detection method is
+ * {@link #DETECTION_METHOD_TCP_METRICS}.
+ *
+ * <p>This value is an int.
+ */
+ public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS =
+ "tcpMetricsCollectionPeriodMillis";
+
+ /**
+ * This key represents the fail rate of TCP packets when the suspected data stall was
+ * detected.
+ *
+ * <p>This key will be included if the data stall detection method is
+ * {@link #DETECTION_METHOD_TCP_METRICS}.
+ *
+ * <p>This value is an int percentage between 0 and 100.
+ */
+ public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate";
+
+ /**
+ * This key represents the consecutive number of DNS timeouts that have occurred.
+ *
+ * <p>The consecutive count will be reset any time a DNS response is received.
+ *
+ * <p>This key will be included if the data stall detection method is
+ * {@link #DETECTION_METHOD_DNS_EVENTS}.
+ *
+ * <p>This value is an int.
+ */
+ public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = {"KEY_"}, value = {
+ KEY_TCP_PACKET_FAIL_RATE,
+ KEY_DNS_CONSECUTIVE_TIMEOUTS
+ })
+ public @interface DataStallReportBundleKeys {}
+
/** The Network for which this DataStallReport applied */
- @NonNull public final Network network;
+ @NonNull private final Network mNetwork;
/**
* The timestamp for the report. The timestamp is taken from {@link
* System#currentTimeMillis}.
*/
- public final long reportTimestamp;
+ private long mReportTimestamp;
/** The detection method used to identify the suspected data stall */
- @DetectionMethod public final int detectionMethod;
+ @DetectionMethod private final int mDetectionMethod;
/** PersistableBundle that may contain additional information on the suspected data stall */
- @NonNull public final PersistableBundle stallDetails;
+ @NonNull private final PersistableBundle mStallDetails;
/**
* Constructor for DataStallReport.
@@ -143,11 +444,104 @@
long reportTimestamp,
@DetectionMethod int detectionMethod,
@NonNull PersistableBundle stallDetails) {
- this.network = network;
- this.reportTimestamp = reportTimestamp;
- this.detectionMethod = detectionMethod;
- this.stallDetails = stallDetails;
+ mNetwork = network;
+ mReportTimestamp = reportTimestamp;
+ mDetectionMethod = detectionMethod;
+ mStallDetails = stallDetails;
}
+
+ /**
+ * Returns the Network for this DataStallReport.
+ *
+ * @return The Network for which this DataStallReport applied
+ */
+ @NonNull
+ public Network getNetwork() {
+ return mNetwork;
+ }
+
+ /**
+ * Returns the epoch timestamp (milliseconds) for when this report was taken.
+ *
+ * @return The timestamp for the report. Taken from {@link System#currentTimeMillis}.
+ */
+ public long getReportTimestamp() {
+ return mReportTimestamp;
+ }
+
+ /**
+ * Returns the detection method used to identify this suspected data stall.
+ *
+ * @return The detection method used to identify the suspected data stall
+ */
+ public int getDetectionMethod() {
+ return mDetectionMethod;
+ }
+
+ /**
+ * Returns a PersistableBundle with additional info for this report.
+ *
+ * <p>Gets a bundle with details about the suspected data stall including information
+ * specific to the monitoring method that detected the data stall.
+ *
+ * @return PersistableBundle that may contain additional information on the suspected data
+ * stall
+ */
+ @NonNull
+ public PersistableBundle getStallDetails() {
+ return new PersistableBundle(mStallDetails);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DataStallReport)) return false;
+ final DataStallReport that = (DataStallReport) o;
+
+ // PersistableBundle is optimized to avoid unparcelling data unless fields are
+ // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over
+ // {@link PersistableBundle#kindofEquals}.
+ return mReportTimestamp == that.mReportTimestamp
+ && mDetectionMethod == that.mDetectionMethod
+ && mNetwork.equals(that.mNetwork)
+ && persistableBundleEquals(mStallDetails, that.mStallDetails);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mNetwork, mReportTimestamp, mDetectionMethod, mStallDetails);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mNetwork, flags);
+ dest.writeLong(mReportTimestamp);
+ dest.writeInt(mDetectionMethod);
+ dest.writeParcelable(mStallDetails, flags);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<DataStallReport> CREATOR =
+ new Creator<DataStallReport>() {
+ public DataStallReport createFromParcel(Parcel in) {
+ return new DataStallReport(
+ in.readParcelable(null),
+ in.readLong(),
+ in.readInt(),
+ in.readParcelable(null));
+ }
+
+ public DataStallReport[] newArray(int size) {
+ return new DataStallReport[size];
+ }
+ };
}
/**
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index e8740c8..11c1a9c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3173,8 +3173,8 @@
*/
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
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);
+ NetworkCapabilities nc, int score, NetworkAgentConfig config) {
+ return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
}
/**
@@ -3184,9 +3184,10 @@
*/
@RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
- NetworkCapabilities nc, int score, NetworkMisc misc, int providerId) {
+ NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
+
try {
- return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc, providerId);
+ return mService.registerNetworkAgent(messenger, ni, lp, nc, score, config, providerId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3aee4d5..186196bd 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -20,9 +20,9 @@
import android.net.ConnectionInfo;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
@@ -153,7 +153,8 @@
void declareNetworkRequestUnfulfillable(in NetworkRequest request);
Network registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
- in NetworkCapabilities nc, int score, in NetworkMisc misc, in int factorySerialNumber);
+ in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+ in int factorySerialNumber);
NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
new file mode 100644
index 0000000..42b4da1
--- /dev/null
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) 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.net;
+
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import static com.android.internal.util.Preconditions.checkStringNotEmpty;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.security.Credentials;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * The Ikev2VpnProfile is a configuration for the platform setup of IKEv2/IPsec VPNs.
+ *
+ * <p>Together with VpnManager, this allows apps to provision IKEv2/IPsec VPNs that do not require
+ * the VPN app to constantly run in the background.
+ *
+ * @see VpnManager
+ * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296 - Internet Key
+ * Exchange, Version 2 (IKEv2)</a>
+ */
+public final class Ikev2VpnProfile extends PlatformVpnProfile {
+ private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
+ private static final String EMPTY_CERT = "";
+
+ @NonNull private final String mServerAddr;
+ @NonNull private final String mUserIdentity;
+
+ // PSK authentication
+ @Nullable private final byte[] mPresharedKey;
+
+ // Username/Password, RSA authentication
+ @Nullable private final X509Certificate mServerRootCaCert;
+
+ // Username/Password authentication
+ @Nullable private final String mUsername;
+ @Nullable private final String mPassword;
+
+ // RSA Certificate authentication
+ @Nullable private final PrivateKey mRsaPrivateKey;
+ @Nullable private final X509Certificate mUserCert;
+
+ @Nullable private final ProxyInfo mProxyInfo;
+ @NonNull private final List<String> mAllowedAlgorithms;
+ private final boolean mIsBypassable; // Defaults in builder
+ private final boolean mIsMetered; // Defaults in builder
+ private final int mMaxMtu; // Defaults in builder
+
+ private Ikev2VpnProfile(
+ int type,
+ @NonNull String serverAddr,
+ @NonNull String userIdentity,
+ @Nullable byte[] presharedKey,
+ @Nullable X509Certificate serverRootCaCert,
+ @Nullable String username,
+ @Nullable String password,
+ @Nullable PrivateKey rsaPrivateKey,
+ @Nullable X509Certificate userCert,
+ @Nullable ProxyInfo proxyInfo,
+ @NonNull List<String> allowedAlgorithms,
+ boolean isBypassable,
+ boolean isMetered,
+ int maxMtu) {
+ super(type);
+
+ checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address");
+ checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
+ checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms");
+
+ mServerAddr = serverAddr;
+ mUserIdentity = userIdentity;
+ mPresharedKey =
+ presharedKey == null ? null : Arrays.copyOf(presharedKey, presharedKey.length);
+ mServerRootCaCert = serverRootCaCert;
+ mUsername = username;
+ mPassword = password;
+ mRsaPrivateKey = rsaPrivateKey;
+ mUserCert = userCert;
+ mProxyInfo = new ProxyInfo(proxyInfo);
+
+ // UnmodifiableList doesn't make a defensive copy by default.
+ mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms));
+
+ mIsBypassable = isBypassable;
+ mIsMetered = isMetered;
+ mMaxMtu = maxMtu;
+
+ validate();
+ }
+
+ private void validate() {
+ // Server Address not validated except to check an address was provided. This allows for
+ // dual-stack servers and hostname based addresses.
+ checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address");
+ checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity");
+
+ // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
+ // networks, the VPN must provide a link fulfilling the stricter of the two conditions
+ // (at least that of the IPv6 MTU).
+ if (mMaxMtu < LinkProperties.MIN_MTU_V6) {
+ throw new IllegalArgumentException(
+ "Max MTU must be at least" + LinkProperties.MIN_MTU_V6);
+ }
+
+ switch (mType) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username");
+ checkNotNull(mPassword, MISSING_PARAM_MSG_TMPL, "Password");
+
+ if (mServerRootCaCert != null) checkCert(mServerRootCaCert);
+
+ break;
+ case TYPE_IKEV2_IPSEC_PSK:
+ checkNotNull(mPresharedKey, MISSING_PARAM_MSG_TMPL, "Preshared Key");
+ break;
+ case TYPE_IKEV2_IPSEC_RSA:
+ checkNotNull(mUserCert, MISSING_PARAM_MSG_TMPL, "User cert");
+ checkNotNull(mRsaPrivateKey, MISSING_PARAM_MSG_TMPL, "RSA Private key");
+
+ checkCert(mUserCert);
+ if (mServerRootCaCert != null) checkCert(mServerRootCaCert);
+
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid auth method set");
+ }
+
+ VpnProfile.validateAllowedAlgorithms(mAllowedAlgorithms);
+ }
+
+ /** Retrieves the server address string. */
+ @NonNull
+ public String getServerAddr() {
+ return mServerAddr;
+ }
+
+ /** Retrieves the user identity. */
+ @NonNull
+ public String getUserIdentity() {
+ return mUserIdentity;
+ }
+
+ /**
+ * Retrieves the pre-shared key.
+ *
+ * <p>May be null if the profile is not using Pre-shared key authentication.
+ */
+ @Nullable
+ public byte[] getPresharedKey() {
+ return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length);
+ }
+
+ /**
+ * Retrieves the certificate for the server's root CA.
+ *
+ * <p>May be null if the profile is not using RSA Digital Signature Authentication or
+ * Username/Password authentication
+ */
+ @Nullable
+ public X509Certificate getServerRootCaCert() {
+ return mServerRootCaCert;
+ }
+
+ /**
+ * Retrieves the username.
+ *
+ * <p>May be null if the profile is not using Username/Password authentication
+ */
+ @Nullable
+ public String getUsername() {
+ return mUsername;
+ }
+
+ /**
+ * Retrieves the password.
+ *
+ * <p>May be null if the profile is not using Username/Password authentication
+ */
+ @Nullable
+ public String getPassword() {
+ return mPassword;
+ }
+
+ /**
+ * Retrieves the RSA private key.
+ *
+ * <p>May be null if the profile is not using RSA Digital Signature authentication
+ */
+ @Nullable
+ public PrivateKey getRsaPrivateKey() {
+ return mRsaPrivateKey;
+ }
+
+ /** Retrieves the user certificate, if any was set. */
+ @Nullable
+ public X509Certificate getUserCert() {
+ return mUserCert;
+ }
+
+ /** Retrieves the proxy information if any was set */
+ @Nullable
+ public ProxyInfo getProxyInfo() {
+ return mProxyInfo;
+ }
+
+ /** Returns all the algorithms allowed by this VPN profile. */
+ @NonNull
+ public List<String> getAllowedAlgorithms() {
+ return mAllowedAlgorithms;
+ }
+
+ /** Returns whether or not the VPN profile should be bypassable. */
+ public boolean isBypassable() {
+ return mIsBypassable;
+ }
+
+ /** Returns whether or not the VPN profile should be always considered metered. */
+ public boolean isMetered() {
+ return mIsMetered;
+ }
+
+ /** Retrieves the maximum MTU set for this VPN profile. */
+ public int getMaxMtu() {
+ return mMaxMtu;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ mType,
+ mServerAddr,
+ mUserIdentity,
+ Arrays.hashCode(mPresharedKey),
+ mServerRootCaCert,
+ mUsername,
+ mPassword,
+ mRsaPrivateKey,
+ mUserCert,
+ mProxyInfo,
+ mAllowedAlgorithms,
+ mIsBypassable,
+ mIsMetered,
+ mMaxMtu);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Ikev2VpnProfile)) {
+ return false;
+ }
+
+ final Ikev2VpnProfile other = (Ikev2VpnProfile) obj;
+ return mType == other.mType
+ && Objects.equals(mServerAddr, other.mServerAddr)
+ && Objects.equals(mUserIdentity, other.mUserIdentity)
+ && Arrays.equals(mPresharedKey, other.mPresharedKey)
+ && Objects.equals(mServerRootCaCert, other.mServerRootCaCert)
+ && Objects.equals(mUsername, other.mUsername)
+ && Objects.equals(mPassword, other.mPassword)
+ && Objects.equals(mRsaPrivateKey, other.mRsaPrivateKey)
+ && Objects.equals(mUserCert, other.mUserCert)
+ && Objects.equals(mProxyInfo, other.mProxyInfo)
+ && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
+ && mIsBypassable == other.mIsBypassable
+ && mIsMetered == other.mIsMetered
+ && mMaxMtu == other.mMaxMtu;
+ }
+
+ /**
+ * Builds a VpnProfile instance for internal use, based on the stored IKEv2/IPsec parameters.
+ *
+ * <p>Redundant authentication information (from previous calls to other setAuth* methods) will
+ * be discarded.
+ *
+ * @hide
+ */
+ @NonNull
+ public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException {
+ final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */);
+ profile.type = mType;
+ profile.server = mServerAddr;
+ profile.ipsecIdentifier = mUserIdentity;
+ profile.proxy = mProxyInfo;
+ profile.setAllowedAlgorithms(mAllowedAlgorithms);
+ profile.isBypassable = mIsBypassable;
+ profile.isMetered = mIsMetered;
+ profile.maxMtu = mMaxMtu;
+ profile.areAuthParamsInline = true;
+ profile.saveLogin = true;
+
+ switch (mType) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ profile.username = mUsername;
+ profile.password = mPassword;
+ profile.ipsecCaCert =
+ mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
+ break;
+ case TYPE_IKEV2_IPSEC_PSK:
+ profile.ipsecSecret = encodeForIpsecSecret(mPresharedKey);
+ break;
+ case TYPE_IKEV2_IPSEC_RSA:
+ profile.ipsecUserCert = certificateToPemString(mUserCert);
+ profile.ipsecSecret = encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
+ profile.ipsecCaCert =
+ mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid auth method set");
+ }
+
+ return profile;
+ }
+
+ /**
+ * Constructs a Ikev2VpnProfile from an internal-use VpnProfile instance.
+ *
+ * <p>Redundant authentication information (not related to profile type) will be discarded.
+ *
+ * @hide
+ */
+ @NonNull
+ public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
+ throws IOException, GeneralSecurityException {
+ final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
+ builder.setProxy(profile.proxy);
+ builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
+ builder.setBypassable(profile.isBypassable);
+ builder.setMetered(profile.isMetered);
+ builder.setMaxMtu(profile.maxMtu);
+
+ switch (profile.type) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ builder.setAuthUsernamePassword(
+ profile.username,
+ profile.password,
+ certificateFromPemString(profile.ipsecCaCert));
+ break;
+ case TYPE_IKEV2_IPSEC_PSK:
+ builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
+ break;
+ case TYPE_IKEV2_IPSEC_RSA:
+ final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
+ final PrivateKey key = getPrivateKey(profile.ipsecSecret);
+ final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
+ builder.setAuthDigitalSignature(userCert, key, serverRootCa);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid auth method set");
+ }
+
+ return builder.build();
+ }
+
+ /**
+ * Converts a X509 Certificate to a PEM-formatted string.
+ *
+ * <p>Must be public due to runtime-package restrictions.
+ *
+ * @hide
+ */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static String certificateToPemString(@Nullable X509Certificate cert)
+ throws IOException, CertificateEncodingException {
+ if (cert == null) {
+ return EMPTY_CERT;
+ }
+
+ // Credentials.convertToPem outputs ASCII bytes.
+ return new String(Credentials.convertToPem(cert), StandardCharsets.US_ASCII);
+ }
+
+ /**
+ * Decodes the provided Certificate(s).
+ *
+ * <p>Will use the first one if the certStr encodes more than one certificate.
+ */
+ @Nullable
+ private static X509Certificate certificateFromPemString(@Nullable String certStr)
+ throws CertificateException {
+ if (certStr == null || EMPTY_CERT.equals(certStr)) {
+ return null;
+ }
+
+ try {
+ final List<X509Certificate> certs =
+ Credentials.convertFromPem(certStr.getBytes(StandardCharsets.US_ASCII));
+ return certs.isEmpty() ? null : certs.get(0);
+ } catch (IOException e) {
+ throw new CertificateException(e);
+ }
+ }
+
+ /** @hide */
+ @NonNull
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static String encodeForIpsecSecret(@NonNull byte[] secret) {
+ checkNotNull(secret, MISSING_PARAM_MSG_TMPL, "secret");
+
+ return Base64.getEncoder().encodeToString(secret);
+ }
+
+ @NonNull
+ private static byte[] decodeFromIpsecSecret(@NonNull String encoded) {
+ checkNotNull(encoded, MISSING_PARAM_MSG_TMPL, "encoded");
+
+ return Base64.getDecoder().decode(encoded);
+ }
+
+ @NonNull
+ private static PrivateKey getPrivateKey(@NonNull String keyStr)
+ throws InvalidKeySpecException, NoSuchAlgorithmException {
+ final PKCS8EncodedKeySpec privateKeySpec =
+ new PKCS8EncodedKeySpec(decodeFromIpsecSecret(keyStr));
+ final KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+ return keyFactory.generatePrivate(privateKeySpec);
+ }
+
+ private static void checkCert(@NonNull X509Certificate cert) {
+ try {
+ certificateToPemString(cert);
+ } catch (GeneralSecurityException | IOException e) {
+ throw new IllegalArgumentException("Certificate could not be encoded");
+ }
+ }
+
+ private static @NonNull <T> T checkNotNull(
+ final T reference, final String messageTemplate, final Object... messageArgs) {
+ return Objects.requireNonNull(reference, String.format(messageTemplate, messageArgs));
+ }
+
+ /** A incremental builder for IKEv2 VPN profiles */
+ public static final class Builder {
+ private int mType = -1;
+ @NonNull private final String mServerAddr;
+ @NonNull private final String mUserIdentity;
+
+ // PSK authentication
+ @Nullable private byte[] mPresharedKey;
+
+ // Username/Password, RSA authentication
+ @Nullable private X509Certificate mServerRootCaCert;
+
+ // Username/Password authentication
+ @Nullable private String mUsername;
+ @Nullable private String mPassword;
+
+ // RSA Certificate authentication
+ @Nullable private PrivateKey mRsaPrivateKey;
+ @Nullable private X509Certificate mUserCert;
+
+ @Nullable private ProxyInfo mProxyInfo;
+ @NonNull private List<String> mAllowedAlgorithms = new ArrayList<>();
+ private boolean mIsBypassable = false;
+ private boolean mIsMetered = true;
+ private int mMaxMtu = 1360;
+
+ /**
+ * Creates a new builder with the basic parameters of an IKEv2/IPsec VPN.
+ *
+ * @param serverAddr the server that the VPN should connect to
+ * @param identity the identity string to be used for IKEv2 authentication
+ */
+ public Builder(@NonNull String serverAddr, @NonNull String identity) {
+ checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "serverAddr");
+ checkNotNull(identity, MISSING_PARAM_MSG_TMPL, "identity");
+
+ mServerAddr = serverAddr;
+ mUserIdentity = identity;
+ }
+
+ private void resetAuthParams() {
+ mPresharedKey = null;
+ mServerRootCaCert = null;
+ mUsername = null;
+ mPassword = null;
+ mRsaPrivateKey = null;
+ mUserCert = null;
+ }
+
+ /**
+ * Set the IKEv2 authentication to use the provided username/password.
+ *
+ * <p>Setting this will configure IKEv2 authentication using EAP-MSCHAPv2. Only one
+ * authentication method may be set. This method will overwrite any previously set
+ * authentication method.
+ *
+ * @param user the username to be used for EAP-MSCHAPv2 authentication
+ * @param pass the password to be used for EAP-MSCHAPv2 authentication
+ * @param serverRootCa the root certificate to be used for verifying the identity of the
+ * server
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @throws IllegalArgumentException if any of the certificates were invalid or of an
+ * unrecognized format
+ */
+ @NonNull
+ public Builder setAuthUsernamePassword(
+ @NonNull String user,
+ @NonNull String pass,
+ @Nullable X509Certificate serverRootCa) {
+ checkNotNull(user, MISSING_PARAM_MSG_TMPL, "user");
+ checkNotNull(pass, MISSING_PARAM_MSG_TMPL, "pass");
+
+ // Test to make sure all auth params can be encoded safely.
+ if (serverRootCa != null) checkCert(serverRootCa);
+
+ resetAuthParams();
+ mUsername = user;
+ mPassword = pass;
+ mServerRootCaCert = serverRootCa;
+ mType = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+ return this;
+ }
+
+ /**
+ * Set the IKEv2 authentication to use Digital Signature Authentication with the given key.
+ *
+ * <p>Setting this will configure IKEv2 authentication using a Digital Signature scheme.
+ * Only one authentication method may be set. This method will overwrite any previously set
+ * authentication method.
+ *
+ * @param userCert the username to be used for RSA Digital signiture authentication
+ * @param key the PrivateKey instance associated with the user ceritificate, used for
+ * constructing the signature
+ * @param serverRootCa the root certificate to be used for verifying the identity of the
+ * server
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @throws IllegalArgumentException if any of the certificates were invalid or of an
+ * unrecognized format
+ */
+ @NonNull
+ public Builder setAuthDigitalSignature(
+ @NonNull X509Certificate userCert,
+ @NonNull PrivateKey key,
+ @Nullable X509Certificate serverRootCa) {
+ checkNotNull(userCert, MISSING_PARAM_MSG_TMPL, "userCert");
+ checkNotNull(key, MISSING_PARAM_MSG_TMPL, "key");
+
+ // Test to make sure all auth params can be encoded safely.
+ checkCert(userCert);
+ if (serverRootCa != null) checkCert(serverRootCa);
+
+ resetAuthParams();
+ mUserCert = userCert;
+ mRsaPrivateKey = key;
+ mServerRootCaCert = serverRootCa;
+ mType = VpnProfile.TYPE_IKEV2_IPSEC_RSA;
+ return this;
+ }
+
+ /**
+ * Set the IKEv2 authentication to use Preshared keys.
+ *
+ * <p>Setting this will configure IKEv2 authentication using a Preshared Key. Only one
+ * authentication method may be set. This method will overwrite any previously set
+ * authentication method.
+ *
+ * @param psk the key to be used for Pre-Shared Key authentication
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ */
+ @NonNull
+ public Builder setAuthPsk(@NonNull byte[] psk) {
+ checkNotNull(psk, MISSING_PARAM_MSG_TMPL, "psk");
+
+ resetAuthParams();
+ mPresharedKey = psk;
+ mType = VpnProfile.TYPE_IKEV2_IPSEC_PSK;
+ return this;
+ }
+
+ /**
+ * Sets whether apps can bypass this VPN connection.
+ *
+ * <p>By default, all traffic from apps are forwarded through the VPN interface and it is
+ * not possible for unprivileged apps to side-step the VPN. If a VPN is set to bypassable,
+ * apps may use methods such as {@link Network#getSocketFactory} or {@link
+ * Network#openConnection} to instead send/receive directly over the underlying network or
+ * any other network they have permissions for.
+ *
+ * @param isBypassable Whether or not the VPN should be considered bypassable. Defaults to
+ * {@code false}.
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ */
+ @NonNull
+ public Builder setBypassable(boolean isBypassable) {
+ mIsBypassable = isBypassable;
+ return this;
+ }
+
+ /**
+ * Sets a proxy for the VPN network.
+ *
+ * <p>Note that this proxy is only a recommendation and it may be ignored by apps.
+ *
+ * @param proxy the ProxyInfo to be set for the VPN network
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ */
+ @NonNull
+ public Builder setProxy(@Nullable ProxyInfo proxy) {
+ mProxyInfo = proxy;
+ return this;
+ }
+
+ /**
+ * Set the upper bound of the maximum transmission unit (MTU) of the VPN interface.
+ *
+ * <p>If it is not set, a safe value will be used. Additionally, the actual link MTU will be
+ * dynamically calculated/updated based on the underlying link's mtu.
+ *
+ * @param mtu the MTU (in bytes) of the VPN interface
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @throws IllegalArgumentException if the value is not at least the minimum IPv6 MTU (1280)
+ */
+ @NonNull
+ public Builder setMaxMtu(int mtu) {
+ // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6
+ // networks, the VPN must provide a link fulfilling the stricter of the two conditions
+ // (at least that of the IPv6 MTU).
+ if (mtu < LinkProperties.MIN_MTU_V6) {
+ throw new IllegalArgumentException(
+ "Max MTU must be at least " + LinkProperties.MIN_MTU_V6);
+ }
+ mMaxMtu = mtu;
+ return this;
+ }
+
+ /**
+ * Marks the VPN network as metered.
+ *
+ * <p>A VPN network is classified as metered when the user is sensitive to heavy data usage
+ * due to monetary costs and/or data limitations. In such cases, you should set this to
+ * {@code true} so that apps on the system can avoid doing large data transfers. Otherwise,
+ * set this to {@code false}. Doing so would cause VPN network to inherit its meteredness
+ * from the underlying network.
+ *
+ * @param isMetered {@code true} if the VPN network should be treated as metered regardless
+ * of underlying network meteredness. Defaults to {@code true}.
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @see NetworkCapabilities.NET_CAPABILITY_NOT_METERED
+ */
+ @NonNull
+ public Builder setMetered(boolean isMetered) {
+ mIsMetered = isMetered;
+ return this;
+ }
+
+ /**
+ * Sets the allowable set of IPsec algorithms
+ *
+ * <p>A list of allowed IPsec algorithms as defined in {@link IpSecAlgorithm}
+ *
+ * @param algorithmNames the list of supported IPsec algorithms
+ * @return this {@link Builder} object to facilitate chaining of method calls
+ * @see IpSecAlgorithm
+ */
+ @NonNull
+ public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) {
+ checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames");
+ VpnProfile.validateAllowedAlgorithms(algorithmNames);
+
+ mAllowedAlgorithms = algorithmNames;
+ return this;
+ }
+
+ /**
+ * Validates, builds and provisions the VpnProfile.
+ *
+ * @throws IllegalArgumentException if any of the required keys or values were invalid
+ */
+ @NonNull
+ public Ikev2VpnProfile build() {
+ return new Ikev2VpnProfile(
+ mType,
+ mServerAddr,
+ mUserIdentity,
+ mPresharedKey,
+ mServerRootCaCert,
+ mUsername,
+ mPassword,
+ mRsaPrivateKey,
+ mUserCert,
+ mProxyInfo,
+ mAllowedAlgorithms,
+ mIsBypassable,
+ mIsMetered,
+ mMaxMtu);
+ }
+ }
+}
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 2792c56..d25ee0e 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -70,9 +70,18 @@
private String mTcpBufferSizes;
private IpPrefix mNat64Prefix;
private boolean mWakeOnLanSupported;
+ private Uri mCaptivePortalApiUrl;
+ private CaptivePortalData mCaptivePortalData;
+
+ /**
+ * Indicates whether parceling should preserve fields that are set based on permissions of
+ * the process receiving the {@link LinkProperties}.
+ */
+ private final transient boolean mParcelSensitiveFields;
private static final int MIN_MTU = 68;
- private static final int MIN_MTU_V6 = 1280;
+ /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */
+ static final int MIN_MTU_V6 = 1280;
private static final int MAX_MTU = 10000;
private static final int INET6_ADDR_LENGTH = 16;
@@ -174,6 +183,7 @@
* Constructs a new {@code LinkProperties} with default values.
*/
public LinkProperties() {
+ mParcelSensitiveFields = false;
}
/**
@@ -182,26 +192,32 @@
@SystemApi
@TestApi
public LinkProperties(@Nullable LinkProperties source) {
- if (source != null) {
- mIfaceName = source.mIfaceName;
- mLinkAddresses.addAll(source.mLinkAddresses);
- mDnses.addAll(source.mDnses);
- mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
- mUsePrivateDns = source.mUsePrivateDns;
- mPrivateDnsServerName = source.mPrivateDnsServerName;
- mPcscfs.addAll(source.mPcscfs);
- mDomains = source.mDomains;
- mRoutes.addAll(source.mRoutes);
- mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
- for (LinkProperties l: source.mStackedLinks.values()) {
- addStackedLink(l);
- }
- setMtu(source.mMtu);
- setDhcpServerAddress(source.getDhcpServerAddress());
- mTcpBufferSizes = source.mTcpBufferSizes;
- mNat64Prefix = source.mNat64Prefix;
- mWakeOnLanSupported = source.mWakeOnLanSupported;
+ this(source, false /* parcelSensitiveFields */);
+ }
+
+ private LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) {
+ mParcelSensitiveFields = parcelSensitiveFields;
+ if (source == null) return;
+ mIfaceName = source.mIfaceName;
+ mLinkAddresses.addAll(source.mLinkAddresses);
+ mDnses.addAll(source.mDnses);
+ mValidatedPrivateDnses.addAll(source.mValidatedPrivateDnses);
+ mUsePrivateDns = source.mUsePrivateDns;
+ mPrivateDnsServerName = source.mPrivateDnsServerName;
+ mPcscfs.addAll(source.mPcscfs);
+ mDomains = source.mDomains;
+ mRoutes.addAll(source.mRoutes);
+ mHttpProxy = (source.mHttpProxy == null) ? null : new ProxyInfo(source.mHttpProxy);
+ for (LinkProperties l: source.mStackedLinks.values()) {
+ addStackedLink(l);
}
+ setMtu(source.mMtu);
+ setDhcpServerAddress(source.getDhcpServerAddress());
+ mTcpBufferSizes = source.mTcpBufferSizes;
+ mNat64Prefix = source.mNat64Prefix;
+ mWakeOnLanSupported = source.mWakeOnLanSupported;
+ mCaptivePortalApiUrl = source.mCaptivePortalApiUrl;
+ mCaptivePortalData = source.mCaptivePortalData;
}
/**
@@ -860,6 +876,11 @@
* Clears this object to its initial state.
*/
public void clear() {
+ if (mParcelSensitiveFields) {
+ throw new UnsupportedOperationException(
+ "Cannot clear LinkProperties when parcelSensitiveFields is set");
+ }
+
mIfaceName = null;
mLinkAddresses.clear();
mDnses.clear();
@@ -875,6 +896,8 @@
mTcpBufferSizes = null;
mNat64Prefix = null;
mWakeOnLanSupported = false;
+ mCaptivePortalApiUrl = null;
+ mCaptivePortalData = null;
}
/**
@@ -945,6 +968,14 @@
resultJoiner.add(mDhcpServerAddress.toString());
}
+ if (mCaptivePortalApiUrl != null) {
+ resultJoiner.add("CaptivePortalApiUrl: " + mCaptivePortalApiUrl);
+ }
+
+ if (mCaptivePortalData != null) {
+ resultJoiner.add("CaptivePortalData: " + mCaptivePortalData);
+ }
+
if (mTcpBufferSizes != null) {
resultJoiner.add("TcpBufferSizes:");
resultJoiner.add(mTcpBufferSizes);
@@ -1479,6 +1510,28 @@
}
/**
+ * Compares this {@code LinkProperties}'s CaptivePortalApiUrl against the target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalCaptivePortalApiUrl(LinkProperties target) {
+ return Objects.equals(mCaptivePortalApiUrl, target.mCaptivePortalApiUrl);
+ }
+
+ /**
+ * Compares this {@code LinkProperties}'s CaptivePortalData against the target.
+ *
+ * @param target LinkProperties to compare.
+ * @return {@code true} if both are identical, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isIdenticalCaptivePortalData(LinkProperties target) {
+ return Objects.equals(mCaptivePortalData, target.mCaptivePortalData);
+ }
+
+ /**
* Set whether the network interface supports WakeOnLAN
*
* @param supported WakeOnLAN supported value
@@ -1499,6 +1552,73 @@
}
/**
+ * Set the URL of the captive portal API endpoint to get more information about the network.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void setCaptivePortalApiUrl(@Nullable Uri url) {
+ mCaptivePortalApiUrl = url;
+ }
+
+ /**
+ * Get the URL of the captive portal API endpoint to get more information about the network.
+ *
+ * <p>This is null unless the application has
+ * {@link android.Manifest.permission.NETWORK_SETTINGS} or
+ * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions, and the network provided
+ * the URL.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @Nullable
+ public Uri getCaptivePortalApiUrl() {
+ return mCaptivePortalApiUrl;
+ }
+
+ /**
+ * Set the CaptivePortalData obtained from the captive portal API (RFC7710bis).
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public void setCaptivePortalData(@Nullable CaptivePortalData data) {
+ mCaptivePortalData = data;
+ }
+
+ /**
+ * Get the CaptivePortalData obtained from the captive portal API (RFC7710bis).
+ *
+ * <p>This is null unless the application has
+ * {@link android.Manifest.permission.NETWORK_SETTINGS} or
+ * {@link NetworkStack#PERMISSION_MAINLINE_NETWORK_STACK} permissions.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @Nullable
+ public CaptivePortalData getCaptivePortalData() {
+ return mCaptivePortalData;
+ }
+
+ /**
+ * Create a copy of this {@link LinkProperties} that will preserve fields that were set
+ * based on the permissions of the process that received this {@link LinkProperties}.
+ *
+ * <p>By default {@link LinkProperties} does not preserve such fields during parceling, as
+ * they should not be shared outside of the process that receives them without appropriate
+ * checks.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @NonNull
+ public LinkProperties makeSensitiveFieldsParcelingCopy() {
+ return new LinkProperties(this, true /* parcelSensitiveFields */);
+ }
+
+ /**
* Compares this {@code LinkProperties} instance against the target
* LinkProperties in {@code obj}. Two LinkPropertieses are equal if
* all their fields are equal in values.
@@ -1537,7 +1657,9 @@
&& isIdenticalMtu(target)
&& isIdenticalTcpBufferSizes(target)
&& isIdenticalNat64Prefix(target)
- && isIdenticalWakeOnLan(target);
+ && isIdenticalWakeOnLan(target)
+ && isIdenticalCaptivePortalApiUrl(target)
+ && isIdenticalCaptivePortalData(target);
}
/**
@@ -1655,7 +1777,8 @@
+ mPcscfs.size() * 67
+ ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode())
+ Objects.hash(mNat64Prefix)
- + (mWakeOnLanSupported ? 71 : 0);
+ + (mWakeOnLanSupported ? 71 : 0)
+ + Objects.hash(mCaptivePortalApiUrl, mCaptivePortalData);
}
/**
@@ -1694,6 +1817,8 @@
dest.writeList(stackedLinks);
dest.writeBoolean(mWakeOnLanSupported);
+ dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalApiUrl : null, 0);
+ dest.writeParcelable(mParcelSensitiveFields ? mCaptivePortalData : null, 0);
}
private static void writeAddresses(@NonNull Parcel dest, @NonNull List<InetAddress> list) {
@@ -1785,6 +1910,9 @@
netProp.addStackedLink(stackedLink);
}
netProp.setWakeOnLanSupported(in.readBoolean());
+
+ netProp.setCaptivePortalApiUrl(in.readParcelable(null));
+ netProp.setCaptivePortalData(in.readParcelable(null));
return netProp;
}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index a5f7d53..7316dfa 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -43,9 +43,10 @@
*
* @hide
*/
-public abstract class NetworkAgent extends Handler {
+public abstract class NetworkAgent {
public final Network network;
+ private final Handler mHandler;
private volatile AsyncChannel mAsyncChannel;
private final String LOG_TAG;
private static final boolean DBG = true;
@@ -220,8 +221,8 @@
this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
}
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
- NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc) {
- this(looper, context, logTag, ni, nc, lp, score, misc, NetworkProvider.ID_NONE);
+ NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
+ this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
}
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
@@ -230,9 +231,9 @@
}
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
- NetworkCapabilities nc, LinkProperties lp, int score, NetworkMisc misc,
+ NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
int providerId) {
- super(looper);
+ mHandler = new NetworkAgentHandler(looper);
LOG_TAG = logTag;
mContext = context;
mProviderId = providerId;
@@ -243,116 +244,124 @@
if (VDBG) log("Registering NetworkAgent");
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
Context.CONNECTIVITY_SERVICE);
- network = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
- new LinkProperties(lp), new NetworkCapabilities(nc), score, misc, providerId);
+ network = cm.registerNetworkAgent(new Messenger(mHandler), new NetworkInfo(ni),
+ new LinkProperties(lp), new NetworkCapabilities(nc), score, config,
+ providerId);
}
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
- if (mAsyncChannel != null) {
- log("Received new connection while already connected!");
- } else {
- if (VDBG) log("NetworkAgent fully connected");
- AsyncChannel ac = new AsyncChannel();
- ac.connected(null, this, msg.replyTo);
- ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
- AsyncChannel.STATUS_SUCCESSFUL);
- synchronized (mPreConnectedQueue) {
- mAsyncChannel = ac;
- for (Message m : mPreConnectedQueue) {
- ac.sendMessage(m);
- }
- mPreConnectedQueue.clear();
- }
- }
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
- if (VDBG) log("CMD_CHANNEL_DISCONNECT");
- if (mAsyncChannel != null) mAsyncChannel.disconnect();
- break;
- }
- case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
- if (DBG) log("NetworkAgent channel lost");
- // let the client know CS is done with us.
- unwanted();
- synchronized (mPreConnectedQueue) {
- mAsyncChannel = null;
- }
- break;
- }
- case CMD_SUSPECT_BAD: {
- log("Unhandled Message " + msg);
- break;
- }
- case CMD_REQUEST_BANDWIDTH_UPDATE: {
- long currentTimeMs = System.currentTimeMillis();
- if (VDBG) {
- log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
- }
- if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
- mPollLceScheduled = false;
- if (mPollLcePending.getAndSet(true) == false) {
- pollLceData();
- }
- } else {
- // deliver the request at a later time rather than discard it completely.
- if (!mPollLceScheduled) {
- long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS -
- currentTimeMs + 1;
- mPollLceScheduled = sendEmptyMessageDelayed(
- CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
- }
- }
- break;
- }
- case CMD_REPORT_NETWORK_STATUS: {
- String redirectUrl = ((Bundle)msg.obj).getString(REDIRECT_URL_KEY);
- if (VDBG) {
- log("CMD_REPORT_NETWORK_STATUS(" +
- (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ") + redirectUrl);
- }
- networkStatus(msg.arg1, redirectUrl);
- break;
- }
- case CMD_SAVE_ACCEPT_UNVALIDATED: {
- saveAcceptUnvalidated(msg.arg1 != 0);
- break;
- }
- case CMD_START_SOCKET_KEEPALIVE: {
- startSocketKeepalive(msg);
- break;
- }
- case CMD_STOP_SOCKET_KEEPALIVE: {
- stopSocketKeepalive(msg);
- break;
- }
+ private class NetworkAgentHandler extends Handler {
+ NetworkAgentHandler(Looper looper) {
+ super(looper);
+ }
- case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
- ArrayList<Integer> thresholds =
- ((Bundle) msg.obj).getIntegerArrayList("thresholds");
- // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
- // rather than convert to int[].
- int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
- for (int i = 0; i < intThresholds.length; i++) {
- intThresholds[i] = thresholds.get(i);
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
+ if (mAsyncChannel != null) {
+ log("Received new connection while already connected!");
+ } else {
+ if (VDBG) log("NetworkAgent fully connected");
+ AsyncChannel ac = new AsyncChannel();
+ ac.connected(null, this, msg.replyTo);
+ ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED,
+ AsyncChannel.STATUS_SUCCESSFUL);
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = ac;
+ for (Message m : mPreConnectedQueue) {
+ ac.sendMessage(m);
+ }
+ mPreConnectedQueue.clear();
+ }
+ }
+ break;
}
- setSignalStrengthThresholds(intThresholds);
- break;
- }
- case CMD_PREVENT_AUTOMATIC_RECONNECT: {
- preventAutomaticReconnect();
- break;
- }
- case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
- addKeepalivePacketFilter(msg);
- break;
- }
- case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
- removeKeepalivePacketFilter(msg);
- break;
+ case AsyncChannel.CMD_CHANNEL_DISCONNECT: {
+ if (VDBG) log("CMD_CHANNEL_DISCONNECT");
+ if (mAsyncChannel != null) mAsyncChannel.disconnect();
+ break;
+ }
+ case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+ if (DBG) log("NetworkAgent channel lost");
+ // let the client know CS is done with us.
+ unwanted();
+ synchronized (mPreConnectedQueue) {
+ mAsyncChannel = null;
+ }
+ break;
+ }
+ case CMD_SUSPECT_BAD: {
+ log("Unhandled Message " + msg);
+ break;
+ }
+ case CMD_REQUEST_BANDWIDTH_UPDATE: {
+ long currentTimeMs = System.currentTimeMillis();
+ if (VDBG) {
+ log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
+ }
+ if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+ mPollLceScheduled = false;
+ if (!mPollLcePending.getAndSet(true)) {
+ pollLceData();
+ }
+ } else {
+ // deliver the request at a later time rather than discard it completely.
+ if (!mPollLceScheduled) {
+ long waitTime = mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS
+ - currentTimeMs + 1;
+ mPollLceScheduled = sendEmptyMessageDelayed(
+ CMD_REQUEST_BANDWIDTH_UPDATE, waitTime);
+ }
+ }
+ break;
+ }
+ case CMD_REPORT_NETWORK_STATUS: {
+ String redirectUrl = ((Bundle) msg.obj).getString(REDIRECT_URL_KEY);
+ if (VDBG) {
+ log("CMD_REPORT_NETWORK_STATUS("
+ + (msg.arg1 == VALID_NETWORK ? "VALID, " : "INVALID, ")
+ + redirectUrl);
+ }
+ networkStatus(msg.arg1, redirectUrl);
+ break;
+ }
+ case CMD_SAVE_ACCEPT_UNVALIDATED: {
+ saveAcceptUnvalidated(msg.arg1 != 0);
+ break;
+ }
+ case CMD_START_SOCKET_KEEPALIVE: {
+ startSocketKeepalive(msg);
+ break;
+ }
+ case CMD_STOP_SOCKET_KEEPALIVE: {
+ stopSocketKeepalive(msg);
+ break;
+ }
+
+ case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
+ ArrayList<Integer> thresholds =
+ ((Bundle) msg.obj).getIntegerArrayList("thresholds");
+ // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
+ // rather than convert to int[].
+ int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
+ for (int i = 0; i < intThresholds.length; i++) {
+ intThresholds[i] = thresholds.get(i);
+ }
+ setSignalStrengthThresholds(intThresholds);
+ break;
+ }
+ case CMD_PREVENT_AUTOMATIC_RECONNECT: {
+ preventAutomaticReconnect();
+ break;
+ }
+ case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
+ addKeepalivePacketFilter(msg);
+ break;
+ }
+ case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
+ removeKeepalivePacketFilter(msg);
+ break;
+ }
}
}
}
diff --git a/core/java/android/net/NetworkMisc.aidl b/core/java/android/net/NetworkAgentConfig.aidl
similarity index 95%
rename from core/java/android/net/NetworkMisc.aidl
rename to core/java/android/net/NetworkAgentConfig.aidl
index c65583f..cb70bdd 100644
--- a/core/java/android/net/NetworkMisc.aidl
+++ b/core/java/android/net/NetworkAgentConfig.aidl
@@ -16,4 +16,4 @@
package android.net;
-parcelable NetworkMisc;
+parcelable NetworkAgentConfig;
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
new file mode 100644
index 0000000..abc6b67
--- /dev/null
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Allows a network transport to provide the system with policy and configuration information about
+ * a particular network when registering a {@link NetworkAgent}. This information cannot change once
+ * the agent is registered.
+ *
+ * @hide
+ */
+@SystemApi
+public final class NetworkAgentConfig implements Parcelable {
+
+ /**
+ * If the {@link Network} is a VPN, whether apps are allowed to bypass the
+ * VPN. This is set by a {@link VpnService} and used by
+ * {@link ConnectivityManager} when creating a VPN.
+ *
+ * @hide
+ */
+ public boolean allowBypass;
+
+ /**
+ * Set if the network was manually/explicitly connected to by the user either from settings
+ * or a 3rd party app. For example, turning on cell data is not explicit but tapping on a wifi
+ * ap in the wifi settings to trigger a connection is explicit. A 3rd party app asking to
+ * connect to a particular access point is also explicit, though this may change in the future
+ * as we want apps to use the multinetwork apis.
+ *
+ * @hide
+ */
+ public boolean explicitlySelected;
+
+ /**
+ * Set if the user desires to use this network even if it is unvalidated. This field has meaning
+ * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
+ * appropriate value based on previous user choice.
+ *
+ * @hide
+ */
+ public boolean acceptUnvalidated;
+
+ /**
+ * Whether the user explicitly set that this network should be validated even if presence of
+ * only partial internet connectivity.
+ *
+ * @hide
+ */
+ public boolean acceptPartialConnectivity;
+
+ /**
+ * Set to avoid surfacing the "Sign in to network" notification.
+ * if carrier receivers/apps are registered to handle the carrier-specific provisioning
+ * procedure, a carrier specific provisioning notification will be placed.
+ * only one notification should be displayed. This field is set based on
+ * which notification should be used for provisioning.
+ *
+ * @hide
+ */
+ public boolean provisioningNotificationDisabled;
+
+ /**
+ *
+ * @return whether the sign in to network notification is enabled by this configuration.
+ */
+ public boolean isProvisioningNotificationEnabled() {
+ return !provisioningNotificationDisabled;
+ }
+
+ /**
+ * For mobile networks, this is the subscriber ID (such as IMSI).
+ *
+ * @hide
+ */
+ public String subscriberId;
+
+ /**
+ * @return the subscriber ID, or null if none.
+ */
+ @Nullable
+ public String getSubscriberId() {
+ return subscriberId;
+ }
+
+ /**
+ * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
+ * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
+ *
+ * @hide
+ */
+ public boolean skip464xlat;
+
+ /**
+ * @return whether NAT64 prefix detection is enabled.
+ */
+ public boolean isNat64DetectionEnabled() {
+ return !skip464xlat;
+ }
+
+ /**
+ * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
+ * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
+ *
+ * @hide
+ */
+ public boolean hasShownBroken;
+
+ /** @hide */
+ public NetworkAgentConfig() {
+ }
+
+ /** @hide */
+ public NetworkAgentConfig(@Nullable NetworkAgentConfig nac) {
+ if (nac != null) {
+ allowBypass = nac.allowBypass;
+ explicitlySelected = nac.explicitlySelected;
+ acceptUnvalidated = nac.acceptUnvalidated;
+ subscriberId = nac.subscriberId;
+ provisioningNotificationDisabled = nac.provisioningNotificationDisabled;
+ skip464xlat = nac.skip464xlat;
+ }
+ }
+
+ /**
+ * Builder class to facilitate constructing {@link NetworkAgentConfig} objects.
+ */
+ public static class Builder {
+ private final NetworkAgentConfig mConfig = new NetworkAgentConfig();
+
+ /**
+ * Sets the subscriber ID for this network.
+ *
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setSubscriberId(@Nullable String subscriberId) {
+ mConfig.subscriberId = subscriberId;
+ return this;
+ }
+
+ /**
+ * Disables active detection of NAT64 (e.g., via RFC 7050 DNS lookups). Used to save power
+ * and reduce idle traffic on networks that are known to be IPv6-only without a NAT64.
+ *
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder disableNat64Detection() {
+ mConfig.skip464xlat = true;
+ return this;
+ }
+
+ /**
+ * Disables the "Sign in to network" notification. Used if the network transport will
+ * perform its own carrier-specific provisioning procedure.
+ *
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder disableProvisioningNotification() {
+ mConfig.provisioningNotificationDisabled = true;
+ return this;
+ }
+
+ /**
+ * Returns the constructed {@link NetworkAgentConfig} object.
+ */
+ @NonNull
+ public NetworkAgentConfig build() {
+ return mConfig;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(allowBypass ? 1 : 0);
+ out.writeInt(explicitlySelected ? 1 : 0);
+ out.writeInt(acceptUnvalidated ? 1 : 0);
+ out.writeString(subscriberId);
+ out.writeInt(provisioningNotificationDisabled ? 1 : 0);
+ out.writeInt(skip464xlat ? 1 : 0);
+ }
+
+ public static final @NonNull Creator<NetworkAgentConfig> CREATOR =
+ new Creator<NetworkAgentConfig>() {
+ @Override
+ public NetworkAgentConfig createFromParcel(Parcel in) {
+ NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
+ networkAgentConfig.allowBypass = in.readInt() != 0;
+ networkAgentConfig.explicitlySelected = in.readInt() != 0;
+ networkAgentConfig.acceptUnvalidated = in.readInt() != 0;
+ networkAgentConfig.subscriberId = in.readString();
+ networkAgentConfig.provisioningNotificationDisabled = in.readInt() != 0;
+ networkAgentConfig.skip464xlat = in.readInt() != 0;
+ return networkAgentConfig;
+ }
+
+ @Override
+ public NetworkAgentConfig[] newArray(int size) {
+ return new NetworkAgentConfig[size];
+ }
+ };
+}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index f43385d..6207661 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -35,6 +35,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
@@ -83,6 +86,7 @@
mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
mUids = null;
mEstablishingVpnAppUid = INVALID_UID;
+ mAdministratorUids.clear();
mSSID = null;
mPrivateDnsBroken = false;
}
@@ -101,6 +105,7 @@
mSignalStrength = nc.mSignalStrength;
setUids(nc.mUids); // Will make the defensive copy
mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid;
+ setAdministratorUids(nc.mAdministratorUids);
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
mSSID = nc.mSSID;
mPrivateDnsBroken = nc.mPrivateDnsBroken;
@@ -833,6 +838,56 @@
}
/**
+ * UIDs of packages that are administrators of this network, or empty if none.
+ *
+ * <p>This field tracks the UIDs of packages that have permission to manage this network.
+ *
+ * <p>Network owners will also be listed as administrators.
+ *
+ * <p>For NetworkCapability instances being sent from the System Server, this value MUST be
+ * empty unless the destination is 1) the System Server, or 2) Telephony. In either case, the
+ * receiving entity must have the ACCESS_FINE_LOCATION permission and target R+.
+ */
+ private final List<Integer> mAdministratorUids = new ArrayList<>();
+
+ /**
+ * Sets the list of UIDs that are administrators of this network.
+ *
+ * <p>UIDs included in administratorUids gain administrator privileges over this Network.
+ * Examples of UIDs that should be included in administratorUids are:
+ * <ul>
+ * <li>Carrier apps with privileges for the relevant subscription
+ * <li>Active VPN apps
+ * <li>Other application groups with a particular Network-related role
+ * </ul>
+ *
+ * <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator.
+ *
+ * <p>An app is granted owner privileges over Networks that it supplies. Owner privileges
+ * implicitly include administrator privileges.
+ *
+ * @param administratorUids the UIDs to be set as administrators of this Network.
+ * @hide
+ */
+ @SystemApi
+ public void setAdministratorUids(@NonNull final List<Integer> administratorUids) {
+ mAdministratorUids.clear();
+ mAdministratorUids.addAll(administratorUids);
+ }
+
+ /**
+ * Retrieves the list of UIDs that are administrators of this Network.
+ *
+ * @return the List of UIDs that are administrators of this Network
+ * @hide
+ */
+ @NonNull
+ @SystemApi
+ public List<Integer> getAdministratorUids() {
+ return Collections.unmodifiableList(mAdministratorUids);
+ }
+
+ /**
* Value indicating that link bandwidth is unspecified.
* @hide
*/
@@ -1283,6 +1338,7 @@
* Gets the SSID of this network, or null if none or unknown.
* @hide
*/
+ @SystemApi
public @Nullable String getSSID() {
return mSSID;
}
@@ -1470,6 +1526,7 @@
public int describeContents() {
return 0;
}
+
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mNetworkCapabilities);
@@ -1483,6 +1540,7 @@
dest.writeArraySet(mUids);
dest.writeString(mSSID);
dest.writeBoolean(mPrivateDnsBroken);
+ dest.writeList(mAdministratorUids);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1503,6 +1561,7 @@
null /* ClassLoader, null for default */);
netCap.mSSID = in.readString();
netCap.mPrivateDnsBroken = in.readBoolean();
+ netCap.setAdministratorUids(in.readArrayList(null));
return netCap;
}
@Override
@@ -1556,6 +1615,10 @@
sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid);
}
+ if (!mAdministratorUids.isEmpty()) {
+ sb.append(" AdministratorUids: ").append(mAdministratorUids);
+ }
+
if (null != mSSID) {
sb.append(" SSID: ").append(mSSID);
}
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/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
deleted file mode 100644
index 4ad52d5..0000000
--- a/core/java/android/net/NetworkMisc.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A grab-bag of information (metadata, policies, properties, etc) about a
- * {@link Network}. Since this contains PII, it should not be sent outside the
- * system.
- *
- * @hide
- */
-public class NetworkMisc implements Parcelable {
-
- /**
- * If the {@link Network} is a VPN, whether apps are allowed to bypass the
- * VPN. This is set by a {@link VpnService} and used by
- * {@link ConnectivityManager} when creating a VPN.
- */
- public boolean allowBypass;
-
- /**
- * Set if the network was manually/explicitly connected to by the user either from settings
- * or a 3rd party app. For example, turning on cell data is not explicit but tapping on a wifi
- * ap in the wifi settings to trigger a connection is explicit. A 3rd party app asking to
- * connect to a particular access point is also explicit, though this may change in the future
- * as we want apps to use the multinetwork apis.
- */
- public boolean explicitlySelected;
-
- /**
- * Set if the user desires to use this network even if it is unvalidated. This field has meaning
- * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
- * appropriate value based on previous user choice.
- */
- public boolean acceptUnvalidated;
-
- /**
- * Whether the user explicitly set that this network should be validated even if presence of
- * only partial internet connectivity.
- */
- public boolean acceptPartialConnectivity;
-
- /**
- * Set to avoid surfacing the "Sign in to network" notification.
- * if carrier receivers/apps are registered to handle the carrier-specific provisioning
- * procedure, a carrier specific provisioning notification will be placed.
- * only one notification should be displayed. This field is set based on
- * which notification should be used for provisioning.
- */
- public boolean provisioningNotificationDisabled;
-
- /**
- * For mobile networks, this is the subscriber ID (such as IMSI).
- */
- public String subscriberId;
-
- /**
- * Set to skip 464xlat. This means the device will treat the network as IPv6-only and
- * will not attempt to detect a NAT64 via RFC 7050 DNS lookups.
- */
- public boolean skip464xlat;
-
- /**
- * Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
- * Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
- */
- public boolean hasShownBroken;
-
- public NetworkMisc() {
- }
-
- public NetworkMisc(NetworkMisc nm) {
- if (nm != null) {
- allowBypass = nm.allowBypass;
- explicitlySelected = nm.explicitlySelected;
- acceptUnvalidated = nm.acceptUnvalidated;
- subscriberId = nm.subscriberId;
- provisioningNotificationDisabled = nm.provisioningNotificationDisabled;
- skip464xlat = nm.skip464xlat;
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel out, int flags) {
- out.writeInt(allowBypass ? 1 : 0);
- out.writeInt(explicitlySelected ? 1 : 0);
- out.writeInt(acceptUnvalidated ? 1 : 0);
- out.writeString(subscriberId);
- out.writeInt(provisioningNotificationDisabled ? 1 : 0);
- out.writeInt(skip464xlat ? 1 : 0);
- }
-
- public static final @android.annotation.NonNull Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
- @Override
- public NetworkMisc createFromParcel(Parcel in) {
- NetworkMisc networkMisc = new NetworkMisc();
- networkMisc.allowBypass = in.readInt() != 0;
- networkMisc.explicitlySelected = in.readInt() != 0;
- networkMisc.acceptUnvalidated = in.readInt() != 0;
- networkMisc.subscriberId = in.readString();
- networkMisc.provisioningNotificationDisabled = in.readInt() != 0;
- networkMisc.skip464xlat = in.readInt() != 0;
- return networkMisc;
- }
-
- @Override
- public NetworkMisc[] newArray(int size) {
- return new NetworkMisc[size];
- }
- };
-}
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/PlatformVpnProfile.java b/core/java/android/net/PlatformVpnProfile.java
new file mode 100644
index 0000000..fbae637
--- /dev/null
+++ b/core/java/android/net/PlatformVpnProfile.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 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.net;
+
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA;
+import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import com.android.internal.net.VpnProfile;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.GeneralSecurityException;
+
+/**
+ * PlatformVpnProfile represents a configuration for a platform-based VPN implementation.
+ *
+ * <p>Platform-based VPNs allow VPN applications to provide configuration and authentication options
+ * to leverage the Android OS' implementations of well-defined control plane (authentication, key
+ * negotiation) and data plane (per-packet encryption) protocols to simplify the creation of VPN
+ * tunnels. In contrast, {@link VpnService} based VPNs must implement both the control and data
+ * planes on a per-app basis.
+ *
+ * @see Ikev2VpnProfile
+ */
+public abstract class PlatformVpnProfile {
+ /**
+ * Alias to platform VPN related types from VpnProfile, for API use.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ TYPE_IKEV2_IPSEC_USER_PASS,
+ TYPE_IKEV2_IPSEC_PSK,
+ TYPE_IKEV2_IPSEC_RSA,
+ })
+ public static @interface PlatformVpnType {}
+
+ public static final int TYPE_IKEV2_IPSEC_USER_PASS = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+ public static final int TYPE_IKEV2_IPSEC_PSK = VpnProfile.TYPE_IKEV2_IPSEC_PSK;
+ public static final int TYPE_IKEV2_IPSEC_RSA = VpnProfile.TYPE_IKEV2_IPSEC_RSA;
+
+ /** @hide */
+ @PlatformVpnType protected final int mType;
+
+ /** @hide */
+ PlatformVpnProfile(@PlatformVpnType int type) {
+ mType = type;
+ }
+ /** Returns the profile integer type. */
+ @PlatformVpnType
+ public final int getType() {
+ return mType;
+ }
+
+ /** Returns a type string describing the VPN profile type */
+ @NonNull
+ public final String getTypeString() {
+ switch (mType) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ return "IKEv2/IPsec Username/Password";
+ case TYPE_IKEV2_IPSEC_PSK:
+ return "IKEv2/IPsec Preshared key";
+ case TYPE_IKEV2_IPSEC_RSA:
+ return "IKEv2/IPsec RSA Digital Signature";
+ default:
+ return "Unknown VPN profile type";
+ }
+ }
+
+ /** @hide */
+ @NonNull
+ public abstract VpnProfile toVpnProfile() throws IOException, GeneralSecurityException;
+
+ /** @hide */
+ @NonNull
+ public static PlatformVpnProfile fromVpnProfile(@NonNull VpnProfile profile)
+ throws IOException, GeneralSecurityException {
+ switch (profile.type) {
+ case TYPE_IKEV2_IPSEC_USER_PASS: // fallthrough
+ case TYPE_IKEV2_IPSEC_PSK: // fallthrough
+ case TYPE_IKEV2_IPSEC_RSA:
+ return Ikev2VpnProfile.fromVpnProfile(profile);
+ default:
+ throw new IllegalArgumentException("Unknown VPN Profile type");
+ }
+ }
+}
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
index 83dbc63..6ae5971 100644
--- a/core/java/android/net/StringNetworkSpecifier.java
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -17,7 +17,6 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -27,7 +26,6 @@
import java.util.Objects;
/** @hide */
-@SystemApi
public final class StringNetworkSpecifier extends NetworkSpecifier implements Parcelable {
/**
* Arbitrary string used to pass (additional) information to the network factory.
diff --git a/core/java/android/net/Uri.aidl b/core/java/android/net/Uri.aidl
index 6bd3be5..b85f63b 100644
--- a/core/java/android/net/Uri.aidl
+++ b/core/java/android/net/Uri.aidl
@@ -16,4 +16,4 @@
package android.net;
-parcelable Uri;
+@JavaOnlyStableParcelable parcelable Uri;
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
new file mode 100644
index 0000000..f95807a
--- /dev/null
+++ b/core/java/android/net/VpnManager.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 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.net;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * This class provides an interface for apps to manage platform VPN profiles
+ *
+ * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without
+ * further app intermediation. When a VPN profile is present and the app is selected as an always-on
+ * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the
+ * app (unlike VpnService).
+ *
+ * <p>VPN apps using supported protocols should preferentially use this API over the {@link
+ * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user
+ * the guarantee that VPN network traffic is not subjected to on-device packet interception.
+ *
+ * @see Ikev2VpnProfile
+ */
+public class VpnManager {
+ @NonNull private final Context mContext;
+ @NonNull private final IConnectivityManager mService;
+
+ /**
+ * Create an instance of the VpnManger with the given context.
+ *
+ * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the
+ * {@link Context.getSystemService()} method call.
+ *
+ * @hide
+ */
+ public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) {
+ mContext = checkNotNull(ctx, "missing Context");
+ mService = checkNotNull(service, "missing IConnectivityManager");
+ }
+
+ /**
+ * Install a VpnProfile configuration keyed on the calling app's package name.
+ *
+ * @param profile the PlatformVpnProfile provided by this package. Will override any previous
+ * PlatformVpnProfile stored for this package.
+ * @return an intent to request user consent if needed (null otherwise).
+ */
+ @Nullable
+ public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ /** Delete the VPN profile configuration that was provisioned by the calling app */
+ public void deleteProvisionedVpnProfile() {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ /**
+ * Request the startup of a previously provisioned VPN.
+ *
+ * @throws SecurityException exception if user or device settings prevent this VPN from being
+ * setup, or if user consent has not been granted
+ */
+ public void startProvisionedVpnProfile() {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ /** Tear down the VPN provided by the calling app (if any) */
+ public void stopProvisionedVpnProfile() {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+}
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 8bb2df0..b9e6ff4 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -455,12 +455,12 @@
*
* @hide
*/
- public synchronized void setConnectedTechnology(int technology) {
- if (mConnectedTechnology == -1) {
- mConnectedTechnology = technology;
- } else {
- throw new IllegalStateException("Close other technology first!");
+ public synchronized boolean setConnectedTechnology(int technology) {
+ if (mConnectedTechnology != -1) {
+ return false;
}
+ mConnectedTechnology = technology;
+ return true;
}
/**
diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java
index b6b347c..ae468fe 100644
--- a/core/java/android/nfc/tech/BasicTagTechnology.java
+++ b/core/java/android/nfc/tech/BasicTagTechnology.java
@@ -75,7 +75,10 @@
if (errorCode == ErrorCodes.SUCCESS) {
// Store this in the tag object
- mTag.setConnectedTechnology(mSelectedTechnology);
+ if (!mTag.setConnectedTechnology(mSelectedTechnology)) {
+ Log.e(TAG, "Close other technology first!");
+ throw new IOException("Only one TagTechnology can be connected at a time.");
+ }
mIsConnected = true;
} else if (errorCode == ErrorCodes.ERROR_NOT_SUPPORTED) {
throw new UnsupportedOperationException("Connecting to " +
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 1f3ae09..dd08070 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8571,6 +8571,7 @@
*
* @hide
*/
+ @SystemApi
public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled";
/**
diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/core/java/android/se/omapi/ISecureElementReader.aidl
index a312c44..41244ab 100644
--- a/core/java/android/se/omapi/ISecureElementReader.aidl
+++ b/core/java/android/se/omapi/ISecureElementReader.aidl
@@ -48,4 +48,10 @@
*/
void closeSessions();
+ /**
+ * Closes all the sessions opened on this reader and resets the reader.
+ * All the channels opened by all these sessions will be closed.
+ * @return true if the reset is successful, false otherwise.
+ */
+ boolean reset();
}
diff --git a/core/java/android/se/omapi/Reader.java b/core/java/android/se/omapi/Reader.java
index 80262f7..7f68d91 100644
--- a/core/java/android/se/omapi/Reader.java
+++ b/core/java/android/se/omapi/Reader.java
@@ -23,6 +23,8 @@
package android.se.omapi;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -150,4 +152,25 @@
} catch (RemoteException ignore) { }
}
}
+
+ /**
+ * Close all the sessions opened on this reader and reset the reader.
+ * All the channels opened by all these sessions will be closed.
+ * @return <code>true</code> if reset success, <code>false</code> otherwise.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.SECURE_ELEMENT_PRIVILEGED)
+ public boolean reset() {
+ if (!mService.isConnected()) {
+ Log.e(TAG, "service is not connected");
+ return false;
+ }
+ synchronized (mLock) {
+ try {
+ closeSessions();
+ return mReader.reset();
+ } catch (RemoteException ignore) {return false;}
+ }
+ }
}
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index 5330cff..f67af85 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -212,20 +212,16 @@
private int getUiOptionsAsFlags() {
int uiOptionsAsFlags = 0;
- try {
- ContentResolver contentResolver = mContext.getContentResolver();
- int inversionEnabled = Settings.Secure.getInt(contentResolver,
- Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
- if (inversionEnabled == 1) {
- uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
- }
- float fontScale = Settings.System.getFloat(contentResolver,
- Settings.System.FONT_SCALE);
- if (fontScale > 1.0) {
- uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
- }
- } catch (SettingNotFoundException e) {
- Log.w(TAG, "Unexpected SettingNotFoundException");
+ ContentResolver contentResolver = mContext.getContentResolver();
+ int inversionEnabled = Settings.Secure.getInt(contentResolver,
+ Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0);
+ if (inversionEnabled == 1) {
+ uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG;
+ }
+ float fontScale = Settings.System.getFloat(contentResolver,
+ Settings.System.FONT_SCALE, (float) 1.0);
+ if (fontScale > 1.0) {
+ uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG;
}
return uiOptionsAsFlags;
}
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index e08a06ab..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/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index ada59d6..e5bbdf4 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -43,6 +43,7 @@
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final class TimeZoneMapping {
+ @NonNull
private libcore.timezone.CountryTimeZones.TimeZoneMapping mDelegate;
TimeZoneMapping(libcore.timezone.CountryTimeZones.TimeZoneMapping delegate) {
diff --git a/core/java/android/timezone/TelephonyLookup.java b/core/java/android/timezone/TelephonyLookup.java
index 39dbe85..eebccf4 100644
--- a/core/java/android/timezone/TelephonyLookup.java
+++ b/core/java/android/timezone/TelephonyLookup.java
@@ -36,12 +36,8 @@
@GuardedBy("sLock")
private static TelephonyLookup sInstance;
- @NonNull
- private final libcore.timezone.TelephonyLookup mDelegate;
-
/**
- * Obtains an instance for use when resolving telephony time zone information. This method never
- * returns {@code null}.
+ * Obtains an instance for use when resolving telephony time zone information.
*/
@NonNull
public static TelephonyLookup getInstance() {
@@ -53,6 +49,9 @@
}
}
+ @NonNull
+ private final libcore.timezone.TelephonyLookup mDelegate;
+
private TelephonyLookup(@NonNull libcore.timezone.TelephonyLookup delegate) {
mDelegate = Objects.requireNonNull(delegate);
}
diff --git a/core/java/android/timezone/TelephonyNetworkFinder.java b/core/java/android/timezone/TelephonyNetworkFinder.java
index a81a516..079d088 100644
--- a/core/java/android/timezone/TelephonyNetworkFinder.java
+++ b/core/java/android/timezone/TelephonyNetworkFinder.java
@@ -23,7 +23,7 @@
import java.util.Objects;
/**
- * A class that can find telephony networks loaded via {@link TelephonyLookup}.
+ * A class that can find telephony network information loaded via {@link TelephonyLookup}.
*
* @hide
*/
diff --git a/core/java/android/timezone/TimeZoneFinder.java b/core/java/android/timezone/TimeZoneFinder.java
index 15dfe62..9327b00 100644
--- a/core/java/android/timezone/TimeZoneFinder.java
+++ b/core/java/android/timezone/TimeZoneFinder.java
@@ -22,8 +22,10 @@
import com.android.internal.annotations.GuardedBy;
+import java.util.Objects;
+
/**
- * A class that can be used to find time zones.
+ * A class that can be used to find time zones using information like country and offset.
*
* @hide
*/
@@ -34,15 +36,8 @@
@GuardedBy("sLock")
private static TimeZoneFinder sInstance;
- private final libcore.timezone.TimeZoneFinder mDelegate;
-
- private TimeZoneFinder(libcore.timezone.TimeZoneFinder delegate) {
- mDelegate = delegate;
- }
-
/**
- * Obtains an instance for use when resolving telephony time zone information. This method never
- * returns {@code null}.
+ * Obtains the singleton instance.
*/
@NonNull
public static TimeZoneFinder getInstance() {
@@ -54,6 +49,22 @@
return sInstance;
}
+ @NonNull
+ private final libcore.timezone.TimeZoneFinder mDelegate;
+
+ private TimeZoneFinder(@NonNull libcore.timezone.TimeZoneFinder delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
+ }
+
+ /**
+ * Returns the IANA rules version associated with the data. If there is no version information
+ * or there is a problem reading the file then {@code null} is returned.
+ */
+ @Nullable
+ public String getIanaVersion() {
+ return mDelegate.getIanaVersion();
+ }
+
/**
* Returns a {@link CountryTimeZones} object associated with the specified country code.
* Caching is handled as needed. If the country code is not recognized or there is an error
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
new file mode 100644
index 0000000..aba7c4c
--- /dev/null
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Version information associated with the set of time zone data on a device.
+ *
+ * <p>Time Zone Data Sets have a major ({@link #getFormatMajorVersion()}) and minor
+ * ({@link #currentFormatMinorVersion()}) version number:
+ * <ul>
+ * <li>Major version numbers are mutually incompatible. e.g. v2 is not compatible with a v1 or a
+ * v3 device.</li>
+ * <li>Minor version numbers are backwards compatible. e.g. a v2.2 data set will work
+ * on a v2.1 device but not a v2.3 device. The minor version is reset to 1 when the major version
+ * is incremented.</li>
+ * </ul>
+ *
+ * <p>Data sets contain time zone rules and other data associated wtih a tzdb release
+ * ({@link #getRulesVersion()}) and an additional Android-specific revision number
+ * ({@link #getRevision()}).
+ *
+ * <p>See platform/system/timezone/README.android for more information.
+ * @hide
+ */
+@VisibleForTesting
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class TzDataSetVersion {
+
+ /**
+ * Returns the major tz data format version supported by this device.
+ */
+ public static int currentFormatMajorVersion() {
+ return libcore.timezone.TzDataSetVersion.currentFormatMajorVersion();
+ }
+
+ /**
+ * Returns the minor tz data format version supported by this device.
+ */
+ public static int currentFormatMinorVersion() {
+ return libcore.timezone.TzDataSetVersion.currentFormatMinorVersion();
+ }
+
+ /**
+ * Returns true if the version information provided would be compatible with this device, i.e.
+ * with the current system image, and set of active modules.
+ */
+ public static boolean isCompatibleWithThisDevice(TzDataSetVersion tzDataSetVersion) {
+ return libcore.timezone.TzDataSetVersion.isCompatibleWithThisDevice(
+ tzDataSetVersion.mDelegate);
+ }
+
+ /**
+ * Reads the current Android time zone data set version file.
+ */
+ @NonNull
+ public static TzDataSetVersion read() throws IOException, TzDataSetException {
+ try {
+ return new TzDataSetVersion(
+ libcore.timezone.TzDataSetVersion.readTimeZoneModuleVersion());
+ } catch (libcore.timezone.TzDataSetVersion.TzDataSetException e) {
+ throw new TzDataSetException(e.getMessage(), e);
+ }
+ }
+
+ /**
+ * A checked exception used in connection with time zone data sets.
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static class TzDataSetException extends Exception {
+
+ /** Creates an instance with a message. */
+ public TzDataSetException(String message) {
+ super(message);
+ }
+
+ /** Creates an instance with a message and a cause. */
+ public TzDataSetException(String message, Throwable cause) {
+ super(message, cause);
+ }
+ }
+
+ @NonNull
+ private final libcore.timezone.TzDataSetVersion mDelegate;
+
+ private TzDataSetVersion(@NonNull libcore.timezone.TzDataSetVersion delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
+ }
+
+ /** Returns the major version number. See {@link TzDataSetVersion}. */
+ public int getFormatMajorVersion() {
+ return mDelegate.formatMajorVersion;
+ }
+
+ /** Returns the minor version number. See {@link TzDataSetVersion}. */
+ public int getFormatMinorVersion() {
+ return mDelegate.formatMinorVersion;
+ }
+
+ /** Returns the tzdb version string. See {@link TzDataSetVersion}. */
+ @NonNull
+ public String getRulesVersion() {
+ return mDelegate.rulesVersion;
+ }
+
+ /** Returns the Android revision. See {@link TzDataSetVersion}. */
+ public int getRevision() {
+ return mDelegate.revision;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ TzDataSetVersion that = (TzDataSetVersion) o;
+ return mDelegate.equals(that.mDelegate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mDelegate);
+ }
+
+ @Override
+ public String toString() {
+ return mDelegate.toString();
+ }
+}
diff --git a/core/java/android/timezone/ZoneInfoDb.java b/core/java/android/timezone/ZoneInfoDb.java
new file mode 100644
index 0000000..eb191e8
--- /dev/null
+++ b/core/java/android/timezone/ZoneInfoDb.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.Objects;
+
+/**
+ * Android's internal factory for java.util.TimeZone objects. Provides access to core library time
+ * zone metadata not available via {@link java.util.TimeZone}.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class ZoneInfoDb {
+
+ private static Object sLock = new Object();
+ @GuardedBy("sLock")
+ private static ZoneInfoDb sInstance;
+
+ /**
+ * Obtains the singleton instance.
+ */
+ @NonNull
+ public static ZoneInfoDb getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ sInstance = new ZoneInfoDb(libcore.timezone.ZoneInfoDB.getInstance());
+ }
+ }
+ return sInstance;
+ }
+
+ @NonNull
+ private final libcore.timezone.ZoneInfoDB mDelegate;
+
+ private ZoneInfoDb(libcore.timezone.ZoneInfoDB delegate) {
+ mDelegate = Objects.requireNonNull(delegate);
+ }
+
+ /**
+ * Returns the tzdb version in use.
+ */
+ @NonNull
+ public String getVersion() {
+ return mDelegate.getVersion();
+ }
+}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index 8439f5a..dbd3f69 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -88,7 +88,7 @@
*
* <p>The list returned may be different from other on-device sources like
* {@link android.icu.util.TimeZone#getRegion(String)} as it can be curated to avoid
- * contentious mappings.
+ * contentious or obsolete mappings.
*
* @param countryCode the ISO 3166-1 alpha-2 code for the country as can be obtained using
* {@link java.util.Locale#getCountry()}
diff --git a/core/java/android/util/proto/ProtoOutputStream.java b/core/java/android/util/proto/ProtoOutputStream.java
index 6efcfbf..7b24ba9 100644
--- a/core/java/android/util/proto/ProtoOutputStream.java
+++ b/core/java/android/util/proto/ProtoOutputStream.java
@@ -16,7 +16,8 @@
package android.util.proto;
-import android.annotation.TestApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.Log;
import java.io.FileDescriptor;
@@ -28,19 +29,23 @@
/**
* Class to write to a protobuf stream.
*
- * Each write method takes an ID code from the protoc generated classes
- * and the value to write. To make a nested object, call #start
- * and then #end when you are done.
+ * <p>
+ * This API is not as convenient or type safe as the standard protobuf
+ * classes. If possible, the best recommended library is to use protobuf lite.
+ * However, in environements (such as the Android platform itself), a
+ * more memory efficient version is necessary.
*
- * The ID codes have type information embedded into them, so if you call
- * the incorrect function you will get an IllegalArgumentException.
+ * <p>Each write method takes an ID code from the protoc generated classes
+ * and the value to write. To make a nested object, call {@link #start(long)}
+ * and then {@link #end(long)} when you are done.
*
- * To retrieve the encoded protobuf stream, call getBytes().
+ * <p>The ID codes have type information embedded into them, so if you call
+ * the incorrect function you will get an {@link IllegalArgumentException}.
*
- * TODO: Add a constructor that takes an OutputStream and write to that
+ * <p>To retrieve the encoded protobuf stream, call {@link #getBytes()}.
+ *
* stream as the top-level objects are finished.
*
- * @hide
*/
/* IMPLEMENTATION NOTES
@@ -99,7 +104,6 @@
* correctly matched pairs of #start and #end calls, and issue
* errors if they are not matched.
*/
-@TestApi
public final class ProtoOutputStream extends ProtoStream {
/**
* @hide
@@ -124,7 +128,9 @@
/**
* An ID given to objects and returned in the token from startObject
* and stored in the buffer until endObject is called, where the two
- * are checked. Starts at -1 and becomes more negative, so the values
+ * are checked.
+ *
+ * <p>Starts at -1 and becomes more negative, so the values
* aren't likely to alias with the size it will be overwritten with,
* which tend to be small, and we will be more likely to catch when
* the caller of endObject uses a stale token that they didn't intend
@@ -133,8 +139,9 @@
private int mNextObjectId = -1;
/**
- * The object token we are expecting in endObject. If another call to
- * startObject happens, this is written to that location, which gives
+ * The object token we are expecting in endObject.
+ *
+ * <p>If another call to startObject happens, this is written to that location, which gives
* us a stack, stored in the space for the as-yet unused size fields.
*/
private long mExpectedObjectToken;
@@ -151,39 +158,45 @@
private boolean mCompacted;
/**
- * Construct a ProtoOutputStream with the default chunk size.
+ * Construct a {@link ProtoOutputStream} with the default chunk size.
+ *
+ * <p>This is for an in-memory proto. The caller should use {@link #getBytes()} for the result.
*/
public ProtoOutputStream() {
this(0);
}
/**
- * Construct a ProtoOutputStream with the given chunk size.
+ * Construct a {@link ProtoOutputStream with the given chunk size.
+ *
+ * <p>This is for an in-memory proto. The caller should use {@link #getBytes()} for the result.
*/
public ProtoOutputStream(int chunkSize) {
mBuffer = new EncodedBuffer(chunkSize);
}
/**
- * Construct a ProtoOutputStream that sits on top of an OutputStream.
- * @more
- * The {@link #flush() flush()} method must be called when done writing
- * to flush any remanining data, althought data *may* be written at intermediate
+ * Construct a {@link ProtoOutputStream} that sits on top of an {@link OutputStream}.
+ *
+ * <p>The {@link #flush()} method must be called when done writing
+ * to flush any remaining data, although data *may* be written at intermediate
* points within the writing as well.
*/
- public ProtoOutputStream(OutputStream stream) {
+ public ProtoOutputStream(@NonNull OutputStream stream) {
this();
mStream = stream;
}
/**
- * Construct a ProtoOutputStream that sits on top of a FileDescriptor.
- * @more
- * The {@link #flush() flush()} method must be called when done writing
- * to flush any remanining data, althought data *may* be written at intermediate
+ * Construct a {@link ProtoOutputStream} that sits on top of a {@link FileDescriptor}.
+ *
+ * <p>The {@link #flush()} method must be called when done writing
+ * to flush any remaining data, although data *may* be written at intermediate
* points within the writing as well.
+ *
+ * @hide
*/
- public ProtoOutputStream(FileDescriptor fd) {
+ public ProtoOutputStream(@NonNull FileDescriptor fd) {
this(new FileOutputStream(fd));
}
@@ -202,7 +215,7 @@
/**
* Write a value for the given fieldId.
*
- * Will automatically convert for the following field types, and
+ * <p>Will automatically convert for the following field types, and
* throw an exception for others: double, float, int32, int64, uint32, uint64,
* sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
*
@@ -337,7 +350,7 @@
/**
* Write a value for the given fieldId.
*
- * Will automatically convert for the following field types, and
+ * <p>Will automatically convert for the following field types, and
* throw an exception for others: double, float, int32, int64, uint32, uint64,
* sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
*
@@ -472,7 +485,7 @@
/**
* Write a value for the given fieldId.
*
- * Will automatically convert for the following field types, and
+ * <p>Will automatically convert for the following field types, and
* throw an exception for others: double, float, int32, int64, uint32, uint64,
* sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
*
@@ -607,7 +620,7 @@
/**
* Write a value for the given fieldId.
*
- * Will automatically convert for the following field types, and
+ * <p>Will automatically convert for the following field types, and
* throw an exception for others: double, float, int32, int64, uint32, uint64,
* sint32, sint64, fixed32, fixed64, sfixed32, sfixed64, bool, enum.
*
@@ -742,7 +755,7 @@
/**
* Write a boolean value for the given fieldId.
*
- * If the field is not a bool field, an exception will be thrown.
+ * <p>If the field is not a bool field, an {@link IllegalStateException} will be thrown.
*
* @param fieldId The field identifier constant from the generated class.
* @param val The value.
@@ -771,12 +784,12 @@
/**
* Write a string value for the given fieldId.
*
- * If the field is not a string field, an exception will be thrown.
+ * <p>If the field is not a string field, an exception will be thrown.
*
* @param fieldId The field identifier constant from the generated class.
* @param val The value.
*/
- public void write(long fieldId, String val) {
+ public void write(long fieldId, @Nullable String val) {
assertNotCompacted();
final int id = (int)fieldId;
@@ -800,12 +813,12 @@
/**
* Write a byte[] value for the given fieldId.
*
- * If the field is not a bytes or object field, an exception will be thrown.
+ * <p>If the field is not a bytes or object field, an exception will be thrown.
*
* @param fieldId The field identifier constant from the generated class.
* @param val The value.
*/
- public void write(long fieldId, byte[] val) {
+ public void write(long fieldId, @Nullable byte[] val) {
assertNotCompacted();
final int id = (int)fieldId;
@@ -836,6 +849,9 @@
/**
* Start a sub object.
+ *
+ * @param fieldId The field identifier constant from the generated class.
+ * @return The token to call {@link #end(long)} with.
*/
public long start(long fieldId) {
assertNotCompacted();
@@ -855,6 +871,8 @@
/**
* End the object started by start() that returned token.
+ *
+ * @param token The token returned from {@link #start(long)}
*/
public void end(long token) {
endObjectImpl(token, getRepeatedFromToken(token));
@@ -870,7 +888,8 @@
/**
* Write a single proto "double" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, double)} instead.
+ * @hide
*/
@Deprecated
public void writeDouble(long fieldId, double val) {
@@ -890,7 +909,8 @@
/**
* Write a single repeated proto "double" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, double)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedDouble(long fieldId, double val) {
@@ -908,10 +928,11 @@
/**
* Write a list of packed proto "double" type field values.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, double)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedDouble(long fieldId, double[] val) {
+ public void writePackedDouble(long fieldId, @Nullable double[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_DOUBLE);
@@ -934,7 +955,8 @@
/**
* Write a single proto "float" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, float)} instead.
+ * @hide
*/
@Deprecated
public void writeFloat(long fieldId, float val) {
@@ -954,7 +976,8 @@
/**
* Write a single repeated proto "float" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, float)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedFloat(long fieldId, float val) {
@@ -972,10 +995,11 @@
/**
* Write a list of packed proto "float" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, float)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedFloat(long fieldId, float[] val) {
+ public void writePackedFloat(long fieldId, @Nullable float[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_FLOAT);
@@ -999,7 +1023,7 @@
/**
* Writes a java int as an usigned varint.
*
- * The unadorned int32 type in protobuf is unfortunate because it
+ * <p>The unadorned int32 type in protobuf is unfortunate because it
* is stored in memory as a signed value, but encodes as unsigned
* varints, which are formally always longs. So here, we encode
* negative values as 64 bits, which will get the sign-extension,
@@ -1017,11 +1041,12 @@
/**
* Write a single proto "int32" type field value.
*
- * Note that these are stored in memory as signed values and written as unsigned
+ * <p>Note that these are stored in memory as signed values and written as unsigned
* varints, which if negative, are 10 bytes long. If you know the data is likely
* to be negative, use "sint32".
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeInt32(long fieldId, int val) {
@@ -1041,11 +1066,12 @@
/**
* Write a single repeated proto "int32" type field value.
*
- * Note that these are stored in memory as signed values and written as unsigned
+ * <p>Note that these are stored in memory as signed values and written as unsigned
* varints, which if negative, are 10 bytes long. If you know the data is likely
* to be negative, use "sint32".
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedInt32(long fieldId, int val) {
@@ -1063,14 +1089,15 @@
/**
* Write a list of packed proto "int32" type field value.
*
- * Note that these are stored in memory as signed values and written as unsigned
+ * <p>Note that these are stored in memory as signed values and written as unsigned
* varints, which if negative, are 10 bytes long. If you know the data is likely
* to be negative, use "sint32".
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedInt32(long fieldId, int[] val) {
+ public void writePackedInt32(long fieldId, @Nullable int[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_INT32);
@@ -1099,7 +1126,8 @@
/**
* Write a single proto "int64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeInt64(long fieldId, long val) {
@@ -1119,7 +1147,8 @@
/**
* Write a single repeated proto "int64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedInt64(long fieldId, long val) {
@@ -1137,10 +1166,11 @@
/**
* Write a list of packed proto "int64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedInt64(long fieldId, long[] val) {
+ public void writePackedInt64(long fieldId, @Nullable long[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_INT64);
@@ -1168,7 +1198,8 @@
/**
* Write a single proto "uint32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeUInt32(long fieldId, int val) {
@@ -1188,7 +1219,8 @@
/**
* Write a single repeated proto "uint32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedUInt32(long fieldId, int val) {
@@ -1206,10 +1238,11 @@
/**
* Write a list of packed proto "uint32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedUInt32(long fieldId, int[] val) {
+ public void writePackedUInt32(long fieldId, @Nullable int[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_UINT32);
@@ -1237,7 +1270,8 @@
/**
* Write a single proto "uint64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeUInt64(long fieldId, long val) {
@@ -1257,7 +1291,8 @@
/**
* Write a single proto "uint64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedUInt64(long fieldId, long val) {
@@ -1275,10 +1310,11 @@
/**
* Write a single proto "uint64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedUInt64(long fieldId, long[] val) {
+ public void writePackedUInt64(long fieldId, @Nullable long[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_UINT64);
@@ -1306,7 +1342,8 @@
/**
* Write a single proto "sint32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeSInt32(long fieldId, int val) {
@@ -1326,7 +1363,8 @@
/**
* Write a single repeated proto "sint32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedSInt32(long fieldId, int val) {
@@ -1344,10 +1382,11 @@
/**
* Write a list of packed proto "sint32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedSInt32(long fieldId, int[] val) {
+ public void writePackedSInt32(long fieldId, @Nullable int[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SINT32);
@@ -1375,7 +1414,8 @@
/**
* Write a single proto "sint64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeSInt64(long fieldId, long val) {
@@ -1395,7 +1435,8 @@
/**
* Write a single repeated proto "sint64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedSInt64(long fieldId, long val) {
@@ -1413,10 +1454,11 @@
/**
* Write a list of packed proto "sint64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedSInt64(long fieldId, long[] val) {
+ public void writePackedSInt64(long fieldId, @Nullable long[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SINT64);
@@ -1443,7 +1485,8 @@
/**
* Write a single proto "fixed32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeFixed32(long fieldId, int val) {
@@ -1463,7 +1506,8 @@
/**
* Write a single repeated proto "fixed32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedFixed32(long fieldId, int val) {
@@ -1481,10 +1525,11 @@
/**
* Write a list of packed proto "fixed32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedFixed32(long fieldId, int[] val) {
+ public void writePackedFixed32(long fieldId, @Nullable int[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_FIXED32);
@@ -1507,7 +1552,8 @@
/**
* Write a single proto "fixed64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeFixed64(long fieldId, long val) {
@@ -1527,7 +1573,8 @@
/**
* Write a single repeated proto "fixed64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedFixed64(long fieldId, long val) {
@@ -1545,10 +1592,11 @@
/**
* Write a list of packed proto "fixed64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedFixed64(long fieldId, long[] val) {
+ public void writePackedFixed64(long fieldId, @Nullable long[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_FIXED64);
@@ -1570,7 +1618,8 @@
/**
* Write a single proto "sfixed32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeSFixed32(long fieldId, int val) {
@@ -1590,7 +1639,8 @@
/**
* Write a single repeated proto "sfixed32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedSFixed32(long fieldId, int val) {
@@ -1608,10 +1658,11 @@
/**
* Write a list of packed proto "sfixed32" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedSFixed32(long fieldId, int[] val) {
+ public void writePackedSFixed32(long fieldId, @Nullable int[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SFIXED32);
@@ -1634,7 +1685,8 @@
/**
* Write a single proto "sfixed64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeSFixed64(long fieldId, long val) {
@@ -1654,7 +1706,8 @@
/**
* Write a single repeated proto "sfixed64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedSFixed64(long fieldId, long val) {
@@ -1672,10 +1725,11 @@
/**
* Write a list of packed proto "sfixed64" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, long)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedSFixed64(long fieldId, long[] val) {
+ public void writePackedSFixed64(long fieldId, @Nullable long[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_SFIXED64);
@@ -1698,7 +1752,8 @@
/**
* Write a single proto "bool" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, boolean)} instead.
+ * @hide
*/
@Deprecated
public void writeBool(long fieldId, boolean val) {
@@ -1719,7 +1774,8 @@
/**
* Write a single repeated proto "bool" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, boolean)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedBool(long fieldId, boolean val) {
@@ -1737,10 +1793,11 @@
/**
* Write a list of packed proto "bool" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, boolean)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedBool(long fieldId, boolean[] val) {
+ public void writePackedBool(long fieldId, @Nullable boolean[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_BOOL);
@@ -1767,10 +1824,11 @@
/**
* Write a single proto "string" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, String)} instead.
+ * @hide
*/
@Deprecated
- public void writeString(long fieldId, String val) {
+ public void writeString(long fieldId, @Nullable String val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_STRING);
@@ -1786,10 +1844,11 @@
/**
* Write a single repeated proto "string" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, String)} instead.
+ * @hide
*/
@Deprecated
- public void writeRepeatedString(long fieldId, String val) {
+ public void writeRepeatedString(long fieldId, @Nullable String val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_STRING);
@@ -1828,10 +1887,11 @@
/**
* Write a single proto "bytes" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, byte[])} instead.
+ * @hide
*/
@Deprecated
- public void writeBytes(long fieldId, byte[] val) {
+ public void writeBytes(long fieldId, @Nullable byte[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES);
@@ -1848,10 +1908,11 @@
/**
* Write a single repeated proto "bytes" type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, byte[])} instead.
+ * @hide
*/
@Deprecated
- public void writeRepeatedBytes(long fieldId, byte[] val) {
+ public void writeRepeatedBytes(long fieldId, @Nullable byte[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_BYTES);
@@ -1874,7 +1935,8 @@
/**
* Write a single proto enum type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeEnum(long fieldId, int val) {
@@ -1894,7 +1956,8 @@
/**
* Write a single repeated proto enum type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
public void writeRepeatedEnum(long fieldId, int val) {
@@ -1912,10 +1975,11 @@
/**
* Write a list of packed proto enum type field value.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, int)} instead.
+ * @hide
*/
@Deprecated
- public void writePackedEnum(long fieldId, int[] val) {
+ public void writePackedEnum(long fieldId, @Nullable int[] val) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_PACKED | FIELD_TYPE_ENUM);
@@ -1940,7 +2004,8 @@
* Returns a token which should be passed to endObject. Calls to endObject must be
* nested properly.
*
- * @deprecated Use #start() instead.
+ * @deprecated Use {@link #start(long)} instead.
+ * @hide
*/
@Deprecated
public long startObject(long fieldId) {
@@ -1953,7 +2018,8 @@
/**
* End a child object. Pass in the token from the correspoinding startObject call.
*
- * @deprecated Use #end() instead.
+ * @deprecated Use {@link #end(long)} instead.
+ * @hide
*/
@Deprecated
public void endObject(long token) {
@@ -1968,7 +2034,8 @@
* Returns a token which should be passed to endObject. Calls to endObject must be
* nested properly.
*
- * @deprecated Use #start() instead.
+ * @deprecated Use {@link #start(long)} instead.
+ * @hide
*/
@Deprecated
public long startRepeatedObject(long fieldId) {
@@ -1981,7 +2048,8 @@
/**
* End a child object. Pass in the token from the correspoinding startRepeatedObject call.
*
- * @deprecated Use #end() instead.
+ * @deprecated Use {@link #end(long)} instead.
+ * @hide
*/
@Deprecated
public void endRepeatedObject(long token) {
@@ -2064,12 +2132,13 @@
}
/**
- * Write an object that has already been flattend.
+ * Write an object that has already been flattened.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, byte[])} instead.
+ * @hide
*/
@Deprecated
- public void writeObject(long fieldId, byte[] value) {
+ public void writeObject(long fieldId, @Nullable byte[] value) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_SINGLE | FIELD_TYPE_MESSAGE);
@@ -2084,12 +2153,13 @@
}
/**
- * Write an object that has already been flattend.
+ * Write an object that has already been flattened.
*
- * @deprecated Use #write instead.
+ * @deprecated Use {@link #write(long, byte[])} instead.
+ * @hide
*/
@Deprecated
- public void writeRepeatedObject(long fieldId, byte[] value) {
+ public void writeRepeatedObject(long fieldId, @Nullable byte[] value) {
assertNotCompacted();
final int id = checkFieldId(fieldId, FIELD_COUNT_REPEATED | FIELD_TYPE_MESSAGE);
@@ -2115,11 +2185,11 @@
}
/**
- * Validates that the fieldId providied is of the type and count from expectedType.
+ * Validates that the fieldId provided is of the type and count from expectedType.
*
- * The type must match exactly to pass this check.
+ * <p>The type must match exactly to pass this check.
*
- * The count must match according to this truth table to pass the check:
+ * <p>The count must match according to this truth table to pass the check:
*
* expectedFlags
* UNKNOWN SINGLE REPEATED PACKED
@@ -2129,7 +2199,7 @@
* REPEATED x false true false
* PACKED x false true true
*
- * @throws IllegalArgumentException if it is not.
+ * @throws {@link IllegalArgumentException} if it is not.
*
* @return The raw ID of that field.
*/
@@ -2201,7 +2271,7 @@
}
/**
- * Write a field tage to the stream.
+ * Write a field tag to the stream.
*/
public void writeTag(int id, int wireType) {
mBuffer.writeRawVarint32((id << FIELD_ID_SHIFT) | wireType);
@@ -2239,10 +2309,10 @@
* Finish the encoding of the data, and return a byte[] with
* the protobuf formatted data.
*
- * After this call, do not call any of the write* functions. The
+ * <p>After this call, do not call any of the write* functions. The
* behavior is undefined.
*/
- public byte[] getBytes() {
+ public @NonNull byte[] getBytes() {
compactIfNecessary();
return mBuffer.getBytes(mBuffer.getReadableSize());
@@ -2289,7 +2359,7 @@
}
/**
- * First compaction pass. Iterate through the data, and fill in the
+ * First compaction pass. Iterate through the data, and fill in the
* nested object sizes so the next pass can compact them.
*/
private int editEncodedSize(int rawSize) {
@@ -2416,10 +2486,10 @@
/**
* Write remaining data to the output stream. If there is no output stream,
* this function does nothing. Any currently open objects (i.e. ones that
- * have not had endObject called for them will not be written). Whether this
+ * have not had {@link #end(long)} called for them will not be written). Whether this
* writes objects that are closed if there are remaining open objects is
* undefined (current implementation does not write it, future ones will).
- * For now, can either call getBytes() or flush(), but not both.
+ * For now, can either call {@link #getBytes()} or {@link #flush()}, but not both.
*/
public void flush() {
if (mStream == null) {
@@ -2457,7 +2527,7 @@
/**
* Dump debugging data about the buffers with the given log tag.
*/
- public void dump(String tag) {
+ public void dump(@NonNull String tag) {
Log.d(tag, mBuffer.getDebugString());
mBuffer.dumpBuffers(tag);
}
diff --git a/core/java/android/util/proto/ProtoStream.java b/core/java/android/util/proto/ProtoStream.java
index 9e2e95a..4969d8a 100644
--- a/core/java/android/util/proto/ProtoStream.java
+++ b/core/java/android/util/proto/ProtoStream.java
@@ -16,28 +16,104 @@
package android.util.proto;
-import android.annotation.TestApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
/**
- * Abstract base class for both protobuf streams.
+ * Base utility class for protobuf streams.
*
- * Contains a set of useful constants and methods used by both
- * ProtoOutputStream and ProtoInputStream
+ * Contains a set of constants and methods used in generated code for
+ * {@link ProtoOutputStream}.
*
* @hide
*/
-@TestApi
-public abstract class ProtoStream {
+public class ProtoStream {
+ /**
+ * Number of bits to shift the field number to form a tag.
+ *
+ * <pre>
+ * // Reading a field number from a tag.
+ * int fieldNumber = tag >>> FIELD_ID_SHIFT;
+ *
+ * // Building a tag from a field number and a wire type.
+ * int tag = (fieldNumber << FIELD_ID_SHIFT) | wireType;
+ * </pre>
+ *
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final int FIELD_ID_SHIFT = 3;
+
+ /**
+ * Mask to select the wire type from a tag.
+ *
+ * <pre>
+ * // Reading a wire type from a tag.
+ * int wireType = tag & WIRE_TYPE_MASK;
+ *
+ * // Building a tag from a field number and a wire type.
+ * int tag = (fieldNumber << FIELD_ID_SHIFT) | wireType;
+ * </pre>
+ *
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final int WIRE_TYPE_MASK = (1 << FIELD_ID_SHIFT) - 1;
+
+ /**
+ * Mask to select the field id from a tag.
+ * @hide (not used by anything, and not actually useful, because you also want
+ * to shift when you mask the field id).
+ */
public static final int FIELD_ID_MASK = ~WIRE_TYPE_MASK;
+ /**
+ * Varint wire type code.
+ *
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final int WIRE_TYPE_VARINT = 0;
+
+ /**
+ * Fixed64 wire type code.
+ *
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final int WIRE_TYPE_FIXED64 = 1;
+
+ /**
+ * Length delimited wire type code.
+ *
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final int WIRE_TYPE_LENGTH_DELIMITED = 2;
+
+ /**
+ * Start group wire type code.
+ *
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final int WIRE_TYPE_START_GROUP = 3;
+
+ /**
+ * End group wire type code.
+ *
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final int WIRE_TYPE_END_GROUP = 4;
+
+ /**
+ * Fixed32 wire type code.
+ *
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final int WIRE_TYPE_FIXED32 = 5;
/**
@@ -51,32 +127,147 @@
*/
public static final long FIELD_TYPE_MASK = 0x0ffL << FIELD_TYPE_SHIFT;
+ /**
+ * Not a real wire type.
+ * @hide
+ */
public static final long FIELD_TYPE_UNKNOWN = 0;
+
+ /*
+ * The FIELD_TYPE_ constants are copied from
+ * external/protobuf/src/google/protobuf/descriptor.h directly, so no
+ * extra mapping needs to be maintained in this case.
+ */
+
/**
- * The types are copied from external/protobuf/src/google/protobuf/descriptor.h directly,
- * so no extra mapping needs to be maintained in this case.
+ * Field type code for double fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, double)
+ * ProtoOutputStream.write(long, double)} method.
*/
public static final long FIELD_TYPE_DOUBLE = 1L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for float fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, float)
+ * ProtoOutputStream.write(long, float)} method.
+ */
public static final long FIELD_TYPE_FLOAT = 2L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for int64 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, long)
+ * ProtoOutputStream.write(long, long)} method.
+ */
public static final long FIELD_TYPE_INT64 = 3L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for uint64 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, long)
+ * ProtoOutputStream.write(long, long)} method.
+ */
public static final long FIELD_TYPE_UINT64 = 4L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for int32 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, int)
+ * ProtoOutputStream.write(long, int)} method.
+ */
public static final long FIELD_TYPE_INT32 = 5L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for fixed64 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, long)
+ * ProtoOutputStream.write(long, long)} method.
+ */
public static final long FIELD_TYPE_FIXED64 = 6L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for fixed32 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, int)
+ * ProtoOutputStream.write(long, int)} method.
+ */
+
+ /**
+ * Field type code for fixed32 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, int)
+ * ProtoOutputStream.write(long, int)} method.
+ */
public static final long FIELD_TYPE_FIXED32 = 7L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for bool fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, boolean)
+ * ProtoOutputStream.write(long, boolean)} method.
+ */
public static final long FIELD_TYPE_BOOL = 8L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for string fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, String)
+ * ProtoOutputStream.write(long, String)} method.
+ */
public static final long FIELD_TYPE_STRING = 9L << FIELD_TYPE_SHIFT;
+
// public static final long FIELD_TYPE_GROUP = 10L << FIELD_TYPE_SHIFT; // Deprecated.
+
+ /**
+ * Field type code for message fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#start(long)
+ * ProtoOutputStream.start(long)} method.
+ */
public static final long FIELD_TYPE_MESSAGE = 11L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for bytes fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, byte[])
+ * ProtoOutputStream.write(long, byte[])} method.
+ */
public static final long FIELD_TYPE_BYTES = 12L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for uint32 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, int)
+ * ProtoOutputStream.write(long, int)} method.
+ */
public static final long FIELD_TYPE_UINT32 = 13L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for enum fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, int)
+ * ProtoOutputStream.write(long, int)} method.
+ */
public static final long FIELD_TYPE_ENUM = 14L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for sfixed32 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, int)
+ * ProtoOutputStream.write(long, int)} method.
+ */
public static final long FIELD_TYPE_SFIXED32 = 15L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for sfixed64 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, long)
+ * ProtoOutputStream.write(long, long)} method.
+ */
public static final long FIELD_TYPE_SFIXED64 = 16L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for sint32 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, int)
+ * ProtoOutputStream.write(long, int)} method.
+ */
public static final long FIELD_TYPE_SINT32 = 17L << FIELD_TYPE_SHIFT;
+
+ /**
+ * Field type code for sint64 fields. Used to build constants in generated
+ * code for use with the {@link ProtoOutputStream#write(long, long)
+ * ProtoOutputStream.write(long, long)} method.
+ */
public static final long FIELD_TYPE_SINT64 = 18L << FIELD_TYPE_SHIFT;
- protected static final String[] FIELD_TYPE_NAMES = new String[]{
+ private static final @NonNull String[] FIELD_TYPE_NAMES = new String[]{
"Double",
"Float",
"Int64",
@@ -100,19 +291,94 @@
//
// FieldId flags for whether the field is single, repeated or packed.
//
+ /**
+ * Bit offset for building a field id to be used with a
+ * <code>{@link ProtoOutputStream}.write(...)</code>.
+ *
+ * @see #FIELD_COUNT_MASK
+ * @see #FIELD_COUNT_UNKNOWN
+ * @see #FIELD_COUNT_SINGLE
+ * @see #FIELD_COUNT_REPEATED
+ * @see #FIELD_COUNT_PACKED
+ */
public static final int FIELD_COUNT_SHIFT = 40;
+
+ /**
+ * Bit mask for selecting the field count when reading a field id that
+ * is used with a <code>{@link ProtoOutputStream}.write(...)</code> method.
+ *
+ * @see #FIELD_COUNT_SHIFT
+ * @see #FIELD_COUNT_MASK
+ * @see #FIELD_COUNT_UNKNOWN
+ * @see #FIELD_COUNT_SINGLE
+ * @see #FIELD_COUNT_REPEATED
+ * @see #FIELD_COUNT_PACKED
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final long FIELD_COUNT_MASK = 0x0fL << FIELD_COUNT_SHIFT;
+ /**
+ * Unknown field count, encoded into a field id used with a
+ * <code>{@link ProtoOutputStream}.write(...)</code> method.
+ *
+ * @see #FIELD_COUNT_SHIFT
+ * @see #FIELD_COUNT_MASK
+ * @see #FIELD_COUNT_SINGLE
+ * @see #FIELD_COUNT_REPEATED
+ * @see #FIELD_COUNT_PACKED
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final long FIELD_COUNT_UNKNOWN = 0;
+
+ /**
+ * Single field count, encoded into a field id used with a
+ * <code>{@link ProtoOutputStream}.write(...)</code> method.
+ *
+ * @see #FIELD_COUNT_SHIFT
+ * @see #FIELD_COUNT_MASK
+ * @see #FIELD_COUNT_UNKNOWN
+ * @see #FIELD_COUNT_REPEATED
+ * @see #FIELD_COUNT_PACKED
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final long FIELD_COUNT_SINGLE = 1L << FIELD_COUNT_SHIFT;
+
+ /**
+ * Repeated field count, encoded into a field id used with a
+ * <code>{@link ProtoOutputStream}.write(...)</code> method.
+ *
+ * @see #FIELD_COUNT_SHIFT
+ * @see #FIELD_COUNT_MASK
+ * @see #FIELD_COUNT_UNKNOWN
+ * @see #FIELD_COUNT_SINGLE
+ * @see #FIELD_COUNT_PACKED
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final long FIELD_COUNT_REPEATED = 2L << FIELD_COUNT_SHIFT;
+
+ /**
+ * Repeated packed field count, encoded into a field id used with a
+ * <code>{@link ProtoOutputStream}.write(...)</code> method.
+ *
+ * @see #FIELD_COUNT_SHIFT
+ * @see #FIELD_COUNT_MASK
+ * @see #FIELD_COUNT_UNKNOWN
+ * @see #FIELD_COUNT_SINGLE
+ * @see #FIELD_COUNT_REPEATED
+ * @see <a href="https://developers.google.com/protocol-buffers/docs/encoding">Protobuf
+ * Encoding</a>
+ */
public static final long FIELD_COUNT_PACKED = 5L << FIELD_COUNT_SHIFT;
/**
* Get the developer-usable name of a field type.
*/
- public static String getFieldTypeString(long fieldType) {
+ public static @Nullable String getFieldTypeString(long fieldType) {
int index = ((int) ((fieldType & FIELD_TYPE_MASK) >>> FIELD_TYPE_SHIFT)) - 1;
if (index >= 0 && index < FIELD_TYPE_NAMES.length) {
return FIELD_TYPE_NAMES[index];
@@ -124,7 +390,7 @@
/**
* Get the developer-usable name of a field count.
*/
- public static String getFieldCountString(long fieldCount) {
+ public static @Nullable String getFieldCountString(long fieldCount) {
if (fieldCount == FIELD_COUNT_SINGLE) {
return "";
} else if (fieldCount == FIELD_COUNT_REPEATED) {
@@ -139,7 +405,7 @@
/**
* Get the developer-usable name of a wire type.
*/
- public static String getWireTypeString(int wireType) {
+ public static @Nullable String getWireTypeString(int wireType) {
switch (wireType) {
case WIRE_TYPE_VARINT:
return "Varint";
@@ -161,7 +427,7 @@
/**
* Get a debug string for a fieldId.
*/
- public static String getFieldIdString(long fieldId) {
+ public static @NonNull String getFieldIdString(long fieldId) {
final long fieldCount = fieldId & FIELD_COUNT_MASK;
String countString = getFieldCountString(fieldCount);
if (countString == null) {
@@ -218,29 +484,39 @@
/**
* Get the encoded tag size from the token.
+ *
+ * @hide
*/
public static int getTagSizeFromToken(long token) {
return (int) (0x7 & (token >> 61));
}
/**
- * Get whether this is a call to startObject (false) or startRepeatedObject (true).
+ * Get whether the token has the repeated bit set to true or false
+ *
+ * @hide
*/
public static boolean getRepeatedFromToken(long token) {
return (0x1 & (token >> 60)) != 0;
}
/**
- * Get the nesting depth of startObject calls from the token.
+ * Get the nesting depth from the token.
+ *
+ * @hide
*/
public static int getDepthFromToken(long token) {
return (int) (0x01ff & (token >> 51));
}
/**
- * Get the object ID from the token. The object ID is a serial number for the
+ * Get the object ID from the token.
+ *
+ * <p>The object ID is a serial number for the
* startObject calls that have happened on this object. The values are truncated
* to 9 bits, but that is sufficient for error checking.
+ *
+ * @hide
*/
public static int getObjectIdFromToken(long token) {
return (int) (0x07ffff & (token >> 32));
@@ -248,6 +524,8 @@
/**
* Get the location of the offset recorded in the token.
+ *
+ * @hide
*/
public static int getOffsetFromToken(long token) {
return (int) token;
@@ -255,8 +533,11 @@
/**
* Convert the object ID to the ordinal value -- the n-th call to startObject.
- * The object IDs start at -1 and count backwards, so that the value is unlikely
+ *
+ * <p>The object IDs start at -1 and count backwards, so that the value is unlikely
* to alias with an actual size field that had been written.
+ *
+ * @hide
*/
public static int convertObjectIdToOrdinal(int objectId) {
return (-1 & 0x07ffff) - objectId;
@@ -265,7 +546,7 @@
/**
* Return a debugging string of a token.
*/
- public static String token2String(long token) {
+ public static @NonNull String token2String(long token) {
if (token == 0L) {
return "Token(0)";
} else {
@@ -277,4 +558,9 @@
+ ')';
}
}
+
+ /**
+ * @hide
+ */
+ protected ProtoStream() {}
}
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
index 03bf590..a71561b 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -24,12 +24,12 @@
/**
* This class contains a list of helper functions to write common proto in
* //frameworks/base/core/proto/android/base directory
+ * @hide
*/
public class ProtoUtils {
/**
* Dump AggStats to ProtoOutputStream
- * @hide
*/
public static void toAggStatsProto(ProtoOutputStream proto, long fieldId,
long min, long average, long max) {
@@ -42,7 +42,6 @@
/**
* Dump Duration to ProtoOutputStream
- * @hide
*/
public static void toDuration(ProtoOutputStream proto, long fieldId, long startMs, long endMs) {
final long token = proto.start(fieldId);
@@ -53,7 +52,6 @@
/**
* Helper function to write bit-wise flags to proto as repeated enums
- * @hide
*/
public static void writeBitWiseFlagsToProtoEnum(ProtoOutputStream proto, long fieldId,
int flags, int[] origEnums, int[] protoEnums) {
diff --git a/core/java/android/util/proto/package.html b/core/java/android/util/proto/package.html
index a636bd4..ef1125b 100644
--- a/core/java/android/util/proto/package.html
+++ b/core/java/android/util/proto/package.html
@@ -1,5 +1,5 @@
+<html>
<body>
Provides utility classes to export protocol buffers from the system.
-
-{@hide}
</body>
+</html>
\ No newline at end of file
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index 4bb012a..bbae0273 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -16,6 +16,7 @@
package com.android.internal.net;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.ProxyInfo;
import android.os.Build;
@@ -23,21 +24,34 @@
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
/**
- * Parcel-like entity class for VPN profiles. To keep things simple, all
- * fields are package private. Methods are provided for serialization, so
- * storage can be implemented easily. Two rules are set for this class.
- * First, all fields must be kept non-null. Second, always make a copy
- * using clone() before modifying.
+ * Profile storage class for a platform VPN.
+ *
+ * <p>This class supports both the Legacy VPN, as well as application-configurable platform VPNs
+ * (such as IKEv2/IPsec).
+ *
+ * <p>This class is serialized and deserialized via the {@link #encode()} and {@link #decode()}
+ * functions for persistent storage in the Android Keystore. The encoding is entirely custom, but
+ * must be kept for backward compatibility for devices upgrading between Android versions.
*
* @hide
*/
-public class VpnProfile implements Cloneable, Parcelable {
+public final class VpnProfile implements Cloneable, Parcelable {
private static final String TAG = "VpnProfile";
+ @VisibleForTesting static final String VALUE_DELIMITER = "\0";
+ @VisibleForTesting static final String LIST_DELIMITER = ",";
+
// Match these constants with R.array.vpn_types.
public static final int TYPE_PPTP = 0;
public static final int TYPE_L2TP_IPSEC_PSK = 1;
@@ -45,39 +59,85 @@
public static final int TYPE_IPSEC_XAUTH_PSK = 3;
public static final int TYPE_IPSEC_XAUTH_RSA = 4;
public static final int TYPE_IPSEC_HYBRID_RSA = 5;
- public static final int TYPE_MAX = 5;
+ public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6;
+ public static final int TYPE_IKEV2_IPSEC_PSK = 7;
+ public static final int TYPE_IKEV2_IPSEC_RSA = 8;
+ public static final int TYPE_MAX = 8;
// Match these constants with R.array.vpn_proxy_settings.
public static final int PROXY_NONE = 0;
public static final int PROXY_MANUAL = 1;
+ private static final String ENCODED_NULL_PROXY_INFO = "\0\0\0\0";
+
// Entity fields.
@UnsupportedAppUsage
- public final String key; // -1
+ public final String key; // -1
+
@UnsupportedAppUsage
- public String name = ""; // 0
+ public String name = ""; // 0
+
@UnsupportedAppUsage
- public int type = TYPE_PPTP; // 1
+ public int type = TYPE_PPTP; // 1
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public String server = ""; // 2
+ public String server = ""; // 2
+
@UnsupportedAppUsage
- public String username = ""; // 3
- public String password = ""; // 4
- public String dnsServers = ""; // 5
- public String searchDomains = ""; // 6
- public String routes = ""; // 7
- public boolean mppe = true; // 8
- public String l2tpSecret = ""; // 9
- public String ipsecIdentifier = "";// 10
- public String ipsecSecret = ""; // 11
- public String ipsecUserCert = ""; // 12
- public String ipsecCaCert = ""; // 13
- public String ipsecServerCert = "";// 14
- public ProxyInfo proxy = null; // 15~18
+ public String username = ""; // 3
+ public String password = ""; // 4
+ public String dnsServers = ""; // 5
+ public String searchDomains = ""; // 6
+ public String routes = ""; // 7
+ public boolean mppe = true; // 8
+ public String l2tpSecret = ""; // 9
+ public String ipsecIdentifier = ""; // 10
+
+ /**
+ * The RSA private key or pre-shared key used for authentication.
+ *
+ * <p>If areAuthParamsInline is {@code true}, this String will be either:
+ *
+ * <ul>
+ * <li>If this is an IKEv2 RSA profile: a PKCS#8 encoded {@link java.security.PrivateKey}
+ * <li>If this is an IKEv2 PSK profile: a string value representing the PSK.
+ * </ul>
+ */
+ public String ipsecSecret = ""; // 11
+
+ /**
+ * The RSA certificate to be used for digital signature authentication.
+ *
+ * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link
+ * java.security.X509Certificate}
+ */
+ public String ipsecUserCert = ""; // 12
+
+ /**
+ * The RSA certificate that should be used to verify the server's end/target certificate.
+ *
+ * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link
+ * java.security.X509Certificate}
+ */
+ public String ipsecCaCert = ""; // 13
+ public String ipsecServerCert = ""; // 14
+ public ProxyInfo proxy = null; // 15~18
+
+ /**
+ * The list of allowable algorithms.
+ *
+ * <p>This list is validated in the setter to ensure that encoding characters (list, value
+ * delimiters) are not present in the algorithm names. See {@link #validateAllowedAlgorithms()}
+ */
+ private List<String> mAllowedAlgorithms = new ArrayList<>(); // 19
+ public boolean isBypassable = false; // 20
+ public boolean isMetered = false; // 21
+ public int maxMtu = 1400; // 22
+ public boolean areAuthParamsInline = false; // 23
// Helper fields.
@UnsupportedAppUsage
- public boolean saveLogin = false;
+ public transient boolean saveLogin = false;
public VpnProfile(String key) {
this.key = key;
@@ -103,6 +163,34 @@
ipsecServerCert = in.readString();
saveLogin = in.readInt() != 0;
proxy = in.readParcelable(null);
+ mAllowedAlgorithms = new ArrayList<>();
+ in.readList(mAllowedAlgorithms, null);
+ isBypassable = in.readBoolean();
+ isMetered = in.readBoolean();
+ maxMtu = in.readInt();
+ areAuthParamsInline = in.readBoolean();
+ }
+
+ /**
+ * Retrieves the list of allowed algorithms.
+ *
+ * <p>The contained elements are as listed in {@link IpSecAlgorithm}
+ */
+ public List<String> getAllowedAlgorithms() {
+ return Collections.unmodifiableList(mAllowedAlgorithms);
+ }
+
+ /**
+ * Validates and sets the list of algorithms that can be used for the IPsec transforms.
+ *
+ * @param allowedAlgorithms the list of allowable algorithms, as listed in {@link
+ * IpSecAlgorithm}.
+ * @throws IllegalArgumentException if any delimiters are used in algorithm names. See {@link
+ * #VALUE_DELIMITER} and {@link LIST_DELIMITER}.
+ */
+ public void setAllowedAlgorithms(List<String> allowedAlgorithms) {
+ validateAllowedAlgorithms(allowedAlgorithms);
+ mAllowedAlgorithms = allowedAlgorithms;
}
@Override
@@ -125,8 +213,18 @@
out.writeString(ipsecServerCert);
out.writeInt(saveLogin ? 1 : 0);
out.writeParcelable(proxy, flags);
+ out.writeList(mAllowedAlgorithms);
+ out.writeBoolean(isBypassable);
+ out.writeBoolean(isMetered);
+ out.writeInt(maxMtu);
+ out.writeBoolean(areAuthParamsInline);
}
+ /**
+ * Decodes a VpnProfile instance from the encoded byte array.
+ *
+ * <p>See {@link #encode()}
+ */
@UnsupportedAppUsage
public static VpnProfile decode(String key, byte[] value) {
try {
@@ -134,9 +232,11 @@
return null;
}
- String[] values = new String(value, StandardCharsets.UTF_8).split("\0", -1);
- // There can be 14 - 19 Bytes in values.length.
- if (values.length < 14 || values.length > 19) {
+ String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1);
+ // Acceptable numbers of values are:
+ // 14-19: Standard profile, with option for serverCert, proxy
+ // 24: Standard profile with serverCert, proxy and platform-VPN parameters.
+ if ((values.length < 14 || values.length > 19) && values.length != 24) {
return null;
}
@@ -164,13 +264,23 @@
String port = (values.length > 16) ? values[16] : "";
String exclList = (values.length > 17) ? values[17] : "";
String pacFileUrl = (values.length > 18) ? values[18] : "";
- if (pacFileUrl.isEmpty()) {
+ if (!host.isEmpty() || !port.isEmpty() || !exclList.isEmpty()) {
profile.proxy = new ProxyInfo(host, port.isEmpty() ?
0 : Integer.parseInt(port), exclList);
- } else {
+ } else if (!pacFileUrl.isEmpty()) {
profile.proxy = new ProxyInfo(pacFileUrl);
}
- } // else profle.proxy = null
+ } // else profile.proxy = null
+
+ // Either all must be present, or none must be.
+ if (values.length >= 24) {
+ profile.mAllowedAlgorithms = Arrays.asList(values[19].split(LIST_DELIMITER));
+ profile.isBypassable = Boolean.parseBoolean(values[20]);
+ profile.isMetered = Boolean.parseBoolean(values[21]);
+ profile.maxMtu = Integer.parseInt(values[22]);
+ profile.areAuthParamsInline = Boolean.parseBoolean(values[23]);
+ }
+
profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
return profile;
} catch (Exception e) {
@@ -179,36 +289,52 @@
return null;
}
+ /**
+ * Encodes a VpnProfile instance to a byte array for storage.
+ *
+ * <p>See {@link #decode(String, byte[])}
+ */
public byte[] encode() {
StringBuilder builder = new StringBuilder(name);
- builder.append('\0').append(type);
- builder.append('\0').append(server);
- builder.append('\0').append(saveLogin ? username : "");
- builder.append('\0').append(saveLogin ? password : "");
- builder.append('\0').append(dnsServers);
- builder.append('\0').append(searchDomains);
- builder.append('\0').append(routes);
- builder.append('\0').append(mppe);
- builder.append('\0').append(l2tpSecret);
- builder.append('\0').append(ipsecIdentifier);
- builder.append('\0').append(ipsecSecret);
- builder.append('\0').append(ipsecUserCert);
- builder.append('\0').append(ipsecCaCert);
- builder.append('\0').append(ipsecServerCert);
+ builder.append(VALUE_DELIMITER).append(type);
+ builder.append(VALUE_DELIMITER).append(server);
+ builder.append(VALUE_DELIMITER).append(saveLogin ? username : "");
+ builder.append(VALUE_DELIMITER).append(saveLogin ? password : "");
+ builder.append(VALUE_DELIMITER).append(dnsServers);
+ builder.append(VALUE_DELIMITER).append(searchDomains);
+ builder.append(VALUE_DELIMITER).append(routes);
+ builder.append(VALUE_DELIMITER).append(mppe);
+ builder.append(VALUE_DELIMITER).append(l2tpSecret);
+ builder.append(VALUE_DELIMITER).append(ipsecIdentifier);
+ builder.append(VALUE_DELIMITER).append(ipsecSecret);
+ builder.append(VALUE_DELIMITER).append(ipsecUserCert);
+ builder.append(VALUE_DELIMITER).append(ipsecCaCert);
+ builder.append(VALUE_DELIMITER).append(ipsecServerCert);
if (proxy != null) {
- builder.append('\0').append(proxy.getHost() != null ? proxy.getHost() : "");
- builder.append('\0').append(proxy.getPort());
- builder.append('\0').append(proxy.getExclusionListAsString() != null ?
- proxy.getExclusionListAsString() : "");
- builder.append('\0').append(proxy.getPacFileUrl().toString());
+ builder.append(VALUE_DELIMITER).append(proxy.getHost() != null ? proxy.getHost() : "");
+ builder.append(VALUE_DELIMITER).append(proxy.getPort());
+ builder.append(VALUE_DELIMITER)
+ .append(
+ proxy.getExclusionListAsString() != null
+ ? proxy.getExclusionListAsString()
+ : "");
+ builder.append(VALUE_DELIMITER).append(proxy.getPacFileUrl().toString());
+ } else {
+ builder.append(ENCODED_NULL_PROXY_INFO);
}
+
+ builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, mAllowedAlgorithms));
+ builder.append(VALUE_DELIMITER).append(isBypassable);
+ builder.append(VALUE_DELIMITER).append(isMetered);
+ builder.append(VALUE_DELIMITER).append(maxMtu);
+ builder.append(VALUE_DELIMITER).append(areAuthParamsInline);
+
return builder.toString().getBytes(StandardCharsets.UTF_8);
}
/**
- * Tests if profile is valid for lockdown, which requires IPv4 address for
- * both server and DNS. Server hostnames would require using DNS before
- * connection.
+ * Tests if profile is valid for lockdown, which requires IPv4 address for both server and DNS.
+ * Server hostnames would require using DNS before connection.
*/
public boolean isValidLockdownProfile() {
return isTypeValidForLockdown()
@@ -238,10 +364,7 @@
return !TextUtils.isEmpty(dnsServers);
}
- /**
- * Returns {@code true} if all DNS servers have numeric addresses,
- * e.g. 8.8.8.8
- */
+ /** Returns {@code true} if all DNS servers have numeric addresses, e.g. 8.8.8.8 */
public boolean areDnsAddressesNumeric() {
try {
for (String dnsServer : dnsServers.split(" +")) {
@@ -253,6 +376,62 @@
return true;
}
+ /**
+ * Validates that the provided list of algorithms does not contain illegal characters.
+ *
+ * @param allowedAlgorithms The list to be validated
+ */
+ public static void validateAllowedAlgorithms(List<String> allowedAlgorithms) {
+ for (final String alg : allowedAlgorithms) {
+ if (alg.contains(VALUE_DELIMITER) || alg.contains(LIST_DELIMITER)) {
+ throw new IllegalArgumentException(
+ "Algorithm contained illegal ('\0' or ',') character");
+ }
+ }
+ }
+
+ /** Generates a hashcode over the VpnProfile. */
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ key, type, server, username, password, dnsServers, searchDomains, routes, mppe,
+ l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert,
+ proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline);
+ }
+
+ /** Checks VPN profiles for interior equality. */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof VpnProfile)) {
+ return false;
+ }
+
+ final VpnProfile other = (VpnProfile) obj;
+ return Objects.equals(key, other.key)
+ && Objects.equals(name, other.name)
+ && type == other.type
+ && Objects.equals(server, other.server)
+ && Objects.equals(username, other.username)
+ && Objects.equals(password, other.password)
+ && Objects.equals(dnsServers, other.dnsServers)
+ && Objects.equals(searchDomains, other.searchDomains)
+ && Objects.equals(routes, other.routes)
+ && mppe == other.mppe
+ && Objects.equals(l2tpSecret, other.l2tpSecret)
+ && Objects.equals(ipsecIdentifier, other.ipsecIdentifier)
+ && Objects.equals(ipsecSecret, other.ipsecSecret)
+ && Objects.equals(ipsecUserCert, other.ipsecUserCert)
+ && Objects.equals(ipsecCaCert, other.ipsecCaCert)
+ && Objects.equals(ipsecServerCert, other.ipsecServerCert)
+ && Objects.equals(proxy, other.proxy)
+ && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms)
+ && isBypassable == other.isBypassable
+ && isMetered == other.isMetered
+ && maxMtu == other.maxMtu
+ && areAuthParamsInline == other.areAuthParamsInline;
+ }
+
+ @NonNull
public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
@Override
public VpnProfile createFromParcel(Parcel in) {
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 13d0c5c..7adb27c 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -19,6 +19,8 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.type.DefaultMimeMapFactory;
import android.os.Build;
@@ -34,6 +36,7 @@
import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
+import dalvik.annotation.compat.VersionCodes;
import dalvik.system.RuntimeHooks;
import dalvik.system.ThreadPrioritySetter;
import dalvik.system.VMRuntime;
@@ -64,8 +67,17 @@
private static volatile boolean mCrashing = false;
+ /*
+ * Native heap allocations will now have a non-zero tag in the most significant byte.
+ * See {@linktourl https://source.android.com/devices/tech/debug/tagged-pointers}.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+ private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
+
private static final native void nativeFinishInit();
private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
+ private static native void nativeDisableHeapPointerTagging();
private static int Clog_e(String tag, String msg, Throwable tr) {
return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
@@ -398,6 +410,20 @@
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
}
+ private static void maybeDisableHeapPointerTagging(long[] disabledCompatChanges) {
+ // Heap tagging needs to be disabled before any additional threads are created, but the
+ // AppCompat framework is not initialized enough at this point.
+ // Check if the change is enabled manually.
+ if (disabledCompatChanges != null) {
+ for (int i = 0; i < disabledCompatChanges.length; i++) {
+ if (disabledCompatChanges[i] == NATIVE_HEAP_POINTER_TAGGING) {
+ nativeDisableHeapPointerTagging();
+ break;
+ }
+ }
+ }
+ }
+
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
// If the application calls System.exit(), terminate the process
@@ -410,6 +436,8 @@
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
+ maybeDisableHeapPointerTagging(disabledCompatChanges);
+
final Arguments args = new Arguments(argv);
// The end of of the RuntimeInit event (see #zygoteInit).
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 8e0e1c6..551541e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -47,6 +47,7 @@
#include <signal.h>
#include <dirent.h>
#include <assert.h>
+#include <bionic/malloc.h>
#include <string>
#include <vector>
@@ -279,6 +280,14 @@
gCurRuntime->setExitWithoutCleanup(exitWithoutCleanup);
}
+static void com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging(
+ JNIEnv* env, jobject clazz) {
+ HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_NONE;
+ if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level))) {
+ ALOGE("ERROR: could not disable heap pointer tagging\n");
+ }
+}
+
/*
* JNI registration.
*/
@@ -286,10 +295,12 @@
int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
const JNINativeMethod methods[] = {
- { "nativeFinishInit", "()V",
- (void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
- { "nativeSetExitWithoutCleanup", "(Z)V",
- (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
+ {"nativeFinishInit", "()V",
+ (void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
+ {"nativeSetExitWithoutCleanup", "(Z)V",
+ (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
+ {"nativeDisableHeapPointerTagging", "()V",
+ (void*)com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging},
};
return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
methods, NELEM(methods));
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 52ce54b..7bf35f9 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -193,7 +193,8 @@
{
int err = memtrack_proc_get(p, pid);
if (err != 0) {
- ALOGW("failed to get memory consumption info: %d", err);
+ // The memtrack HAL may not be available, do not log to avoid flooding
+ // logcat.
return err;
}
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/proto/OWNERS b/core/proto/OWNERS
index 74ced89..4892faa 100644
--- a/core/proto/OWNERS
+++ b/core/proto/OWNERS
@@ -14,6 +14,7 @@
# Frameworks
ogunwale@google.com
jjaggi@google.com
+per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com
# Launcher
hyunyoungs@google.com
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 4aa44fc..9bc0d96 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5393,6 +5393,10 @@
<!-- Description of media type: presentation file, such as PPT. The 'extension' variable is the file name extension. [CHAR LIMIT=32] -->
<string name="mime_type_presentation_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> presentation</string>
+ <!-- Strings for Bluetooth service -->
+ <!-- toast message informing user that Bluetooth stays on after airplane mode is turned on. [CHAR LIMIT=NONE] -->
+ <string name="bluetooth_airplane_mode_toast">Bluetooth will stay on during airplane mode</string>
+
<!-- Strings for car -->
<!-- String displayed when loading a user in the car [CHAR LIMIT=30] -->
<string name="car_loading_profile">Loading</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 383fcd4..cd43e9f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3795,6 +3795,9 @@
<java-symbol type="string" name="mime_type_presentation" />
<java-symbol type="string" name="mime_type_presentation_ext" />
+ <!-- For Bluetooth service -->
+ <java-symbol type="string" name="bluetooth_airplane_mode_toast" />
+
<!-- For high refresh rate displays -->
<java-symbol type="integer" name="config_defaultPeakRefreshRate" />
<java-symbol type="integer" name="config_defaultRefreshRateInZone" />
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..e2a389b
--- /dev/null
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -0,0 +1,116 @@
+/*
+ * 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 {
+ /**
+ * @hide
+ */
+ protected 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/media/java/android/media/tv/DvbDeviceInfo.java b/media/java/android/media/tv/DvbDeviceInfo.java
index 96c8528..54fc39e 100644
--- a/media/java/android/media/tv/DvbDeviceInfo.java
+++ b/media/java/android/media/tv/DvbDeviceInfo.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Parcel;
@@ -23,9 +24,13 @@
import android.util.Log;
/**
- * Simple container for information about DVB device.
- * Not for third-party developers.
+ * A digital video broadcasting (DVB) device.
*
+ * <p> Simple wrapper around a <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB
+ * v3</a> device.
+ *
+ * @see TvInputManager#getDvbDeviceList()
+ * @see TvInputManager#openDvbDevice(DvbDeviceInfo, int)
* @hide
*/
@SystemApi
@@ -67,17 +72,19 @@
}
/**
- * Returns the adapter ID of DVB device, in terms of enumerating the DVB device adapters
- * installed in the system. The adapter ID counts from zero.
+ * Returns the adapter ID.
+ *
+ * <p>DVB Adapters contain one or more devices.
*/
+ @IntRange(from = 0)
public int getAdapterId() {
return mAdapterId;
}
/**
- * Returns the device ID of DVB device, in terms of enumerating the DVB devices attached to
- * the same device adapter. The device ID counts from zero.
+ * Returns the device ID.
*/
+ @IntRange(from = 0)
public int getDeviceId() {
return mDeviceId;
}
diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java
index 6197c70..5babb16 100644
--- a/media/java/android/media/tv/TvContentRating.java
+++ b/media/java/android/media/tv/TvContentRating.java
@@ -17,7 +17,6 @@
package android.media.tv;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
import android.text.TextUtils;
import com.android.internal.util.Preconditions;
@@ -179,6 +178,10 @@
* <td>TV content rating system for Canada (French)</td>
* </tr>
* <tr>
+ * <td>DTMB</td>
+ * <td>DTMB content rating system</td>
+ * </tr>
+ * <tr>
* <td>DVB</td>
* <td>DVB content rating system</td>
* </tr>
@@ -199,10 +202,18 @@
* <td>TV content rating system for South Korea</td>
* </tr>
* <tr>
+ * <td>NZ_TV</td>
+ * <td>TV content rating system for New Zealand</td>
+ * </tr>
+ * <tr>
* <td>SG_TV</td>
* <td>TV content rating system for Singapore</td>
* </tr>
* <tr>
+ * <td>TH_TV</td>
+ * <td>TV content rating system for Thailand</td>
+ * </tr>
+ * <tr>
* <td>US_MV</td>
* <td>Movie content rating system for the United States</td>
* </tr>
@@ -356,6 +367,67 @@
* <td>Only to be viewed by adults</td>
* </tr>
* <tr>
+ * <td valign="top" rowspan="15">DTMB</td>
+ * <td>DTMB_4</td>
+ * <td>Recommended for ages 4 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_5</td>
+ * <td>Recommended for ages 5 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_6</td>
+ * <td>Recommended for ages 6 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_7</td>
+ * <td>Recommended for ages 7 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_8</td>
+ * <td>Recommended for ages 8 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_9</td>
+ * <td>Recommended for ages 9 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_10</td>
+ * <td>Recommended for ages 10 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_11</td>
+ * <td>Recommended for ages 11 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_12</td>
+ * <td>Recommended for ages 12 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_13</td>
+ * <td>Recommended for ages 13 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_14</td>
+ * <td>Recommended for ages 14 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_15</td>
+ * <td>Recommended for ages 15 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_16</td>
+ * <td>Recommended for ages 16 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_17</td>
+ * <td>Recommended for ages 17 and over</td>
+ * </tr>
+ * <tr>
+ * <td>DTMB_18</td>
+ * <td>Recommended for ages 18 and over</td>
+ * </tr>
+ * <tr>
* <td valign="top" rowspan="15">DVB</td>
* <td>DVB_4</td>
* <td>Recommended for ages 4 and over</td>
@@ -648,6 +720,22 @@
* <td>For adults only</td>
* </tr>
* <tr>
+ * <td valign="top" rowspan="3">NZ_TV</td>
+ * <td>NZ_TV_G</td>
+ * <td>Programmes which exclude material likely to be unsuitable for children. Programmes
+ * may not necessarily be designed for child viewers but should not contain material likely
+ * to alarm or distress them</td>
+ * </tr>
+ * <tr>
+ * <td>NZ_TV_PGR</td>
+ * <td>Programmes containing material more suited for mature audiences but not necessarily
+ * unsuitable for child viewers when subject to the guidance of a parent or an adult</td>
+ * </tr>
+ * <tr>
+ * <td>NZ_TV_AO</td>
+ * <td>Programmes containing adult themes and directed primarily at mature audiences</td>
+ * </tr>
+ * <tr>
* <td valign="top" rowspan="6">SG_TV</td>
* <td>SG_TV_G</td>
* <td>Suitable for all ages</td>
@@ -674,6 +762,31 @@
* <td>Suitable for adults aged 21 and above</td>
* </tr>
* <tr>
+ * <td valign="top" rowspan="6">TH_TV</td>
+ * <td>TH_TV_4</td>
+ * <td>Suitable for audiences 3 to 5 years of age</td>
+ * </tr>
+ * <tr>
+ * <td>TH_TV_6</td>
+ * <td>Suitable for audiences 6 to 12 years of age</td>
+ * </tr>
+ * <tr>
+ * <td>TH_TV_10</td>
+ * <td>Suitable for all audiences</td>
+ * </tr>
+ * <tr>
+ * <td>TH_TV_13</td>
+ * <td>Parental guidance suggested for viewers age below 13</td>
+ * </tr>
+ * <tr>
+ * <td>TH_TV_18</td>
+ * <td>Parental guidance suggested for viewers age below 18</td>
+ * </tr>
+ * <tr>
+ * <td>TH_TV_19</td>
+ * <td>Not suitable for children and teenagers</td>
+ * </tr>
+ * <tr>
* <td valign="top" rowspan="5">US_MV</td>
* <td>US_MV_G</td>
* <td>General audiences</td>
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 854ea43..ed4fe4b 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -1664,7 +1664,7 @@
}
/**
- * Returns the list of currently available DVB devices on the system.
+ * Returns the list of currently available DVB frontend devices on the system.
*
* @return the list of {@link DvbDeviceInfo} objects representing available DVB devices.
* @hide
@@ -1681,16 +1681,17 @@
}
/**
- * Returns a {@link ParcelFileDescriptor} of a specified DVB device for a given
- * {@link DvbDeviceInfo}
+ * Returns a {@link ParcelFileDescriptor} of a specified DVB device of a given type for a given
+ * {@link DvbDeviceInfo}.
*
* @param info A {@link DvbDeviceInfo} to open a DVB device.
- * @param deviceType A DVB device type. The type can be {@link #DVB_DEVICE_DEMUX},
- * {@link #DVB_DEVICE_DVR} or {@link #DVB_DEVICE_FRONTEND}.
+ * @param deviceType A DVB device type.
* @return a {@link ParcelFileDescriptor} of a specified DVB device for a given
- * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo}
- * failed to open.
+ * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo}
+ * failed to open.
* @throws IllegalArgumentException if {@code deviceType} is invalid or the device is not found.
+
+ * @see <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB API v3</a>
* @hide
*/
@SystemApi
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index 4318a0a..d4c4a62 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -352,8 +352,7 @@
if (!TextUtils.equals(mId, obj.mId) || mType != obj.mType
|| !TextUtils.equals(mLanguage, obj.mLanguage)
|| !TextUtils.equals(mDescription, obj.mDescription)
- || mEncrypted != obj.mEncrypted
- || !Objects.equals(mExtra, obj.mExtra)) {
+ || mEncrypted != obj.mEncrypted) {
return false;
}
@@ -381,7 +380,16 @@
@Override
public int hashCode() {
- return Objects.hashCode(mId);
+ int result = Objects.hash(mId, mType, mLanguage, mDescription);
+
+ if (mType == TYPE_AUDIO) {
+ result = Objects.hash(result, mAudioChannelCount, mAudioSampleRate);
+ } else if (mType == TYPE_VIDEO) {
+ result = Objects.hash(result, mVideoWidth, mVideoHeight, mVideoFrameRate,
+ mVideoPixelAspectRatio);
+ }
+
+ return result;
}
public static final @android.annotation.NonNull Parcelable.Creator<TvTrackInfo> CREATOR =
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 8d858dc..1fb50a9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1061,7 +1061,7 @@
// keep compatibility with apps that aren't direct boot aware.
// SysUI should just ignore this broadcast because it was already received
// and processed previously.
- if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
// Guarantee mTelephonyCapable state after SysUI crash and restart
if (args.simState == State.ABSENT) {
mHandler.obtainMessage(MSG_TELEPHONY_CAPABLE, true).sendToTarget();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 17c200e..b2f4f16 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -605,8 +605,7 @@
break;
case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
- if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,
- false)) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
}
updateSimState(intent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 7a09455..4523374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -517,7 +517,7 @@
break;
case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
- if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, false)) {
break;
}
// Might have different subscriptions now.
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index db6177a..21585ed 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -133,7 +133,7 @@
Assert.assertTrue("onSimStateChanged not called",
mKeyguardUpdateMonitor.hasSimStateJustChanged());
- intent.putExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK, true);
+ intent.putExtra(Intent.EXTRA_REBROADCAST_ON_UNLOCK, true);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
mTestableLooper.processAllMessages();
Assert.assertFalse("onSimStateChanged should have been skipped",
diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
new file mode 100644
index 0000000..31cd5d5
--- /dev/null
+++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java
@@ -0,0 +1,258 @@
+/*
+ * 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 com.android.server;
+
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHearingAid;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothProfile.ServiceListener;
+import android.content.Context;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * The BluetoothAirplaneModeListener handles system airplane mode change callback and checks
+ * whether we need to inform BluetoothManagerService on this change.
+ *
+ * The information of airplane mode turns on would not be passed to the BluetoothManagerService
+ * when Bluetooth is on and Bluetooth is in one of the following situations:
+ * 1. Bluetooth A2DP is connected.
+ * 2. Bluetooth Hearing Aid profile is connected.
+ */
+class BluetoothAirplaneModeListener {
+ private static final String TAG = "BluetoothAirplaneModeListener";
+ @VisibleForTesting static final String TOAST_COUNT = "bluetooth_airplane_toast_count";
+
+ private static final int MSG_AIRPLANE_MODE_CHANGED = 0;
+
+ @VisibleForTesting static final int MAX_TOAST_COUNT = 10; // 10 times
+
+ private final BluetoothManagerService mBluetoothManager;
+ private final BluetoothAirplaneModeHandler mHandler;
+ private AirplaneModeHelper mAirplaneHelper;
+
+ @VisibleForTesting int mToastCount = 0;
+
+ BluetoothAirplaneModeListener(BluetoothManagerService service, Looper looper, Context context) {
+ mBluetoothManager = service;
+
+ mHandler = new BluetoothAirplaneModeHandler(looper);
+ context.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
+ mAirplaneModeObserver);
+ }
+
+ private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean unused) {
+ // Post from system main thread to android_io thread.
+ Message msg = mHandler.obtainMessage(MSG_AIRPLANE_MODE_CHANGED);
+ mHandler.sendMessage(msg);
+ }
+ };
+
+ private class BluetoothAirplaneModeHandler extends Handler {
+ BluetoothAirplaneModeHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_AIRPLANE_MODE_CHANGED:
+ handleAirplaneModeChange();
+ break;
+ default:
+ Log.e(TAG, "Invalid message: " + msg.what);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Call after boot complete
+ */
+ @VisibleForTesting
+ void start(AirplaneModeHelper helper) {
+ Log.i(TAG, "start");
+ mAirplaneHelper = helper;
+ mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT);
+ }
+
+ @VisibleForTesting
+ boolean shouldPopToast() {
+ if (mToastCount >= MAX_TOAST_COUNT) {
+ return false;
+ }
+ mToastCount++;
+ mAirplaneHelper.setSettingsInt(TOAST_COUNT, mToastCount);
+ return true;
+ }
+
+ @VisibleForTesting
+ void handleAirplaneModeChange() {
+ if (shouldSkipAirplaneModeChange()) {
+ Log.i(TAG, "Ignore airplane mode change");
+ // We have to store Bluetooth state here, so if user turns off Bluetooth
+ // after airplane mode is turned on, we don't forget to turn on Bluetooth
+ // when airplane mode turns off.
+ mAirplaneHelper.setSettingsInt(Settings.Global.BLUETOOTH_ON,
+ BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
+ if (shouldPopToast()) {
+ mAirplaneHelper.showToastMessage();
+ }
+ return;
+ }
+ mAirplaneHelper.onAirplaneModeChanged(mBluetoothManager);
+ }
+
+ @VisibleForTesting
+ boolean shouldSkipAirplaneModeChange() {
+ if (mAirplaneHelper == null) {
+ return false;
+ }
+ if (!mAirplaneHelper.isBluetoothOn() || !mAirplaneHelper.isAirplaneModeOn()
+ || !mAirplaneHelper.isA2dpOrHearingAidConnected()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Helper class that handles callout and callback methods without
+ * complex logic.
+ */
+ @VisibleForTesting
+ public static class AirplaneModeHelper {
+ private volatile BluetoothA2dp mA2dp;
+ private volatile BluetoothHearingAid mHearingAid;
+ private final BluetoothAdapter mAdapter;
+ private final Context mContext;
+
+ AirplaneModeHelper(Context context) {
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ mContext = context;
+
+ mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP);
+ mAdapter.getProfileProxy(mContext, mProfileServiceListener,
+ BluetoothProfile.HEARING_AID);
+ }
+
+ private final ServiceListener mProfileServiceListener = new ServiceListener() {
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ // Setup Bluetooth profile proxies
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dp = (BluetoothA2dp) proxy;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAid = (BluetoothHearingAid) proxy;
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ // Clear Bluetooth profile proxies
+ switch (profile) {
+ case BluetoothProfile.A2DP:
+ mA2dp = null;
+ break;
+ case BluetoothProfile.HEARING_AID:
+ mHearingAid = null;
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ @VisibleForTesting
+ public boolean isA2dpOrHearingAidConnected() {
+ return isA2dpConnected() || isHearingAidConnected();
+ }
+
+ @VisibleForTesting
+ public boolean isBluetoothOn() {
+ final BluetoothAdapter adapter = mAdapter;
+ if (adapter == null) {
+ return false;
+ }
+ return adapter.getLeState() == BluetoothAdapter.STATE_ON;
+ }
+
+ @VisibleForTesting
+ public boolean isAirplaneModeOn() {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+ }
+
+ @VisibleForTesting
+ public void onAirplaneModeChanged(BluetoothManagerService managerService) {
+ managerService.onAirplaneModeChanged();
+ }
+
+ @VisibleForTesting
+ public int getSettingsInt(String name) {
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ name, 0);
+ }
+
+ @VisibleForTesting
+ public void setSettingsInt(String name, int value) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ name, value);
+ }
+
+ @VisibleForTesting
+ public void showToastMessage() {
+ Resources r = mContext.getResources();
+ final CharSequence text = r.getString(
+ R.string.bluetooth_airplane_mode_toast, 0);
+ Toast.makeText(mContext, text, Toast.LENGTH_LONG).show();
+ }
+
+ private boolean isA2dpConnected() {
+ final BluetoothA2dp a2dp = mA2dp;
+ if (a2dp == null) {
+ return false;
+ }
+ return a2dp.getConnectedDevices().size() > 0;
+ }
+
+ private boolean isHearingAidConnected() {
+ final BluetoothHearingAid hearingAid = mHearingAid;
+ if (hearingAid == null) {
+ return false;
+ }
+ return hearingAid.getConnectedDevices().size() > 0;
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index cdfd310..fa8eda5 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -68,6 +68,7 @@
import android.util.StatsLog;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.server.pm.UserRestrictionsUtils;
@@ -138,7 +139,8 @@
// Bluetooth persisted setting is on
// but Airplane mode will affect Bluetooth state at start up
// and Airplane mode will have higher priority.
- private static final int BLUETOOTH_ON_AIRPLANE = 2;
+ @VisibleForTesting
+ static final int BLUETOOTH_ON_AIRPLANE = 2;
private static final int SERVICE_IBLUETOOTH = 1;
private static final int SERVICE_IBLUETOOTHGATT = 2;
@@ -159,6 +161,8 @@
private boolean mBinding;
private boolean mUnbinding;
+ private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
+
// used inside handler thread
private boolean mQuietEnable = false;
private boolean mEnable;
@@ -257,68 +261,65 @@
}
};
- private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean unused) {
- synchronized (this) {
- if (isBluetoothPersistedStateOn()) {
- if (isAirplaneModeOn()) {
- persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
- } else {
- persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
- }
- }
-
- int st = BluetoothAdapter.STATE_OFF;
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- st = mBluetooth.getState();
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call getState", e);
- return;
- } finally {
- mBluetoothLock.readLock().unlock();
- }
-
- Slog.d(TAG,
- "Airplane Mode change - current state: " + BluetoothAdapter.nameForState(
- st) + ", isAirplaneModeOn()=" + isAirplaneModeOn());
-
+ public void onAirplaneModeChanged() {
+ synchronized (this) {
+ if (isBluetoothPersistedStateOn()) {
if (isAirplaneModeOn()) {
- // Clear registered LE apps to force shut-off
- clearBleApps();
-
- // If state is BLE_ON make sure we trigger disableBLE
- if (st == BluetoothAdapter.STATE_BLE_ON) {
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- addActiveLog(
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
- mContext.getPackageName(), false);
- mBluetooth.onBrEdrDown();
- mEnable = false;
- mEnableExternal = false;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call onBrEdrDown", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- } else if (st == BluetoothAdapter.STATE_ON) {
- sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
- mContext.getPackageName());
- }
- } else if (mEnableExternal) {
- sendEnableMsg(mQuietEnableExternal,
- BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
- mContext.getPackageName());
+ persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
+ } else {
+ persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
}
}
+
+ int st = BluetoothAdapter.STATE_OFF;
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) {
+ st = mBluetooth.getState();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call getState", e);
+ return;
+ } finally {
+ mBluetoothLock.readLock().unlock();
+ }
+
+ Slog.d(TAG,
+ "Airplane Mode change - current state: " + BluetoothAdapter.nameForState(
+ st) + ", isAirplaneModeOn()=" + isAirplaneModeOn());
+
+ if (isAirplaneModeOn()) {
+ // Clear registered LE apps to force shut-off
+ clearBleApps();
+
+ // If state is BLE_ON make sure we trigger disableBLE
+ if (st == BluetoothAdapter.STATE_BLE_ON) {
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) {
+ addActiveLog(
+ BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
+ mContext.getPackageName(), false);
+ mBluetooth.onBrEdrDown();
+ mEnable = false;
+ mEnableExternal = false;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call onBrEdrDown", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
+ }
+ } else if (st == BluetoothAdapter.STATE_ON) {
+ sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
+ mContext.getPackageName());
+ }
+ } else if (mEnableExternal) {
+ sendEnableMsg(mQuietEnableExternal,
+ BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE,
+ mContext.getPackageName());
+ }
}
- };
+ }
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -430,9 +431,8 @@
Settings.Global.getString(mContentResolver, Settings.Global.AIRPLANE_MODE_RADIOS);
if (airplaneModeRadios == null || airplaneModeRadios.contains(
Settings.Global.RADIO_BLUETOOTH)) {
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
- mAirplaneModeObserver);
+ mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
+ this, IoThread.get().getLooper(), context);
}
int systemUiUid = -1;
@@ -478,6 +478,17 @@
return state != BLUETOOTH_OFF;
}
+ private boolean isBluetoothPersistedStateOnAirplane() {
+ if (!supportBluetoothPersistedState()) {
+ return false;
+ }
+ int state = Settings.Global.getInt(mContentResolver, Settings.Global.BLUETOOTH_ON, -1);
+ if (DBG) {
+ Slog.d(TAG, "Bluetooth persisted state: " + state);
+ }
+ return state == BLUETOOTH_ON_AIRPLANE;
+ }
+
/**
* Returns true if the Bluetooth saved state is BLUETOOTH_ON_BLUETOOTH
*/
@@ -954,10 +965,12 @@
}
synchronized (mReceiver) {
- if (persist) {
- persistBluetoothSetting(BLUETOOTH_OFF);
+ if (!isBluetoothPersistedStateOnAirplane()) {
+ if (persist) {
+ persistBluetoothSetting(BLUETOOTH_OFF);
+ }
+ mEnableExternal = false;
}
- mEnableExternal = false;
sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST,
packageName);
}
@@ -1185,6 +1198,10 @@
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
mHandler.sendMessage(getMsg);
}
+ if (mBluetoothAirplaneModeListener != null) {
+ mBluetoothAirplaneModeListener.start(
+ new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext));
+ }
}
/**
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index da42edd..11f0a04 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -39,6 +39,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
@@ -86,12 +87,11 @@
import android.net.NattSocketKeepalive;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
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;
import android.net.NetworkMonitorManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkProvider;
@@ -212,6 +212,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
@@ -1573,48 +1574,49 @@
enforceAccessPermission();
final int uid = Binder.getCallingUid();
NetworkState state = getUnfilteredActiveNetworkState(uid);
- return state.linkProperties;
+ if (state.linkProperties == null) return null;
+ return linkPropertiesRestrictedForCallerPermissions(state.linkProperties,
+ Binder.getCallingPid(), uid);
}
@Override
public LinkProperties getLinkPropertiesForType(int networkType) {
enforceAccessPermission();
NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
- if (nai != null) {
- synchronized (nai) {
- return new LinkProperties(nai.linkProperties);
- }
- }
- return null;
+ final LinkProperties lp = getLinkProperties(nai);
+ if (lp == null) return null;
+ return linkPropertiesRestrictedForCallerPermissions(
+ lp, Binder.getCallingPid(), Binder.getCallingUid());
}
// TODO - this should be ALL networks
@Override
public LinkProperties getLinkProperties(Network network) {
enforceAccessPermission();
- return getLinkProperties(getNetworkAgentInfoForNetwork(network));
+ final LinkProperties lp = getLinkProperties(getNetworkAgentInfoForNetwork(network));
+ if (lp == null) return null;
+ return linkPropertiesRestrictedForCallerPermissions(
+ lp, Binder.getCallingPid(), Binder.getCallingUid());
}
- private LinkProperties getLinkProperties(NetworkAgentInfo nai) {
+ @Nullable
+ private LinkProperties getLinkProperties(@Nullable NetworkAgentInfo nai) {
if (nai == null) {
return null;
}
synchronized (nai) {
- return new LinkProperties(nai.linkProperties);
+ return nai.linkProperties;
}
}
private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
- if (nai != null) {
- synchronized (nai) {
- if (nai.networkCapabilities != null) {
- return networkCapabilitiesRestrictedForCallerPermissions(
- nai.networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
- }
- }
+ if (nai == null) return null;
+ synchronized (nai) {
+ if (nai.networkCapabilities == null) return null;
+ return networkCapabilitiesRestrictedForCallerPermissions(
+ nai.networkCapabilities,
+ Binder.getCallingPid(), Binder.getCallingUid());
}
- return null;
}
@Override
@@ -1633,13 +1635,38 @@
if (newNc.getNetworkSpecifier() != null) {
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
}
+ newNc.setAdministratorUids(Collections.EMPTY_LIST);
return newNc;
}
+ private LinkProperties linkPropertiesRestrictedForCallerPermissions(
+ LinkProperties lp, int callerPid, int callerUid) {
+ if (lp == null) return new LinkProperties();
+
+ // Only do a permission check if sanitization is needed, to avoid unnecessary binder calls.
+ final boolean needsSanitization =
+ (lp.getCaptivePortalApiUrl() != null || lp.getCaptivePortalData() != null);
+ if (!needsSanitization) {
+ return new LinkProperties(lp);
+ }
+
+ if (checkSettingsPermission(callerPid, callerUid)) {
+ return lp.makeSensitiveFieldsParcelingCopy();
+ }
+
+ final LinkProperties newLp = new LinkProperties(lp);
+ // Sensitive fields would not be parceled anyway, but sanitize for consistency before the
+ // object gets parceled.
+ newLp.setCaptivePortalApiUrl(null);
+ newLp.setCaptivePortalData(null);
+ return newLp;
+ }
+
private void restrictRequestUidsForCaller(NetworkCapabilities nc) {
if (!checkSettingsPermission()) {
nc.setSingleUid(Binder.getCallingUid());
}
+ nc.setAdministratorUids(Collections.EMPTY_LIST);
}
private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) {
@@ -2624,8 +2651,8 @@
if (nai.everConnected) {
loge("ERROR: cannot call explicitlySelected on already-connected network");
}
- nai.networkMisc.explicitlySelected = toBool(msg.arg1);
- nai.networkMisc.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
+ nai.networkAgentConfig.explicitlySelected = toBool(msg.arg1);
+ nai.networkAgentConfig.acceptUnvalidated = toBool(msg.arg1) && toBool(msg.arg2);
// Mark the network as temporarily accepting partial connectivity so that it
// will be validated (and possibly become default) even if it only provides
// partial internet access. Note that if user connects to partial connectivity
@@ -2633,7 +2660,7 @@
// out of wifi coverage) and if the same wifi is available again, the device
// will auto connect to this wifi even though the wifi has "no internet".
// TODO: Evaluate using a separate setting in IpMemoryStore.
- nai.networkMisc.acceptPartialConnectivity = toBool(msg.arg2);
+ nai.networkAgentConfig.acceptPartialConnectivity = toBool(msg.arg2);
break;
}
case NetworkAgent.EVENT_SOCKET_KEEPALIVE: {
@@ -2665,10 +2692,10 @@
}
// Only show the notification when the private DNS is broken and the
// PRIVATE_DNS_BROKEN notification hasn't shown since last valid.
- if (privateDnsBroken && !nai.networkMisc.hasShownBroken) {
+ if (privateDnsBroken && !nai.networkAgentConfig.hasShownBroken) {
showNetworkNotification(nai, NotificationType.PRIVATE_DNS_BROKEN);
}
- nai.networkMisc.hasShownBroken = privateDnsBroken;
+ nai.networkAgentConfig.hasShownBroken = privateDnsBroken;
} else if (nai.networkCapabilities.isPrivateDnsBroken()) {
// If probePrivateDnsCompleted is false but nai.networkCapabilities says
// private DNS is broken, it means this network is being reevaluated.
@@ -2678,7 +2705,7 @@
nai.networkCapabilities.setPrivateDnsBroken(false);
final int oldScore = nai.getCurrentScore();
updateCapabilities(oldScore, nai, nai.networkCapabilities);
- nai.networkMisc.hasShownBroken = false;
+ nai.networkAgentConfig.hasShownBroken = false;
}
break;
}
@@ -2737,7 +2764,7 @@
// If network becomes valid, the hasShownBroken should be reset for
// that network so that the notification will be fired when the private
// DNS is broken again.
- nai.networkMisc.hasShownBroken = false;
+ nai.networkAgentConfig.hasShownBroken = false;
}
} else if (partialConnectivityChanged) {
updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities);
@@ -2796,9 +2823,10 @@
loge("EVENT_PROVISIONING_NOTIFICATION from unknown NetworkMonitor");
break;
}
- if (!nai.networkMisc.provisioningNotificationDisabled) {
+ if (!nai.networkAgentConfig.provisioningNotificationDisabled) {
mNotifier.showNotification(netId, NotificationType.SIGN_IN, nai, null,
- (PendingIntent) msg.obj, nai.networkMisc.explicitlySelected);
+ (PendingIntent) msg.obj,
+ nai.networkAgentConfig.explicitlySelected);
}
}
break;
@@ -2835,26 +2863,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);
}
}
@@ -3163,8 +3176,8 @@
// This should never fail. Specifying an already in use NetID will cause failure.
if (networkAgent.isVPN()) {
mNetd.networkCreateVpn(networkAgent.network.netId,
- (networkAgent.networkMisc == null
- || !networkAgent.networkMisc.allowBypass));
+ (networkAgent.networkAgentConfig == null
+ || !networkAgent.networkAgentConfig.allowBypass));
} else {
mNetd.networkCreatePhysical(networkAgent.network.netId,
getNetworkPermission(networkAgent.networkCapabilities));
@@ -3464,16 +3477,16 @@
return;
}
- if (!nai.networkMisc.explicitlySelected) {
+ if (!nai.networkAgentConfig.explicitlySelected) {
Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
}
- if (accept != nai.networkMisc.acceptUnvalidated) {
- nai.networkMisc.acceptUnvalidated = accept;
+ if (accept != nai.networkAgentConfig.acceptUnvalidated) {
+ nai.networkAgentConfig.acceptUnvalidated = accept;
// If network becomes partial connectivity and user already accepted to use this
// network, we should respect the user's option and don't need to popup the
// PARTIAL_CONNECTIVITY notification to user again.
- nai.networkMisc.acceptPartialConnectivity = accept;
+ nai.networkAgentConfig.acceptPartialConnectivity = accept;
rematchAllNetworksAndRequests();
sendUpdatedScoreToFactories(nai);
}
@@ -3510,8 +3523,8 @@
return;
}
- if (accept != nai.networkMisc.acceptPartialConnectivity) {
- nai.networkMisc.acceptPartialConnectivity = accept;
+ if (accept != nai.networkAgentConfig.acceptPartialConnectivity) {
+ nai.networkAgentConfig.acceptPartialConnectivity = accept;
}
// TODO: Use the current design or save the user choice into IpMemoryStore.
@@ -3736,7 +3749,7 @@
action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY;
// Don't bother the user with a high-priority notification if the network was not
// explicitly selected by the user.
- highPriority = nai.networkMisc.explicitlySelected;
+ highPriority = nai.networkAgentConfig.explicitlySelected;
break;
default:
Slog.wtf(TAG, "Unknown notification type " + type);
@@ -3769,14 +3782,15 @@
// automatically connects to a network that has partial Internet access, the user will
// always be able to use it, either because they've already chosen "don't ask again" or
// because we have prompt them.
- if (nai.partialConnectivity && !nai.networkMisc.acceptPartialConnectivity) {
+ if (nai.partialConnectivity && !nai.networkAgentConfig.acceptPartialConnectivity) {
return true;
}
// If a network has no Internet access, only prompt if the network was explicitly selected
// and if the user has not already told us to use the network regardless of whether it
// validated or not.
- if (nai.networkMisc.explicitlySelected && !nai.networkMisc.acceptUnvalidated) {
+ if (nai.networkAgentConfig.explicitlySelected
+ && !nai.networkAgentConfig.acceptUnvalidated) {
return true;
}
@@ -5501,9 +5515,9 @@
*/
public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkMisc networkMisc) {
+ int currentScore, NetworkAgentConfig networkAgentConfig) {
return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities,
- currentScore, networkMisc, NetworkProvider.ID_NONE);
+ currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
}
/**
@@ -5518,13 +5532,13 @@
* later : see {@link #updateCapabilities}.
* @param currentScore the initial score of the network. See
* {@link NetworkAgentInfo#getCurrentScore}.
- * @param networkMisc metadata about the network. This is never updated.
+ * @param networkAgentConfig 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 Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
- int currentScore, NetworkMisc networkMisc, int providerId) {
+ int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
enforceNetworkFactoryPermission();
LinkProperties lp = new LinkProperties(linkProperties);
@@ -5536,8 +5550,8 @@
ns.putIntExtension(NetworkScore.LEGACY_SCORE, currentScore);
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
- ns, mContext, mTrackerHandler, new NetworkMisc(networkMisc), this, mNetd,
- mDnsResolver, mNMS, providerId);
+ ns, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), this,
+ mNetd, mDnsResolver, mNMS, providerId);
// Make sure the network capabilities reflect what the agent info says.
nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
final String extraInfo = networkInfo.getExtraInfo();
@@ -5807,6 +5821,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,
@@ -5847,11 +5874,6 @@
} else {
newNc.addCapability(NET_CAPABILITY_FOREGROUND);
}
- if (nai.isSuspended()) {
- newNc.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
- } else {
- newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
- }
if (nai.partialConnectivity) {
newNc.addCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY);
} else {
@@ -5859,6 +5881,12 @@
}
newNc.setPrivateDnsBroken(nai.networkCapabilities.isPrivateDnsBroken());
+ // TODO : remove this once all factories are updated to send NOT_SUSPENDED and NOT_ROAMING
+ if (!newNc.hasTransport(TRANSPORT_CELLULAR)) {
+ newNc.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ newNc.addCapability(NET_CAPABILITY_NOT_ROAMING);
+ }
+
return newNc;
}
@@ -5878,21 +5906,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);
@@ -5903,6 +5921,19 @@
// on this network. We might have been called by rematchNetworkAndRequests when a
// network changed foreground state.
processListenRequests(nai);
+ final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (prevSuspended != suspended || prevRoaming != roaming) {
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
+ notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+ : ConnectivityManager.CALLBACK_RESUMED);
+ // updateNetworkInfo will mix in the suspended info from the capabilities and
+ // take appropriate action for the network having possibly changed state.
+ updateNetworkInfo(nai, nai.networkInfo);
+ }
} else {
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
@@ -5910,6 +5941,9 @@
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+ // TODO : static analysis indicates that prevNc can't be null here (getAndSetNetworkCaps
+ // never returns null), so mark the relevant members and functions in nai as @NonNull and
+ // remove this test
if (prevNc != null) {
final boolean oldMetered = prevNc.isMetered();
final boolean newMetered = newNc.isMetered();
@@ -5958,7 +5992,7 @@
LinkProperties lp) {
if (nc == null || lp == null) return false;
return nai.isVPN()
- && !nai.networkMisc.allowBypass
+ && !nai.networkAgentConfig.allowBypass
&& nc.getEstablishingVpnAppUid() != Process.SYSTEM_UID
&& lp.getInterfaceName() != null
&& (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute());
@@ -6131,7 +6165,8 @@
case ConnectivityManager.CALLBACK_AVAILABLE: {
putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions(
networkAgent.networkCapabilities, nri.mPid, nri.mUid));
- putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+ putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
+ networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
msg.arg1 = arg1;
break;
@@ -6148,7 +6183,8 @@
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
- putParcelable(bundle, new LinkProperties(networkAgent.linkProperties));
+ putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
+ networkAgent.linkProperties, nri.mPid, nri.mUid));
break;
}
case ConnectivityManager.CALLBACK_BLK_CHANGED: {
@@ -6257,6 +6293,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();
@@ -6302,8 +6362,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.
@@ -6311,15 +6371,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());
@@ -6422,39 +6489,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);
}
/**
@@ -6476,12 +6516,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
@@ -6513,6 +6565,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,
@@ -6604,10 +6674,31 @@
}
}
- private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
+ @NonNull
+ private NetworkInfo mixInInfo(@NonNull final NetworkAgentInfo nai, @NonNull NetworkInfo info) {
+ final NetworkInfo newInfo = new NetworkInfo(info);
+ // The suspended and roaming bits are managed in NetworkCapabilities.
+ final boolean suspended =
+ !nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ if (suspended && info.getDetailedState() == NetworkInfo.DetailedState.CONNECTED) {
+ // Only override the state with SUSPENDED if the network is currently in CONNECTED
+ // state. This is because the network could have been suspended before connecting,
+ // or it could be disconnecting while being suspended, and in both these cases
+ // the state should not be overridden. Note that the only detailed state that
+ // maps to State.CONNECTED is DetailedState.CONNECTED, so there is also no need to
+ // worry about multiple different substates of CONNECTED.
+ newInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, info.getReason(),
+ info.getExtraInfo());
+ }
+ newInfo.setRoaming(!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ return newInfo;
+ }
+
+ private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo info) {
+ final NetworkInfo newInfo = mixInInfo(networkAgent, info);
+
final NetworkInfo.State state = newInfo.getState();
NetworkInfo oldInfo = null;
- final int oldScore = networkAgent.getCurrentScore();
synchronized (networkAgent) {
oldInfo = networkAgent.networkInfo;
networkAgent.networkInfo = newInfo;
@@ -6649,7 +6740,7 @@
// command must be sent after updating LinkProperties to maximize chances of
// NetworkMonitor seeing the correct LinkProperties when starting.
// TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call.
- if (networkAgent.networkMisc.acceptPartialConnectivity) {
+ if (networkAgent.networkAgentConfig.acceptPartialConnectivity) {
networkAgent.networkMonitor().setAcceptPartialConnectivity();
}
networkAgent.networkMonitor().notifyNetworkConnected(
@@ -6689,17 +6780,6 @@
}
} else if (networkAgent.created && (oldInfo.getState() == NetworkInfo.State.SUSPENDED ||
state == NetworkInfo.State.SUSPENDED)) {
- // going into or coming out of SUSPEND: re-score and notify
- if (networkAgent.getCurrentScore() != oldScore) {
- rematchAllNetworksAndRequests();
- }
- updateCapabilities(networkAgent.getCurrentScore(), networkAgent,
- networkAgent.networkCapabilities);
- // TODO (b/73132094) : remove this call once the few users of onSuspended and
- // onResumed have been removed.
- notifyNetworkCallbacks(networkAgent, (state == NetworkInfo.State.SUSPENDED ?
- ConnectivityManager.CALLBACK_SUSPENDED :
- ConnectivityManager.CALLBACK_RESUMED));
mLegacyTypeTracker.update(networkAgent);
}
}
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/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 7fd98e0..c125b1b 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -17,6 +17,7 @@
import android.content.ContentResolver;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.debug.AdbManagerInternal;
import android.debug.IAdbManager;
@@ -260,6 +261,30 @@
}
}
+ /**
+ * @return true if the device supports secure ADB over Wi-Fi.
+ * @hide
+ */
+ @Override
+ public boolean isAdbWifiSupported() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.MANAGE_DEBUGGING, "AdbService");
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
+ }
+
+ /**
+ * @return true if the device supports secure ADB over Wi-Fi and device pairing by
+ * QR code.
+ * @hide
+ */
+ @Override
+ public boolean isAdbWifiQrSupported() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.MANAGE_DEBUGGING, "AdbService");
+ return isAdbWifiSupported() && mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_CAMERA_ANY);
+ }
+
private void setAdbEnabled(boolean enable) {
if (DEBUG) Slog.d(TAG, "setAdbEnabled(" + enable + "), mAdbEnabled=" + mAdbEnabled);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index b311233..1452e25 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1538,6 +1538,9 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+ // Also, add the UserHandle for mainline modules which can't use the @hide
+ // EXTRA_USER_HANDLE.
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId));
mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
@@ -1554,6 +1557,9 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId);
+ // Also, add the UserHandle for mainline modules which can't use the @hide
+ // EXTRA_USER_HANDLE.
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId));
mInjector.broadcastIntent(intent,
null, null, 0, null, null, null, AppOpsManager.OP_NONE,
null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
@@ -1563,6 +1569,9 @@
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND);
intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+ // Also, add the UserHandle for mainline modules which can't use the @hide
+ // EXTRA_USER_HANDLE.
+ intent.putExtra(Intent.EXTRA_USER, UserHandle.of(newUserId));
mInjector.broadcastIntent(intent,
null, null, 0, null, null,
new String[] {android.Manifest.permission.MANAGE_USERS},
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index aea6d8d..f636d67 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -116,7 +116,8 @@
&& !lp.hasIpv4Address();
// If the network tells us it doesn't use clat, respect that.
- final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat;
+ final boolean skip464xlat = (nai.netAgentConfig() != null)
+ && nai.netAgentConfig().skip464xlat;
return supported && connected && isIpv6OnlyNetwork && !skip464xlat;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 5e085ca..d66aec5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -23,9 +23,9 @@
import android.net.INetworkMonitor;
import android.net.LinkProperties;
import android.net.Network;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.net.NetworkMonitorManager;
import android.net.NetworkRequest;
import android.net.NetworkScore;
@@ -127,7 +127,7 @@
// This should only be modified by ConnectivityService, via setNetworkCapabilities().
// TODO: make this private with a getter.
public NetworkCapabilities networkCapabilities;
- public final NetworkMisc networkMisc;
+ public final NetworkAgentConfig networkAgentConfig;
// Indicates if netd has been told to create this Network. From this point on the appropriate
// routing rules are setup and routes are added so packets can begin flowing over the Network.
// This is a sticky bit; once set it is never cleared.
@@ -261,7 +261,7 @@
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, @NonNull NetworkScore ns, Context context,
- Handler handler, NetworkMisc misc, ConnectivityService connService, INetd netd,
+ Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber) {
this.messenger = messenger;
asyncChannel = ac;
@@ -274,7 +274,7 @@
mConnService = connService;
mContext = context;
mHandler = handler;
- networkMisc = misc;
+ networkAgentConfig = config;
this.factorySerialNumber = factorySerialNumber;
}
@@ -309,8 +309,8 @@
return mConnService;
}
- public NetworkMisc netMisc() {
- return networkMisc;
+ public NetworkAgentConfig netAgentConfig() {
+ return networkAgentConfig;
}
public Handler handler() {
@@ -451,15 +451,6 @@
&& !isLingering();
}
- /**
- * Returns whether this network is currently suspended. A network is suspended if it is still
- * connected but data temporarily fails to transfer. See {@link NetworkInfo.State#SUSPENDED}
- * and {@link NetworkCapabilities#NET_CAPABILITY_NOT_SUSPENDED}.
- */
- public boolean isSuspended() {
- return networkInfo.getState() == NetworkInfo.State.SUSPENDED;
- }
-
// Does this network satisfy request?
public boolean satisfies(NetworkRequest request) {
return created &&
@@ -487,7 +478,8 @@
// selected and we're trying to see what its score could be. This ensures that we don't tear
// down an explicitly selected network before the user gets a chance to prefer it when
// a higher-scoring network (e.g., Ethernet) is available.
- if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
+ if (networkAgentConfig.explicitlySelected
+ && (networkAgentConfig.acceptUnvalidated || pretendValidated)) {
return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
}
@@ -533,7 +525,8 @@
synchronized (this) {
// Network objects are outwardly immutable so there is no point in duplicating.
// Duplicating also precludes sharing socket factories and connection pools.
- final String subscriberId = (networkMisc != null) ? networkMisc.subscriberId : null;
+ final String subscriberId = (networkAgentConfig != null)
+ ? networkAgentConfig.subscriberId : null;
return new NetworkState(new NetworkInfo(networkInfo),
new LinkProperties(linkProperties),
new NetworkCapabilities(networkCapabilities), network, subscriberId, null);
@@ -641,13 +634,13 @@
+ "nc{" + networkCapabilities + "} Score{" + getCurrentScore() + "} "
+ "everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} "
+ "created{" + created + "} lingering{" + isLingering() + "} "
- + "explicitlySelected{" + networkMisc.explicitlySelected + "} "
- + "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} "
+ + "explicitlySelected{" + networkAgentConfig.explicitlySelected + "} "
+ + "acceptUnvalidated{" + networkAgentConfig.acceptUnvalidated + "} "
+ "everCaptivePortalDetected{" + everCaptivePortalDetected + "} "
+ "lastCaptivePortalDetected{" + lastCaptivePortalDetected + "} "
+ "captivePortalValidationPending{" + captivePortalValidationPending + "} "
+ "partialConnectivity{" + partialConnectivity + "} "
- + "acceptPartialConnectivity{" + networkMisc.acceptPartialConnectivity + "} "
+ + "acceptPartialConnectivity{" + networkAgentConfig.acceptPartialConnectivity + "} "
+ "clat{" + clatd + "} "
+ "}";
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 08e73ef..69ab47a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -53,10 +53,10 @@
import android.net.LocalSocketAddress;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkMisc;
import android.net.NetworkProvider;
import android.net.RouteInfo;
import android.net.UidRange;
@@ -914,7 +914,7 @@
* has certain changes, in which case this method would just return {@code false}.
*/
private boolean updateLinkPropertiesInPlaceIfPossible(NetworkAgent agent, VpnConfig oldConfig) {
- // NetworkMisc cannot be updated without registering a new NetworkAgent.
+ // NetworkAgentConfig cannot be updated without registering a new NetworkAgent.
if (oldConfig.allowBypass != mConfig.allowBypass) {
Log.i(TAG, "Handover not possible due to changes to allowBypass");
return false;
@@ -947,8 +947,8 @@
mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null);
- NetworkMisc networkMisc = new NetworkMisc();
- networkMisc.allowBypass = mConfig.allowBypass && !mLockdown;
+ NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig();
+ networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown;
mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid());
mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle,
@@ -957,7 +957,7 @@
try {
mNetworkAgent = new NetworkAgent(mLooper, mContext, NETWORKTYPE /* logtag */,
mNetworkInfo, mNetworkCapabilities, lp,
- ConnectivityConstants.VPN_DEFAULT_SCORE, networkMisc,
+ ConnectivityConstants.VPN_DEFAULT_SCORE, networkAgentConfig,
NetworkProvider.ID_VPN) {
@Override
public void unwanted() {
diff --git a/services/core/java/com/android/server/lights/OWNERS b/services/core/java/com/android/server/lights/OWNERS
index c7c6d56..0e795b9 100644
--- a/services/core/java/com/android/server/lights/OWNERS
+++ b/services/core/java/com/android/server/lights/OWNERS
@@ -1,2 +1,3 @@
michaelwr@google.com
-dangittik@google.com
+santoscordon@google.com
+flc@google.com
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/core/java/com/android/server/policy/GlobalKeyManager.java b/services/core/java/com/android/server/policy/GlobalKeyManager.java
index e08c004..157f825 100644
--- a/services/core/java/com/android/server/policy/GlobalKeyManager.java
+++ b/services/core/java/com/android/server/policy/GlobalKeyManager.java
@@ -74,7 +74,7 @@
Intent intent = new Intent(Intent.ACTION_GLOBAL_BUTTON)
.setComponent(component)
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .putExtra(Intent.EXTRA_KEY_EVENT, event);
+ .putExtra(Intent.EXTRA_KEY_EVENT, new KeyEvent(event));
context.sendBroadcastAsUser(intent, UserHandle.CURRENT, null);
return true;
}
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/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
new file mode 100644
index 0000000..968a402
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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 com.android.server;
+
+import static org.mockito.Mockito.*;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.os.Looper;
+import android.provider.Settings;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class BluetoothAirplaneModeListenerTest {
+ private Context mContext;
+ private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener;
+ private BluetoothAdapter mBluetoothAdapter;
+ private AirplaneModeHelper mHelper;
+
+ @Mock BluetoothManagerService mBluetoothManagerService;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+
+ mHelper = mock(AirplaneModeHelper.class);
+ when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT))
+ .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT);
+ doNothing().when(mHelper).setSettingsInt(anyString(), anyInt());
+ doNothing().when(mHelper).showToastMessage();
+ doNothing().when(mHelper).onAirplaneModeChanged(any(BluetoothManagerService.class));
+
+ mBluetoothAirplaneModeListener = new BluetoothAirplaneModeListener(
+ mBluetoothManagerService, Looper.getMainLooper(), mContext);
+ mBluetoothAirplaneModeListener.start(mHelper);
+ }
+
+ @Test
+ public void testIgnoreOnAirplanModeChange() {
+ Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
+
+ when(mHelper.isBluetoothOn()).thenReturn(true);
+ Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
+
+ when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
+ Assert.assertFalse(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
+
+ when(mHelper.isAirplaneModeOn()).thenReturn(true);
+ Assert.assertTrue(mBluetoothAirplaneModeListener.shouldSkipAirplaneModeChange());
+ }
+
+ @Test
+ public void testHandleAirplaneModeChange_InvokeAirplaneModeChanged() {
+ mBluetoothAirplaneModeListener.handleAirplaneModeChange();
+ verify(mHelper).onAirplaneModeChanged(mBluetoothManagerService);
+ }
+
+ @Test
+ public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_NotPopToast() {
+ mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
+ when(mHelper.isBluetoothOn()).thenReturn(true);
+ when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
+ when(mHelper.isAirplaneModeOn()).thenReturn(true);
+ mBluetoothAirplaneModeListener.handleAirplaneModeChange();
+
+ verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
+ BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
+ verify(mHelper, times(0)).showToastMessage();
+ verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
+ }
+
+ @Test
+ public void testHandleAirplaneModeChange_NotInvokeAirplaneModeChanged_PopToast() {
+ mBluetoothAirplaneModeListener.mToastCount = 0;
+ when(mHelper.isBluetoothOn()).thenReturn(true);
+ when(mHelper.isA2dpOrHearingAidConnected()).thenReturn(true);
+ when(mHelper.isAirplaneModeOn()).thenReturn(true);
+ mBluetoothAirplaneModeListener.handleAirplaneModeChange();
+
+ verify(mHelper).setSettingsInt(Settings.Global.BLUETOOTH_ON,
+ BluetoothManagerService.BLUETOOTH_ON_AIRPLANE);
+ verify(mHelper).showToastMessage();
+ verify(mHelper, times(0)).onAirplaneModeChanged(mBluetoothManagerService);
+ }
+
+ @Test
+ public void testIsPopToast_PopToast() {
+ mBluetoothAirplaneModeListener.mToastCount = 0;
+ Assert.assertTrue(mBluetoothAirplaneModeListener.shouldPopToast());
+ verify(mHelper).setSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT, 1);
+ }
+
+ @Test
+ public void testIsPopToast_NotPopToast() {
+ mBluetoothAirplaneModeListener.mToastCount = BluetoothAirplaneModeListener.MAX_TOAST_COUNT;
+ Assert.assertFalse(mBluetoothAirplaneModeListener.shouldPopToast());
+ verify(mHelper, times(0)).setSettingsInt(anyString(), anyInt());
+ }
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 826a89e..acf51f3 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -547,8 +547,14 @@
*/
public static final int PROPERTY_VOIP_AUDIO_MODE = 0x00001000;
+ /**
+ * Indicates that the call is an adhoc conference call. This property can be set for both
+ * incoming and outgoing calls.
+ */
+ public static final int PROPERTY_IS_ADHOC_CONFERENCE = 0x00002000;
+
//******************************************************************************************
- // Next PROPERTY value: 0x00002000
+ // Next PROPERTY value: 0x00004000
//******************************************************************************************
private final String mTelecomCallId;
@@ -726,6 +732,9 @@
if (hasProperty(properties, PROPERTY_VOIP_AUDIO_MODE)) {
builder.append(" PROPERTY_VOIP_AUDIO_MODE");
}
+ if (hasProperty(properties, PROPERTY_IS_ADHOC_CONFERENCE)) {
+ builder.append(" PROPERTY_IS_ADHOC_CONFERENCE");
+ }
builder.append("]");
return builder.toString();
}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 456290c..6b0845f 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -69,6 +69,7 @@
public void onConnectionEvent(Conference c, String event, Bundle extras) {}
public void onCallerDisplayNameChanged(
Conference c, String callerDisplayName, int presentation) {}
+ public void onRingbackRequested(Conference c, boolean ringback) {}
}
private final Set<Listener> mListeners = new CopyOnWriteArraySet<>();
@@ -97,6 +98,7 @@
private int mAddressPresentation;
private String mCallerDisplayName;
private int mCallerDisplayNamePresentation;
+ private boolean mRingbackRequested = false;
private final Connection.Listener mConnectionDeathListener = new Connection.Listener() {
@Override
@@ -170,6 +172,14 @@
}
/**
+ * Returns whether this conference is requesting that the system play a ringback tone
+ * on its behalf.
+ */
+ public final boolean isRingbackRequested() {
+ return mRingbackRequested;
+ }
+
+ /**
* Returns the capabilities of the conference. See {@code CAPABILITY_*} constants in class
* {@link Connection} for valid values.
*
@@ -308,6 +318,35 @@
public void onConnectionAdded(Connection connection) {}
/**
+ * Notifies this Conference, which is in {@code STATE_RINGING}, of
+ * a request to accept.
+ * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+ * the default dialer's {@link InCallService}.
+ *
+ * @param videoState The video state in which to answer the connection.
+ */
+ public void onAnswer(int videoState) {}
+
+ /**
+ * Notifies this Conference, which is in {@code STATE_RINGING}, of
+ * a request to accept.
+ * For managed {@link ConnectionService}s, this will be called when the user answers a call via
+ * the default dialer's {@link InCallService}.
+ * @hide
+ */
+ public final void onAnswer() {
+ onAnswer(VideoProfile.STATE_AUDIO_ONLY);
+ }
+
+ /**
+ * Notifies this Conference, which is in {@code STATE_RINGING}, of
+ * a request to reject.
+ * For managed {@link ConnectionService}s, this will be called when the user rejects a call via
+ * the default dialer's {@link InCallService}.
+ */
+ public void onReject() {}
+
+ /**
* Sets state to be on hold.
*/
public final void setOnHold() {
@@ -322,9 +361,17 @@
}
/**
+ * Sets state to be ringing.
+ */
+ public final void setRinging() {
+ setState(Connection.STATE_RINGING);
+ }
+
+ /**
* Sets state to be active.
*/
public final void setActive() {
+ setRingbackRequested(false);
setState(Connection.STATE_ACTIVE);
}
@@ -436,6 +483,21 @@
}
/**
+ * Requests that the framework play a ringback tone. This is to be invoked by implementations
+ * that do not play a ringback tone themselves in the conference's audio stream.
+ *
+ * @param ringback Whether the ringback tone is to be played.
+ */
+ public final void setRingbackRequested(boolean ringback) {
+ if (mRingbackRequested != ringback) {
+ mRingbackRequested = ringback;
+ for (Listener l : mListeners) {
+ l.onRingbackRequested(this, ringback);
+ }
+ }
+ }
+
+ /**
* Set the video state for the conference.
* Valid values: {@link VideoProfile#STATE_AUDIO_ONLY},
* {@link VideoProfile#STATE_BIDIRECTIONAL},
@@ -640,14 +702,6 @@
}
private void setState(int newState) {
- if (newState != Connection.STATE_ACTIVE &&
- newState != Connection.STATE_HOLDING &&
- newState != Connection.STATE_DISCONNECTED) {
- Log.w(this, "Unsupported state transition for Conference call.",
- Connection.stateToString(newState));
- return;
- }
-
if (mState != newState) {
int oldState = mState;
mState = newState;
@@ -657,6 +711,37 @@
}
}
+ private static class FailureSignalingConference extends Conference {
+ private boolean mImmutable = false;
+ public FailureSignalingConference(DisconnectCause disconnectCause,
+ PhoneAccountHandle phoneAccount) {
+ super(phoneAccount);
+ setDisconnected(disconnectCause);
+ mImmutable = true;
+ }
+ public void checkImmutable() {
+ if (mImmutable) {
+ throw new UnsupportedOperationException("Conference is immutable");
+ }
+ }
+ }
+
+ /**
+ * Return a {@code Conference} which represents a failed conference attempt. The returned
+ * {@code Conference} will have a {@link android.telecom.DisconnectCause} and as specified,
+ * and a {@link #getState()} of {@code STATE_DISCONNECTED}.
+ * <p>
+ * The returned {@code Conference} can be assumed to {@link #destroy()} itself when appropriate,
+ * so users of this method need not maintain a reference to its return value to destroy it.
+ *
+ * @param disconnectCause The disconnect cause, ({@see android.telecomm.DisconnectCause}).
+ * @return A {@code Conference} which indicates failure.
+ */
+ public @NonNull static Conference createFailedConference(
+ @NonNull DisconnectCause disconnectCause, @NonNull PhoneAccountHandle phoneAccount) {
+ return new FailureSignalingConference(disconnectCause, phoneAccount);
+ }
+
private final void clearConferenceableList() {
for (Connection c : mConferenceableConnections) {
c.removeConnectionListener(mConnectionDeathListener);
@@ -667,11 +752,13 @@
@Override
public String toString() {
return String.format(Locale.US,
- "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]",
+ "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s,"
+ + "isRingbackRequested: %s, ThisObject %s]",
Connection.stateToString(mState),
Call.Details.capabilitiesToString(mConnectionCapabilities),
getVideoState(),
getVideoProvider(),
+ isRingbackRequested() ? "Y" : "N",
super.toString());
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index f205ec6..c934625 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -497,8 +497,17 @@
@TestApi
public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11;
+ /**
+ * Set by the framework to indicate that it is an adhoc conference call.
+ * <p>
+ * This is used for Outgoing and incoming conference calls.
+ *
+ */
+ public static final int PROPERTY_IS_ADHOC_CONFERENCE = 1 << 12;
+
+
//**********************************************************************************************
- // Next PROPERTY value: 1<<12
+ // Next PROPERTY value: 1<<13
//**********************************************************************************************
/**
@@ -1018,6 +1027,10 @@
builder.append(isLong ? " PROPERTY_REMOTELY_HOSTED" : " remote_hst");
}
+ if ((properties & PROPERTY_IS_ADHOC_CONFERENCE) == PROPERTY_IS_ADHOC_CONFERENCE) {
+ builder.append(isLong ? " PROPERTY_IS_ADHOC_CONFERENCE" : " adhoc_conf");
+ }
+
builder.append("]");
return builder.toString();
}
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 221f8f1..6d7ceca 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -26,6 +26,9 @@
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Simple data container encapsulating a request to some entity to
* create a new {@link Connection}.
@@ -46,6 +49,8 @@
private boolean mShouldShowIncomingCallUi = false;
private ParcelFileDescriptor mRttPipeToInCall;
private ParcelFileDescriptor mRttPipeFromInCall;
+ private List<Uri> mParticipants;
+ private boolean mIsAdhocConference = false;
public Builder() { }
@@ -59,6 +64,15 @@
}
/**
+ * Sets the participants for the resulting {@link ConnectionRequest}
+ * @param participants The participants to which the {@link Connection} is to connect.
+ */
+ public @NonNull Builder setParticipants(@Nullable List<Uri> participants) {
+ this.mParticipants = participants;
+ return this;
+ }
+
+ /**
* Sets the address for the resulting {@link ConnectionRequest}
* @param address The address(e.g., phone number) to which the {@link Connection} is to
* connect.
@@ -108,6 +122,16 @@
}
/**
+ * Sets isAdhocConference for the resulting {@link ConnectionRequest}
+ * @param isAdhocConference {@code true} if it is a adhoc conference call
+ * {@code false}, if not a adhoc conference call
+ */
+ public @NonNull Builder setIsAdhocConferenceCall(boolean isAdhocConference) {
+ this.mIsAdhocConference = isAdhocConference;
+ return this;
+ }
+
+ /**
* Sets the RTT pipe for transferring text into the {@link ConnectionService} for the
* resulting {@link ConnectionRequest}
* @param rttPipeFromInCall The data pipe to read from.
@@ -141,7 +165,9 @@
mTelecomCallId,
mShouldShowIncomingCallUi,
mRttPipeFromInCall,
- mRttPipeToInCall);
+ mRttPipeToInCall,
+ mParticipants,
+ mIsAdhocConference);
}
}
@@ -155,6 +181,8 @@
private final ParcelFileDescriptor mRttPipeFromInCall;
// Cached return value of getRttTextStream -- we don't want to wrap it more than once.
private Connection.RttTextStream mRttTextStream;
+ private List<Uri> mParticipants;
+ private final boolean mIsAdhocConference;
/**
* @param accountHandle The accountHandle which should be used to place the call.
@@ -214,6 +242,21 @@
boolean shouldShowIncomingCallUi,
ParcelFileDescriptor rttPipeFromInCall,
ParcelFileDescriptor rttPipeToInCall) {
+ this(accountHandle, handle, extras, videoState, telecomCallId,
+ shouldShowIncomingCallUi, rttPipeFromInCall, rttPipeToInCall, null, false);
+ }
+
+ private ConnectionRequest(
+ PhoneAccountHandle accountHandle,
+ Uri handle,
+ Bundle extras,
+ int videoState,
+ String telecomCallId,
+ boolean shouldShowIncomingCallUi,
+ ParcelFileDescriptor rttPipeFromInCall,
+ ParcelFileDescriptor rttPipeToInCall,
+ List<Uri> participants,
+ boolean isAdhocConference) {
mAccountHandle = accountHandle;
mAddress = handle;
mExtras = extras;
@@ -222,6 +265,8 @@
mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
mRttPipeFromInCall = rttPipeFromInCall;
mRttPipeToInCall = rttPipeToInCall;
+ mParticipants = participants;
+ mIsAdhocConference = isAdhocConference;
}
private ConnectionRequest(Parcel in) {
@@ -233,6 +278,11 @@
mShouldShowIncomingCallUi = in.readInt() == 1;
mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader());
mRttPipeToInCall = in.readParcelable(getClass().getClassLoader());
+
+ mParticipants = new ArrayList<Uri>();
+ in.readList(mParticipants, getClass().getClassLoader());
+
+ mIsAdhocConference = in.readInt() == 1;
}
/**
@@ -246,6 +296,11 @@
public Uri getAddress() { return mAddress; }
/**
+ * The participants to which the {@link Connection} is to connect.
+ */
+ public @Nullable List<Uri> getParticipants() { return mParticipants; }
+
+ /**
* Application-specific extra data. Used for passing back information from an incoming
* call {@code Intent}, and for any proprietary extensions arranged between a client
* and servant {@code ConnectionService} which agree on a vocabulary for such data.
@@ -290,6 +345,13 @@
}
/**
+ * @return {@code true} if the call is a adhoc conference call else @return {@code false}
+ */
+ public boolean isAdhocConferenceCall() {
+ return mIsAdhocConference;
+ }
+
+ /**
* Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the connection
* service to the in-call UI. In order to obtain an
* {@link java.io.InputStream} from this {@link ParcelFileDescriptor}, use
@@ -345,11 +407,12 @@
@Override
public String toString() {
- return String.format("ConnectionRequest %s %s",
+ return String.format("ConnectionRequest %s %s isAdhocConf: %s",
mAddress == null
? Uri.EMPTY
: Connection.toLogSafePhoneNumber(mAddress.toString()),
- bundleToString(mExtras));
+ bundleToString(mExtras),
+ isAdhocConferenceCall() ? "Y" : "N");
}
private static String bundleToString(Bundle extras){
@@ -406,5 +469,7 @@
destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0);
destination.writeParcelable(mRttPipeFromInCall, 0);
destination.writeParcelable(mRttPipeToInCall, 0);
+ destination.writeList(mParticipants);
+ destination.writeInt(mIsAdhocConference ? 1 : 0);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 3a0494e..440f044 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -154,6 +154,9 @@
private static final String SESSION_CONNECTION_SERVICE_FOCUS_LOST = "CS.cSFL";
private static final String SESSION_CONNECTION_SERVICE_FOCUS_GAINED = "CS.cSFG";
private static final String SESSION_HANDOVER_FAILED = "CS.haF";
+ private static final String SESSION_CREATE_CONF = "CS.crConf";
+ private static final String SESSION_CREATE_CONF_COMPLETE = "CS.crConfC";
+ private static final String SESSION_CREATE_CONF_FAILED = "CS.crConfF";
private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
private static final int MSG_CREATE_CONNECTION = 2;
@@ -188,6 +191,9 @@
private static final int MSG_HANDOVER_FAILED = 32;
private static final int MSG_HANDOVER_COMPLETE = 33;
private static final int MSG_DEFLECT = 34;
+ private static final int MSG_CREATE_CONFERENCE = 35;
+ private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
+ private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
private static Connection sNullConnection;
@@ -291,6 +297,63 @@
}
@Override
+ public void createConference(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ String id,
+ ConnectionRequest request,
+ boolean isIncoming,
+ boolean isUnknown,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CREATE_CONF);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionManagerPhoneAccount;
+ args.arg2 = id;
+ args.arg3 = request;
+ args.arg4 = Log.createSubsession();
+ args.argi1 = isIncoming ? 1 : 0;
+ args.argi2 = isUnknown ? 1 : 0;
+ mHandler.obtainMessage(MSG_CREATE_CONFERENCE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void createConferenceComplete(String id, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CREATE_CONF_COMPLETE);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = id;
+ args.arg2 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_CREATE_CONFERENCE_COMPLETE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void createConferenceFailed(
+ PhoneAccountHandle connectionManagerPhoneAccount,
+ String callId,
+ ConnectionRequest request,
+ boolean isIncoming,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CREATE_CONF_FAILED);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = request;
+ args.arg3 = Log.createSubsession();
+ args.arg4 = connectionManagerPhoneAccount;
+ args.argi1 = isIncoming ? 1 : 0;
+ mHandler.obtainMessage(MSG_CREATE_CONFERENCE_FAILED, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void handoverFailed(String callId, ConnectionRequest request, int reason,
Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED);
@@ -802,6 +865,106 @@
}
break;
}
+ case MSG_CREATE_CONFERENCE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg4, SESSION_HANDLER + SESSION_CREATE_CONN);
+ try {
+ final PhoneAccountHandle connectionManagerPhoneAccount =
+ (PhoneAccountHandle) args.arg1;
+ final String id = (String) args.arg2;
+ final ConnectionRequest request = (ConnectionRequest) args.arg3;
+ final boolean isIncoming = args.argi1 == 1;
+ final boolean isUnknown = args.argi2 == 1;
+ if (!mAreAccountsInitialized) {
+ Log.d(this, "Enqueueing pre-initconference request %s", id);
+ mPreInitializationConnectionRequests.add(
+ new android.telecom.Logging.Runnable(
+ SESSION_HANDLER + SESSION_CREATE_CONF + ".pIConfR",
+ null /*lock*/) {
+ @Override
+ public void loggedRun() {
+ createConference(connectionManagerPhoneAccount,
+ id,
+ request,
+ isIncoming,
+ isUnknown);
+ }
+ }.prepare());
+ } else {
+ createConference(connectionManagerPhoneAccount,
+ id,
+ request,
+ isIncoming,
+ isUnknown);
+ }
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_CREATE_CONFERENCE_COMPLETE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg2,
+ SESSION_HANDLER + SESSION_CREATE_CONN_COMPLETE);
+ try {
+ final String id = (String) args.arg1;
+ if (!mAreAccountsInitialized) {
+ Log.d(this, "Enqueueing pre-init conference request %s", id);
+ mPreInitializationConnectionRequests.add(
+ new android.telecom.Logging.Runnable(
+ SESSION_HANDLER + SESSION_CREATE_CONF_COMPLETE
+ + ".pIConfR",
+ null /*lock*/) {
+ @Override
+ public void loggedRun() {
+ notifyCreateConferenceComplete(id);
+ }
+ }.prepare());
+ } else {
+ notifyCreateConferenceComplete(id);
+ }
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_CREATE_CONFERENCE_FAILED: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER +
+ SESSION_CREATE_CONN_FAILED);
+ try {
+ final String id = (String) args.arg1;
+ final ConnectionRequest request = (ConnectionRequest) args.arg2;
+ final boolean isIncoming = args.argi1 == 1;
+ final PhoneAccountHandle connectionMgrPhoneAccount =
+ (PhoneAccountHandle) args.arg4;
+ if (!mAreAccountsInitialized) {
+ Log.d(this, "Enqueueing pre-init conference request %s", id);
+ mPreInitializationConnectionRequests.add(
+ new android.telecom.Logging.Runnable(
+ SESSION_HANDLER + SESSION_CREATE_CONF_FAILED
+ + ".pIConfR",
+ null /*lock*/) {
+ @Override
+ public void loggedRun() {
+ createConferenceFailed(connectionMgrPhoneAccount, id,
+ request, isIncoming);
+ }
+ }.prepare());
+ } else {
+ Log.i(this, "createConferenceFailed %s", id);
+ createConferenceFailed(connectionMgrPhoneAccount, id, request,
+ isIncoming);
+ }
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+
case MSG_HANDOVER_FAILED: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg3, SESSION_HANDLER +
@@ -1162,6 +1325,12 @@
public void onStateChanged(Conference conference, int oldState, int newState) {
String id = mIdByConference.get(conference);
switch (newState) {
+ case Connection.STATE_RINGING:
+ mAdapter.setRinging(id);
+ break;
+ case Connection.STATE_DIALING:
+ mAdapter.setDialing(id);
+ break;
case Connection.STATE_ACTIVE:
mAdapter.setActive(id);
break;
@@ -1292,6 +1461,13 @@
mAdapter.onConnectionEvent(id, event, extras);
}
}
+
+ @Override
+ public void onRingbackRequested(Conference c, boolean ringback) {
+ String id = mIdByConference.get(c);
+ Log.d(this, "Adapter conference onRingback %b", ringback);
+ mAdapter.setRingbackRequested(id, ringback);
+ }
};
private final Connection.Listener mConnectionListener = new Connection.Listener() {
@@ -1534,6 +1710,70 @@
return super.onUnbind(intent);
}
+
+ /**
+ * This can be used by telecom to either create a new outgoing conference call or attach
+ * to an existing incoming conference call. In either case, telecom will cycle through a
+ * set of services and call createConference until a connection service cancels the process
+ * or completes it successfully.
+ */
+ private void createConference(
+ final PhoneAccountHandle callManagerAccount,
+ final String callId,
+ final ConnectionRequest request,
+ boolean isIncoming,
+ boolean isUnknown) {
+
+ Conference conference = null;
+ conference = isIncoming ? onCreateIncomingConference(callManagerAccount, request)
+ : onCreateOutgoingConference(callManagerAccount, request);
+
+ Log.d(this, "createConference, conference: %s", conference);
+ if (conference == null) {
+ Log.i(this, "createConference, implementation returned null conference.");
+ conference = Conference.createFailedConference(
+ new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONFERENCE"),
+ request.getAccountHandle());
+ }
+ if (conference.getExtras() != null) {
+ conference.getExtras().putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId);
+ }
+ mConferenceById.put(callId, conference);
+ mIdByConference.put(conference, callId);
+ conference.addListener(mConferenceListener);
+ ParcelableConference parcelableConference = new ParcelableConference(
+ request.getAccountHandle(),
+ conference.getState(),
+ conference.getConnectionCapabilities(),
+ conference.getConnectionProperties(),
+ Collections.<String>emptyList(), //connectionIds
+ conference.getVideoProvider() == null ?
+ null : conference.getVideoProvider().getInterface(),
+ conference.getVideoState(),
+ conference.getConnectTimeMillis(),
+ conference.getConnectionStartElapsedRealTime(),
+ conference.getStatusHints(),
+ conference.getExtras(),
+ conference.getAddress(),
+ conference.getAddressPresentation(),
+ conference.getCallerDisplayName(),
+ conference.getCallerDisplayNamePresentation(),
+ conference.getDisconnectCause(),
+ conference.isRingbackRequested());
+ if (conference.getState() != Connection.STATE_DISCONNECTED) {
+ conference.setTelecomCallId(callId);
+ mAdapter.setVideoProvider(callId, conference.getVideoProvider());
+ mAdapter.setVideoState(callId, conference.getVideoState());
+ onConferenceAdded(conference);
+ }
+
+ Log.d(this, "createConference, calling handleCreateConferenceSuccessful %s", callId);
+ mAdapter.handleCreateConferenceComplete(
+ callId,
+ request,
+ parcelableConference);
+ }
+
/**
* This can be used by telecom to either create a new outgoing call or attach to an existing
* incoming call. In either case, telecom will cycle through a set of services and call
@@ -1645,6 +1885,18 @@
}
}
+ private void createConferenceFailed(final PhoneAccountHandle callManagerAccount,
+ final String callId, final ConnectionRequest request,
+ boolean isIncoming) {
+
+ Log.i(this, "createConferenceFailed %s", callId);
+ if (isIncoming) {
+ onCreateIncomingConferenceFailed(callManagerAccount, request);
+ } else {
+ onCreateOutgoingConferenceFailed(callManagerAccount, request);
+ }
+ }
+
private void handoverFailed(final String callId, final ConnectionRequest request,
int reason) {
@@ -1669,6 +1921,24 @@
"notifyCreateConnectionComplete"));
}
+ /**
+ * Called by Telecom when the creation of a new Conference has completed and it is now added
+ * to Telecom.
+ * @param callId The ID of the connection.
+ */
+ private void notifyCreateConferenceComplete(final String callId) {
+ Log.i(this, "notifyCreateConferenceComplete %s", callId);
+ if (callId == null) {
+ // This could happen if the conference fails quickly and is removed from the
+ // ConnectionService before Telecom sends the create conference complete callback.
+ Log.w(this, "notifyCreateConferenceComplete: callId is null.");
+ return;
+ }
+ onCreateConferenceComplete(findConferenceForAction(callId,
+ "notifyCreateConferenceComplete"));
+ }
+
+
private void abort(String callId) {
Log.d(this, "abort %s", callId);
findConnectionForAction(callId, "abort").onAbort();
@@ -1676,12 +1946,20 @@
private void answerVideo(String callId, int videoState) {
Log.d(this, "answerVideo %s", callId);
- findConnectionForAction(callId, "answer").onAnswer(videoState);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "answer").onAnswer(videoState);
+ } else {
+ findConferenceForAction(callId, "answer").onAnswer(videoState);
+ }
}
private void answer(String callId) {
Log.d(this, "answer %s", callId);
- findConnectionForAction(callId, "answer").onAnswer();
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "answer").onAnswer();
+ } else {
+ findConferenceForAction(callId, "answer").onAnswer();
+ }
}
private void deflect(String callId, Uri address) {
@@ -1691,7 +1969,11 @@
private void reject(String callId) {
Log.d(this, "reject %s", callId);
- findConnectionForAction(callId, "reject").onReject();
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "reject").onReject();
+ } else {
+ findConferenceForAction(callId, "reject").onReject();
+ }
}
private void reject(String callId, String rejectWithMessage) {
@@ -2198,6 +2480,21 @@
ConnectionRequest request) {
return null;
}
+ /**
+ * Create a {@code Connection} given an incoming request. This is used to attach to existing
+ * incoming conference call.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request Details about the incoming call.
+ * @return The {@code Connection} object to satisfy this call, or {@code null} to
+ * not handle the call.
+ */
+ public @Nullable Conference onCreateIncomingConference(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @Nullable ConnectionRequest request) {
+ return null;
+ }
/**
* Called after the {@link Connection} returned by
@@ -2212,6 +2509,19 @@
}
/**
+ * Called after the {@link Conference} returned by
+ * {@link #onCreateIncomingConference(PhoneAccountHandle, ConnectionRequest)}
+ * or {@link #onCreateOutgoingConference(PhoneAccountHandle, ConnectionRequest)} has been
+ * added to the {@link ConnectionService} and sent to Telecom.
+ *
+ * @param conference the {@link Conference}.
+ * @hide
+ */
+ public void onCreateConferenceComplete(Conference conference) {
+ }
+
+
+ /**
* Called by Telecom to inform the {@link ConnectionService} that its request to create a new
* incoming {@link Connection} was denied.
* <p>
@@ -2250,6 +2560,47 @@
}
/**
+ * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+ * incoming {@link Conference} was denied.
+ * <p>
+ * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
+ * {@link Conference}, but Telecom has determined that the call cannot be allowed at this time.
+ * The {@link ConnectionService} is responsible for silently rejecting the new incoming
+ * {@link Conference}.
+ * <p>
+ * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request The incoming connection request.
+ */
+ public void onCreateIncomingConferenceFailed(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @Nullable ConnectionRequest request) {
+ }
+
+ /**
+ * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+ * outgoing {@link Conference} was denied.
+ * <p>
+ * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
+ * {@link Conference}, but Telecom has determined that the call cannot be placed at this time.
+ * The {@link ConnectionService} is responisible for informing the user that the
+ * {@link Conference} cannot be made at this time.
+ * <p>
+ * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
+ *
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
+ * @param request The outgoing connection request.
+ */
+ public void onCreateOutgoingConferenceFailed(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @Nullable ConnectionRequest request) {
+ }
+
+
+ /**
* Trigger recalculate functinality for conference calls. This is used when a Telephony
* Connection is part of a conference controller but is not yet added to Connection
* Service and hence cannot be added to the conference call.
@@ -2289,6 +2640,36 @@
}
/**
+ * Create a {@code Conference} given an outgoing request. This is used to initiate new
+ * outgoing conference call.
+ *
+ * @param connectionManagerPhoneAccount The connection manager account to use for managing
+ * this call.
+ * <p>
+ * If this parameter is not {@code null}, it means that this {@code ConnectionService}
+ * has registered one or more {@code PhoneAccount}s having
+ * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER}. This parameter will contain
+ * one of these {@code PhoneAccount}s, while the {@code request} will contain another
+ * (usually but not always distinct) {@code PhoneAccount} to be used for actually
+ * making the connection.
+ * <p>
+ * If this parameter is {@code null}, it means that this {@code ConnectionService} is
+ * being asked to make a direct connection. The
+ * {@link ConnectionRequest#getAccountHandle()} of parameter {@code request} will be
+ * a {@code PhoneAccount} registered by this {@code ConnectionService} to use for
+ * making the connection.
+ * @param request Details about the outgoing call.
+ * @return The {@code Conference} object to satisfy this call, or the result of an invocation
+ * of {@link Connection#createFailedConnection(DisconnectCause)} to not handle the call.
+ */
+ public @Nullable Conference onCreateOutgoingConference(
+ @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
+ @Nullable ConnectionRequest request) {
+ return null;
+ }
+
+
+ /**
* Called by Telecom to request that a {@link ConnectionService} creates an instance of an
* outgoing handover {@link Connection}.
* <p>
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 04e930c..8f27323 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -100,6 +100,19 @@
}
}
+ void handleCreateConferenceComplete(
+ String id,
+ ConnectionRequest request,
+ ParcelableConference conference) {
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.handleCreateConferenceComplete(id, request, conference,
+ Log.getExternalSession());
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
/**
* Sets a call's state to active (e.g., an ongoing call where two parties can actively
* communicate).
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 60b2172f..79ad51b 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -75,6 +75,7 @@
private static final int MSG_SET_PHONE_ACCOUNT_CHANGED = 34;
private static final int MSG_CONNECTION_SERVICE_FOCUS_RELEASED = 35;
private static final int MSG_SET_CONFERENCE_STATE = 36;
+ private static final int MSG_HANDLE_CREATE_CONFERENCE_COMPLETE = 37;
private final IConnectionServiceAdapter mDelegate;
@@ -103,6 +104,19 @@
}
break;
}
+ case MSG_HANDLE_CREATE_CONFERENCE_COMPLETE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.handleCreateConferenceComplete(
+ (String) args.arg1,
+ (ConnectionRequest) args.arg2,
+ (ParcelableConference) args.arg3,
+ null /*Session.Info*/);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
case MSG_SET_ACTIVE:
mDelegate.setActive((String) msg.obj, null /*Session.Info*/);
break;
@@ -366,6 +380,20 @@
}
@Override
+ public void handleCreateConferenceComplete(
+ String id,
+ ConnectionRequest request,
+ ParcelableConference conference,
+ Session.Info sessionInfo) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = id;
+ args.arg2 = request;
+ args.arg3 = conference;
+ mHandler.obtainMessage(MSG_HANDLE_CREATE_CONFERENCE_COMPLETE, args).sendToTarget();
+ }
+
+
+ @Override
public void setActive(String connectionId, Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget();
}
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index ede0594..90b69a3 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -47,6 +47,34 @@
private final int mAddressPresentation;
private final String mCallerDisplayName;
private final int mCallerDisplayNamePresentation;
+ private DisconnectCause mDisconnectCause;
+ private boolean mRingbackRequested;
+
+ public ParcelableConference(
+ PhoneAccountHandle phoneAccount,
+ int state,
+ int connectionCapabilities,
+ int connectionProperties,
+ List<String> connectionIds,
+ IVideoProvider videoProvider,
+ int videoState,
+ long connectTimeMillis,
+ long connectElapsedTimeMillis,
+ StatusHints statusHints,
+ Bundle extras,
+ Uri address,
+ int addressPresentation,
+ String callerDisplayName,
+ int callerDisplayNamePresentation,
+ DisconnectCause disconnectCause,
+ boolean ringbackRequested) {
+ this(phoneAccount, state, connectionCapabilities, connectionProperties, connectionIds,
+ videoProvider, videoState, connectTimeMillis, connectElapsedTimeMillis,
+ statusHints, extras, address, addressPresentation, callerDisplayName,
+ callerDisplayNamePresentation);
+ mDisconnectCause = disconnectCause;
+ mRingbackRequested = ringbackRequested;
+ }
public ParcelableConference(
PhoneAccountHandle phoneAccount,
@@ -79,6 +107,8 @@
mAddressPresentation = addressPresentation;
mCallerDisplayName = callerDisplayName;
mCallerDisplayNamePresentation = callerDisplayNamePresentation;
+ mDisconnectCause = null;
+ mRingbackRequested = false;
}
@Override
@@ -100,6 +130,10 @@
.append(mVideoState)
.append(", VideoProvider: ")
.append(mVideoProvider)
+ .append(", isRingbackRequested: ")
+ .append(mRingbackRequested)
+ .append(", disconnectCause: ")
+ .append(mDisconnectCause)
.toString();
}
@@ -151,6 +185,13 @@
return mAddress;
}
+ public final DisconnectCause getDisconnectCause() {
+ return mDisconnectCause;
+ }
+
+ public boolean isRingbackRequested() {
+ return mRingbackRequested;
+ }
public int getHandlePresentation() {
return mAddressPresentation;
}
@@ -177,11 +218,14 @@
int addressPresentation = source.readInt();
String callerDisplayName = source.readString();
int callerDisplayNamePresentation = source.readInt();
+ DisconnectCause disconnectCause = source.readParcelable(classLoader);
+ boolean isRingbackRequested = source.readInt() == 1;
return new ParcelableConference(phoneAccount, state, capabilities, properties,
connectionIds, videoCallProvider, videoState, connectTimeMillis,
connectElapsedTimeMillis, statusHints, extras, address, addressPresentation,
- callerDisplayName, callerDisplayNamePresentation);
+ callerDisplayName, callerDisplayNamePresentation, disconnectCause,
+ isRingbackRequested);
}
@Override
@@ -215,5 +259,7 @@
destination.writeInt(mAddressPresentation);
destination.writeString(mCallerDisplayName);
destination.writeInt(mCallerDisplayNamePresentation);
+ destination.writeParcelable(mDisconnectCause, 0);
+ destination.writeInt(mRingbackRequested ? 1 : 0);
}
}
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index bb858cb..abb210f 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -331,7 +331,17 @@
*/
public static final int CAPABILITY_EMERGENCY_PREFERRED = 0x2000;
- /* NEXT CAPABILITY: 0x4000 */
+ /**
+ * An adhoc conference call is established by providing a list of addresses to
+ * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the
+ * {@link ConnectionService} is responsible for connecting all indicated participants
+ * to a conference simultaneously.
+ * This is in contrast to conferences formed by merging calls together (e.g. using
+ * {@link android.telecom.Call#mergeConference()}).
+ */
+ public static final int CAPABILITY_ADHOC_CONFERENCE_CALLING = 0x4000;
+
+ /* NEXT CAPABILITY: 0x8000 */
/**
* URI scheme for telephone number URIs.
@@ -1054,6 +1064,9 @@
if (hasCapabilities(CAPABILITY_RTT)) {
sb.append("Rtt");
}
+ if (hasCapabilities(CAPABILITY_ADHOC_CONFERENCE_CALLING)) {
+ sb.append("AdhocConf");
+ }
return sb.toString();
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 1e73bd6..76640e0 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -101,6 +101,14 @@
}
@Override
+ public void handleCreateConferenceComplete(
+ String id,
+ ConnectionRequest request,
+ ParcelableConference parcel,
+ Session.Info info) {
+ }
+
+ @Override
public void setActive(String callId, Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "setActive")
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index ffb2779..f1dca03 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1803,6 +1803,45 @@
}
/**
+ * Registers a new incoming conference. A {@link ConnectionService} should invoke this method
+ * when it has an incoming conference. For managed {@link ConnectionService}s, the specified
+ * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and
+ * the user must have enabled the corresponding {@link PhoneAccount}. This can be checked using
+ * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have
+ * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call.
+ * <p>
+ * The incoming conference you are adding is assumed to have a video state of
+ * {@link VideoProfile#STATE_AUDIO_ONLY}, unless the extra value
+ * {@link #EXTRA_INCOMING_VIDEO_STATE} is specified.
+ * <p>
+ * Once invoked, this method will cause the system to bind to the {@link ConnectionService}
+ * associated with the {@link PhoneAccountHandle} and request additional information about the
+ * call (See {@link ConnectionService#onCreateIncomingConference}) before starting the incoming
+ * call UI.
+ * <p>
+ * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either
+ * the {@link PhoneAccountHandle} does not correspond to a registered {@link PhoneAccount} or
+ * the associated {@link PhoneAccount} is not currently enabled by the user.
+ *
+ * @param phoneAccount A {@link PhoneAccountHandle} registered with
+ * {@link #registerPhoneAccount}.
+ * @param extras A bundle that will be passed through to
+ * {@link ConnectionService#onCreateIncomingConference}.
+ */
+
+ public void addNewIncomingConference(@NonNull PhoneAccountHandle phoneAccount,
+ @NonNull Bundle extras) {
+ try {
+ if (isServiceConnected()) {
+ getTelecomService().addNewIncomingConference(
+ phoneAccount, extras == null ? new Bundle() : extras);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException adding a new incoming conference: " + phoneAccount, e);
+ }
+ }
+
+ /**
* Registers a new unknown call with Telecom. This can only be called by the system Telephony
* service. This is invoked when Telephony detects a new unknown connection that was neither
* a new incoming call, nor an user-initiated outgoing call.
@@ -2006,6 +2045,42 @@
}
}
+
+ /**
+ * Place a new conference call with the provided participants using the system telecom service
+ * This method doesn't support placing of emergency calls.
+ *
+ * An adhoc conference call is established by providing a list of addresses to
+ * {@code TelecomManager#startConference(List<Uri>, int videoState)} where the
+ * {@link ConnectionService} is responsible for connecting all indicated participants
+ * to a conference simultaneously.
+ * This is in contrast to conferences formed by merging calls together (e.g. using
+ * {@link android.telecom.Call#mergeConference()}).
+ *
+ * The following keys are supported in the supplied extras.
+ * <ul>
+ * <li>{@link #EXTRA_PHONE_ACCOUNT_HANDLE}</li>
+ * <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
+ * <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
+ * </ul>
+ *
+ * @param participants List of participants to start conference with
+ * @param extras Bundle of extras to use with the call
+ */
+ @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+ public void startConference(@NonNull List<Uri> participants,
+ @NonNull Bundle extras) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.startConference(participants, extras,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#placeCall", e);
+ }
+ }
+ }
+
/**
* Enables and disables specified phone account.
*
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index e35093c..96f2483 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -53,6 +53,20 @@
void createConnectionFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
+ void createConference(
+ in PhoneAccountHandle connectionManagerPhoneAccount,
+ String callId,
+ in ConnectionRequest request,
+ boolean isIncoming,
+ boolean isUnknown,
+ in Session.Info sessionInfo);
+
+ void createConferenceComplete(String callId, in Session.Info sessionInfo);
+
+ void createConferenceFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
+ in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
+
+
void abort(String callId, in Session.Info sessionInfo);
void answerVideo(String callId, int videoState, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 9cf098c..4f63e08 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -44,6 +44,12 @@
in ParcelableConnection connection,
in Session.Info sessionInfo);
+ void handleCreateConferenceComplete(
+ String callId,
+ in ConnectionRequest request,
+ in ParcelableConference connection,
+ in Session.Info sessionInfo);
+
void setActive(String callId, in Session.Info sessionInfo);
void setRinging(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 204c37e..9a47ae1 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -242,11 +242,22 @@
void addNewIncomingCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
/**
+ * @see TelecomServiceImpl#addNewIncomingConference
+ */
+ void addNewIncomingConference(in PhoneAccountHandle phoneAccount, in Bundle extras);
+
+ /**
* @see TelecomServiceImpl#addNewUnknownCall
*/
void addNewUnknownCall(in PhoneAccountHandle phoneAccount, in Bundle extras);
/**
+ * @see TelecomServiceImpl#startConference
+ */
+ void startConference(in List<Uri> participants, in Bundle extras,
+ String callingPackage);
+
+ /**
* @see TelecomServiceImpl#placeCall
*/
void placeCall(in Uri handle, in Bundle extras, String callingPackage);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 61e67be..71aaa6e 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1079,6 +1079,14 @@
public static final String KEY_IGNORE_RTT_MODE_SETTING_BOOL =
"ignore_rtt_mode_setting_bool";
+
+ /**
+ * Determines whether adhoc conference calls are supported by a carrier. When {@code true},
+ * adhoc conference calling is supported, {@code false otherwise}.
+ */
+ public static final String KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL =
+ "support_adhoc_conference_calls_bool";
+
/**
* Determines whether conference calls are supported by a carrier. When {@code true},
* conference calling is supported, {@code false otherwise}.
@@ -3111,7 +3119,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";
/**
@@ -3523,6 +3530,7 @@
sDefaults.putBoolean(KEY_CALL_FORWARDING_MAP_NON_NUMBER_TO_VOICEMAIL_BOOL, false);
sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
+ sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java
index c706d28..9b4292f 100644
--- a/telephony/java/android/telephony/ImsManager.java
+++ b/telephony/java/android/telephony/ImsManager.java
@@ -54,6 +54,43 @@
"com.android.internal.intent.action.ACTION_FORBIDDEN_NO_SERVICE_AUTHORIZATION";
/**
+ * An intent action indicating that IMS registration for WiFi calling has resulted in an error.
+ * Contains error information that should be displayed to the user.
+ * <p>
+ * This intent will contain the following extra key/value pairs:
+ * {@link #EXTRA_WFC_REGISTRATION_FAILURE_TITLE}
+ * and {@link #EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE}, which contain carrier specific
+ * error information that should be displayed to the user.
+ * <p>
+ * Usage: This intent is sent as an ordered broadcast. If the settings application is going
+ * to show the error information specified to the user, it should respond to
+ * {@link android.content.BroadcastReceiver#setResultCode(int)} with
+ * {@link android.app.Activity#RESULT_CANCELED}, which will signal to the framework that the
+ * event was handled. If the framework does not receive a response to the ordered broadcast,
+ * it will then show a notification to the user indicating that there was a registration
+ * failure.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_WFC_IMS_REGISTRATION_ERROR =
+ "android.telephony.ims.action.WFC_IMS_REGISTRATION_ERROR";
+
+ /**
+ * An extra key corresponding to a String value which contains the carrier specific title to be
+ * displayed as part of the message shown to the user when there is an error registering for
+ * WiFi calling.
+ */
+ public static final String EXTRA_WFC_REGISTRATION_FAILURE_TITLE =
+ "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_TITLE";
+
+ /**
+ * An extra key corresponding to a String value which contains the carrier specific message to
+ * be displayed as part of the message shown to the user when there is an error registering for
+ * WiFi calling.
+ */
+ public static final String EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE =
+ "android.telephony.ims.extra.WFC_REGISTRATION_FAILURE_MESSAGE";
+
+ /**
* Use {@link Context#getSystemService(String)} to get an instance of this class.
* @hide
*/
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 2f9e6ac..0074772 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -28,7 +28,6 @@
import android.content.Intent;
import android.content.res.Resources;
import android.database.Cursor;
-import android.location.CountryDetector;
import android.net.Uri;
import android.os.PersistableBundle;
import android.provider.Contacts;
@@ -2032,6 +2031,7 @@
private static boolean isEmergencyNumberInternal(int subId, String number,
String defaultCountryIso,
boolean useExactMatch) {
+ // TODO: clean up all the callers that pass in a defaultCountryIso, since it's ignored now.
try {
if (useExactMatch) {
return TelephonyManager.getDefault().isEmergencyNumber(number);
@@ -2193,18 +2193,7 @@
private static boolean isLocalEmergencyNumberInternal(int subId, String number,
Context context,
boolean useExactMatch) {
- String countryIso;
- CountryDetector detector = (CountryDetector) context.getSystemService(
- Context.COUNTRY_DETECTOR);
- if (detector != null && detector.detectCountry() != null) {
- countryIso = detector.detectCountry().getCountryIso();
- } else {
- Locale locale = context.getResources().getConfiguration().locale;
- countryIso = locale.getCountry();
- Rlog.w(LOG_TAG, "No CountryDetector; falling back to countryIso based on locale: "
- + countryIso);
- }
- return isEmergencyNumberInternal(subId, number, countryIso, useExactMatch);
+ return isEmergencyNumberInternal(subId, number, null /* unused */, useExactMatch);
}
/**
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index b5dea7c..0ba36b1 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1577,7 +1577,7 @@
}
/**
- * Copy a raw SMS PDU to the ICC.
+ * Copies a raw SMS PDU to the ICC.
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
@@ -1591,21 +1591,26 @@
* operation is performed on the correct subscription.
* </p>
*
- * @param smsc the SMSC for this message, or NULL for the default SMSC
- * @param pdu the raw PDU to store
- * @param status message status (STATUS_ON_ICC_READ, STATUS_ON_ICC_UNREAD,
- * STATUS_ON_ICC_SENT, STATUS_ON_ICC_UNSENT)
- * @return true for success
+ * @param smsc the SMSC for this messag or null for the default SMSC.
+ * @param pdu the raw PDU to store.
+ * @param status message status. One of these status:
+ * <code>STATUS_ON_ICC_READ</code>
+ * <code>STATUS_ON_ICC_UNREAD</code>
+ * <code>STATUS_ON_ICC_SENT</code>
+ * <code>STATUS_ON_ICC_UNSENT</code>
+ * @return true for success. Otherwise false.
*
- * @throws IllegalArgumentException if pdu is NULL
- * {@hide}
+ * @throws IllegalArgumentException if pdu is null.
+ * @hide
*/
- @UnsupportedAppUsage
- public boolean copyMessageToIcc(byte[] smsc, byte[] pdu,int status) {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.ACCESS_MESSAGES_ON_ICC)
+ public boolean copyMessageToIcc(
+ @Nullable byte[] smsc, @NonNull byte[] pdu, @StatusOnIcc int status) {
boolean success = false;
- if (null == pdu) {
- throw new IllegalArgumentException("pdu is NULL");
+ if (pdu == null) {
+ throw new IllegalArgumentException("pdu is null");
}
try {
ISms iSms = getISmsService();
@@ -1622,7 +1627,7 @@
}
/**
- * Delete the specified message from the ICC.
+ * Deletes the specified message from the ICC.
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
@@ -1706,7 +1711,7 @@
}
/**
- * Retrieves all messages currently stored on ICC.
+ * Retrieves all messages currently stored on the ICC.
* ICC (Integrated Circuit Card) is the card of the device.
* For example, this can be the SIM or USIM for GSM.
*
@@ -1872,8 +1877,7 @@
}
/**
- * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
- * records returned by <code>getAllMessagesFromIcc()</code>
+ * Creates a list of <code>SmsMessage</code>s from a list of SmsRawData records.
*
* <p class="note"><strong>Note:</strong> This method is intended for internal use by carrier
* applications or the Telephony framework and will never trigger an SMS disambiguation
@@ -1885,8 +1889,7 @@
* operation is performed on the correct subscription.
* </p>
*
- * @param records SMS EF records, returned by
- * <code>getAllMessagesFromIcc</code>
+ * @param records SMS EF records.
* @return <code>ArrayList</code> of <code>SmsMessage</code> objects.
*/
private ArrayList<SmsMessage> createMessageListFromRawRecords(List<SmsRawData> records) {
@@ -1897,7 +1900,7 @@
SmsRawData data = records.get(i);
// List contains all records, including "free" records (null)
if (data != null) {
- SmsMessage sms = SmsMessage.createFromEfRecord(i+1, data.getBytes(),
+ SmsMessage sms = SmsMessage.createFromEfRecord(i + 1, data.getBytes(),
getSubscriptionId());
if (sms != null) {
messages.add(sms);
@@ -2037,6 +2040,17 @@
return ret;
}
+ /** @hide */
+ @IntDef(prefix = { "STATUS_ON_ICC_" }, value = {
+ STATUS_ON_ICC_FREE,
+ STATUS_ON_ICC_READ,
+ STATUS_ON_ICC_UNREAD,
+ STATUS_ON_ICC_SENT,
+ STATUS_ON_ICC_UNSENT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusOnIcc {}
+
// see SmsMessage.getStatusOnIcc
/** Free space (TS 51.011 10.5.3 / 3GPP2 C.S0023 3.4.27). */
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 30a61c3..8d9b1dd 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -278,41 +278,24 @@
}
/**
- * Create an SmsMessage from an SMS EF record.
+ * Creates an SmsMessage from an SMS EF record.
*
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param index Index of SMS EF record.
* @param data Record data.
* @return An SmsMessage representing the record.
*
* @hide
*/
public static SmsMessage createFromEfRecord(int index, byte[] data) {
- SmsMessageBase wrappedMessage;
-
- if (isCdmaVoice()) {
- wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromEfRecord(
- index, data);
- } else {
- wrappedMessage = com.android.internal.telephony.gsm.SmsMessage.createFromEfRecord(
- index, data);
- }
-
- if (wrappedMessage != null) {
- return new SmsMessage(wrappedMessage);
- } else {
- Rlog.e(LOG_TAG, "createFromEfRecord(): wrappedMessage is null");
- return null;
- }
+ return createFromEfRecord(index, data, SmsManager.getDefaultSmsSubscriptionId());
}
/**
- * Create an SmsMessage from an SMS EF record.
+ * Creates an SmsMessage from an SMS EF record.
*
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param index Index of SMS EF record.
* @param data Record data.
- * @param subId Subscription Id of the SMS
+ * @param subId Subscription Id associated with the record.
* @return An SmsMessage representing the record.
*
* @hide
@@ -602,13 +585,15 @@
*/
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message.
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
* This method will not attempt to use any GSM national language 7 bit encodings.
*
- * @param scAddress Service Centre address. Null means use default.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message, boolean statusReportRequested) {
@@ -621,17 +606,16 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message.
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
* This method will not attempt to use any GSM national language 7 bit encodings.
*
- * @param scAddress Service Centre address. Null means use default.
+ * @param scAddress Service Centre address. Null means use default.
* @param destinationAddress the address of the destination for the message.
- * @param message String representation of the message payload.
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param subId Subscription of the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @param subId subscription of the message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
* @hide
*/
public static SubmitPdu getSubmitPdu(String scAddress,
@@ -649,17 +633,16 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address & port.
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
* This method will not attempt to use any GSM national language 7 bit encodings.
*
- * @param scAddress Service Centre address. null == use default
- * @param destinationAddress the address of the destination for the message
- * @param destinationPort the port to deliver the message to at the
- * destination
- * @param data the data for the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param destinationPort the port to deliver the message to at the destination.
+ * @param data the data for the message.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, short destinationPort, byte[] data,
@@ -677,6 +660,55 @@
return new SubmitPdu(spb);
}
+ // TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new
+ // DeliverPdu accordingly.
+
+ /**
+ * Gets an SMS PDU to store in the ICC.
+ *
+ * @param subId subscription of the message.
+ * @param status message status. One of these status:
+ * <code>SmsManager.STATUS_ON_ICC_READ</code>
+ * <code>SmsManager.STATUS_ON_ICC_UNREAD</code>
+ * <code>SmsManager.STATUS_ON_ICC_SENT</code>
+ * <code>SmsManager.STATUS_ON_ICC_UNSENT</code>
+ * @param scAddress Service Centre address. Null means use default.
+ * @param address destination or originating address.
+ * @param message string representation of the message payload.
+ * @param date the time stamp the message was received.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public static SubmitPdu getSmsPdu(int subId, @SmsManager.StatusOnIcc int status,
+ @Nullable String scAddress, @NonNull String address, @NonNull String message,
+ long date) {
+ SubmitPduBase spb;
+ if (isCdmaVoice(subId)) { // 3GPP2 format
+ if (status == SmsManager.STATUS_ON_ICC_READ
+ || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
+ spb = com.android.internal.telephony.cdma.SmsMessage.getDeliverPdu(address,
+ message, date);
+ } else { // Submit PDU
+ spb = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(scAddress,
+ address, message, false /* statusReportRequested */, null /* smsHeader */);
+ }
+ } else { // 3GPP format
+ if (status == SmsManager.STATUS_ON_ICC_READ
+ || status == SmsManager.STATUS_ON_ICC_UNREAD) { // Deliver PDU
+ spb = com.android.internal.telephony.gsm.SmsMessage.getDeliverPdu(scAddress,
+ address, message, date);
+ } else { // Submit PDU
+ spb = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(scAddress,
+ address, message, false /* statusReportRequested */, null /* header */);
+ }
+ }
+
+ return spb != null ? new SubmitPdu(spb) : null;
+ }
+
/**
* Get an SMS-SUBMIT PDU's encoded message.
* This is used by Bluetooth MAP profile to handle long non UTF-8 SMS messages.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index bd8321e..839889f 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -196,11 +196,8 @@
NETWORK_SELECTION_MODE_MANUAL})
public @interface NetworkSelectionMode {}
- /** @hide */
public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0;
- /** @hide */
public static final int NETWORK_SELECTION_MODE_AUTO = 1;
- /** @hide */
public static final int NETWORK_SELECTION_MODE_MANUAL = 2;
/** The otaspMode passed to PhoneStateListener#onOtaspChanged */
@@ -2349,7 +2346,7 @@
@UnsupportedAppUsage
public boolean isNetworkRoaming(int subId) {
int phoneId = SubscriptionManager.getPhoneId(subId);
- return getTelephonyProperty(subId, TelephonyProperties.operator_is_roaming(), false);
+ return getTelephonyProperty(phoneId, TelephonyProperties.operator_is_roaming(), false);
}
/**
@@ -7524,14 +7521,18 @@
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
-
- * @return the network selection mode.
+ * <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 #hasCarrierPrivileges}).
*
- * @hide
+ * @return the network selection mode.
*/
- @NetworkSelectionMode
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public int getNetworkSelectionMode() {
+ @SuppressAutoDoc // No support for carrier privileges (b/72967236).
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PRECISE_PHONE_STATE
+ })
+ public @NetworkSelectionMode int getNetworkSelectionMode() {
int mode = NETWORK_SELECTION_MODE_UNKNOWN;
try {
ITelephony telephony = getITelephony();
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index d6dea2c..a7d553b 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -294,15 +294,30 @@
* updateImsCallRatFromExtras(Bundle)} to determine whether to set the
* {@link android.telecom.TelecomManager#EXTRA_CALL_NETWORK_TYPE} extra value and
* {@link android.telecom.Connection#PROPERTY_WIFI} property on a connection.
+ * @deprecated the constants associated with this extra are hidden, instead use
+ * {@link #EXTRA_CALL_NETWORK_TYPE}.
*/
+ @Deprecated
public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech";
/**
+ * Extra key with an {@code int} value which can be set in {@link #setCallExtraInt(String, int)}
+ * to indicate the network type used for a call.
+ * <p>
+ * Valid values are defined by {@code TelephonyManager.NETWORK_TYPE_*} constants. An example may
+ * be {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+ */
+ public static final String EXTRA_CALL_NETWORK_TYPE =
+ "android.telephony.ims.extra.CALL_NETWORK_TYPE";
+
+ /**
* Similar to {@link #EXTRA_CALL_RAT_TYPE}, except with a lowercase 'c'. Used to ensure
* compatibility with modems that are non-compliant with the {@link #EXTRA_CALL_RAT_TYPE}
* extra key. Should be removed when the non-compliant modems are fixed.
* @hide
+ * @deprecated Use {@link #EXTRA_CALL_NETWORK_TYPE} instead.
*/
+ @Deprecated
public static final String EXTRA_CALL_RAT_TYPE_ALT = "callRadioTech";
/** @hide */
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index 5adc99e..1b583fd 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -25,8 +25,6 @@
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
-import java.util.Objects;
-
/**
* Provides the call initiation/termination, and media exchange between two IMS endpoints.
* It directly communicates with IMS service which implements the IMS protocol behavior.
@@ -346,7 +344,7 @@
}
/**
- * Called when an {@link ImsCallSession} may handover from one radio technology to another.
+ * Called when an {@link ImsCallSession} may handover from one network type to another.
* For example, the session may handover from WIFI to LTE if conditions are right.
* <p>
* If handover is attempted,
@@ -355,24 +353,24 @@
* called to indicate the success or failure of the handover.
*
* @param session IMS session object
- * @param srcAccessTech original access technology
- * @param targetAccessTech new access technology
+ * @param srcNetworkType original network type
+ * @param targetNetworkType new network type
*/
- public void callSessionMayHandover(ImsCallSession session, int srcAccessTech,
- int targetAccessTech) {
+ public void callSessionMayHandover(ImsCallSession session, int srcNetworkType,
+ int targetNetworkType) {
// no-op
}
/**
- * Called when session access technology changes
+ * Called when session network type changes
*
* @param session IMS session object
- * @param srcAccessTech original access technology
- * @param targetAccessTech new access technology
+ * @param srcNetworkType original network type
+ * @param targetNetworkType new network type
* @param reasonInfo
*/
public void callSessionHandover(ImsCallSession session,
- int srcAccessTech, int targetAccessTech,
+ int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
// no-op
}
@@ -381,12 +379,12 @@
* Called when session access technology change fails
*
* @param session IMS session object
- * @param srcAccessTech original access technology
- * @param targetAccessTech new access technology
+ * @param srcNetworkType original access technology
+ * @param targetNetworkType new access technology
* @param reasonInfo handover failure reason
*/
public void callSessionHandoverFailed(ImsCallSession session,
- int srcAccessTech, int targetAccessTech,
+ int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
// no-op
}
@@ -1303,20 +1301,19 @@
/**
* Notifies of a case where a {@link ImsCallSession} may
* potentially handover from one radio technology to another.
- * @param srcAccessTech The source radio access technology; one of the access technology
- * constants defined in {@link android.telephony.ServiceState}. For
- * example
- * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
- * @param targetAccessTech The target radio access technology; one of the access technology
- * constants defined in {@link android.telephony.ServiceState}. For
- * example
- * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ * @param srcNetworkType The source network type; one of the network type constants defined
+ * in {@link android.telephony.TelephonyManager}. For example
+ * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
+ * @param targetNetworkType The target radio access technology; one of the network type
+ * constants defined in {@link android.telephony.TelephonyManager}.
+ * For example
+ * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}.
*/
@Override
- public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
+ public void callSessionMayHandover(int srcNetworkType, int targetNetworkType) {
if (mListener != null) {
- mListener.callSessionMayHandover(ImsCallSession.this, srcAccessTech,
- targetAccessTech);
+ mListener.callSessionMayHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType);
}
}
@@ -1324,11 +1321,11 @@
* Notifies of handover information for this call
*/
@Override
- public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+ public void callSessionHandover(int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
- targetAccessTech, reasonInfo);
+ mListener.callSessionHandover(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo);
}
}
@@ -1336,11 +1333,11 @@
* Notifies of handover failure info for this call
*/
@Override
- public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+ public void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
ImsReasonInfo reasonInfo) {
if (mListener != null) {
- mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
- targetAccessTech, reasonInfo);
+ mListener.callSessionHandoverFailed(ImsCallSession.this, srcNetworkType,
+ targetNetworkType, reasonInfo);
}
}
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index e11886f..025721c 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -17,10 +17,13 @@
package android.telephony.ims;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.RemoteException;
+import android.telephony.Annotation;
import android.telephony.CallQuality;
+import android.telephony.ServiceState;
import android.telephony.ims.aidl.IImsCallSessionListener;
import android.telephony.ims.stub.ImsCallSessionImplBase;
@@ -476,11 +479,27 @@
* @param targetAccessTech The target radio access technology; one of the access technology
* constants defined in {@link android.telephony.ServiceState}. For example
* {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+ * @deprecated Uses hidden constants for radio access technology, use
+ * {@link #onMayHandover(int, int)} instead.
*/
- public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
- {
+ @Deprecated
+ public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) {
+ // Use new API internally.
+ onMayHandover(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech));
+ }
+
+ /**
+ * Notify the framework that the associated {@link ImsCallSession} may handover from one network
+ * type to another.
+ *
+ * @param srcNetworkType The source network type.
+ * @param targetNetworkType The target network type.
+ */
+ public void onMayHandover(@Annotation.NetworkType int srcNetworkType,
+ @Annotation.NetworkType int targetNetworkType) {
try {
- mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+ mListener.callSessionMayHandover(srcNetworkType, targetNetworkType);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -494,11 +513,29 @@
* @param targetAccessTech new access technology, defined in
* {@link android.telephony.ServiceState}.
* @param reasonInfo The {@link ImsReasonInfo} associated with this handover.
+ * @deprecated Uses hidden radio access technology constants, use
+ * {@link #onHandover(int, int, ImsReasonInfo)} instead.
*/
+ @Deprecated
public void callSessionHandover(int srcAccessTech, int targetAccessTech,
ImsReasonInfo reasonInfo) {
+ // Use new API internally.
+ onHandover(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+ }
+
+ /**
+ * Notify the framework that the associated {@link ImsCallSession} has handed over from one
+ * network type to another.
+ *
+ * @param srcNetworkType original network type.
+ * @param targetNetworkType target network type after handover..
+ * @param reasonInfo An optional {@link ImsReasonInfo} associated with this handover.
+ */
+ public void onHandover(@Annotation.NetworkType int srcNetworkType,
+ @Annotation.NetworkType int targetNetworkType, @Nullable ImsReasonInfo reasonInfo) {
try {
- mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+ mListener.callSessionHandover(srcNetworkType, targetNetworkType, reasonInfo);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
@@ -510,11 +547,28 @@
* @param srcAccessTech original access technology
* @param targetAccessTech new access technology
* @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+ * @deprecated Uses hidden radio access technology constants, use
+ * {@link #onHandoverFailed(int, int, ImsReasonInfo)} instead
*/
+ @Deprecated
public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
ImsReasonInfo reasonInfo) {
+ // Use new API internally.
+ onHandoverFailed(ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
+ }
+
+ /**
+ * The IMS call session's access technology change has failed..
+ *
+ * @param srcNetworkType original network type.
+ * @param targetNetworkType target network type that the handover failed for.
+ * @param reasonInfo An {@link ImsReasonInfo} detailing the reason for the failure.
+ */
+ public void onHandoverFailed(@Annotation.NetworkType int srcNetworkType,
+ @Annotation.NetworkType int targetNetworkType, @NonNull ImsReasonInfo reasonInfo) {
try {
- mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+ mListener.callSessionHandoverFailed(srcNetworkType, targetNetworkType, reasonInfo);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 666a688..aa4f77d 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -102,6 +102,101 @@
// Inheriting values from ImsConfig for backwards compatibility.
/**
+ * AMR CODEC Mode Value set, 0-7 in comma separated sequence.
+ * <p>
+ * This corresponds to the {@code mode-set} parameter for the AMR codec.
+ * See 3GPP TS 26.101 Table 1A for more information.
+ * <p>
+ * <UL>
+ * <LI>0 - AMR 4.75 kbit/s</LI>
+ * <LI>1 - AMR 5.15 kbit/s</LI>
+ * <LI>2 - AMR 5.90 kbit/s</LI>
+ * <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI>
+ * <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI>
+ * <LI>5 - AMR 7.95 kbit/s</LI>
+ * <LI>6 - AMR 10.2 kbit/s</LI>
+ * <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI>
+ * </UL>
+ * <p>
+ * Value is in String format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_AMR_CODEC_MODE_SET_VALUES = 0;
+
+ /**
+ * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence.
+ * <p>
+ * This corresponds to the {@code mode-set} parameter for the AMR wideband codec.
+ * See 3GPP TS 26.101 Table 1A for more information.
+ * <p>
+ * <UL>
+ * <LI>0 - AMR 4.75 kbit/s</LI>
+ * <LI>1 - AMR 5.15 kbit/s</LI>
+ * <LI>2 - AMR 5.90 kbit/s</LI>
+ * <LI>3 - AMR 6.70 kbit/s (PDC-EFR)</LI>
+ * <LI>4 - AMR 7.40 kbit/s (TDMA-EFR)</LI>
+ * <LI>5 - AMR 7.95 kbit/s</LI>
+ * <LI>6 - AMR 10.2 kbit/s</LI>
+ * <LI>7 - AMR 12.2 kbit/s (GSM-EFR)</LI>
+ * </UL>
+ * <p>
+ * Value is in String format.
+ * @see #setProvisioningStringValue(int, String)
+ * @see #getProvisioningStringValue(int)
+ */
+ public static final int KEY_AMR_WB_CODEC_MODE_SET_VALUES = 1;
+
+ /**
+ * SIP Session Timer value (seconds).
+ * <p>
+ * See RFC4028 for more information.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_SESSION_TIMER_SEC = 2;
+
+ /**
+ * Minimum SIP Session Expiration Timer in (seconds).
+ * <p>
+ * See RFC4028 for more information.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC = 3;
+
+ /**
+ * SIP_INVITE cancellation time out value (in milliseconds). Integer format.
+ * <p>
+ * See RFC4028 for more information.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_INVITE_CANCELLATION_TIMER_MS = 4;
+
+ /**
+ * Delay time when an iRAT transitions from eHRPD/HRPD/1xRTT to LTE.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_TRANSITION_TO_LTE_DELAY_MS = 5;
+
+ /**
+ * Silent redial status of Enabled (True), or Disabled (False).
+ * Value is in boolean format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_ENABLE_SILENT_REDIAL = 6;
+
+ /**
* An integer key representing the SIP T1 timer value in milliseconds for the associated
* subscription.
* <p>
@@ -117,6 +212,28 @@
public static final int KEY_T1_TIMER_VALUE_MS = 7;
/**
+ * SIP T2 timer value in milliseconds. See RFC 3261 for information.
+ * <p>
+ * The T2 timer is the maximum retransmit interval for non-INVITE requests and INVITE responses.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_T2_TIMER_VALUE_MS = 8;
+
+ /**
+ * SIP TF timer value in milliseconds. See RFC 3261 for information.
+ * <p>
+ * The TF timer is the non-INVITE transaction timeout timer.
+ * <p>
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_TF_TIMER_VALUE_MS = 9;
+
+ /**
* An integer key representing the voice over LTE (VoLTE) provisioning status for the
* associated subscription. Determines whether the user can register for voice services over
* LTE.
@@ -141,6 +258,43 @@
public static final int KEY_VT_PROVISIONING_STATUS = 11;
/**
+ * Domain Name for the device to populate the request URI for REGISTRATION.
+ * Value is in String format.
+ * @see #setProvisioningStringValue(int, String)
+ * @see #getProvisioningStringValue(int)
+ */
+ public static final int KEY_REGISTRATION_DOMAIN_NAME = 12;
+
+ /**
+ * Device Outgoing SMS based on either 3GPP or 3GPP2 standards.
+ * Value is in Integer format.
+ * Valid values are {@link #SMS_FORMAT_3GPP} and {@link #SMS_FORMAT_3GPP2}.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SMS_FORMAT = 13;
+
+ /**
+ * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP2 SMS format is used.
+ * See {@link android.telephony.SmsMessage#FORMAT_3GPP2} for more information.
+ */
+ public static final int SMS_FORMAT_3GPP2 = 0;
+
+ /**
+ * Value used with {@link #KEY_SMS_FORMAT} to indicate 3GPP SMS format is used.
+ * See {@link android.telephony.SmsMessage#FORMAT_3GPP} for more information.
+ */
+ public static final int SMS_FORMAT_3GPP = 1;
+
+ /**
+ * Turns SMS over IMS ON/OFF on the device.
+ * Value is in Integer format. ON (1), OFF(0).
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SMS_OVER_IP_ENABLED = 14;
+
+ /**
* An integer key associated with the carrier configured SIP PUBLISH timer, which dictates the
* expiration time in seconds for published online availability in RCS presence.
* <p>
@@ -223,7 +377,7 @@
public static final int KEY_RCS_MAX_NUM_ENTRIES_IN_RCL = 22;
/**
- * An integer associated with the expiration timer used duriing the SIP subscription of a
+ * An integer associated with the expiration timer used during the SIP subscription of a
* Request Contained List (RCL), which is used to retrieve the RCS capabilities of the contact
* book.
* <p>
@@ -234,6 +388,14 @@
public static final int KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC = 23;
/**
+ * Applies compression to LIST Subscription.
+ * Value is in Integer format. Enable (1), Disable(0).
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION = 24;
+
+ /**
* An integer key representing the RCS enhanced address book (EAB) provisioning status for the
* associated subscription. Determines whether or not SIP OPTIONS or presence will be used to
* retrieve RCS capabilities for the user's contacts.
@@ -271,6 +433,349 @@
public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27;
/**
+ * Enable voice over wifi. Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE = 28;
+
+ /**
+ * Mobile data enabled.
+ * Value is in Integer format. On (1), OFF(0).
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_MOBILE_DATA_ENABLED = 29;
+
+ /**
+ * VoLTE user opted in status.
+ * Value is in Integer format. Opted-in (1) Opted-out (0).
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_VOLTE_USER_OPT_IN_STATUS = 30;
+
+ /**
+ * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
+ * Value is in String format.
+ */
+ public static final int KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS = 31;
+
+ /**
+ * Keep Alive Enabled for SIP.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_KEEP_ALIVE_ENABLED = 32;
+
+ /**
+ * Registration retry Base Time value in seconds.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_REGISTRATION_RETRY_BASE_TIME_SEC = 33;
+
+ /**
+ * Registration retry Max Time value in seconds.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_REGISTRATION_RETRY_MAX_TIME_SEC = 34;
+
+ /**
+ * Smallest RTP port for speech codec.
+ * Value is in integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+
+ public static final int KEY_RTP_SPEECH_START_PORT = 35;
+
+ /**
+ * Largest RTP port for speech code.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_RTP_SPEECH_END_PORT = 36;
+
+ /**
+ * SIP Timer A's value in milliseconds. Timer A is the INVITE request retransmit interval (in
+ * milliseconds), for UDP only.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS = 37;
+
+ /**
+ * SIP Timer B's value in milliseconds. Timer B is the wait time for INVITE message to be,
+ * in milliseconds.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_INVITE_ACK_WAIT_TIME_MS = 38;
+
+ /**
+ * SIP Timer D's value in milliseconds. Timer D is the wait time for response retransmits of
+ * the invite client transactions, in milliseconds.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS = 39;
+
+ /**
+ * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE request retransmit
+ * interval (in milliseconds), for UDP only.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS = 40;
+
+ /**
+ * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction timeout timer,
+ * in milliseconds.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS = 41;
+
+ /**
+ * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
+ * retransmit interval.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS = 42;
+
+ /**
+ * SIP Timer H's value in milliseconds. Timer H is the value of wait time for
+ * ACK receipt.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS = 43;
+
+ /**
+ * SIP Timer I's value in milliseconds. Timer I is the value of wait time for
+ * ACK retransmits.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS = 44;
+
+ /**
+ * SIP Timer J's value in milliseconds. Timer J is the value of wait time for
+ * non-invite request retransmission.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS = 45;
+
+ /**
+ * SIP Timer K's value in milliseconds. Timer K is the value of wait time for
+ * non-invite response retransmits.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS = 46;
+
+ /**
+ * AMR WB octet aligned dynamic payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE = 47;
+
+ /**
+ * AMR WB bandwidth efficient payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 48;
+
+ /**
+ * AMR octet aligned dynamic payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE = 49;
+
+ /**
+ * AMR bandwidth efficient payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE = 50;
+
+ /**
+ * DTMF WB payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_DTMF_WB_PAYLOAD_TYPE = 51;
+
+ /**
+ * DTMF NB payload type.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_DTMF_NB_PAYLOAD_TYPE = 52;
+
+ /**
+ * AMR Default encoding mode.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_AMR_DEFAULT_ENCODING_MODE = 53;
+
+ /**
+ * SMS Public Service Identity.
+ * Value is in String format.
+ */
+ public static final int KEY_SMS_PUBLIC_SERVICE_IDENTITY = 54;
+
+ /**
+ * Video Quality - VideoQualityFeatureValuesConstants.
+ * Valid values are: {@link #VIDEO_QUALITY_HIGH} and {@link #VIDEO_QUALITY_LOW}.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_VIDEO_QUALITY = 55;
+
+ /**
+ * Used with {@link #KEY_VIDEO_QUALITY} to indicate low video quality.
+ */
+ public static final int VIDEO_QUALITY_LOW = 0;
+
+ /**
+ * Used with {@link #KEY_VIDEO_QUALITY} to indicate high video quality.
+ */
+ public static final int VIDEO_QUALITY_HIGH = 1;
+
+ /**
+ * LTE to WIFI handover threshold.
+ * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= {@link #KEY_WIFI_THRESHOLD_A}.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_LTE_THRESHOLD_1 = 56;
+
+ /**
+ * WIFI to LTE handover threshold.
+ * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link
+ * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+ * Value is in Integer format.
+ *
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_LTE_THRESHOLD_2 = 57;
+
+ /**
+ * LTE to WIFI handover threshold.
+ * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi < {@link
+ * #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+ * Value is in Integer format.
+ *
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_LTE_THRESHOLD_3 = 58;
+
+ /**
+ * 1x to WIFI handover threshold.
+ * Handover from 1x to WiFi if 1x < {@link #KEY_1X_THRESHOLD}.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_1X_THRESHOLD = 59;
+
+ /**
+ * LTE to WIFI threshold A.
+ * Handover from LTE to WiFi if LTE < {@link #KEY_LTE_THRESHOLD_1} and WiFi >= {@link
+ * #KEY_WIFI_THRESHOLD_A}.
+ * Value is in Integer format.
+ *
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_WIFI_THRESHOLD_A = 60;
+
+ /**
+ * WiFi to LTRE handover threshold B.
+ * Handover from WiFi to LTE if LTE >= {@link #KEY_LTE_THRESHOLD_3} or (WiFi <
+ * {@link #KEY_WIFI_THRESHOLD_B} and LTE >= {@link #KEY_LTE_THRESHOLD_2}).
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_WIFI_THRESHOLD_B = 61;
+
+ /**
+ * LTE ePDG timer (in seconds).
+ * Device shall not handover back to LTE until the T_ePDG_LTE timer expires.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_LTE_EPDG_TIMER_SEC = 62;
+
+ /**
+ * WiFi ePDG timer (in seconds).
+ * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires.
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_WIFI_EPDG_TIMER_SEC = 63;
+
+ /**
+ * 1x ePDG timer (in seconds).
+ * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+ */
+ public static final int KEY_1X_EPDG_TIMER_SEC = 64;
+
+ /**
+ * MultiEndpoint status: Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_MULTIENDPOINT_ENABLED = 65;
+
+ /**
+ * RTT status: Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ * @see #setProvisioningIntValue(int, int)
+ * @see #getProvisioningIntValue(int)
+ */
+ public static final int KEY_RTT_ENABLED = 66;
+
+ /**
* Callback for IMS provisioning changes.
*/
public static class Callback {
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index d64e67a..cc2ebb9 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -92,11 +92,11 @@
/**
* Notifies of handover information for this call
*/
- void callSessionHandover(int srcAccessTech, int targetAccessTech,
+ void callSessionHandover(int srcNetworkType, int targetNetworkType,
in ImsReasonInfo reasonInfo);
- void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+ void callSessionHandoverFailed(int srcNetworkType, int targetNetworkType,
in ImsReasonInfo reasonInfo);
- void callSessionMayHandover(int srcAccessTech, int targetAccessTech);
+ void callSessionMayHandover(int srcNetworkType, int targetNetworkType);
/**
* Notifies the TTY mode change by remote party.
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
index acab738..75bd6a7 100644
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -20,6 +20,7 @@
import android.os.Message;
import android.os.RemoteException;
import android.telephony.CallQuality;
+import android.telephony.ServiceState;
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsCallSession;
import android.telephony.ims.ImsConferenceState;
@@ -547,19 +548,25 @@
@Override
public void callSessionHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech,
ImsReasonInfo reasonInfo) throws RemoteException {
- mNewListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+ mNewListener.callSessionHandover(
+ ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
}
@Override
public void callSessionHandoverFailed(IImsCallSession i, int srcAccessTech,
int targetAccessTech, ImsReasonInfo reasonInfo) throws RemoteException {
- mNewListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+ mNewListener.callSessionHandoverFailed(
+ ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech), reasonInfo);
}
@Override
public void callSessionMayHandover(IImsCallSession i, int srcAccessTech, int targetAccessTech)
throws RemoteException {
- mNewListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+ mNewListener.callSessionMayHandover(
+ ServiceState.rilRadioTechnologyToNetworkType(srcAccessTech),
+ ServiceState.rilRadioTechnologyToNetworkType(targetAccessTech));
}
@Override
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index 3ec4f34..f13371c 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -17,6 +17,8 @@
package android.telephony.ims.stub;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Bundle;
@@ -206,6 +208,13 @@
return ImsUtImplBase.this.updateCallBarringForServiceClass(
cbType, action, barrList, serviceClass);
}
+
+ @Override
+ public int updateCallBarringWithPassword(int cbType, int action, String[] barrList,
+ int serviceClass, String password) throws RemoteException {
+ return ImsUtImplBase.this.updateCallBarringWithPassword(
+ cbType, action, barrList, serviceClass, password);
+ }
};
/**
@@ -328,6 +337,14 @@
}
/**
+ * Updates the configuration of the call barring for specified service class with password.
+ */
+ public int updateCallBarringWithPassword(int cbType, int action, @Nullable String[] barrList,
+ int serviceClass, @NonNull String password) {
+ return -1;
+ }
+
+ /**
* Updates the configuration of the call forward.
*/
public int updateCallForward(int action, int condition, String number, int serviceClass,
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index 0d86e2b..0f6ce13 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -135,374 +135,596 @@
/**
* AMR CODEC Mode Value set, 0-7 in comma separated sequence.
* Value is in String format.
+ * @deprecated use {@link ProvisioningManager#KEY_AMR_CODEC_MODE_SET_VALUES} instead.
*/
- public static final int VOCODER_AMRMODESET = CONFIG_START;
+ @Deprecated
+ public static final int VOCODER_AMRMODESET =
+ ProvisioningManager.KEY_AMR_CODEC_MODE_SET_VALUES;
/**
* Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence.
* Value is in String format.
+ * @deprecated use {@link ProvisioningManager#KEY_AMR_WB_CODEC_MODE_SET_VALUES} instead.
*/
- public static final int VOCODER_AMRWBMODESET = 1;
+ @Deprecated
+ public static final int VOCODER_AMRWBMODESET =
+ ProvisioningManager.KEY_AMR_WB_CODEC_MODE_SET_VALUES;
/**
* SIP Session Timer value (seconds).
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_SIP_SESSION_TIMER_SEC} instead.
*/
- public static final int SIP_SESSION_TIMER = 2;
+ @Deprecated
+ public static final int SIP_SESSION_TIMER = ProvisioningManager.KEY_SIP_SESSION_TIMER_SEC;
/**
* Minimum SIP Session Expiration Timer in (seconds).
* Value is in Integer format.
+ * @deprecated use
+ * {@link ProvisioningManager#KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC} instead.
*/
- public static final int MIN_SE = 3;
+ @Deprecated
+ public static final int MIN_SE =
+ ProvisioningManager.KEY_MINIMUM_SIP_SESSION_EXPIRATION_TIMER_SEC;
/**
* SIP_INVITE cancellation time out value (in milliseconds). Integer format.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_SIP_INVITE_CANCELLATION_TIMER_MS} instead.
*/
- public static final int CANCELLATION_TIMER = 4;
+ @Deprecated
+ public static final int CANCELLATION_TIMER =
+ ProvisioningManager.KEY_SIP_INVITE_CANCELLATION_TIMER_MS;
/**
* Delay time when an iRAT transition from eHRPD/HRPD/1xRTT to LTE.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_TRANSITION_TO_LTE_DELAY_MS} instead.
*/
- public static final int TDELAY = 5;
+ @Deprecated
+ public static final int TDELAY = ProvisioningManager.KEY_TRANSITION_TO_LTE_DELAY_MS;
/**
* Silent redial status of Enabled (True), or Disabled (False).
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_ENABLE_SILENT_REDIAL} instead.
*/
- public static final int SILENT_REDIAL_ENABLE = 6;
+ @Deprecated
+ public static final int SILENT_REDIAL_ENABLE = ProvisioningManager.KEY_ENABLE_SILENT_REDIAL;
/**
* SIP T1 timer value in milliseconds. See RFC 3261 for define.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_T1_TIMER_VALUE_MS} instead.
*/
+ @Deprecated
public static final int SIP_T1_TIMER = ProvisioningManager.KEY_T1_TIMER_VALUE_MS;
/**
* SIP T2 timer value in milliseconds. See RFC 3261 for define.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_T2_TIMER_VALUE_MS} instead.
*/
- public static final int SIP_T2_TIMER = 8;
+ @Deprecated
+ public static final int SIP_T2_TIMER = ProvisioningManager.KEY_T2_TIMER_VALUE_MS;
- /**
+ /**
* SIP TF timer value in milliseconds. See RFC 3261 for define.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_TF_TIMER_VALUE_MS} instead.
*/
- public static final int SIP_TF_TIMER = 9;
+ @Deprecated
+ public static final int SIP_TF_TIMER = ProvisioningManager.KEY_TF_TIMER_VALUE_MS;
/**
* VoLTE status for VLT/s status of Enabled (1), or Disabled (0).
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_VOLTE_PROVISIONING_STATUS} instead.
*/
+ @Deprecated
public static final int VLT_SETTING_ENABLED =
ProvisioningManager.KEY_VOLTE_PROVISIONING_STATUS;
/**
* VoLTE status for LVC/s status of Enabled (1), or Disabled (0).
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_VT_PROVISIONING_STATUS} instead.
*/
+ @Deprecated
public static final int LVC_SETTING_ENABLED =
ProvisioningManager.KEY_VT_PROVISIONING_STATUS;
+
/**
* Domain Name for the device to populate the request URI for REGISTRATION.
* Value is in String format.
+ * @deprecated use {@link ProvisioningManager#KEY_REGISTRATION_DOMAIN_NAME}.
*/
- public static final int DOMAIN_NAME = 12;
+ @Deprecated
+ public static final int DOMAIN_NAME = ProvisioningManager.KEY_REGISTRATION_DOMAIN_NAME;
+
/**
* Device Outgoing SMS based on either 3GPP or 3GPP2 standards.
* Value is in Integer format. 3GPP2(0), 3GPP(1)
- */
- public static final int SMS_FORMAT = 13;
+ * @deprecated use {@link ProvisioningManager#KEY_SMS_FORMAT}.
+ */
+ @Deprecated
+ public static final int SMS_FORMAT = ProvisioningManager.KEY_SMS_FORMAT;
+
/**
* Turns IMS ON/OFF on the device.
* Value is in Integer format. ON (1), OFF(0).
- */
- public static final int SMS_OVER_IP = 14;
+ * @deprecated use {@link ProvisioningManager#KEY_SMS_OVER_IP_ENABLED}.
+ */
+ @Deprecated
+ public static final int SMS_OVER_IP = ProvisioningManager.KEY_SMS_OVER_IP_ENABLED;
+
/**
* Requested expiration for Published Online availability.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_TIMER_SEC}.
*/
+ @Deprecated
public static final int PUBLISH_TIMER = ProvisioningManager.KEY_RCS_PUBLISH_TIMER_SEC;
+
/**
* Requested expiration for Published Offline availability.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC}.
*/
+ @Deprecated
public static final int PUBLISH_TIMER_EXTENDED =
ProvisioningManager.KEY_RCS_PUBLISH_TIMER_EXTENDED_SEC;
+
/**
*
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITY_DISCOVERY_ENABLED}.
*/
+ @Deprecated
public static final int CAPABILITY_DISCOVERY_ENABLED =
ProvisioningManager.KEY_RCS_CAPABILITY_DISCOVERY_ENABLED;
+
/**
- * Period of time the capability information of the contact is cached on handset.
+ * Period of time the capability information of the contact is cached on handset.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC}.
*/
+ @Deprecated
public static final int CAPABILITIES_CACHE_EXPIRATION =
ProvisioningManager.KEY_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC;
+
/**
* Peiod of time the availability information of a contact is cached on device.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC}.
*/
+ @Deprecated
public static final int AVAILABILITY_CACHE_EXPIRATION =
ProvisioningManager.KEY_RCS_AVAILABILITY_CACHE_EXPIRATION_SEC;
+
/**
* Interval between successive capabilities polling.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC}.
*/
+ @Deprecated
public static final int CAPABILITIES_POLL_INTERVAL =
ProvisioningManager.KEY_RCS_CAPABILITIES_POLL_INTERVAL_SEC;
+
/**
* Minimum time between two published messages from the device.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS}.
*/
+ @Deprecated
public static final int SOURCE_THROTTLE_PUBLISH =
ProvisioningManager.KEY_RCS_PUBLISH_SOURCE_THROTTLE_MS;
+
/**
* The Maximum number of MDNs contained in one Request Contained List.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RCS_MAX_NUM_ENTRIES_IN_RCL}.
*/
+ @Deprecated
public static final int MAX_NUMENTRIES_IN_RCL =
ProvisioningManager.KEY_RCS_MAX_NUM_ENTRIES_IN_RCL;
+
/**
* Expiration timer for subscription of a Request Contained List, used in capability
* polling.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC}.
*/
+ @Deprecated
public static final int CAPAB_POLL_LIST_SUB_EXP =
ProvisioningManager.KEY_RCS_CAPABILITY_POLL_LIST_SUB_EXP_SEC;
+
/**
* Applies compression to LIST Subscription.
* Value is in Integer format. Enable (1), Disable(0).
+ * @deprecated use {@link ProvisioningManager#KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION}.
*/
- public static final int GZIP_FLAG = 24;
+ @Deprecated
+ public static final int GZIP_FLAG = ProvisioningManager.KEY_USE_GZIP_FOR_LIST_SUBSCRIPTION;
+
/**
* VOLTE Status for EAB/s status of Enabled (1), or Disabled (0).
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_EAB_PROVISIONING_STATUS}.
*/
+ @Deprecated
public static final int EAB_SETTING_ENABLED =
ProvisioningManager.KEY_EAB_PROVISIONING_STATUS;
+
/**
* Wi-Fi calling roaming status.
* Value is in Integer format. ON (1), OFF(0).
+ * @deprecated use {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE}
+ * instead.
*/
+ @Deprecated
public static final int VOICE_OVER_WIFI_ROAMING =
ProvisioningManager.KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE;
+
/**
- * Wi-Fi calling modem - WfcModeFeatureValueConstants.
+ * Wi-Fi calling mode - WfcModeFeatureValueConstants.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_MODE_OVERRIDE}
+ * instead.
*/
+ @Deprecated
public static final int VOICE_OVER_WIFI_MODE =
ProvisioningManager.KEY_VOICE_OVER_WIFI_MODE_OVERRIDE;
+
/**
* VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0).
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE}.
*/
- public static final int VOICE_OVER_WIFI_SETTING_ENABLED = 28;
+ @Deprecated
+ public static final int VOICE_OVER_WIFI_SETTING_ENABLED =
+ ProvisioningManager.KEY_VOICE_OVER_WIFI_ENABLED_OVERRIDE;
+
+
/**
* Mobile data enabled.
* Value is in Integer format. On (1), OFF(0).
+ * @deprecated use {@link ProvisioningManager#KEY_MOBILE_DATA_ENABLED}.
*/
- public static final int MOBILE_DATA_ENABLED = 29;
+ @Deprecated
+ public static final int MOBILE_DATA_ENABLED = ProvisioningManager.KEY_MOBILE_DATA_ENABLED;
+
/**
* VoLTE user opted in status.
* Value is in Integer format. Opted-in (1) Opted-out (0).
+ * @deprecated use {@link ProvisioningManager#KEY_VOLTE_USER_OPT_IN_STATUS}.
*/
- public static final int VOLTE_USER_OPT_IN_STATUS = 30;
+ @Deprecated
+ public static final int VOLTE_USER_OPT_IN_STATUS =
+ ProvisioningManager.KEY_VOLTE_USER_OPT_IN_STATUS;
+
/**
* Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
* Value is in String format.
+ * @deprecated use {@link ProvisioningManager#KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS}.
*/
- public static final int LBO_PCSCF_ADDRESS = 31;
+ @Deprecated
+ public static final int LBO_PCSCF_ADDRESS =
+ ProvisioningManager.KEY_LOCAL_BREAKOUT_PCSCF_ADDRESS;
+
/**
* Keep Alive Enabled for SIP.
* Value is in Integer format. On(1), OFF(0).
+ * @deprecated use {@link ProvisioningManager#KEY_SIP_KEEP_ALIVE_ENABLED}.
*/
- public static final int KEEP_ALIVE_ENABLED = 32;
+ @Deprecated
+ public static final int KEEP_ALIVE_ENABLED = ProvisioningManager.KEY_SIP_KEEP_ALIVE_ENABLED;
+
/**
* Registration retry Base Time value in seconds.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_REGISTRATION_RETRY_BASE_TIME_SEC}.
*/
- public static final int REGISTRATION_RETRY_BASE_TIME_SEC = 33;
+ @Deprecated
+ public static final int REGISTRATION_RETRY_BASE_TIME_SEC =
+ ProvisioningManager.KEY_REGISTRATION_RETRY_BASE_TIME_SEC;
+
/**
* Registration retry Max Time value in seconds.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_REGISTRATION_RETRY_MAX_TIME_SEC}.
*/
- public static final int REGISTRATION_RETRY_MAX_TIME_SEC = 34;
+ @Deprecated
+ public static final int REGISTRATION_RETRY_MAX_TIME_SEC =
+ ProvisioningManager.KEY_REGISTRATION_RETRY_MAX_TIME_SEC;
+
/**
* Smallest RTP port for speech codec.
* Value is in integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RTP_SPEECH_START_PORT}.
*/
- public static final int SPEECH_START_PORT = 35;
+ @Deprecated
+ public static final int SPEECH_START_PORT = ProvisioningManager.KEY_RTP_SPEECH_START_PORT;
+
/**
* Largest RTP port for speech code.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RTP_SPEECH_END_PORT}.
*/
- public static final int SPEECH_END_PORT = 36;
+ @Deprecated
+ public static final int SPEECH_END_PORT = ProvisioningManager.KEY_RTP_SPEECH_END_PORT;
+
/**
* SIP Timer A's value in milliseconds. Timer A is the INVITE request
* retransmit interval, for UDP only.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS}.
*/
- public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = 37;
+ @Deprecated
+ public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC =
+ ProvisioningManager.KEY_SIP_INVITE_REQUEST_TRANSMIT_INTERVAL_MS;
+
/**
* SIP Timer B's value in milliseconds. Timer B is the wait time for
* INVITE message to be acknowledged.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_SIP_INVITE_ACK_WAIT_TIME_MS}.
*/
- public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = 38;
+ @Deprecated
+ public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC =
+ ProvisioningManager.KEY_SIP_INVITE_ACK_WAIT_TIME_MS;
+
/**
* SIP Timer D's value in milliseconds. Timer D is the wait time for
* response retransmits of the invite client transactions.
* Value is in Integer format.
+ * @deprecated use
+ * {@link ProvisioningManager#KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS}.
*/
- public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = 39;
+ @Deprecated
+ public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC =
+ ProvisioningManager.KEY_SIP_INVITE_RESPONSE_RETRANSMIT_WAIT_TIME_MS;
+
/**
* SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE
* request retransmit interval, for UDP only.
* Value is in Integer format.
+ * @deprecated use
+ * {@link ProvisioningManager#KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS}.
*/
- public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = 40;
+ @Deprecated
+ public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC =
+ ProvisioningManager.KEY_SIP_NON_INVITE_REQUEST_RETRANSMIT_INTERVAL_MS;
+
/**
* SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction
* timeout timer.
* Value is in Integer format.
+ * @deprecated use
+ * {@link ProvisioningManager#KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS}.
*/
- public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = 41;
+ @Deprecated
+ public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC =
+ ProvisioningManager.KEY_SIP_NON_INVITE_TRANSACTION_TIMEOUT_TIMER_MS;
+
/**
* SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
* retransmit interval.
* Value is in Integer format.
+ * @deprecated use
+ * {@link ProvisioningManager#KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS}.
*/
- public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = 42;
+ @Deprecated
+ public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC =
+ ProvisioningManager.KEY_SIP_INVITE_RESPONSE_RETRANSMIT_INTERVAL_MS;
+
/**
* SIP Timer H's value in milliseconds. Timer H is the value of wait time for
* ACK receipt.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS}.
*/
- public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = 43;
+ @Deprecated
+ public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC =
+ ProvisioningManager.KEY_SIP_ACK_RECEIPT_WAIT_TIME_MS;
+
/**
* SIP Timer I's value in milliseconds. Timer I is the value of wait time for
* ACK retransmits.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS}.
*/
- public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = 44;
+ @Deprecated
+ public static final int SIP_ACK_RETX_WAIT_TIME_MSEC =
+ ProvisioningManager.KEY_SIP_ACK_RETRANSMIT_WAIT_TIME_MS;
+
/**
* SIP Timer J's value in milliseconds. Timer J is the value of wait time for
* non-invite request retransmission.
* Value is in Integer format.
+ * @deprecated use
+ * {@link ProvisioningManager#KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS}.
*/
- public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = 45;
+ @Deprecated
+ public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC =
+ ProvisioningManager.KEY_SIP_NON_INVITE_REQUEST_RETRANSMISSION_WAIT_TIME_MS;
+
/**
* SIP Timer K's value in milliseconds. Timer K is the value of wait time for
* non-invite response retransmits.
* Value is in Integer format.
+ * @deprecated use
+ * {@link ProvisioningManager#KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS}.
*/
- public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = 46;
+ @Deprecated
+ public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC =
+ ProvisioningManager.KEY_SIP_NON_INVITE_RESPONSE_RETRANSMISSION_WAIT_TIME_MS;
+
/**
* AMR WB octet aligned dynamic payload type.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE}.
*/
- public static final int AMR_WB_OCTET_ALIGNED_PT = 47;
+ @Deprecated
+ public static final int AMR_WB_OCTET_ALIGNED_PT =
+ ProvisioningManager.KEY_AMR_WB_OCTET_ALIGNED_PAYLOAD_TYPE;
+
/**
* AMR WB bandwidth efficient payload type.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE}.
*/
- public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = 48;
+ @Deprecated
+ public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT =
+ ProvisioningManager.KEY_AMR_WB_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE;
+
/**
* AMR octet aligned dynamic payload type.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE}.
*/
- public static final int AMR_OCTET_ALIGNED_PT = 49;
+ @Deprecated
+ public static final int AMR_OCTET_ALIGNED_PT =
+ ProvisioningManager.KEY_AMR_OCTET_ALIGNED_PAYLOAD_TYPE;
+
/**
* AMR bandwidth efficient payload type.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE}.
*/
- public static final int AMR_BANDWIDTH_EFFICIENT_PT = 50;
+ @Deprecated
+ public static final int AMR_BANDWIDTH_EFFICIENT_PT =
+ ProvisioningManager.KEY_AMR_BANDWIDTH_EFFICIENT_PAYLOAD_TYPE;
+
/**
* DTMF WB payload type.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_DTMF_WB_PAYLOAD_TYPE}.
*/
- public static final int DTMF_WB_PT = 51;
+ @Deprecated
+ public static final int DTMF_WB_PT = ProvisioningManager.KEY_DTMF_WB_PAYLOAD_TYPE;
+
/**
* DTMF NB payload type.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_DTMF_NB_PAYLOAD_TYPE}.
*/
- public static final int DTMF_NB_PT = 52;
+ @Deprecated
+ public static final int DTMF_NB_PT = ProvisioningManager.KEY_DTMF_NB_PAYLOAD_TYPE;
+
/**
* AMR Default encoding mode.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_AMR_DEFAULT_ENCODING_MODE}.
*/
- public static final int AMR_DEFAULT_MODE = 53;
+ @Deprecated
+ public static final int AMR_DEFAULT_MODE =
+ ProvisioningManager.KEY_AMR_DEFAULT_ENCODING_MODE;
+
/**
* SMS Public Service Identity.
* Value is in String format.
+ * @deprecated use {@link ProvisioningManager#KEY_SMS_PUBLIC_SERVICE_IDENTITY}.
*/
- public static final int SMS_PSI = 54;
+ @Deprecated
+ public static final int SMS_PSI = ProvisioningManager.KEY_SMS_PUBLIC_SERVICE_IDENTITY;
+
/**
* Video Quality - VideoQualityFeatureValuesConstants.
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_VIDEO_QUALITY}.
*/
- public static final int VIDEO_QUALITY = 55;
+ @Deprecated
+ public static final int VIDEO_QUALITY = ProvisioningManager.KEY_VIDEO_QUALITY;
+
/**
* LTE threshold.
* Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+ * @deprecated use {@link ProvisioningManager#KEY_LTE_THRESHOLD_1}.
*/
- public static final int TH_LTE1 = 56;
+ @Deprecated
+ public static final int TH_LTE1 = ProvisioningManager.KEY_LTE_THRESHOLD_1;
+
/**
* LTE threshold.
* Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+ * @deprecated use {@link ProvisioningManager#KEY_LTE_THRESHOLD_2}.
*/
- public static final int TH_LTE2 = 57;
+ @Deprecated
+ public static final int TH_LTE2 = ProvisioningManager.KEY_LTE_THRESHOLD_2;
+
/**
* LTE threshold.
* Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+ * @deprecated use {@link ProvisioningManager#KEY_LTE_THRESHOLD_3}.
*/
- public static final int TH_LTE3 = 58;
+ @Deprecated
+ public static final int TH_LTE3 = ProvisioningManager.KEY_LTE_THRESHOLD_3;
+
/**
* 1x threshold.
* Handover from 1x to WiFi if 1x < TH1x
+ * @deprecated use {@link ProvisioningManager#KEY_1X_THRESHOLD}.
*/
- public static final int TH_1x = 59;
+ @Deprecated
+ public static final int TH_1x = ProvisioningManager.KEY_1X_THRESHOLD;
+
/**
* WiFi threshold.
* Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+ * @deprecated use {@link ProvisioningManager#KEY_WIFI_THRESHOLD_A}.
*/
- public static final int VOWT_A = 60;
+ @Deprecated
+ public static final int VOWT_A = ProvisioningManager.KEY_WIFI_THRESHOLD_A;
+
/**
* WiFi threshold.
* Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+ * @deprecated use {@link ProvisioningManager#KEY_WIFI_THRESHOLD_B}.
*/
- public static final int VOWT_B = 61;
+ @Deprecated
+ public static final int VOWT_B = ProvisioningManager.KEY_WIFI_THRESHOLD_B;
+
/**
* LTE ePDG timer.
* Device shall not handover back to LTE until the T_ePDG_LTE timer expires.
+ * @deprecated use {@link ProvisioningManager#KEY_LTE_EPDG_TIMER_SEC}.
*/
- public static final int T_EPDG_LTE = 62;
+ @Deprecated
+ public static final int T_EPDG_LTE = ProvisioningManager.KEY_LTE_EPDG_TIMER_SEC;
+
/**
* WiFi ePDG timer.
* Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires.
+ * @deprecated use {@link ProvisioningManager#KEY_WIFI_EPDG_TIMER_SEC}.
*/
- public static final int T_EPDG_WIFI = 63;
+ @Deprecated
+ public static final int T_EPDG_WIFI = ProvisioningManager.KEY_WIFI_EPDG_TIMER_SEC;
+
/**
* 1x ePDG timer.
* Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+ * @deprecated use {@link ProvisioningManager#KEY_1X_EPDG_TIMER_SEC}.
*/
- public static final int T_EPDG_1X = 64;
+ @Deprecated
+ public static final int T_EPDG_1X = ProvisioningManager.KEY_1X_EPDG_TIMER_SEC;
+
/**
* MultiEndpoint status: Enabled (1), or Disabled (0).
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_MULTIENDPOINT_ENABLED}.
*/
- public static final int VICE_SETTING_ENABLED = 65;
+ @Deprecated
+ public static final int VICE_SETTING_ENABLED = ProvisioningManager.KEY_MULTIENDPOINT_ENABLED;
/**
* RTT status: Enabled (1), or Disabled (0).
* Value is in Integer format.
+ * @deprecated use {@link ProvisioningManager#KEY_RTT_ENABLED}.
*/
- public static final int RTT_SETTING_ENABLED = 66;
+ @Deprecated
+ public static final int RTT_SETTING_ENABLED = ProvisioningManager.KEY_RTT_ENABLED;
// Expand the operator config items as needed here, need to change
// PROVISIONED_CONFIG_END after that.
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
index 15f8371..4a5380e 100644
--- a/telephony/java/com/android/ims/ImsUtInterface.java
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -166,6 +166,12 @@
String[] barrList, int serviceClass);
/**
+ * Modifies the configuration of the call barring for specified service class with password.
+ */
+ public void updateCallBarring(int cbType, int action, Message result,
+ String[] barrList, int serviceClass, String password);
+
+ /**
* Modifies the configuration of the call forward.
*/
public void updateCallForward(int action, int condition, String number,
diff --git a/telephony/java/com/android/ims/internal/IImsUt.aidl b/telephony/java/com/android/ims/internal/IImsUt.aidl
index 4f97cc5..302be65 100644
--- a/telephony/java/com/android/ims/internal/IImsUt.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUt.aidl
@@ -122,4 +122,10 @@
*/
int updateCallBarringForServiceClass(int cbType, int action, in String[] barrList,
int serviceClass);
+
+ /**
+ * Updates the configuration of the call barring for specified service class with password.
+ */
+ int updateCallBarringWithPassword(int cbType, int action, in String[] barrList,
+ int serviceClass, String password);
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index b4d3ec9..eef96c4 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -205,8 +205,6 @@
public static final String ACTION_SIM_STATE_CHANGED
= Intent.ACTION_SIM_STATE_CHANGED;
- public static final String EXTRA_REBROADCAST_ON_UNLOCK= "rebroadcastOnUnlock";
-
/**
* <p>Broadcast Action: It indicates the Emergency callback mode blocks datacall/sms
* <p class="note">.
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 832502c..d0c8024 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -148,10 +148,9 @@
}
/**
- * Create an SmsMessage from an SMS EF record.
+ * Creates an SmsMessage from an SMS EF record.
*
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by RuimSmsInterfaceManager.getAllMessagesFromIcc + 1.
+ * @param index Index of SMS EF record.
* @param data Record data.
* @return An SmsMessage representing the record.
*
@@ -202,26 +201,16 @@
}
/**
- * TODO(cleanup): why do getSubmitPdu methods take an scAddr input
- * and do nothing with it? GSM allows us to specify a SC (eg,
- * when responding to an SMS that explicitly requests the response
- * is sent to a specific SC), or pass null to use the default
- * value. Is there no similar notion in CDMA? Or do we just not
- * have it hooked up?
- */
-
- /**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddr Service Centre address. Null means use default.
- * @param destAddr Address of the recipient.
- * @param message String representation of the message payload.
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param smsHeader Array containing the data for the User Data Header, preceded
- * by the Element Identifiers.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddr Service Centre address. No use for this message.
+ * @param destAddr the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @param smsHeader array containing the data for the User Data Header, preceded by the Element
+ * Identifiers.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -231,18 +220,17 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddr Service Centre address. Null means use default.
- * @param destAddr Address of the recipient.
- * @param message String representation of the message payload.
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param smsHeader Array containing the data for the User Data Header, preceded
- * by the Element Identifiers.
- * @param priority Priority level of the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddr Service Centre address. No use for this message.
+ * @param destAddr the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @param smsHeader array containing the data for the User Data Header, preceded by the Element
+ * Identifiers.
+ * @param priority priority level of the message.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -265,16 +253,15 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address and port.
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
*
- * @param scAddr Service Centre address. null == use default
- * @param destAddr the address of the destination for the message
- * @param destPort the port to deliver the message to at the
- * destination
- * @param data the data for the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddr Service Centre address. No use for this message.
+ * @param destAddr the address of the destination for the message.
+ * @param destPort the port to deliver the message to at the destination.
+ * @param data the data for the message.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort,
@@ -305,14 +292,13 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address & port
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
*
- * @param destAddr the address of the destination for the message
- * @param userData the data for the message
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param destAddr the address of the destination for the message.
+ * @param userData the data for the message.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
@@ -321,15 +307,14 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address & port
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
*
- * @param destAddr the address of the destination for the message
- * @param userData the data for the message
- * @param statusReportRequested Indicates whether a report is requested for this message.
- * @param priority Priority level of the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param destAddr the address of the destination for the message.
+ * @param userData the data for the message.
+ * @param statusReportRequested indicates whether a report is requested for this message.
+ * @param priority Priority level of the message.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
@@ -1059,6 +1044,72 @@
}
/**
+ * Gets an SMS-DELIVER PDU for a originating address and a message.
+ *
+ * @param origAddr the address of the originating for the message.
+ * @param message string representation of the message payload.
+ * @param date the time stamp the message was received.
+ * @return a <code>SubmitPdu</code> containing null SC address and the encoded message. Returns
+ * null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getDeliverPdu(String origAddr, String message, long date) {
+ if (origAddr == null || message == null) {
+ return null;
+ }
+
+ CdmaSmsAddress addr = CdmaSmsAddress.parse(origAddr);
+ if (addr == null) return null;
+
+ BearerData bearerData = new BearerData();
+ bearerData.messageType = BearerData.MESSAGE_TYPE_DELIVER;
+
+ bearerData.messageId = 0;
+
+ bearerData.deliveryAckReq = false;
+ bearerData.userAckReq = false;
+ bearerData.readAckReq = false;
+ bearerData.reportReq = false;
+
+ bearerData.userData = new UserData();
+ bearerData.userData.payloadStr = message;
+
+ bearerData.msgCenterTimeStamp = BearerData.TimeStamp.fromMillis(date);
+
+ byte[] encodedBearerData = BearerData.encode(bearerData);
+ if (encodedBearerData == null) return null;
+
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(100);
+ DataOutputStream dos = new DataOutputStream(baos);
+ dos.writeInt(SmsEnvelope.TELESERVICE_WMT);
+ dos.writeInt(0); // servicePresent
+ dos.writeInt(0); // serviceCategory
+ dos.write(addr.digitMode);
+ dos.write(addr.numberMode);
+ dos.write(addr.ton); // number_type
+ dos.write(addr.numberPlan);
+ dos.write(addr.numberOfDigits);
+ dos.write(addr.origBytes, 0, addr.origBytes.length); // digits
+ // Subaddress is not supported.
+ dos.write(0); // subaddressType
+ dos.write(0); // subaddr_odd
+ dos.write(0); // subaddr_nbr_of_digits
+ dos.write(encodedBearerData.length);
+ dos.write(encodedBearerData, 0, encodedBearerData.length);
+ dos.close();
+
+ SubmitPdu pdu = new SubmitPdu();
+ pdu.encodedMessage = baos.toByteArray();
+ pdu.encodedScAddress = null;
+ return pdu;
+ } catch (IOException ex) {
+ Rlog.e(LOG_TAG, "creating Deliver PDU failed: " + ex);
+ }
+ return null;
+ }
+
+ /**
* Creates byte array (pseudo pdu) from SMS object.
* Note: Do not call this method more than once per object!
* @hide
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index ca03333..f4a96a6 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -32,6 +32,7 @@
import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.BitwiseOutputStream;
+import java.io.ByteArrayOutputStream;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -284,6 +285,33 @@
return ts;
}
+ public static TimeStamp fromMillis(long timeInMillis) {
+ TimeStamp ts = new TimeStamp();
+ LocalDateTime localDateTime =
+ Instant.ofEpochMilli(timeInMillis).atZone(ts.mZoneId).toLocalDateTime();
+ int year = localDateTime.getYear();
+ if (year < 1996 || year > 2095) return null;
+ ts.year = year;
+ ts.month = localDateTime.getMonthValue();
+ ts.monthDay = localDateTime.getDayOfMonth();
+ ts.hour = localDateTime.getHour();
+ ts.minute = localDateTime.getMinute();
+ ts.second = localDateTime.getSecond();
+ return ts;
+ }
+
+ public byte[] toByteArray() {
+ int year = this.year % 100; // 00 - 99
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream(6);
+ outStream.write((((year / 10) & 0x0F) << 4) | ((year % 10) & 0x0F));
+ outStream.write((((month / 10) << 4) & 0xF0) | ((month % 10) & 0x0F));
+ outStream.write((((monthDay / 10) << 4) & 0xF0) | ((monthDay % 10) & 0x0F));
+ outStream.write((((hour / 10) << 4) & 0xF0) | ((hour % 10) & 0x0F));
+ outStream.write((((minute / 10) << 4) & 0xF0) | ((minute % 10) & 0x0F));
+ outStream.write((((second / 10) << 4) & 0xF0) | ((second % 10) & 0x0F));
+ return outStream.toByteArray();
+ }
+
public long toMillis() {
LocalDateTime localDateTime =
LocalDateTime.of(year, month + 1, monthDay, hour, minute, second);
@@ -957,6 +985,12 @@
}
}
+ private static void encodeMsgCenterTimeStamp(BearerData bData, BitwiseOutputStream outStream)
+ throws BitwiseOutputStream.AccessException {
+ outStream.write(8, 6);
+ outStream.writeByteArray(8 * 6, bData.msgCenterTimeStamp.toByteArray());
+ };
+
/**
* Create serialized representation for BearerData object.
* (See 3GPP2 C.R1001-F, v1.0, section 4.5 for layout details)
@@ -1021,6 +1055,10 @@
outStream.write(8, SUBPARAM_SERVICE_CATEGORY_PROGRAM_RESULTS);
encodeScpResults(bData, outStream);
}
+ if (bData.msgCenterTimeStamp != null) {
+ outStream.write(8, SUBPARAM_MESSAGE_CENTER_TIME_STAMP);
+ encodeMsgCenterTimeStamp(bData, outStream);
+ }
return outStream.toByteArray();
} catch (BitwiseOutputStream.AccessException ex) {
Rlog.e(LOG_TAG, "BearerData encode failed: " + ex);
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index f54da3b..4538193 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -42,8 +42,11 @@
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.text.ParseException;
+import java.time.Instant;
import java.time.LocalDateTime;
+import java.time.ZoneId;
import java.time.ZoneOffset;
+import java.time.ZonedDateTime;
/**
* A Short Message Service message.
@@ -168,10 +171,9 @@
}
/**
- * Create an SmsMessage from an SMS EF record.
+ * Creates an SmsMessage from an SMS EF record.
*
- * @param index Index of SMS record. This should be index in ArrayList
- * returned by SmsManager.getAllMessagesFromSim + 1.
+ * @param index Index of SMS EF record.
* @param data Record data.
* @return An SmsMessage representing the record.
*
@@ -260,12 +262,15 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddress Service Centre address. Null means use default.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param header a byte array containing the data for the User Data Header.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -278,17 +283,19 @@
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message using the
- * specified encoding.
+ * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
*
- * @param scAddress Service Centre address. Null means use default.
- * @param encoding Encoding defined by constants in
- * com.android.internal.telephony.SmsConstants.ENCODING_*
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param header a byte array containing the data for the User Data Header.
+ * @param encoding encoding defined by constants in
+ * com.android.internal.telephony.SmsConstants.ENCODING_*
* @param languageTable
* @param languageShiftTable
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -301,18 +308,20 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message using the
- * specified encoding.
+ * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
*
- * @param scAddress Service Centre address. Null means use default.
- * @param encoding Encoding defined by constants in
- * com.android.internal.telephony.SmsConstants.ENCODING_*
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param header a byte array containing the data for the User Data Header.
+ * @param encoding encoding defined by constants in
+ * com.android.internal.telephony.SmsConstants.ENCODING_*
* @param languageTable
* @param languageShiftTable
* @param validityPeriod Validity Period of the message in Minutes.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
* @hide
*/
@UnsupportedAppUsage
@@ -484,12 +493,14 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddress Service Centre address. Null means use default.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddress,
@@ -500,15 +511,15 @@
}
/**
- * Get an SMS-SUBMIT PDU for a destination address and a message
+ * Gets an SMS-SUBMIT PDU for a destination address and a message.
*
- * @param scAddress Service Centre address. Null means use default.
- * @param destinationAddress the address of the destination for the message
- * @param statusReportRequested staus report of the message Requested
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param message string representation of the message payload.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
* @param validityPeriod Validity Period of the message in Minutes.
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
@UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddress,
@@ -519,16 +530,15 @@
}
/**
- * Get an SMS-SUBMIT PDU for a data message to a destination address & port
+ * Gets an SMS-SUBMIT PDU for a data message to a destination address & port.
*
- * @param scAddress Service Centre address. null == use default
- * @param destinationAddress the address of the destination for the message
- * @param destinationPort the port to deliver the message to at the
- * destination
- * @param data the data for the message
- * @return a <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message.
- * Returns null on encode error.
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
+ * @param destinationPort the port to deliver the message to at the destination.
+ * @param data the data for the message.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
*/
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, int destinationPort, byte[] data,
@@ -552,8 +562,7 @@
SubmitPdu ret = new SubmitPdu();
ByteArrayOutputStream bo = getSubmitPduHead(
- scAddress, destinationAddress, (byte) 0x41, // MTI = SMS-SUBMIT,
- // TP-UDHI = true
+ scAddress, destinationAddress, (byte) 0x41, /* TP-MTI=SMS-SUBMIT, TP-UDHI=true */
statusReportRequested, ret);
// Skip encoding pdu if error occurs when create pdu head and the error will be handled
// properly later on encodedMessage sanity check.
@@ -580,16 +589,18 @@
}
/**
- * Create the beginning of a SUBMIT PDU. This is the part of the
- * SUBMIT PDU that is common to the two versions of {@link #getSubmitPdu},
- * one of which takes a byte array and the other of which takes a
+ * Creates the beginning of a SUBMIT PDU.
+ *
+ * This is the part of the SUBMIT PDU that is common to the two versions of
+ * {@link #getSubmitPdu}, one of which takes a byte array and the other of which takes a
* <code>String</code>.
*
- * @param scAddress Service Centre address. null == use default
- * @param destinationAddress the address of the destination for the message
+ * @param scAddress Service Centre address. Null means use default.
+ * @param destinationAddress the address of the destination for the message.
* @param mtiByte
- * @param ret <code>SubmitPdu</code> containing the encoded SC
- * address, if applicable, and the encoded message. Returns null on encode error.
+ * @param statusReportRequested indicates whether a report is reuested for this message.
+ * @param ret <code>SubmitPdu</code>.
+ * @return a byte array of the beginning of a SUBMIT PDU. Null for invalid destinationAddress.
*/
@UnsupportedAppUsage
private static ByteArrayOutputStream getSubmitPduHead(
@@ -637,6 +648,161 @@
return bo;
}
+ /**
+ * Gets an SMS-DELIVER PDU for an originating address and a message.
+ *
+ * @param scAddress Service Centre address. Null means use default.
+ * @param originatingAddress the address of the originating for the message.
+ * @param message string representation of the message payload.
+ * @param date the time stamp the message was received.
+ * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+ * encoded message. Returns null on encode error.
+ * @hide
+ */
+ public static SubmitPdu getDeliverPdu(
+ String scAddress, String originatingAddress, String message, long date) {
+ if (originatingAddress == null || message == null) {
+ return null;
+ }
+
+ // Find the best encoding to use
+ TextEncodingDetails ted = calculateLength(message, false);
+ int encoding = ted.codeUnitSize;
+ int languageTable = ted.languageTable;
+ int languageShiftTable = ted.languageShiftTable;
+ byte[] header = null;
+
+ if (encoding == ENCODING_7BIT && (languageTable != 0 || languageShiftTable != 0)) {
+ SmsHeader smsHeader = new SmsHeader();
+ smsHeader.languageTable = languageTable;
+ smsHeader.languageShiftTable = languageShiftTable;
+ header = SmsHeader.toByteArray(smsHeader);
+ }
+
+ SubmitPdu ret = new SubmitPdu();
+
+ ByteArrayOutputStream bo = new ByteArrayOutputStream(MAX_USER_DATA_BYTES + 40);
+
+ // SMSC address with length octet, or 0
+ if (scAddress == null) {
+ ret.encodedScAddress = null;
+ } else {
+ ret.encodedScAddress =
+ PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength(scAddress);
+ }
+
+ // TP-Message-Type-Indicator
+ bo.write(0); // SMS-DELIVER
+
+ byte[] oaBytes;
+
+ oaBytes = PhoneNumberUtils.networkPortionToCalledPartyBCD(originatingAddress);
+
+ // Return null for invalid originating address
+ if (oaBytes == null) return null;
+
+ // Originating address length in BCD digits, ignoring TON byte and pad
+ // TODO Should be better.
+ bo.write((oaBytes.length - 1) * 2 - ((oaBytes[oaBytes.length - 1] & 0xf0) == 0xf0 ? 1 : 0));
+
+ // Originating Address
+ bo.write(oaBytes, 0, oaBytes.length);
+
+ // TP-Protocol-Identifier
+ bo.write(0);
+
+ // User Data (and length)
+ byte[] userData;
+ try {
+ if (encoding == ENCODING_7BIT) {
+ userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header,
+ languageTable, languageShiftTable);
+ } else { // Assume UCS-2
+ try {
+ userData = encodeUCS2(message, header);
+ } catch (UnsupportedEncodingException uex) {
+ Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex);
+ return null;
+ }
+ }
+ } catch (EncodeException ex) {
+ if (ex.getError() == EncodeException.ERROR_EXCEED_SIZE) {
+ Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex);
+ return null;
+ } else {
+ // Encoding to the 7-bit alphabet failed. Let's see if we can send it as a UCS-2
+ // encoded message
+ try {
+ userData = encodeUCS2(message, header);
+ encoding = ENCODING_16BIT;
+ } catch (EncodeException ex1) {
+ Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex1);
+ return null;
+ } catch (UnsupportedEncodingException uex) {
+ Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex);
+ return null;
+ }
+ }
+ }
+
+ if (encoding == ENCODING_7BIT) {
+ if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) {
+ // Message too long
+ Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)");
+ return null;
+ }
+ // TP-Data-Coding-Scheme
+ // Default encoding, uncompressed
+ bo.write(0x00);
+ } else { // Assume UCS-2
+ if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) {
+ // Message too long
+ Rlog.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)");
+ return null;
+ }
+ // TP-Data-Coding-Scheme
+ // UCS-2 encoding, uncompressed
+ bo.write(0x08);
+ }
+
+ // TP-Service-Centre-Time-Stamp
+ byte[] scts = new byte[7];
+
+ ZonedDateTime zoneDateTime = Instant.ofEpochMilli(date).atZone(ZoneId.systemDefault());
+ LocalDateTime localDateTime = zoneDateTime.toLocalDateTime();
+
+ // It indicates the difference, expressed in quarters of an hour, between the local time and
+ // GMT.
+ int timezoneOffset = zoneDateTime.getOffset().getTotalSeconds() / 60 / 15;
+ boolean negativeOffset = timezoneOffset < 0;
+ if (negativeOffset) {
+ timezoneOffset = -timezoneOffset;
+ }
+ int year = localDateTime.getYear();
+ int month = localDateTime.getMonthValue();
+ int day = localDateTime.getDayOfMonth();
+ int hour = localDateTime.getHour();
+ int minute = localDateTime.getMinute();
+ int second = localDateTime.getSecond();
+
+ year = year > 2000 ? year - 2000 : year - 1900;
+ scts[0] = (byte) ((((year % 10) & 0x0F) << 4) | ((year / 10) & 0x0F));
+ scts[1] = (byte) ((((month % 10) & 0x0F) << 4) | ((month / 10) & 0x0F));
+ scts[2] = (byte) ((((day % 10) & 0x0F) << 4) | ((day / 10) & 0x0F));
+ scts[3] = (byte) ((((hour % 10) & 0x0F) << 4) | ((hour / 10) & 0x0F));
+ scts[4] = (byte) ((((minute % 10) & 0x0F) << 4) | ((minute / 10) & 0x0F));
+ scts[5] = (byte) ((((second % 10) & 0x0F) << 4) | ((second / 10) & 0x0F));
+ scts[6] = (byte) ((((timezoneOffset % 10) & 0x0F) << 4) | ((timezoneOffset / 10) & 0x0F));
+ if (negativeOffset) {
+ scts[0] |= 0x08; // Negative offset
+ }
+ bo.write(scts, 0, scts.length);
+
+ bo.write(userData, 0, userData.length);
+ ret.encodedMessage = bo.toByteArray();
+ return ret;
+ }
+
private static class PduParser {
@UnsupportedAppUsage
byte mPdu[];
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index a7328ac..3f311c9 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -75,6 +75,9 @@
private static final LinkAddress LINKADDRV4 = new LinkAddress(ADDRV4, 32);
private static final LinkAddress LINKADDRV6 = new LinkAddress(ADDRV6, 128);
private static final LinkAddress LINKADDRV6LINKLOCAL = new LinkAddress("fe80::1/64");
+ private static final Uri CAPPORT_API_URL = Uri.parse("https://test.example.com/capportapi");
+ private static final CaptivePortalData CAPPORT_DATA = new CaptivePortalData.Builder()
+ .setVenueInfoUrl(Uri.parse("https://test.example.com/venue")).build();
private static InetAddress address(String addrString) {
return InetAddresses.parseNumericAddress(addrString);
@@ -101,6 +104,8 @@
assertFalse(lp.isIpv6Provisioned());
assertFalse(lp.isPrivateDnsActive());
assertFalse(lp.isWakeOnLanSupported());
+ assertNull(lp.getCaptivePortalApiUrl());
+ assertNull(lp.getCaptivePortalData());
}
private LinkProperties makeTestObject() {
@@ -124,6 +129,8 @@
lp.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96"));
lp.setDhcpServerAddress(DHCPSERVER);
lp.setWakeOnLanSupported(true);
+ lp.setCaptivePortalApiUrl(CAPPORT_API_URL);
+ lp.setCaptivePortalData(CAPPORT_DATA);
return lp;
}
@@ -165,6 +172,12 @@
assertTrue(source.isIdenticalWakeOnLan(target));
assertTrue(target.isIdenticalWakeOnLan(source));
+ assertTrue(source.isIdenticalCaptivePortalApiUrl(target));
+ assertTrue(target.isIdenticalCaptivePortalApiUrl(source));
+
+ assertTrue(source.isIdenticalCaptivePortalData(target));
+ assertTrue(target.isIdenticalCaptivePortalData(source));
+
// Check result of equals().
assertTrue(source.equals(target));
assertTrue(target.equals(source));
@@ -963,6 +976,8 @@
source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
source.setWakeOnLanSupported(true);
+ source.setCaptivePortalApiUrl(CAPPORT_API_URL);
+ source.setCaptivePortalData(CAPPORT_DATA);
source.setDhcpServerAddress((Inet4Address) GATEWAY1);
@@ -970,7 +985,13 @@
stacked.setInterfaceName("test-stacked");
source.addStackedLink(stacked);
- assertParcelSane(source, 16 /* fieldCount */);
+ assertParcelSane(source.makeSensitiveFieldsParcelingCopy(), 18 /* fieldCount */);
+
+ // Verify that without using a sensitiveFieldsParcelingCopy, sensitive fields are cleared.
+ final LinkProperties sanitized = new LinkProperties(source);
+ sanitized.setCaptivePortalApiUrl(null);
+ sanitized.setCaptivePortalData(null);
+ assertEquals(sanitized, parcelingRoundTrip(source));
}
@Test
@@ -1113,4 +1134,22 @@
lp.clear();
assertFalse(lp.isWakeOnLanSupported());
}
+
+ @Test
+ public void testCaptivePortalApiUrl() {
+ final LinkProperties lp = makeTestObject();
+ assertEquals(CAPPORT_API_URL, lp.getCaptivePortalApiUrl());
+
+ lp.clear();
+ assertNull(lp.getCaptivePortalApiUrl());
+ }
+
+ @Test
+ public void testCaptivePortalData() {
+ final LinkProperties lp = makeTestObject();
+ assertEquals(CAPPORT_DATA, lp.getCaptivePortalData());
+
+ lp.clear();
+ assertNull(lp.getCaptivePortalData());
+ }
}
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 1569112..797fd83 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -271,7 +271,7 @@
.addCapability(NET_CAPABILITY_NOT_METERED);
assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
- assertParcelSane(netCap, 12);
+ assertParcelSane(netCap, 13);
}
@Test
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 0bf64b9..1c69209 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -35,9 +36,9 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.net.NetworkProvider;
import android.net.NetworkSpecifier;
import android.net.SocketKeepalive;
@@ -74,6 +75,7 @@
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
case TRANSPORT_ETHERNET:
@@ -114,7 +116,7 @@
public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp) {
super(wrapper.mHandlerThread.getLooper(), wrapper.mContext, wrapper.mLogTag,
wrapper.mNetworkInfo, wrapper.mNetworkCapabilities, lp, wrapper.mScore,
- new NetworkMisc(), NetworkProvider.ID_NONE);
+ new NetworkAgentConfig(), NetworkProvider.ID_NONE);
mWrapper = wrapper;
}
@@ -206,13 +208,11 @@
}
public void suspend() {
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.SUSPENDED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
}
public void resume() {
- mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
- mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ addCapability(NET_CAPABILITY_NOT_SUSPENDED);
}
public void disconnect() {
diff --git a/tests/net/java/android/net/CaptivePortalDataTest.kt b/tests/net/java/android/net/CaptivePortalDataTest.kt
new file mode 100644
index 0000000..0071438
--- /dev/null
+++ b/tests/net/java/android/net/CaptivePortalDataTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 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.net
+
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.testutils.assertParcelSane
+import com.android.testutils.assertParcelingIsLossless
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CaptivePortalDataTest {
+ private val data = CaptivePortalData.Builder()
+ .setRefreshTime(123L)
+ .setUserPortalUrl(Uri.parse("https://portal.example.com/test"))
+ .setVenueInfoUrl(Uri.parse("https://venue.example.com/test"))
+ .setSessionExtendable(true)
+ .setBytesRemaining(456L)
+ .setExpiryTime(789L)
+ .setCaptive(true)
+ .build()
+
+ private fun makeBuilder() = CaptivePortalData.Builder(data)
+
+ @Test
+ fun testParcelUnparcel() {
+ assertParcelSane(data, fieldCount = 7)
+
+ assertParcelingIsLossless(makeBuilder().setUserPortalUrl(null).build())
+ assertParcelingIsLossless(makeBuilder().setVenueInfoUrl(null).build())
+ }
+
+ @Test
+ fun testEquals() {
+ assertEquals(data, makeBuilder().build())
+
+ assertNotEqualsAfterChange { it.setRefreshTime(456L) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(Uri.parse("https://example.com/")) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(null) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(Uri.parse("https://example.com/")) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(null) }
+ assertNotEqualsAfterChange { it.setSessionExtendable(false) }
+ assertNotEqualsAfterChange { it.setBytesRemaining(789L) }
+ assertNotEqualsAfterChange { it.setExpiryTime(12L) }
+ assertNotEqualsAfterChange { it.setCaptive(false) }
+ }
+
+ private fun CaptivePortalData.mutate(mutator: (CaptivePortalData.Builder) -> Unit) =
+ CaptivePortalData.Builder(this).apply { mutator(this) }.build()
+
+ private fun assertNotEqualsAfterChange(mutator: (CaptivePortalData.Builder) -> Unit) {
+ assertNotEquals(data, data.mutate(mutator))
+ }
+}
\ No newline at end of file
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
new file mode 100644
index 0000000..065add4
--- /dev/null
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
+import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.os.PersistableBundle;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class ConnectivityDiagnosticsManagerTest {
+ private static final int NET_ID = 1;
+ private static final int DETECTION_METHOD = 2;
+ private static final long TIMESTAMP = 10L;
+ private static final String INTERFACE_NAME = "interface";
+ private static final String BUNDLE_KEY = "key";
+ private static final String BUNDLE_VALUE = "value";
+
+ private ConnectivityReport createSampleConnectivityReport() {
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ return new ConnectivityReport(
+ new Network(NET_ID), TIMESTAMP, linkProperties, networkCapabilities, bundle);
+ }
+
+ private ConnectivityReport createDefaultConnectivityReport() {
+ return new ConnectivityReport(
+ new Network(0),
+ 0L,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY);
+ }
+
+ @Test
+ public void testPersistableBundleEquals() {
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(
+ null, PersistableBundle.EMPTY));
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(
+ PersistableBundle.EMPTY, null));
+ assertTrue(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(
+ PersistableBundle.EMPTY, PersistableBundle.EMPTY));
+
+ final PersistableBundle a = new PersistableBundle();
+ a.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ final PersistableBundle b = new PersistableBundle();
+ b.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ final PersistableBundle c = new PersistableBundle();
+ c.putString(BUNDLE_KEY, null);
+
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(PersistableBundle.EMPTY, a));
+ assertFalse(
+ ConnectivityDiagnosticsManager.persistableBundleEquals(a, PersistableBundle.EMPTY));
+
+ assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(a, b));
+ assertTrue(ConnectivityDiagnosticsManager.persistableBundleEquals(b, a));
+
+ assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(a, c));
+ assertFalse(ConnectivityDiagnosticsManager.persistableBundleEquals(c, a));
+ }
+
+ @Test
+ public void testConnectivityReportEquals() {
+ assertEquals(createSampleConnectivityReport(), createSampleConnectivityReport());
+ assertEquals(createDefaultConnectivityReport(), createDefaultConnectivityReport());
+
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(NET_ID),
+ 0L,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ TIMESTAMP,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ 0L,
+ linkProperties,
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ TIMESTAMP,
+ new LinkProperties(),
+ networkCapabilities,
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultConnectivityReport(),
+ new ConnectivityReport(
+ new Network(0),
+ TIMESTAMP,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ bundle));
+ }
+
+ @Test
+ public void testConnectivityReportParcelUnparcel() {
+ assertParcelSane(createSampleConnectivityReport(), 5);
+ }
+
+ private DataStallReport createSampleDataStallReport() {
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+ return new DataStallReport(new Network(NET_ID), TIMESTAMP, DETECTION_METHOD, bundle);
+ }
+
+ private DataStallReport createDefaultDataStallReport() {
+ return new DataStallReport(new Network(0), 0L, 0, PersistableBundle.EMPTY);
+ }
+
+ @Test
+ public void testDataStallReportEquals() {
+ assertEquals(createSampleDataStallReport(), createSampleDataStallReport());
+ assertEquals(createDefaultDataStallReport(), createDefaultDataStallReport());
+
+ final PersistableBundle bundle = new PersistableBundle();
+ bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+
+ assertNotEquals(
+ createDefaultDataStallReport(),
+ new DataStallReport(new Network(NET_ID), 0L, 0, PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultDataStallReport(),
+ new DataStallReport(new Network(0), TIMESTAMP, 0, PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultDataStallReport(),
+ new DataStallReport(new Network(0), 0L, DETECTION_METHOD, PersistableBundle.EMPTY));
+ assertNotEquals(
+ createDefaultDataStallReport(), new DataStallReport(new Network(0), 0L, 0, bundle));
+ }
+
+ @Test
+ public void testDataStallReportParcelUnparcel() {
+ assertParcelSane(createSampleDataStallReport(), 4);
+ }
+}
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
new file mode 100644
index 0000000..d6a2176
--- /dev/null
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 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.net;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+import android.test.mock.MockContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.net.VpnProfile;
+import com.android.org.bouncycastle.x509.X509V1CertificateGenerator;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+import javax.security.auth.x500.X500Principal;
+
+/** Unit tests for {@link Ikev2VpnProfile.Builder}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class Ikev2VpnProfileTest {
+ private static final String SERVER_ADDR_STRING = "1.2.3.4";
+ private static final String IDENTITY_STRING = "Identity";
+ private static final String USERNAME_STRING = "username";
+ private static final String PASSWORD_STRING = "pa55w0rd";
+ private static final String EXCL_LIST = "exclList";
+ private static final byte[] PSK_BYTES = "preSharedKey".getBytes();
+ private static final int TEST_MTU = 1300;
+
+ private final MockContext mMockContext =
+ new MockContext() {
+ @Override
+ public String getOpPackageName() {
+ return "fooPackage";
+ }
+ };
+ private final ProxyInfo mProxy = new ProxyInfo(SERVER_ADDR_STRING, -1, EXCL_LIST);
+
+ private X509Certificate mUserCert;
+ private X509Certificate mServerRootCa;
+ private PrivateKey mPrivateKey;
+
+ @Before
+ public void setUp() throws Exception {
+ mServerRootCa = generateRandomCertAndKeyPair().cert;
+
+ final CertificateAndKey userCertKey = generateRandomCertAndKeyPair();
+ mUserCert = userCertKey.cert;
+ mPrivateKey = userCertKey.key;
+ }
+
+ private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() {
+ final Ikev2VpnProfile.Builder builder =
+ new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING);
+
+ builder.setBypassable(true);
+ builder.setProxy(mProxy);
+ builder.setMaxMtu(TEST_MTU);
+ builder.setMetered(true);
+
+ return builder;
+ }
+
+ @Test
+ public void testBuildValidProfileWithOptions() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ // Check non-auth parameters correctly stored
+ assertEquals(SERVER_ADDR_STRING, profile.getServerAddr());
+ assertEquals(IDENTITY_STRING, profile.getUserIdentity());
+ assertEquals(mProxy, profile.getProxyInfo());
+ assertTrue(profile.isBypassable());
+ assertTrue(profile.isMetered());
+ assertEquals(TEST_MTU, profile.getMaxMtu());
+ }
+
+ @Test
+ public void testBuildUsernamePasswordProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertEquals(USERNAME_STRING, profile.getUsername());
+ assertEquals(PASSWORD_STRING, profile.getPassword());
+ assertEquals(mServerRootCa, profile.getServerRootCaCert());
+
+ assertNull(profile.getPresharedKey());
+ assertNull(profile.getRsaPrivateKey());
+ assertNull(profile.getUserCert());
+ }
+
+ @Test
+ public void testBuildDigitalSignatureProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertEquals(profile.getUserCert(), mUserCert);
+ assertEquals(mPrivateKey, profile.getRsaPrivateKey());
+ assertEquals(profile.getServerRootCaCert(), mServerRootCa);
+
+ assertNull(profile.getPresharedKey());
+ assertNull(profile.getUsername());
+ assertNull(profile.getPassword());
+ }
+
+ @Test
+ public void testBuildPresharedKeyProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final Ikev2VpnProfile profile = builder.build();
+ assertNotNull(profile);
+
+ assertArrayEquals(PSK_BYTES, profile.getPresharedKey());
+
+ assertNull(profile.getServerRootCaCert());
+ assertNull(profile.getUsername());
+ assertNull(profile.getPassword());
+ assertNull(profile.getRsaPrivateKey());
+ assertNull(profile.getUserCert());
+ }
+
+ @Test
+ public void testBuildNoAuthMethodSet() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.build();
+ fail("Expected exception due to lack of auth method");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testBuildInvalidMtu() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ try {
+ builder.setMaxMtu(500);
+ fail("Expected exception due to too-small MTU");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ private void verifyVpnProfileCommon(VpnProfile profile) {
+ assertEquals(SERVER_ADDR_STRING, profile.server);
+ assertEquals(IDENTITY_STRING, profile.ipsecIdentifier);
+ assertEquals(mProxy, profile.proxy);
+ assertTrue(profile.isBypassable);
+ assertTrue(profile.isMetered);
+ assertEquals(TEST_MTU, profile.maxMtu);
+ }
+
+ @Test
+ public void testPskConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret);
+
+ // Check nothing else is set
+ assertEquals("", profile.username);
+ assertEquals("", profile.password);
+ assertEquals("", profile.ipsecUserCert);
+ assertEquals("", profile.ipsecCaCert);
+ }
+
+ @Test
+ public void testUsernamePasswordConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(USERNAME_STRING, profile.username);
+ assertEquals(PASSWORD_STRING, profile.password);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+ // Check nothing else is set
+ assertEquals("", profile.ipsecUserCert);
+ assertEquals("", profile.ipsecSecret);
+ }
+
+ @Test
+ public void testRsaConvertToVpnProfile() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+
+ verifyVpnProfileCommon(profile);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
+ assertEquals(
+ Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()),
+ profile.ipsecSecret);
+ assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
+
+ // Check nothing else is set
+ assertEquals("", profile.username);
+ assertEquals("", profile.password);
+ }
+
+ @Test
+ public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.username = USERNAME_STRING;
+ profile.password = PASSWORD_STRING;
+ profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa);
+ profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getUsername());
+ assertNull(result.getPassword());
+ assertNull(result.getUserCert());
+ assertNull(result.getRsaPrivateKey());
+ assertNull(result.getServerRootCaCert());
+ }
+
+ @Test
+ public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.ipsecSecret = new String(PSK_BYTES);
+ profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert);
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getPresharedKey());
+ assertNull(result.getUserCert());
+ assertNull(result.getRsaPrivateKey());
+ }
+
+ @Test
+ public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final VpnProfile profile = builder.build().toVpnProfile();
+ profile.username = USERNAME_STRING;
+ profile.password = PASSWORD_STRING;
+
+ final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile);
+ assertNull(result.getUsername());
+ assertNull(result.getPassword());
+ assertNull(result.getPresharedKey());
+ }
+
+ @Test
+ public void testPskConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthPsk(PSK_BYTES);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testUsernamePasswordConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ @Test
+ public void testRsaConversionIsLossless() throws Exception {
+ final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
+
+ builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
+ final Ikev2VpnProfile ikeProfile = builder.build();
+
+ assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile()));
+ }
+
+ private static class CertificateAndKey {
+ public final X509Certificate cert;
+ public final PrivateKey key;
+
+ CertificateAndKey(X509Certificate cert, PrivateKey key) {
+ this.cert = cert;
+ this.key = key;
+ }
+ }
+
+ private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception {
+ final Date validityBeginDate =
+ new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L));
+ final Date validityEndDate =
+ new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L));
+
+ // Generate a keypair
+ final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
+ keyPairGenerator.initialize(512);
+ final KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+ final X500Principal dnName = new X500Principal("CN=test.android.com");
+ final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator();
+ certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis()));
+ certGen.setSubjectDN(dnName);
+ certGen.setIssuerDN(dnName);
+ certGen.setNotBefore(validityBeginDate);
+ certGen.setNotAfter(validityEndDate);
+ certGen.setPublicKey(keyPair.getPublic());
+ certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
+
+ final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL");
+ return new CertificateAndKey(cert, keyPair.getPrivate());
+ }
+}
diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java
new file mode 100644
index 0000000..655c4d1
--- /dev/null
+++ b/tests/net/java/android/net/VpnManagerTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 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.net;
+
+import static org.mockito.Mockito.mock;
+
+import android.test.mock.MockContext;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link VpnManager}. */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class VpnManagerTest {
+ private static final String VPN_PROFILE_KEY = "KEY";
+
+ private IConnectivityManager mMockCs;
+ private VpnManager mVpnManager;
+ private final MockContext mMockContext =
+ new MockContext() {
+ @Override
+ public String getOpPackageName() {
+ return "fooPackage";
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mMockCs = mock(IConnectivityManager.class);
+ mVpnManager = new VpnManager(mMockContext, mMockCs);
+ }
+
+ @Test
+ public void testProvisionVpnProfile() throws Exception {
+ try {
+ mVpnManager.provisionVpnProfile(mock(PlatformVpnProfile.class));
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testDeleteProvisionedVpnProfile() throws Exception {
+ try {
+ mVpnManager.deleteProvisionedVpnProfile();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testStartProvisionedVpnProfile() throws Exception {
+ try {
+ mVpnManager.startProvisionedVpnProfile();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+
+ @Test
+ public void testStopProvisionedVpnProfile() throws Exception {
+ try {
+ mVpnManager.stopProvisionedVpnProfile();
+ } catch (UnsupportedOperationException expected) {
+ }
+ }
+}
diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java
new file mode 100644
index 0000000..8a4b533
--- /dev/null
+++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 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 com.android.internal.net;
+
+import static com.android.testutils.ParcelUtilsKt.assertParcelSane;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.net.IpSecAlgorithm;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+/** Unit tests for {@link VpnProfile}. */
+@SmallTest
+@RunWith(JUnit4.class)
+public class VpnProfileTest {
+ private static final String DUMMY_PROFILE_KEY = "Test";
+
+ @Test
+ public void testDefaults() throws Exception {
+ final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY);
+
+ assertEquals(DUMMY_PROFILE_KEY, p.key);
+ assertEquals("", p.name);
+ assertEquals(VpnProfile.TYPE_PPTP, p.type);
+ assertEquals("", p.server);
+ assertEquals("", p.username);
+ assertEquals("", p.password);
+ assertEquals("", p.dnsServers);
+ assertEquals("", p.searchDomains);
+ assertEquals("", p.routes);
+ assertTrue(p.mppe);
+ assertEquals("", p.l2tpSecret);
+ assertEquals("", p.ipsecIdentifier);
+ assertEquals("", p.ipsecSecret);
+ assertEquals("", p.ipsecUserCert);
+ assertEquals("", p.ipsecCaCert);
+ assertEquals("", p.ipsecServerCert);
+ assertEquals(null, p.proxy);
+ assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty());
+ assertFalse(p.isBypassable);
+ assertFalse(p.isMetered);
+ assertEquals(1400, p.maxMtu);
+ assertFalse(p.areAuthParamsInline);
+ }
+
+ private VpnProfile getSampleIkev2Profile(String key) {
+ final VpnProfile p = new VpnProfile(key);
+
+ p.name = "foo";
+ p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS;
+ p.server = "bar";
+ p.username = "baz";
+ p.password = "qux";
+ p.dnsServers = "8.8.8.8";
+ p.searchDomains = "";
+ p.routes = "0.0.0.0/0";
+ p.mppe = false;
+ p.l2tpSecret = "";
+ p.ipsecIdentifier = "quux";
+ p.ipsecSecret = "quuz";
+ p.ipsecUserCert = "corge";
+ p.ipsecCaCert = "grault";
+ p.ipsecServerCert = "garply";
+ p.proxy = null;
+ p.setAllowedAlgorithms(
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ IpSecAlgorithm.AUTH_HMAC_SHA512,
+ IpSecAlgorithm.CRYPT_AES_CBC));
+ p.isBypassable = true;
+ p.isMetered = true;
+ p.maxMtu = 1350;
+ p.areAuthParamsInline = true;
+
+ // Not saved, but also not compared.
+ p.saveLogin = true;
+
+ return p;
+ }
+
+ @Test
+ public void testEquals() {
+ assertEquals(
+ getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY));
+
+ final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ modified.maxMtu--;
+ assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified);
+ }
+
+ @Test
+ public void testParcelUnparcel() {
+ assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22);
+ }
+
+ @Test
+ public void testSetInvalidAlgorithmValueDelimiter() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+
+ try {
+ profile.setAllowedAlgorithms(
+ Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test"));
+ fail("Expected failure due to value separator in algorithm name");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testSetInvalidAlgorithmListDelimiter() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+
+ try {
+ profile.setAllowedAlgorithms(
+ Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test"));
+ fail("Expected failure due to value separator in algorithm name");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ @Test
+ public void testEncodeDecode() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertEquals(profile, decoded);
+ }
+
+ @Test
+ public void testEncodeDecodeTooManyValues() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final byte[] tooManyValues =
+ (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes();
+
+ assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues));
+ }
+
+ @Test
+ public void testEncodeDecodeInvalidNumberOfValues() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ final String encoded = new String(profile.encode());
+ final byte[] tooFewValues =
+ encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes();
+
+ assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues));
+ }
+
+ @Test
+ public void testEncodeDecodeLoginsNotSaved() {
+ final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
+ profile.saveLogin = false;
+
+ final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
+ assertNotEquals(profile, decoded);
+
+ // Add the username/password back, everything else must be equal.
+ decoded.username = profile.username;
+ decoded.password = profile.password;
+ assertEquals(profile, decoded);
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1901a1d..783f8d1 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -21,6 +21,8 @@
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_SUPL;
@@ -114,6 +116,7 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.Manifest;
import android.annotation.NonNull;
import android.app.AlarmManager;
import android.app.NotificationManager;
@@ -129,6 +132,7 @@
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.net.CaptivePortalData;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.PacketKeepalive;
@@ -165,6 +169,7 @@
import android.net.RouteInfo;
import android.net.SocketKeepalive;
import android.net.UidRange;
+import android.net.Uri;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
@@ -243,8 +248,10 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -347,6 +354,8 @@
@Spy private Resources mResources;
private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
+ // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
+ private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
MockContext(Context base, ContentProvider settingsProvider) {
super(base);
@@ -417,13 +426,39 @@
}
@Override
+ public int checkPermission(String permission, int pid, int uid) {
+ final Integer granted = mMockedPermissions.get(permission);
+ if (granted == null) {
+ // All non-mocked permissions should be held by the test or unnecessary: check as
+ // normal to make sure the code does not rely on unexpected permissions.
+ return super.checkPermission(permission, pid, uid);
+ }
+ return granted;
+ }
+
+ @Override
public void enforceCallingOrSelfPermission(String permission, String message) {
- // The mainline permission can only be held if signed with the network stack certificate
- // Skip testing for this permission.
- if (NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK.equals(permission)) return;
- // All other permissions should be held by the test or unnecessary: check as normal to
- // make sure the code does not rely on unexpected permissions.
- super.enforceCallingOrSelfPermission(permission, message);
+ final Integer granted = mMockedPermissions.get(permission);
+ if (granted == null) {
+ super.enforceCallingOrSelfPermission(permission, message);
+ return;
+ }
+
+ if (!granted.equals(PERMISSION_GRANTED)) {
+ throw new SecurityException("[Test] permission denied: " + permission);
+ }
+ }
+
+ /**
+ * Mock checks for the specified permission, and have them behave as per {@code granted}.
+ *
+ * <p>Passing null reverts to default behavior, which does a real permission check on the
+ * test package.
+ * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
+ * {@link PackageManager#PERMISSION_DENIED}.
+ */
+ public void setPermission(String permission, Integer granted) {
+ mMockedPermissions.put(permission, granted);
}
@Override
@@ -1750,6 +1785,66 @@
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
}
+ private void doNetworkCallbacksSanitizationTest(boolean sanitized) throws Exception {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ mCm.registerNetworkCallback(wifiRequest, callback);
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+
+ final LinkProperties newLp = new LinkProperties();
+ final Uri capportUrl = Uri.parse("https://capport.example.com/api");
+ final CaptivePortalData capportData = new CaptivePortalData.Builder()
+ .setCaptive(true).build();
+ newLp.setCaptivePortalApiUrl(capportUrl);
+ newLp.setCaptivePortalData(capportData);
+ mWiFiNetworkAgent.sendLinkProperties(newLp);
+
+ final Uri expectedCapportUrl = sanitized ? null : capportUrl;
+ final CaptivePortalData expectedCapportData = sanitized ? null : capportData;
+ callback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+ Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())
+ && Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
+ defaultCallback.expectLinkPropertiesThat(mWiFiNetworkAgent, lp ->
+ Objects.equals(expectedCapportUrl, lp.getCaptivePortalApiUrl())
+ && Objects.equals(expectedCapportData, lp.getCaptivePortalData()));
+
+ final LinkProperties lp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork());
+ assertEquals(expectedCapportUrl, lp.getCaptivePortalApiUrl());
+ assertEquals(expectedCapportData, lp.getCaptivePortalData());
+ }
+
+ @Test
+ public void networkCallbacksSanitizationTest_Sanitize() throws Exception {
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS,
+ PERMISSION_DENIED);
+ doNetworkCallbacksSanitizationTest(true /* sanitized */);
+ }
+
+ @Test
+ public void networkCallbacksSanitizationTest_NoSanitize_NetworkStack() throws Exception {
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_GRANTED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_DENIED);
+ doNetworkCallbacksSanitizationTest(false /* sanitized */);
+ }
+
+ @Test
+ public void networkCallbacksSanitizationTest_NoSanitize_Settings() throws Exception {
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_DENIED);
+ mServiceContext.setPermission(Manifest.permission.NETWORK_SETTINGS, PERMISSION_GRANTED);
+ doNetworkCallbacksSanitizationTest(false /* sanitized */);
+ }
+
@Test
public void testMultipleLingering() throws Exception {
// This test would be flaky with the default 120ms timer: that is short enough that
@@ -2628,6 +2723,8 @@
final String testKey = "testkey";
final String testValue = "testvalue";
testBundle.putString(testKey, testValue);
+ mServiceContext.setPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ PERMISSION_GRANTED);
mCm.startCaptivePortalApp(wifiNetwork, testBundle);
final Intent signInIntent = mServiceContext.expectStartActivityIntent(TIMEOUT_MS);
assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, signInIntent.getAction());
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 82cb193..e863266 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -37,7 +37,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.net.NetworkProvider;
import android.net.NetworkScore;
import android.os.INetworkManagementService;
@@ -75,7 +74,6 @@
@Mock INetd mNetd;
@Mock INetworkManagementService mNMS;
@Mock Context mCtx;
- @Mock NetworkMisc mMisc;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
@@ -358,7 +356,7 @@
NetworkScore ns = new NetworkScore();
ns.putIntExtension(NetworkScore.LEGACY_SCORE, 50);
NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
- caps, ns, mCtx, null, mMisc, mConnService, mNetd, mDnsResolver, mNMS,
+ caps, ns, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS,
NetworkProvider.ID_NONE);
nai.everValidated = true;
return nai;
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
index b709af1..9b24887 100644
--- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -33,8 +33,8 @@
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.NetworkAgentConfig;
import android.net.NetworkInfo;
-import android.net.NetworkMisc;
import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.test.TestLooper;
@@ -63,7 +63,6 @@
static final int NETID = 42;
@Mock ConnectivityService mConnectivity;
- @Mock NetworkMisc mMisc;
@Mock IDnsResolver mDnsResolver;
@Mock INetd mNetd;
@Mock INetworkManagementService mNms;
@@ -72,6 +71,7 @@
TestLooper mLooper;
Handler mHandler;
+ NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
Nat464Xlat makeNat464Xlat() {
return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) {
@@ -93,7 +93,7 @@
mNai.networkInfo = new NetworkInfo(null);
mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
when(mNai.connService()).thenReturn(mConnectivity);
- when(mNai.netMisc()).thenReturn(mMisc);
+ when(mNai.netAgentConfig()).thenReturn(mAgentConfig);
when(mNai.handler()).thenReturn(mHandler);
when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
@@ -104,7 +104,7 @@
String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b "
+ "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
nai.networkInfo.getDetailedState(),
- mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+ mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
nai.linkProperties.getLinkAddresses());
assertEquals(msg, expected, Nat464Xlat.requiresClat(nai));
}
@@ -113,7 +113,7 @@
String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b "
+ "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(),
nai.networkInfo.getDetailedState(),
- mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(),
+ mAgentConfig.skip464xlat, nai.linkProperties.getNat64Prefix(),
nai.linkProperties.getLinkAddresses());
assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai));
}
@@ -151,11 +151,11 @@
assertRequiresClat(true, mNai);
assertShouldStartClat(true, mNai);
- mMisc.skip464xlat = true;
+ mAgentConfig.skip464xlat = true;
assertRequiresClat(false, mNai);
assertShouldStartClat(false, mNai);
- mMisc.skip464xlat = false;
+ mAgentConfig.skip464xlat = false;
assertRequiresClat(true, mNai);
assertShouldStartClat(true, mNai);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index f85bb5f..b9eb3f2 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -3194,27 +3194,27 @@
* soft AP state and number of connected devices immediately after a successful call to this API
* via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state
* indicates that the latest attempt to start soft AP has failed. Caller can unregister a
- * previously registered callback using {@link unregisterSoftApCallback}
+ * previously registered callback using {@link #unregisterSoftApCallback}
* <p>
* Applications should have the
* {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers
* without the permission will trigger a {@link java.lang.SecurityException}.
* <p>
*
- * @param executor The executor to execute the callbacks of the {@code executor}
- * object. If null, then the application's main executor will be used.
+ * @param executor The Executor on whose thread to execute the callbacks of the {@code callback}
+ * object.
* @param callback Callback for soft AP events
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
- public void registerSoftApCallback(@Nullable @CallbackExecutor Executor executor,
+ public void registerSoftApCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull SoftApCallback callback) {
+ if (executor == null) throw new IllegalArgumentException("executor cannot be null");
if (callback == null) throw new IllegalArgumentException("callback cannot be null");
Log.v(TAG, "registerSoftApCallback: callback=" + callback + ", executor=" + executor);
- executor = (executor == null) ? mContext.getMainExecutor() : executor;
Binder binder = new Binder();
try {
mService.registerSoftApCallback(binder, new SoftApCallbackProxy(executor, callback),
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 4260c20..b130c17 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -693,6 +693,18 @@
}
/**
+ * Verify an IllegalArgumentException is thrown if executor is null.
+ */
+ @Test
+ public void registerSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForExecutor() {
+ try {
+ mWifiManager.registerSoftApCallback(null, mSoftApCallback);
+ fail("expected IllegalArgumentException");
+ } catch (IllegalArgumentException expected) {
+ }
+ }
+
+ /**
* Verify an IllegalArgumentException is thrown if callback is not provided.
*/
@Test
@@ -705,16 +717,6 @@
}
/**
- * Verify main looper is used when handler is not provided.
- */
- @Test
- public void registerSoftApCallbackUsesMainLooperOnNullArgumentForHandler() {
- when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
- mWifiManager.registerSoftApCallback(null, mSoftApCallback);
- verify(mContext).getMainExecutor();
- }
-
- /**
* Verify the call to registerSoftApCallback goes to WifiServiceImpl.
*/
@Test