diff options
157 files changed, 9810 insertions, 1814 deletions
diff --git a/Android.bp b/Android.bp index f57acfa4ab60..3046b20c7096 100644 --- a/Android.bp +++ b/Android.bp @@ -53,6 +53,7 @@ filegroup { "core/java/android/view/DisplayAdjustments.java", ], path: "core/java", + visibility: ["//frameworks/base/test-mock"], } filegroup { @@ -73,6 +74,14 @@ filegroup { } 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 @@ filegroup { ":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 @@ filegroup { ":platform-compat-native-aidl", // AIDL sources from external directories + ":credstore_aidl", ":dumpstate_aidl", ":framework_native_aidl", ":gatekeeper_aidl", @@ -289,6 +300,7 @@ java_defaults { "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 02ef7c164f93..7ebca90212d9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -9824,6 +9824,7 @@ package android.content { 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 @@ -16637,7 +16638,9 @@ package android.hardware.biometrics { ctor public BiometricPrompt.CryptoObject(@NonNull java.security.Signature); ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Cipher); ctor public BiometricPrompt.CryptoObject(@NonNull javax.crypto.Mac); + ctor public BiometricPrompt.CryptoObject(@NonNull android.security.identity.IdentityCredential); 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 +17578,9 @@ package android.hardware.fingerprint { 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 +18292,7 @@ package android.icu.lang { 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 +18321,7 @@ package android.icu.lang { 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 +18372,7 @@ package android.icu.lang { 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 +22545,7 @@ package android.icu.util { 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 +28692,6 @@ package android.net { 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 +28701,29 @@ package android.net { 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; } - 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 } public class ConnectivityManager { @@ -40921,6 +40936,7 @@ package android.se.omapi { public final class SEService { ctor public SEService(@NonNull android.content.Context, @NonNull java.util.concurrent.Executor, @NonNull android.se.omapi.SEService.OnConnectedListener); method @NonNull public android.se.omapi.Reader[] getReaders(); + method @NonNull public android.se.omapi.Reader getUiccReader(int); method @NonNull public String getVersion(); method public boolean isConnected(); method public void shutdown(); @@ -41058,6 +41074,138 @@ package android.security { } +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 { @@ -43467,6 +43615,7 @@ package android.telecom { 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 @@ -43544,6 +43693,7 @@ package android.telecom { 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(); @@ -43558,6 +43708,8 @@ package android.telecom { 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(); @@ -43566,6 +43718,7 @@ package android.telecom { 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(); @@ -43585,6 +43738,8 @@ package android.telecom { 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); @@ -43742,6 +43897,7 @@ package android.telecom { 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 @@ -43814,8 +43970,10 @@ package android.telecom { 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; @@ -43835,9 +43993,13 @@ package android.telecom { 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); @@ -43952,6 +44114,7 @@ package android.telecom { 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 @@ -44147,6 +44310,7 @@ package android.telecom { 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(); @@ -44174,6 +44338,7 @@ package android.telecom { 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"; @@ -44652,6 +44817,7 @@ package android.telephony { 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"; @@ -45182,7 +45348,7 @@ package android.telephony { 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 @@ -45609,6 +45775,7 @@ package android.telephony { 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(); @@ -45741,6 +45908,9 @@ package android.telephony { 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 @@ -46152,6 +46322,37 @@ package android.telephony.gsm { package android.telephony.ims { + public final class ImsException extends java.lang.Exception { + method public int getCode(); + field public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; // 0x1 + field public static final int CODE_ERROR_UNSPECIFIED = 0; // 0x0 + field public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; // 0x2 + } + + public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { + method @NonNull @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getVoWiFiModeSetting(); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isAdvancedCallingSettingEnabled(); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isTtyOverVolteEnabled(); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiRoamingSettingEnabled(); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled(); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVtSettingEnabled(); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback); + field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1 + field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0 + field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2 + } + + public static class ImsMmTelManager.CapabilityCallback { + ctor public ImsMmTelManager.CapabilityCallback(); + method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities); + } + public final class ImsReasonInfo implements android.os.Parcelable { ctor public ImsReasonInfo(int, int, @Nullable String); method public int describeContents(); @@ -46335,6 +46536,38 @@ package android.telephony.ims { field public static final int EXTRA_CODE_CALL_RETRY_SILENT_REDIAL = 2; // 0x2 } + public interface RegistrationManager { + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); + field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0 + field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2 + field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1 + } + + public static class RegistrationManager.RegistrationCallback { + ctor public RegistrationManager.RegistrationCallback(); + method public void onRegistered(int); + method public void onRegistering(int); + method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo); + method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo); + } + +} + +package android.telephony.ims.feature { + + public class MmTelFeature { + } + + public static class MmTelFeature.MmTelCapabilities { + field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8 + field public static final int CAPABILITY_TYPE_UT = 4; // 0x4 + field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2 + field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1 + } + } package android.telephony.mbms { @@ -49360,6 +49593,71 @@ package android.util { } +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 07106babeb76..5c31f41e2e3c 100644 --- a/api/lint-baseline.txt +++ b/api/lint-baseline.txt @@ -485,6 +485,12 @@ HiddenSuperclass: android.util.StatsLog: +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_0: 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 c8253a0b9e88..c657e0049670 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -118,9 +118,31 @@ package android.timezone { } 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 98701fd5c599..0e42ac1011a1 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1440,12 +1440,13 @@ package android.bluetooth { 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 @@ package android.bluetooth { } 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 } @@ -1653,6 +1658,7 @@ package android.content { field public static final String EXTRA_PERMISSION_GROUP_NAME = "android.intent.extra.PERMISSION_GROUP_NAME"; field public static final String EXTRA_PERMISSION_NAME = "android.intent.extra.PERMISSION_NAME"; field public static final String EXTRA_REASON = "android.intent.extra.REASON"; + field @Deprecated public static final String EXTRA_REBROADCAST_ON_UNLOCK = "rebroadcastOnUnlock"; field public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK"; field public static final String EXTRA_RESULT_NEEDED = "android.intent.extra.RESULT_NEEDED"; field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME"; @@ -2087,6 +2093,15 @@ package android.content.rollback { } +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 { @@ -4293,6 +4308,32 @@ package android.net { 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); @@ -4424,6 +4465,8 @@ package android.net { 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(); @@ -4437,9 +4480,12 @@ package android.net { 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); @@ -4468,8 +4514,26 @@ package android.net { 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 @Nullable public String getSSID(); method @NonNull public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String); @@ -4506,6 +4570,10 @@ package android.net { 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); } @@ -7024,6 +7092,14 @@ package android.provider { } +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 { @@ -8825,7 +8901,7 @@ package android.telephony { 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 @@ -9106,6 +9182,7 @@ package android.telephony { } 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); @@ -9117,6 +9194,7 @@ package android.telephony { 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); } @@ -9781,7 +9859,8 @@ package android.telephony.ims { 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"; @@ -9813,8 +9892,8 @@ package android.telephony.ims { 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); @@ -9822,7 +9901,7 @@ package android.telephony.ims { 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); @@ -9844,6 +9923,9 @@ package android.telephony.ims { 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 { @@ -9875,10 +9957,6 @@ package android.telephony.ims { ctor public ImsException(@Nullable String); ctor public ImsException(@Nullable String, int); ctor public ImsException(@Nullable String, int, @Nullable Throwable); - method public int getCode(); - field public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; // 0x1 - field public static final int CODE_ERROR_UNSPECIFIED = 0; // 0x0 - field public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; // 0x2 } public final class ImsExternalCallState implements android.os.Parcelable { @@ -9901,26 +9979,19 @@ package android.telephony.ims { 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 { - method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isTtyOverVolteEnabled(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiRoamingSettingEnabled(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int); @@ -9930,16 +10001,6 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback); - field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1 - field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0 - field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2 - } - - public static class ImsMmTelManager.CapabilityCallback { - ctor public ImsMmTelManager.CapabilityCallback(); - method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities); } @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback { @@ -10203,7 +10264,27 @@ package android.telephony.ims { 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 @@ -10213,16 +10294,52 @@ package android.telephony.ims { 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 { @@ -10279,24 +10396,6 @@ package android.telephony.ims { method @NonNull public android.telephony.ims.RcsContactUceCapability build(); } - public interface RegistrationManager { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); - field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0 - field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2 - field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1 - } - - public static class RegistrationManager.RegistrationCallback { - ctor public RegistrationManager.RegistrationCallback(); - method public void onRegistered(int); - method public void onRegistering(int); - method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo); - method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo); - } - } package android.telephony.ims.feature { @@ -10359,7 +10458,7 @@ package android.telephony.ims.feature { method public void onFeatureReady(); method public void onFeatureRemoved(); method public boolean queryCapabilityConfiguration(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); - method public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus(); + method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus(); method public void setUiTtyMode(int, @Nullable android.os.Message); method @android.telephony.ims.feature.MmTelFeature.ProcessCallResult public int shouldProcessCall(@NonNull String[]); field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL"; @@ -10375,10 +10474,6 @@ package android.telephony.ims.feature { method public final void addCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); method public final boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); method public final void removeCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); - field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8 - field public static final int CAPABILITY_TYPE_UT = 4; // 0x4 - field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2 - field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1 } @IntDef(flag=true, value={android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MmTelFeature.MmTelCapabilities.MmTelCapability { @@ -10562,6 +10657,7 @@ package android.telephony.ims.stub { 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/system-removed.txt b/api/system-removed.txt index 57ee408c561c..5802f6cc09b6 100644 --- a/api/system-removed.txt +++ b/api/system-removed.txt @@ -130,18 +130,6 @@ package android.media.tv { } -package android.net { - - @Deprecated public final class StringNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { - ctor public StringNetworkSpecifier(@NonNull String); - method public int describeContents(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.StringNetworkSpecifier> CREATOR; - field @NonNull public final String specifier; - } - -} - package android.net.wifi { @Deprecated public class BatchedScanResult implements android.os.Parcelable { diff --git a/api/test-current.txt b/api/test-current.txt index de35bb3c5052..98b224d366b9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1361,6 +1361,32 @@ package android.net { 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 @@ package android.net { 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 @@ package android.net { 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 @@ package android.telecom { 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 @@ package android.telephony.ims { 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 @@ package android.telephony.ims { 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 @@ package android.telephony.ims { 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 @@ package android.telephony.ims { 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 { @@ -3248,10 +3285,6 @@ package android.telephony.ims { ctor public ImsException(@Nullable String); ctor public ImsException(@Nullable String, int); ctor public ImsException(@Nullable String, int, @Nullable Throwable); - method public int getCode(); - field public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; // 0x1 - field public static final int CODE_ERROR_UNSPECIFIED = 0; // 0x0 - field public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; // 0x2 } public final class ImsExternalCallState implements android.os.Parcelable { @@ -3274,26 +3307,19 @@ package android.telephony.ims { 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 { - method @NonNull public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException; method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getVoWiFiModeSetting(); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getVoWiFiRoamingModeSetting(); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAdvancedCallingSettingEnabled(); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isAvailable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void isSupported(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isTtyOverVolteEnabled(); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isVoWiFiRoamingSettingEnabled(); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isVoWiFiSettingEnabled(); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isVtSettingEnabled(); method @Deprecated @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int); @@ -3303,16 +3329,6 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean); method @Deprecated @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback); - field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1 - field public static final int WIFI_MODE_WIFI_ONLY = 0; // 0x0 - field public static final int WIFI_MODE_WIFI_PREFERRED = 2; // 0x2 - } - - public static class ImsMmTelManager.CapabilityCallback { - ctor public ImsMmTelManager.CapabilityCallback(); - method public void onCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities); } @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback { @@ -3572,7 +3588,27 @@ package android.telephony.ims { 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 @@ -3582,16 +3618,52 @@ package android.telephony.ims { 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 { @@ -3600,24 +3672,6 @@ package android.telephony.ims { method public void onProvisioningStringChanged(int, @NonNull String); } - public interface RegistrationManager { - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void getRegistrationTransportType(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RegistrationManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.RegistrationManager.RegistrationCallback); - field public static final int REGISTRATION_STATE_NOT_REGISTERED = 0; // 0x0 - field public static final int REGISTRATION_STATE_REGISTERED = 2; // 0x2 - field public static final int REGISTRATION_STATE_REGISTERING = 1; // 0x1 - } - - public static class RegistrationManager.RegistrationCallback { - ctor public RegistrationManager.RegistrationCallback(); - method public void onRegistered(int); - method public void onRegistering(int); - method public void onTechnologyChangeFailed(int, @Nullable android.telephony.ims.ImsReasonInfo); - method public void onUnregistered(@Nullable android.telephony.ims.ImsReasonInfo); - } - } package android.telephony.ims.feature { @@ -3680,7 +3734,7 @@ package android.telephony.ims.feature { method public void onFeatureReady(); method public void onFeatureRemoved(); method public boolean queryCapabilityConfiguration(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int); - method public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus(); + method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus(); method public void setUiTtyMode(int, @Nullable android.os.Message); method @android.telephony.ims.feature.MmTelFeature.ProcessCallResult public int shouldProcessCall(@NonNull String[]); field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL"; @@ -3696,10 +3750,6 @@ package android.telephony.ims.feature { method public final void addCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); method public final boolean isCapable(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); method public final void removeCapabilities(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int); - field public static final int CAPABILITY_TYPE_SMS = 8; // 0x8 - field public static final int CAPABILITY_TYPE_UT = 4; // 0x4 - field public static final int CAPABILITY_TYPE_VIDEO = 2; // 0x2 - field public static final int CAPABILITY_TYPE_VOICE = 1; // 0x1 } @IntDef(flag=true, value={android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT, android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_SMS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface MmTelFeature.MmTelCapabilities.MmTelCapability { @@ -3883,6 +3933,7 @@ package android.telephony.ims.stub { 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); @@ -4084,138 +4135,10 @@ package android.util.proto { 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 7d3cd28c22d6..6ff48eac6ca5 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -720,7 +720,18 @@ public class AppOpsManager { /** @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 class AppOpsManager { 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 class AppOpsManager { 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 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { OPSTR_ACCESS_ACCESSIBILITY, OPSTR_READ_DEVICE_IDENTIFIERS, OPSTR_ACCESS_MEDIA_LOCATION, + OPSTR_ACTIVATE_PLATFORM_VPN, }; /** @@ -1480,6 +1506,7 @@ public class AppOpsManager { "ACCESS_ACCESSIBILITY", "READ_DEVICE_IDENTIFIERS", "ACCESS_MEDIA_LOCATION", + "ACTIVATE_PLATFORM_VPN" }; /** @@ -1579,6 +1606,7 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { null, // ACCESS_ACCESSIBILITY null, // READ_DEVICE_IDENTIFIERS null, // ACCESS_MEDIA_LOCATION + null, // ACTIVATE_PLATFORM_VPN }; /** @@ -1776,6 +1805,7 @@ public class AppOpsManager { false, // ACCESS_ACCESSIBILITY false, // READ_DEVICE_IDENTIFIERS false, // ACCESS_MEDIA_LOCATION + false, // ACTIVATE_PLATFORM_VPN }; /** @@ -1873,6 +1903,7 @@ public class AppOpsManager { 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 @@ public class AppOpsManager { 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 88976e182e6d..8e12a221b5f7 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -98,6 +98,7 @@ import android.media.session.MediaSessionManager; 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; @@ -369,6 +370,18 @@ final class SystemServiceRegistry { return new IpSecManager(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/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 1869c6fa7621..bd0a39c06b26 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1222,6 +1222,7 @@ public final class BluetoothAdapter { 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 @@ public final class BluetoothAdapter { */ @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/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 024bb06098ab..ec63fd058b16 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -30,6 +30,7 @@ import android.content.Context; 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 @@ import java.util.List; * @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 @@ public final class BluetoothPan implements BluetoothProfile { 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 @@ public final class BluetoothPan implements BluetoothProfile { return mProfileConnector.getService(); } - + @RequiresPermission(Manifest.permission.BLUETOOTH) protected void finalize() { + if (mCloseGuard != null) { + mCloseGuard.warnIfOpen(); + } close(); } @@ -316,6 +326,7 @@ public final class BluetoothPan implements BluetoothProfile { * @hide */ @Override + @RequiresPermission(Manifest.permission.BLUETOOTH) public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { if (VDBG) log("getDevicesMatchingStates()"); final IBluetoothPan service = getService(); @@ -335,6 +346,7 @@ public final class BluetoothPan implements BluetoothProfile { * {@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 @@ public final class BluetoothPan implements BluetoothProfile { * * @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 @@ public final class BluetoothPan implements BluetoothProfile { * * @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/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 638e6de06be7..7538df8bbe5f 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -146,7 +146,7 @@ public interface BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi int A2DP_SINK = 11; /** @@ -154,7 +154,7 @@ public interface BluetoothProfile { * * @hide */ - @UnsupportedAppUsage + @SystemApi int AVRCP_CONTROLLER = 12; /** @@ -169,6 +169,7 @@ public interface BluetoothProfile { * * @hide */ + @SystemApi int HEADSET_CLIENT = 16; /** @@ -176,6 +177,7 @@ public interface BluetoothProfile { * * @hide */ + @SystemApi int PBAP_CLIENT = 17; /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index cd4af968ebfc..725fd0531911 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3881,6 +3881,16 @@ public abstract class Context { /** * 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 * * @see #getSystemService(String) diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9c7bf1f7c996..9cba7aab3c87 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -4011,6 +4011,7 @@ public class Intent implements Parcelable, Cloneable { * <p> * @see #EXTRA_SIM_STATE * @see #EXTRA_SIM_LOCKED_REASON + * @see #EXTRA_REBROADCAST_ON_UNLOCK * * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} @@ -4187,6 +4188,18 @@ public class Intent implements Parcelable, Cloneable { public static final String SIM_ABSENT_ON_PERM_DISABLED = "PERM_DISABLED"; /** + * The extra used with {@link #ACTION_SIM_STATE_CHANGED} for indicating whether this broadcast + * is a rebroadcast on unlock. Defaults to {@code false} if not specified. + * + * @hide + * @deprecated Use {@link #ACTION_SIM_CARD_STATE_CHANGED} or + * {@link #ACTION_SIM_APPLICATION_STATE_CHANGED} + */ + @Deprecated + @SystemApi + public static final String EXTRA_REBROADCAST_ON_UNLOCK = "rebroadcastOnUnlock"; + + /** * Broadcast Action: indicate that the phone service state has changed. * The intent will have the following extra values:</p> * <p> diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java index ae3d79490c98..0a76bedcd66e 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 @@ public class AdbManager { 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 79e0794fd9af..c48fc07791c0 100644 --- a/core/java/android/debug/IAdbManager.aidl +++ b/core/java/android/debug/IAdbManager.aidl @@ -41,4 +41,15 @@ interface IAdbManager { * 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 1142a07bc66c..2497ea9c7950 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -32,6 +32,7 @@ import android.os.CancellationSignal; 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan 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 class BiometricPrompt implements BiometricAuthenticator, BiometricConstan 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 787dc6696cd3..0af18dfb0e3a 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 @@ import javax.crypto.Mac; /** * 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 @@ public class CryptoObject { 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 @@ public class CryptoObject { } /** + * 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 315af32580aa..16f96888bb0a 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -44,6 +44,7 @@ import android.os.Looper; 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 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing 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 class FingerprintManager implements BiometricAuthenticator, BiometricFing 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/CaptivePortalData.aidl b/core/java/android/net/CaptivePortalData.aidl new file mode 100644 index 000000000000..1d57ee759136 --- /dev/null +++ b/core/java/android/net/CaptivePortalData.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +@JavaOnlyStableParcelable parcelable CaptivePortalData; diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java new file mode 100644 index 000000000000..1357803a6cb8 --- /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/ConnectivityDiagnosticsManager.aidl b/core/java/android/net/ConnectivityDiagnosticsManager.aidl new file mode 100644 index 000000000000..82ba0ca113c5 --- /dev/null +++ b/core/java/android/net/ConnectivityDiagnosticsManager.aidl @@ -0,0 +1,21 @@ +/** + * + * 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; + +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 6afdb5ef1b16..a6f9b96269e1 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -18,10 +18,18 @@ package android.net; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; +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 +55,47 @@ import java.util.concurrent.Executor; * </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 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 +118,148 @@ public class ConnectivityDiagnosticsManager { @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<>() { + 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 {} + /** 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 +278,101 @@ public class ConnectivityDiagnosticsManager { 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. + * + * @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 674c58d00dcd..11c1a9c32d8a 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -3169,24 +3169,25 @@ public class ConnectivityManager { /** * @hide * Register a NetworkAgent with ConnectivityService. - * @return NetID corresponding to NetworkAgent. + * @return Network corresponding to NetworkAgent. */ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) - public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp, - NetworkCapabilities nc, int score, NetworkMisc misc) { - return registerNetworkAgent(messenger, ni, lp, nc, score, misc, NetworkProvider.ID_NONE); + public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp, + NetworkCapabilities nc, int score, NetworkAgentConfig config) { + return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE); } /** * @hide * Register a NetworkAgent with ConnectivityService. - * @return NetID corresponding to NetworkAgent. + * @return Network corresponding to NetworkAgent. */ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) - public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp, - NetworkCapabilities nc, int score, NetworkMisc misc, int providerId) { + public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp, + 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 e6a0379ff629..186196bd31c7 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -20,9 +20,9 @@ import android.app.PendingIntent; 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; @@ -152,8 +152,9 @@ interface IConnectivityManager void declareNetworkRequestUnfulfillable(in NetworkRequest request); - int registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp, - in NetworkCapabilities nc, int score, in NetworkMisc misc, in int factorySerialNumber); + Network registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp, + 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/LinkProperties.java b/core/java/android/net/LinkProperties.java index 2792c564988a..ec773ef4a1e2 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -70,6 +70,14 @@ public final class LinkProperties implements Parcelable { 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; @@ -174,6 +182,7 @@ public final class LinkProperties implements Parcelable { * Constructs a new {@code LinkProperties} with default values. */ public LinkProperties() { + mParcelSensitiveFields = false; } /** @@ -182,26 +191,32 @@ public final class LinkProperties implements Parcelable { @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 +875,11 @@ public final class LinkProperties implements Parcelable { * 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 +895,8 @@ public final class LinkProperties implements Parcelable { mTcpBufferSizes = null; mNat64Prefix = null; mWakeOnLanSupported = false; + mCaptivePortalApiUrl = null; + mCaptivePortalData = null; } /** @@ -945,6 +967,14 @@ public final class LinkProperties implements Parcelable { 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 +1509,28 @@ public final class LinkProperties implements Parcelable { } /** + * 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 +1551,73 @@ public final class LinkProperties implements Parcelable { } /** + * 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 +1656,9 @@ public final class LinkProperties implements Parcelable { && isIdenticalMtu(target) && isIdenticalTcpBufferSizes(target) && isIdenticalNat64Prefix(target) - && isIdenticalWakeOnLan(target); + && isIdenticalWakeOnLan(target) + && isIdenticalCaptivePortalApiUrl(target) + && isIdenticalCaptivePortalData(target); } /** @@ -1655,7 +1776,8 @@ public final class LinkProperties implements Parcelable { + mPcscfs.size() * 67 + ((null == mPrivateDnsServerName) ? 0 : mPrivateDnsServerName.hashCode()) + Objects.hash(mNat64Prefix) - + (mWakeOnLanSupported ? 71 : 0); + + (mWakeOnLanSupported ? 71 : 0) + + Objects.hash(mCaptivePortalApiUrl, mCaptivePortalData); } /** @@ -1694,6 +1816,8 @@ public final class LinkProperties implements Parcelable { 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 +1909,9 @@ public final class LinkProperties implements Parcelable { 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 5f6cc6eced5d..7316dfa5d1c6 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -43,11 +43,10 @@ import java.util.concurrent.atomic.AtomicBoolean; * * @hide */ -public abstract class NetworkAgent extends Handler { - // Guaranteed to be valid (not NETID_UNSET), otherwise registerNetworkAgent() would have thrown - // an exception. - public final int netId; +public 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; @@ -222,8 +221,8 @@ public abstract class NetworkAgent extends Handler { 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, @@ -232,9 +231,9 @@ public abstract class NetworkAgent extends Handler { } 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; @@ -245,116 +244,124 @@ public abstract class NetworkAgent extends Handler { if (VDBG) log("Registering NetworkAgent"); ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService( Context.CONNECTIVITY_SERVICE); - netId = 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); + private class NetworkAgentHandler extends Handler { + NetworkAgentHandler(Looper looper) { + super(looper); + } + + @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(); } - mPreConnectedQueue.clear(); } + break; } - 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; + case AsyncChannel.CMD_CHANNEL_DISCONNECT: { + if (VDBG) log("CMD_CHANNEL_DISCONNECT"); + if (mAsyncChannel != null) mAsyncChannel.disconnect(); + break; } - 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."); + 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; } - if (currentTimeMs >= (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) { - mPollLceScheduled = false; - if (mPollLcePending.getAndSet(true) == false) { - pollLceData(); + 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."); } - } 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); + 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; } - 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); + 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; } - 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); + 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; } - 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 index c65583fb530a..cb70bdd31260 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 000000000000..abc6b67efb11 --- /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 f43385d1f2e0..8ebd1392240d 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -1283,6 +1283,7 @@ public final class NetworkCapabilities implements Parcelable { * Gets the SSID of this network, or null if none or unknown. * @hide */ + @SystemApi public @Nullable String getSSID() { return mSSID; } diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java index 824ddb8dd260..e27103755e6d 100644 --- a/core/java/android/net/NetworkFactory.java +++ b/core/java/android/net/NetworkFactory.java @@ -115,13 +115,6 @@ public class NetworkFactory extends Handler { */ 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 4ad52d5aa1bc..000000000000 --- 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 3be49d530c64..ee4379a85b6b 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -467,6 +467,19 @@ public class NetworkRequest implements Parcelable { } /** + * Returns true iff. the capabilities requested in this NetworkRequest are satisfied by the + * provided {@link NetworkCapabilities}. + * + * @param nc Capabilities that should satisfy this NetworkRequest. null capabilities do not + * satisfy any request. + * @hide + */ + @SystemApi + public boolean satisfiedBy(@Nullable NetworkCapabilities nc) { + return networkCapabilities.satisfiedByNetworkCapabilities(nc); + } + + /** * @see Builder#addTransportType(int) */ public boolean hasTransport(@Transport int transportType) { diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java index 530c84a8f3f0..6ae59716cfd8 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; @@ -26,17 +25,7 @@ import com.android.internal.util.Preconditions; import java.util.Objects; -/** - * @deprecated use other subclass of {@link android.net.NetworkSpecifier}, - * eg. {@link android.net.TelephonyNetworkSpecifier}, - * {@link android.net.wifi.WifiNetworkSpecifier} instead. - * @see {@link android.net.NetworkRequest#setNetworkSpecifier(String)} for details. - * @removed this class was tentatively made SystemApi in December 2019 in the scramble for - * publishing mainline APIs, it should be removed before R release is published. - * @hide - */ -@Deprecated -@SystemApi +/** @hide */ 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 6bd3be5e041b..b85f63bbcdd2 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/nfc/Tag.java b/core/java/android/nfc/Tag.java index 8bb2df0bba58..b9e6ff4a5a9e 100644 --- a/core/java/android/nfc/Tag.java +++ b/core/java/android/nfc/Tag.java @@ -455,12 +455,12 @@ public final class Tag implements Parcelable { * * @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 b6b347ca0619..ae468fead7a2 100644 --- a/core/java/android/nfc/tech/BasicTagTechnology.java +++ b/core/java/android/nfc/tech/BasicTagTechnology.java @@ -75,7 +75,10 @@ abstract class BasicTagTechnology implements TagTechnology { 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 6daa5b4dc6d8..92574968e965 100644 --- a/core/java/android/os/AppZygote.java +++ b/core/java/android/os/AppZygote.java @@ -21,6 +21,8 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; +import dalvik.system.VMRuntime; + /** * AppZygote is responsible for interfacing with an application-specific zygote. * @@ -113,7 +115,7 @@ public class AppZygote { "app_zygote", // seInfo abi, // abi abi, // acceptedAbiList - null, // instructionSet + VMRuntime.getInstructionSet(abi), // instructionSet mZygoteUidGidMin, mZygoteUidGidMax); diff --git a/core/java/android/se/omapi/ISecureElementReader.aidl b/core/java/android/se/omapi/ISecureElementReader.aidl index a312c445395a..41244ab058e0 100644 --- a/core/java/android/se/omapi/ISecureElementReader.aidl +++ b/core/java/android/se/omapi/ISecureElementReader.aidl @@ -48,4 +48,10 @@ interface ISecureElementReader { */ 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 80262f7533c8..7f68d9188650 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 @@ public final class Reader { } 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/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java index 00060ab8ef4a..a5c5c613e1f2 100644 --- a/core/java/android/se/omapi/SEService.java +++ b/core/java/android/se/omapi/SEService.java @@ -98,6 +98,8 @@ public final class SEService { private static final String TAG = "OMAPI.SEService"; + private static final String UICC_TERMINAL = "SIM"; + private final Object mLock = new Object(); /** The client context (e.g. activity). */ @@ -190,32 +192,33 @@ public final class SEService { * is of length 0. */ public @NonNull Reader[] getReaders() { - if (mSecureElementService == null) { - throw new IllegalStateException("service not connected to system"); - } - String[] readerNames; - try { - readerNames = mSecureElementService.getReaders(); - } catch (RemoteException e) { - throw new RuntimeException(e); - } + loadReaders(); - Reader[] readers = new Reader[readerNames.length]; - int i = 0; - for (String readerName : readerNames) { - if (mReaders.get(readerName) == null) { - try { - mReaders.put(readerName, new Reader(this, readerName, - getReader(readerName))); - readers[i++] = mReaders.get(readerName); - } catch (Exception e) { - Log.e(TAG, "Error adding Reader: " + readerName, e); - } - } else { - readers[i++] = mReaders.get(readerName); - } - } - return readers; + return mReaders.values().toArray(new Reader[0]); + } + + /** + * Obtain a UICC Reader instance with specific slot number from the SecureElementService + * + * @param slotNumber The index of the uicc slot. The index starts from 1. + * @throws IllegalArgumentException if the reader object corresponding to the uiccSlotNumber + * is not exist. + * @return A Reader object for this uicc slot. + */ + public @NonNull Reader getUiccReader(int slotNumber) { + if (slotNumber < 1) { + throw new IllegalArgumentException("slotNumber should be larger than 0"); + } + loadReaders(); + + String readerName = UICC_TERMINAL + slotNumber; + Reader reader = mReaders.get(readerName); + + if (reader == null) { + throw new IllegalArgumentException("Reader:" + readerName + " doesn't exist"); + } + + return reader; } /** @@ -270,4 +273,30 @@ public final class SEService { throw new IllegalStateException(e.getMessage()); } } + + /** + * Load available Secure Element Readers + */ + private void loadReaders() { + if (mSecureElementService == null) { + throw new IllegalStateException("service not connected to system"); + } + String[] readerNames; + try { + readerNames = mSecureElementService.getReaders(); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + + for (String readerName : readerNames) { + if (mReaders.get(readerName) == null) { + try { + mReaders.put(readerName, new Reader(this, readerName, + getReader(readerName))); + } catch (Exception e) { + Log.e(TAG, "Error adding Reader: " + readerName, e); + } + } + } + } } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index e08a06abad45..f886cf58b850 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -178,6 +178,10 @@ public class PhoneStateListener { * 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 @@ public class PhoneStateListener { /** * 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 @@ public class PhoneStateListener { * 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 ada59d6d7d55..e5bbdf4b6eea 100644 --- a/core/java/android/timezone/CountryTimeZones.java +++ b/core/java/android/timezone/CountryTimeZones.java @@ -43,6 +43,7 @@ public final class CountryTimeZones { @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 39dbe85cb485..eebccf4aa577 100644 --- a/core/java/android/timezone/TelephonyLookup.java +++ b/core/java/android/timezone/TelephonyLookup.java @@ -36,12 +36,8 @@ public class TelephonyLookup { @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 @@ public class TelephonyLookup { } } + @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 a81a516c4b33..079d0882f191 100644 --- a/core/java/android/timezone/TelephonyNetworkFinder.java +++ b/core/java/android/timezone/TelephonyNetworkFinder.java @@ -23,7 +23,7 @@ import android.annotation.SystemApi; 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 15dfe62bb789..9327b001a9c8 100644 --- a/core/java/android/timezone/TimeZoneFinder.java +++ b/core/java/android/timezone/TimeZoneFinder.java @@ -22,8 +22,10 @@ import android.annotation.SystemApi; 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 @@ public final class TimeZoneFinder { @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 @@ public final class TimeZoneFinder { 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 000000000000..aba7c4c15903 --- /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 000000000000..eb191e8e3272 --- /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 8439f5aad0b1..dbd3f69e214e 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -88,7 +88,7 @@ public class TimeUtils { * * <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 6efcfbfd05f8..7b24ba997307 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 @@ import java.io.UnsupportedEncodingException; /** * 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}. + * + * <p>To retrieve the encoded protobuf stream, call {@link #getBytes()}. * - * TODO: Add a constructor that takes an OutputStream and write to that * stream as the top-level objects are finished. * - * @hide */ /* IMPLEMENTATION NOTES @@ -99,7 +104,6 @@ import java.io.UnsupportedEncodingException; * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { 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 @@ public final class ProtoOutputStream extends ProtoStream { 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { } /** - * 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 @@ public final class ProtoOutputStream extends ProtoStream { } /** - * 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 @@ public final class ProtoOutputStream extends ProtoStream { } /** - * 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 @@ public final class ProtoOutputStream extends ProtoStream { * 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 @@ public final class ProtoOutputStream extends ProtoStream { } /** - * 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 @@ public final class ProtoOutputStream extends ProtoStream { * 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 @@ public final class ProtoOutputStream extends ProtoStream { } /** - * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 @@ public final class ProtoOutputStream extends ProtoStream { /** * 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 9e2e95a5923d..4969d8a7fbe7 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 abstract class ProtoStream { */ 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 @@ public abstract class ProtoStream { // // 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 @@ public abstract class ProtoStream { /** * 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 @@ public abstract class ProtoStream { /** * 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 @@ public abstract class ProtoStream { /** * 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 @@ public abstract class ProtoStream { /** * 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 @@ public abstract class ProtoStream { /** * Get the location of the offset recorded in the token. + * + * @hide */ public static int getOffsetFromToken(long token) { return (int) token; @@ -255,8 +533,11 @@ public abstract class ProtoStream { /** * 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 @@ public abstract class ProtoStream { /** * 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 @@ public abstract class ProtoStream { + ')'; } } + + /** + * @hide + */ + protected ProtoStream() {} } diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java index 03bf590902c0..a71561b7614b 100644 --- a/core/java/android/util/proto/ProtoUtils.java +++ b/core/java/android/util/proto/ProtoUtils.java @@ -24,12 +24,12 @@ import java.io.IOException; /** * 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 @@ public class ProtoUtils { /** * 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 @@ public class ProtoUtils { /** * 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 a636bd457d3d..ef1125b5f248 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/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 52ce54bc8924..7bf35f9a9864 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -193,7 +193,8 @@ static int read_memtrack_memory(struct memtrack_proc* p, int pid, { 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 78ccba4c29dc..486df621f4f1 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -1063,22 +1063,16 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, 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 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, 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/res/res/values/strings.xml b/core/res/res/values/strings.xml index 4aa44fc6ebaa..9bc0d969aed9 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 383fcd4753f0..cd43e9fca526 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 000000000000..a2fcef56b780 --- /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 e7348d52adfe..d58d9393c0b8 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 a818119f8103..eec2072c6d5d 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -67,7 +67,6 @@ applications that come with the platform <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 @@ applications that come with the platform </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 000000000000..e69de29bb2d1 --- /dev/null +++ b/identity/MODULE_LICENSE_APACHE2 diff --git a/identity/NOTICE b/identity/NOTICE new file mode 100644 index 000000000000..64aaa8dbd68e --- /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 000000000000..533d90b2cd13 --- /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 000000000000..10e451c97707 --- /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 000000000000..3d5945065ad7 --- /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 000000000000..1933882aa4bf --- /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 000000000000..e7a6c8969482 --- /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 000000000000..c520331ab72d --- /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 000000000000..dcc6b95aec02 --- /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 000000000000..ef7afca6b888 --- /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 000000000000..335636cb07ae --- /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 000000000000..754e44af5411 --- /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 000000000000..265f271b54f7 --- /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 000000000000..bd439199f914 --- /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 000000000000..c8113803d5d3 --- /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 000000000000..a1dfc77adb29 --- /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 000000000000..3f7027005830 --- /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 000000000000..b0c073c33a8d --- /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 000000000000..7a6169ea1d5b --- /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 000000000000..7f404037fbb1 --- /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 000000000000..44370a1780f8 --- /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 000000000000..0982c8a4ab31 --- /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 000000000000..8c2406043f67 --- /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 000000000000..f454b2ce2af9 --- /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 000000000000..6eefeb8f3f2a --- /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 000000000000..e2a389bfd4da --- /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/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java index 4318a0ae7d06..d4c4a62932e6 100644 --- a/media/java/android/media/tv/TvTrackInfo.java +++ b/media/java/android/media/tv/TvTrackInfo.java @@ -352,8 +352,7 @@ public final class TvTrackInfo implements Parcelable { 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 @@ public final class TvTrackInfo implements Parcelable { @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/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index abfee1d415d6..9765074a4927 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -184,13 +184,28 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> } } + /** + * Connect this device. + * + * @param connectAllProfiles {@code true} to connect all profile, {@code false} otherwise. + * + * @deprecated use {@link #connect()} instead. + */ + @Deprecated public void connect(boolean connectAllProfiles) { + connect(); + } + + /** + * Connect this device. + */ + public void connect() { if (!ensurePaired()) { return; } mConnectAttempted = SystemClock.elapsedRealtime(); - connectWithoutResettingTimer(connectAllProfiles); + connectAllEnabledProfiles(); } public long getHiSyncId() { @@ -211,10 +226,10 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> void onBondingDockConnect() { // Attempt to connect if UUIDs are available. Otherwise, // we will connect when the ACTION_UUID intent arrives. - connect(false); + connect(); } - private void connectWithoutResettingTimer(boolean connectAllProfiles) { + private void connectAllEnabledProfiles() { synchronized (mProfileLock) { // Try to initialize the profiles if they were not. if (mProfiles.isEmpty()) { @@ -229,36 +244,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return; } - int preferredProfiles = 0; - for (LocalBluetoothProfile profile : mProfiles) { - if (connectAllProfiles ? profile.accessProfileEnabled() - : profile.isAutoConnectable()) { - if (profile.isPreferred(mDevice)) { - ++preferredProfiles; - connectInt(profile); - } - } - } - if (BluetoothUtils.D) Log.d(TAG, "Preferred profiles = " + preferredProfiles); - - if (preferredProfiles == 0) { - connectAutoConnectableProfiles(); - } - } - } - - private void connectAutoConnectableProfiles() { - if (!ensurePaired()) { - return; - } - - synchronized (mProfileLock) { - for (LocalBluetoothProfile profile : mProfiles) { - if (profile.isAutoConnectable()) { - profile.setPreferred(mDevice, true); - connectInt(profile); - } - } + mLocalAdapter.connectAllEnabledProfiles(mDevice); } } @@ -625,7 +611,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> */ if (!mProfiles.isEmpty() && ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime())) { - connectWithoutResettingTimer(false); + connectAllEnabledProfiles(); } dispatchAttributesChanged(); @@ -644,7 +630,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> refresh(); if (bondState == BluetoothDevice.BOND_BONDED && mDevice.isBondingInitiatedLocally()) { - connect(false); + connect(); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 4e16c66d736b..56b14c6b652d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -116,7 +116,7 @@ public class LocalMediaManager implements BluetoothCallback { final CachedBluetoothDevice cachedDevice = ((BluetoothMediaDevice) device).getCachedDevice(); if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) { - cachedDevice.connect(true); + cachedDevice.connect(); return; } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 98bb74ad0718..4b9f6c22d78c 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -127,7 +127,7 @@ public class LocalMediaManagerTest { mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.connectDevice(device); - verify(cachedDevice).connect(true); + verify(cachedDevice).connect(); } @Test diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 8d858dcb25df..1fb50a9a61de 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1061,7 +1061,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { // 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 17c200eaef6a..b2f4f16c8005 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 @@ public class PhoneStatusBarPolicy 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 7a09455017dd..45233744eabd 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 @@ public class NetworkControllerImpl extends BroadcastReceiver 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 db6177a63f96..21585ed5ccb7 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 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { 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 000000000000..31cd5d519d87 --- /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 cdfd310a85ae..fa8eda54e53c 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.Slog; 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private boolean mBinding; private boolean mUnbinding; + private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; + // used inside handler thread private boolean mQuietEnable = false; private boolean mEnable; @@ -257,68 +261,65 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; - 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); - } + public void onAirplaneModeChanged() { + 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(); + 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()); + 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 (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(); + // 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; } - } else if (st == BluetoothAdapter.STATE_ON) { - sendDisableMsg(BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, - mContext.getPackageName()); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to call onBrEdrDown", e); + } finally { + mBluetoothLock.readLock().unlock(); } - } else if (mEnableExternal) { - sendEnableMsg(mQuietEnableExternal, - BluetoothProtoEnums.ENABLE_DISABLE_REASON_AIRPLANE_MODE, + } 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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 657207237bf0..76c119db0ad9 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_SUSPENDED; 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.MatchAllNetworkSpecifier; 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; @@ -1573,48 +1573,49 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ -1636,6 +1637,29 @@ public class ConnectivityService extends IConnectivityManager.Stub 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()); @@ -2624,8 +2648,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 +2657,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 +2689,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } // 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 +2702,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 +2761,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 +2820,10 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 +2860,11 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 +3173,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 +3474,16 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 +3520,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 +3746,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 +3779,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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; } @@ -5496,11 +5507,14 @@ public class ConnectivityService extends IConnectivityManager.Stub // changes that would conflict throughout the automerger graph. Having this method temporarily // helps with the process of going through with all these dependent changes across the entire // tree. - public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, + /** + * Register a new agent. {@see #registerNetworkAgent} below. + */ + public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, - int currentScore, NetworkMisc networkMisc) { + int currentScore, NetworkAgentConfig networkAgentConfig) { return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities, - currentScore, networkMisc, NetworkProvider.ID_NONE); + currentScore, networkAgentConfig, NetworkProvider.ID_NONE); } /** @@ -5515,12 +5529,13 @@ public class ConnectivityService extends IConnectivityManager.Stub * 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 int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, + 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); @@ -5532,8 +5547,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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(); @@ -5551,7 +5566,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // If the network disconnects or sends any other event before that, messages are deferred by // NetworkAgent until nai.asyncChannel.connect(), which will be called when finalizing the // registration. - return nai.network.netId; + return nai.network; } private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) { @@ -5803,6 +5818,19 @@ public class ConnectivityService extends IConnectivityManager.Stub 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, @@ -5843,11 +5871,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } 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 { @@ -5855,6 +5878,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } 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; } @@ -5874,21 +5903,11 @@ public class ConnectivityService extends IConnectivityManager.Stub * @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); @@ -5899,6 +5918,19 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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. @@ -5906,6 +5938,9 @@ public class ConnectivityService extends IConnectivityManager.Stub 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(); @@ -5954,7 +5989,7 @@ public class ConnectivityService extends IConnectivityManager.Stub 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()); @@ -6127,7 +6162,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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; @@ -6144,7 +6180,8 @@ public class ConnectivityService extends IConnectivityManager.Stub 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: { @@ -6253,6 +6290,30 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + // An accumulator class to gather the list of changes that result from a rematch. + // TODO : enrich to represent an entire set of changes to apply. + private static class NetworkReassignment { + static class NetworkBgStatePair { + @NonNull final NetworkAgentInfo mNetwork; + final boolean mOldBackground; + NetworkBgStatePair(@NonNull final NetworkAgentInfo network, + final boolean oldBackground) { + mNetwork = network; + mOldBackground = oldBackground; + } + } + + @NonNull private final Set<NetworkBgStatePair> mRematchedNetworks = new ArraySet<>(); + + @NonNull Iterable<NetworkBgStatePair> getRematchedNetworks() { + return mRematchedNetworks; + } + + void addRematchedNetwork(@NonNull final NetworkBgStatePair network) { + mRematchedNetworks.add(network); + } + } + private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork( @NonNull final NetworkAgentInfo newNetwork) { final int score = newNetwork.getCurrentScore(); @@ -6298,8 +6359,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // needed. A network is needed if it is the best network for // one or more NetworkRequests, or if it is a VPN. // - // - Tears down newNetwork if it just became validated - // but turns out to be unneeded. + // - Writes into the passed reassignment object all changes that should be done for + // rematching this network with all requests, to be applied later. // // NOTE: This function only adds NetworkRequests that "newNetwork" could satisfy, // it does not remove NetworkRequests that other Networks could better satisfy. @@ -6307,15 +6368,22 @@ public class ConnectivityService extends IConnectivityManager.Stub // This function should be used when possible instead of {@code rematchAllNetworksAndRequests} // as it performs better by a factor of the number of Networks. // + // TODO : stop writing to the passed reassignment. This is temporarily more useful, but + // it's unidiomatic Java and it's hard to read. + // + // @param changes a currently-building list of changes to write to // @param newNetwork is the network to be matched against NetworkRequests. // @param now the time the rematch starts, as returned by SystemClock.elapsedRealtime(); - private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork, long now) { + private void rematchNetworkAndRequests(@NonNull final NetworkReassignment changes, + @NonNull final NetworkAgentInfo newNetwork, final long now) { ensureRunningOnConnectivityServiceThread(); if (!newNetwork.everConnected) return; boolean isNewDefault = false; NetworkAgentInfo oldDefaultNetwork = null; - final boolean wasBackgroundNetwork = newNetwork.isBackgroundNetwork(); + changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork, + newNetwork.isBackgroundNetwork())); + final int score = newNetwork.getCurrentScore(); if (VDBG || DDBG) log("rematching " + newNetwork.name()); @@ -6418,39 +6486,12 @@ public class ConnectivityService extends IConnectivityManager.Stub if (newNetwork.getCurrentScore() != score) { Slog.wtf(TAG, String.format( "BUG: %s changed score during rematch: %d -> %d", - newNetwork.name(), score, newNetwork.getCurrentScore())); + newNetwork.name(), score, newNetwork.getCurrentScore())); } // Notify requested networks are available after the default net is switched, but // before LegacyTypeTracker sends legacy broadcasts for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri); - - // Finally, process listen requests and update capabilities if the background state has - // changed for this network. For consistency with previous behavior, send onLost callbacks - // before onAvailable. - processNewlyLostListenRequests(newNetwork); - - // Maybe the network changed background states. Update its capabilities. - final boolean backgroundChanged = wasBackgroundNetwork != newNetwork.isBackgroundNetwork(); - if (backgroundChanged) { - final NetworkCapabilities newNc = mixInCapabilities(newNetwork, - newNetwork.networkCapabilities); - - final int oldPermission = getNetworkPermission(newNetwork.networkCapabilities); - final int newPermission = getNetworkPermission(newNc); - if (oldPermission != newPermission) { - try { - mNMS.setNetworkPermission(newNetwork.network.netId, newPermission); - } catch (RemoteException e) { - loge("Exception in setNetworkPermission: " + e); - } - } - - newNetwork.getAndSetNetworkCapabilities(newNc); - notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED); - } - - processNewlySatisfiedListenRequests(newNetwork); } /** @@ -6472,12 +6513,24 @@ public class ConnectivityService extends IConnectivityManager.Stub // scoring network and then a higher scoring network, which could produce multiple // callbacks. Arrays.sort(nais); + final NetworkReassignment changes = new NetworkReassignment(); for (final NetworkAgentInfo nai : nais) { - rematchNetworkAndRequests(nai, now); + rematchNetworkAndRequests(changes, nai, now); } final NetworkAgentInfo newDefaultNetwork = getDefaultNetwork(); + for (final NetworkReassignment.NetworkBgStatePair event : changes.getRematchedNetworks()) { + // Process listen requests and update capabilities if the background state has + // changed for this network. For consistency with previous behavior, send onLost + // callbacks before onAvailable. + processNewlyLostListenRequests(event.mNetwork); + if (event.mOldBackground != event.mNetwork.isBackgroundNetwork()) { + applyBackgroundChangeForRematch(event.mNetwork); + } + processNewlySatisfiedListenRequests(event.mNetwork); + } + for (final NetworkAgentInfo nai : nais) { // Rematching may have altered the linger state of some networks, so update all linger // timers. updateLingerState reads the state from the network agent and does nothing @@ -6509,6 +6562,24 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + /** + * 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, @@ -6600,10 +6671,31 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - 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; @@ -6645,7 +6737,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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( @@ -6685,17 +6777,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } 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 1ff455ea7a8f..c34dd984f865 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 7894788cb0b6..000000000000 --- 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 8b436c5f2435..62743aac55f6 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -287,7 +287,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { 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 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } 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 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { 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 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); } - if ((events & PhoneStateListener.LISTEN_IMS_CALL_DISCONNECT_CAUSES) != 0) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.READ_PRECISE_PHONE_STATE, null); - } - return true; } diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java index d19d2ddd7c3c..a7e36b23a98b 100644 --- a/services/core/java/com/android/server/TestNetworkService.java +++ b/services/core/java/com/android/server/TestNetworkService.java @@ -219,7 +219,7 @@ class TestNetworkService extends ITestNetworkManager.Stub { // Has to be in TestNetworkAgent to ensure all teardown codepaths properly clean up // resources, even for binder death or unwanted calls. synchronized (mTestNetworkTracker) { - mTestNetworkTracker.remove(netId); + mTestNetworkTracker.remove(network.netId); } } } @@ -338,7 +338,7 @@ class TestNetworkService extends ITestNetworkManager.Stub { callingUid, binder); - mTestNetworkTracker.put(agent.netId, agent); + mTestNetworkTracker.put(agent.network.netId, agent); } } catch (SocketException e) { throw new UncheckedIOException(e); diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java index 7fd98e0043c9..c125b1baf860 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 @@ package com.android.server.adb; 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 @@ public class AdbService extends IAdbManager.Stub { } } + /** + * @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/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index aea6d8d24312..f636d67c3d1d 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 @@ public class Nat464Xlat extends BaseNetworkObserver { && !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 5e085ca293a4..d66aec576137 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.INetd; 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 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // 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 class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { 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 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { mConnService = connService; mContext = context; mHandler = handler; - networkMisc = misc; + networkAgentConfig = config; this.factorySerialNumber = factorySerialNumber; } @@ -309,8 +309,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { return mConnService; } - public NetworkMisc netMisc() { - return networkMisc; + public NetworkAgentConfig netAgentConfig() { + return networkAgentConfig; } public Handler handler() { @@ -451,15 +451,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { && !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 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { // 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 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { 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 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { + "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 a46ada873408..69ab47a61362 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.LocalSocket; 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; @@ -848,7 +848,7 @@ public class Vpn { } public int getNetId() { - return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET; + return mNetworkAgent != null ? mNetworkAgent.network.netId : NETID_UNSET; } private LinkProperties makeLinkProperties() { @@ -914,7 +914,7 @@ public class Vpn { * 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 @@ public class Vpn { 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 @@ public class Vpn { 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 c7c6d5658d1d..0e795b9a5ef8 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 c60fed073a15..9760185ca6df 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.BestClock; 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 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // 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 e991a91fe8ba..609a41560e7b 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 @@ class PackageManagerShellCommand extends ShellCommand { } abandonSession = false; - if (!params.sessionParams.isStaged || !params.waitForStagedSessionReady) { + if (!params.sessionParams.isStaged || !params.mWaitForStagedSessionReady) { pw.println("Success"); return 0; } @@ -1197,7 +1197,7 @@ class PackageManagerShellCommand extends ShellCommand { + 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 @@ class PackageManagerShellCommand extends ShellCommand { SessionParams sessionParams; String installerPackageName; int userId = UserHandle.USER_ALL; - boolean waitForStagedSessionReady = false; + boolean mWaitForStagedSessionReady = true; long timeoutMs = DEFAULT_WAIT_MS; } @@ -2615,13 +2615,16 @@ class PackageManagerShellCommand extends ShellCommand { 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 e08c004866ea..157f8256ce50 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 @@ final class GlobalKeyManager { 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 b5b21f4189b4..c56634146c7b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1695,7 +1695,7 @@ public final class SystemServer { 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 000000000000..968a402ff3b7 --- /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 826a89eb38bb..acf51f3856d3 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -547,8 +547,14 @@ public final class Call { */ 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 @@ public final class Call { 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 456290cd772a..6b0845f5d12b 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -69,6 +69,7 @@ public abstract class Conference extends Conferenceable { 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 @@ public abstract class Conference extends Conferenceable { 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 @@ public abstract class Conference extends Conferenceable { } /** + * 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 abstract class Conference extends Conferenceable { 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 @@ public abstract class Conference extends Conferenceable { } /** + * 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 @@ public abstract class Conference extends Conferenceable { } /** + * 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 @@ public abstract class Conference extends Conferenceable { } 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 @@ public abstract class Conference extends Conferenceable { } } + 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 @@ public abstract class Conference extends Conferenceable { @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 f205ec64f49b..c934625f588b 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -497,8 +497,17 @@ public abstract class Connection extends Conferenceable { @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 @@ public abstract class Connection extends Conferenceable { 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 221f8f129744..6d7ceca0a2cd 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -26,6 +26,9 @@ import android.os.Parcel; 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 @@ public final class ConnectionRequest implements Parcelable { private boolean mShouldShowIncomingCallUi = false; private ParcelFileDescriptor mRttPipeToInCall; private ParcelFileDescriptor mRttPipeFromInCall; + private List<Uri> mParticipants; + private boolean mIsAdhocConference = false; public Builder() { } @@ -59,6 +64,15 @@ public final class ConnectionRequest implements Parcelable { } /** + * 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 @@ public final class ConnectionRequest implements Parcelable { } /** + * 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 @@ public final class ConnectionRequest implements Parcelable { mTelecomCallId, mShouldShowIncomingCallUi, mRttPipeFromInCall, - mRttPipeToInCall); + mRttPipeToInCall, + mParticipants, + mIsAdhocConference); } } @@ -155,6 +181,8 @@ public final class ConnectionRequest implements Parcelable { 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 @@ public final class ConnectionRequest implements Parcelable { 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 @@ public final class ConnectionRequest implements Parcelable { mShouldShowIncomingCallUi = shouldShowIncomingCallUi; mRttPipeFromInCall = rttPipeFromInCall; mRttPipeToInCall = rttPipeToInCall; + mParticipants = participants; + mIsAdhocConference = isAdhocConference; } private ConnectionRequest(Parcel in) { @@ -233,6 +278,11 @@ public final class ConnectionRequest implements Parcelable { 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 final class ConnectionRequest implements Parcelable { 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 @@ public final class ConnectionRequest implements Parcelable { } /** + * @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 @@ public final class ConnectionRequest implements Parcelable { @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 @@ public final class ConnectionRequest implements Parcelable { 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 3a0494e17db9..440f044fdcf7 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -154,6 +154,9 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { } @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 @@ public abstract class ConnectionService extends Service { } 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 abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { } } + 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 @@ public abstract class ConnectionService extends Service { "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 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { } /** + * 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 @@ public abstract class ConnectionService extends Service { } /** + * 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 @@ public abstract class ConnectionService extends Service { } /** + * 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 04e930ccd954..8f273233044e 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java @@ -100,6 +100,19 @@ final class ConnectionServiceAdapter implements DeathRecipient { } } + 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 60b2172fdeca..79ad51b92b81 100644 --- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java +++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java @@ -75,6 +75,7 @@ final class ConnectionServiceAdapterServant { 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 @@ final class ConnectionServiceAdapterServant { } 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 @@ final class ConnectionServiceAdapterServant { } @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 ede05943772e..90b69a338c7e 100644 --- a/telecomm/java/android/telecom/ParcelableConference.java +++ b/telecomm/java/android/telecom/ParcelableConference.java @@ -47,6 +47,34 @@ public final class ParcelableConference implements Parcelable { 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 @@ public final class ParcelableConference implements Parcelable { mAddressPresentation = addressPresentation; mCallerDisplayName = callerDisplayName; mCallerDisplayNamePresentation = callerDisplayNamePresentation; + mDisconnectCause = null; + mRingbackRequested = false; } @Override @@ -100,6 +130,10 @@ public final class ParcelableConference implements Parcelable { .append(mVideoState) .append(", VideoProvider: ") .append(mVideoProvider) + .append(", isRingbackRequested: ") + .append(mRingbackRequested) + .append(", disconnectCause: ") + .append(mDisconnectCause) .toString(); } @@ -151,6 +185,13 @@ public final class ParcelableConference implements Parcelable { return mAddress; } + public final DisconnectCause getDisconnectCause() { + return mDisconnectCause; + } + + public boolean isRingbackRequested() { + return mRingbackRequested; + } public int getHandlePresentation() { return mAddressPresentation; } @@ -177,11 +218,14 @@ public final class ParcelableConference implements Parcelable { 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 @@ public final class ParcelableConference implements Parcelable { 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 bb858cb0761b..abb210f13376 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -331,7 +331,17 @@ public final class PhoneAccount implements Parcelable { */ 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 @@ public final class PhoneAccount implements Parcelable { 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 1e73bd61d68e..76640e036eeb 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -101,6 +101,14 @@ final class RemoteConnectionService { } @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 ffb27797d340..f1dca0387369 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1803,6 +1803,45 @@ public class TelecomManager { } /** + * 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 @@ public class TelecomManager { } } + + /** + * 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 e35093c9656a..96f2483f32f9 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -53,6 +53,20 @@ oneway interface IConnectionService { 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 9cf098c75177..4f63e08abce6 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl @@ -44,6 +44,12 @@ oneway interface IConnectionServiceAdapter { 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 204c37e9aa38..9a47ae15e64a 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -242,11 +242,22 @@ interface ITelecomService { 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 61e67be2333f..71aaa6e95352 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1079,6 +1079,14 @@ public class CarrierConfigManager { 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 @@ public class CarrierConfigManager { * 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 @@ public class CarrierConfigManager { 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 c706d288b7f2..9b4292f42172 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -54,6 +54,43 @@ public class ImsManager { "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 2f9e6ac0f9ff..0074772a221d 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -28,7 +28,6 @@ import android.content.Context; 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 @@ public class PhoneNumberUtils { 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 @@ public class PhoneNumberUtils { 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 b5dea7c02148..0ba36b17f4ad 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -1577,7 +1577,7 @@ public final class SmsManager { } /** - * 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 @@ public final class SmsManager { * 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 @@ public final class SmsManager { } /** - * 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 @@ public final class SmsManager { } /** - * 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 @@ public final class SmsManager { } /** - * 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 @@ public final class SmsManager { * 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 @@ public final class SmsManager { 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 @@ public final class SmsManager { 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 30a61c31ab81..8d9b1dd438ea 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -278,41 +278,24 @@ public class SmsMessage { } /** - * 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 @@ public class SmsMessage { */ /** - * 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 @@ public class SmsMessage { } /** - * 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 @@ public class SmsMessage { } /** - * 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 @@ public class SmsMessage { 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 bd8321e21f1b..839889f91c4a 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -196,11 +196,8 @@ public class TelephonyManager { 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 @@ public class TelephonyManager { @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 @@ public class TelephonyManager { * * <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 d6dea2c8974b..a7d553b60abf 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -294,15 +294,30 @@ public final class ImsCallProfile implements Parcelable { * 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 5adc99e11478..1b583fd29965 100644 --- a/telephony/java/android/telephony/ims/ImsCallSession.java +++ b/telephony/java/android/telephony/ims/ImsCallSession.java @@ -25,8 +25,6 @@ import android.util.Log; 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 @@ public class ImsCallSession { } /** - * 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 @@ public class ImsCallSession { * 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 @@ public class ImsCallSession { * 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 @@ public class ImsCallSession { /** * 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 @@ public class ImsCallSession { * 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 @@ public class ImsCallSession { * 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 e11886f2bea5..025721c89f70 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 @@ public class ImsCallSessionListener { * @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 @@ public class ImsCallSessionListener { * @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 @@ public class ImsCallSessionListener { * @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/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java index 39af2e770882..cb3f0f92625e 100644 --- a/telephony/java/android/telephony/ims/ImsException.java +++ b/telephony/java/android/telephony/ims/ImsException.java @@ -30,10 +30,7 @@ import java.lang.annotation.RetentionPolicy; /** * This class defines an IMS-related exception that has been thrown while interacting with a * device or carrier provided ImsService implementation. - * @hide */ -@SystemApi -@TestApi public final class ImsException extends Exception { /** @@ -83,7 +80,10 @@ public final class ImsException extends Exception { /** * A new {@link ImsException} with an unspecified {@link ImsErrorCode} code. * @param message an optional message to detail the error condition more specifically. + * @hide */ + @SystemApi + @TestApi public ImsException(@Nullable String message) { super(getMessage(message, CODE_ERROR_UNSPECIFIED)); } @@ -91,7 +91,10 @@ public final class ImsException extends Exception { /** * A new {@link ImsException} that includes an {@link ImsErrorCode} error code. * @param message an optional message to detail the error condition more specifically. + * @hide */ + @SystemApi + @TestApi public ImsException(@Nullable String message, @ImsErrorCode int code) { super(getMessage(message, code)); mCode = code; @@ -102,7 +105,10 @@ public final class ImsException extends Exception { * {@link Throwable} that contains the original error that was thrown to lead to this Exception. * @param message an optional message to detail the error condition more specifically. * @param cause the {@link Throwable} that caused this {@link ImsException} to be created. + * @hide */ + @SystemApi + @TestApi public ImsException(@Nullable String message, @ImsErrorCode int code, @Nullable Throwable cause) { super(getMessage(message, code), cause); diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 5fd0af564d34..c66672f13129 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -23,6 +23,8 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressAutoDoc; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.TestApi; import android.content.Context; @@ -56,10 +58,7 @@ import java.util.function.Consumer; * associated subscription. * * @see #createForSubscriptionId(int) - * @hide */ -@SystemApi -@TestApi public class ImsMmTelManager implements RegistrationManager { /** @@ -94,9 +93,11 @@ public class ImsMmTelManager implements RegistrationManager { * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) (RegistrationCallback) * @see #unregisterImsRegistrationCallback(RegistrationCallback) * @deprecated Use {@link RegistrationManager.RegistrationCallback} instead. + * @hide */ // Do not add to this class, add to RegistrationManager.RegistrationCallback instead. @Deprecated + @SystemApi @TestApi public static class RegistrationCallback extends RegistrationManager.RegistrationCallback { /** @@ -141,7 +142,7 @@ public class ImsMmTelManager implements RegistrationManager { /** * Receives IMS capability status updates from the ImsService. This information is also - * available via the {@link #isAvailable(int, int)} method below. + * available via the {@see #isAvailable(int, int)} method below. * * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) (CapabilityCallback) * @see #unregisterMmTelCapabilityCallback(CapabilityCallback) @@ -190,7 +191,7 @@ public class ImsMmTelManager implements RegistrationManager { * If unavailable, the feature is not able to support the unavailable capability at this * time. * - * This information can also be queried using the {@link #isAvailable(int, int)} API. + * This information can also be queried using the {@see #isAvailable(int, int)} API. * * @param capabilities The new availability of the capabilities. */ @@ -218,8 +219,20 @@ public class ImsMmTelManager implements RegistrationManager { * * @param subId The ID of the subscription that this ImsMmTelManager will use. * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList() + * + * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * * @throws IllegalArgumentException if the subscription is invalid. + * */ + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE + }) + @SuppressLint("ManagerLookup") public static @NonNull ImsMmTelManager createForSubscriptionId(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId)) { throw new IllegalArgumentException("Invalid subscription ID"); @@ -258,8 +271,10 @@ public class ImsMmTelManager implements RegistrationManager { * reason. * @deprecated Use {@link RegistrationManager#registerImsRegistrationCallback(Executor, * RegistrationManager.RegistrationCallback)} instead. + * @hide */ @Deprecated + @SystemApi @TestApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor, @NonNull RegistrationCallback c) throws ImsException { @@ -284,9 +299,20 @@ public class ImsMmTelManager implements RegistrationManager { } } - /**{@inheritDoc}*/ + /** + * + * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * + * {@inheritDoc} + * + */ @Override - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor, @NonNull RegistrationManager.RegistrationCallback c) throws ImsException { if (c == null) { @@ -317,8 +343,10 @@ public class ImsMmTelManager implements RegistrationManager { * @see #registerImsRegistrationCallback(Executor, RegistrationCallback) * @deprecated Use {@link #unregisterImsRegistrationCallback( * RegistrationManager.RegistrationCallback)}. + * @hide */ @Deprecated + @SystemApi @TestApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) { if (c == null) { @@ -331,9 +359,20 @@ public class ImsMmTelManager implements RegistrationManager { } } - /**{@inheritDoc}*/ + /** + * + * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * Access by profile owners is deprecated and will be removed in a future release. + * + *{@inheritDoc} + */ @Override - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterImsRegistrationCallback( @NonNull RegistrationManager.RegistrationCallback c) { if (c == null) { @@ -346,9 +385,13 @@ public class ImsMmTelManager implements RegistrationManager { } } - /**{@inheritDoc}*/ + /** + * {@inheritDoc} + * @hide + */ @Override - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi @TestApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) { if (stateCallback == null) { @@ -369,9 +412,19 @@ public class ImsMmTelManager implements RegistrationManager { } } - /**{@inheritDoc}*/ + /** + * <p>Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE + * READ_PRECISE_PHONE_STATE} or that the calling app has carrier privileges + * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). + * Access by profile owners is deprecated and will be removed in a future release. + * + *{@inheritDoc} + */ @Override - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void getRegistrationTransportType(@NonNull @CallbackExecutor Executor executor, @NonNull @AccessNetworkConstants.TransportType Consumer<Integer> transportTypeCallback) { @@ -397,12 +450,25 @@ public class ImsMmTelManager implements RegistrationManager { /** * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service * availability updates for the subscription specified in - * {@link #createForSubscriptionId(int)}. The method {@link #isAvailable(int, int)} + * {@link #createForSubscriptionId(int)}. The method {@see #isAvailable(int, int)} * can also be used to query this information at any time. * * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to * subscription changed events and call * {@link #unregisterMmTelCapabilityCallback(CapabilityCallback)} to clean up. + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * <li>The caller is the default SMS app for the device.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. * * When the callback is registered, it will initiate the callback c to be called with the * current capabilities. @@ -418,7 +484,10 @@ public class ImsMmTelManager implements RegistrationManager { * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed * reason. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor, @NonNull CapabilityCallback c) throws ImsException { if (c == null) { @@ -450,10 +519,27 @@ public class ImsMmTelManager implements RegistrationManager { * When the subscription associated with this callback is removed (SIM removed, ESIM swap, * etc...), this callback will automatically be removed. If this method is called for an * inactive subscription, it will result in a no-op. + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * <li>The caller is the default SMS app for the device.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * * @param c The MmTel {@link CapabilityCallback} to be removed. * @see #registerMmTelCapabilityCallback(Executor, CapabilityCallback) */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public void unregisterMmTelCapabilityCallback(@NonNull CapabilityCallback c) { if (c == null) { throw new IllegalArgumentException("Must include a non-null RegistrationCallback."); @@ -475,6 +561,19 @@ public class ImsMmTelManager implements RegistrationManager { * <p> * Note: If the carrier configuration for advanced calling is not editable or hidden, this * method will always return the default value. + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * <li>The caller is the default SMS app for the device.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. * * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL * @see android.telephony.CarrierConfigManager#KEY_EDITABLE_ENHANCED_4G_LTE_BOOL @@ -486,7 +585,10 @@ public class ImsMmTelManager implements RegistrationManager { * active (SIM is not inserted, ESIM inactive) or invalid. * @return true if the user's setting for advanced calling is enabled, false otherwise. */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isAdvancedCallingSettingEnabled() { try { return getITelephony().isAdvancedCallingSettingEnabled(mSubId); @@ -524,8 +626,10 @@ public class ImsMmTelManager implements RegistrationManager { * @see #isAdvancedCallingSettingEnabled() * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. + * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi @TestApi public void setAdvancedCallingSettingEnabled(boolean isEnabled) { try { getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled); @@ -556,13 +660,15 @@ public class ImsMmTelManager implements RegistrationManager { * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} * @param capability The IMS MmTel capability to query, can be one of the following: * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO, + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} * @return {@code true} if the MmTel IMS capability is capable for this subscription, false * otherwise. + * @hide */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi @TestApi public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { try { @@ -583,12 +689,14 @@ public class ImsMmTelManager implements RegistrationManager { * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} * @param capability The IMS MmTel capability to query, can be one of the following: * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO, + * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} * @return {@code true} if the MmTel IMS capability is available for this subscription, false * otherwise. + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { @@ -613,7 +721,9 @@ public class ImsMmTelManager implements RegistrationManager { * capability is supported on this carrier network for the transport specified. * @throws ImsException if the subscription is no longer valid or the IMS service is not * available. + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @AccessNetworkConstants.TransportType int transportType, @@ -642,12 +752,32 @@ public class ImsMmTelManager implements RegistrationManager { /** * The user's setting for whether or not they have enabled the "Video Calling" setting. * + * <p> + * Note: If the carrier configuration for advanced calling is not editable or hidden, this + * method will always return the default value. + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * <li>The caller is the default SMS app for the device.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @return true if the user’s “Video Calling” setting is currently enabled. * @see #setVtSettingEnabled(boolean) */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). public boolean isVtSettingEnabled() { try { return getITelephony().isVtSettingEnabled(mSubId); @@ -669,7 +799,9 @@ public class ImsMmTelManager implements RegistrationManager { * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @see #isVtSettingEnabled() + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean isEnabled) { try { @@ -689,11 +821,28 @@ public class ImsMmTelManager implements RegistrationManager { /** * @return true if the user's setting for Voice over WiFi is enabled and false if it is not. * + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * <li>The caller is the default SMS app for the device.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @see #setVoWiFiSettingEnabled(boolean) */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiSettingEnabled() { try { return getITelephony().isVoWiFiSettingEnabled(mSubId); @@ -716,7 +865,9 @@ public class ImsMmTelManager implements RegistrationManager { * active (SIM is not inserted, ESIM inactive) or invalid. * @param isEnabled true if the user's setting for Voice over WiFi is enabled, false otherwise= * @see #isVoWiFiSettingEnabled() + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean isEnabled) { try { @@ -736,13 +887,30 @@ public class ImsMmTelManager implements RegistrationManager { /** * Returns the user's voice over WiFi roaming setting associated with the current subscription. * + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * <li>The caller is the default SMS app for the device.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @return true if the user's setting for Voice over WiFi while roaming is enabled, false * if disabled. * @see #setVoWiFiRoamingSettingEnabled(boolean) */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isVoWiFiRoamingSettingEnabled() { try { return getITelephony().isVoWiFiRoamingSettingEnabled(mSubId); @@ -766,7 +934,9 @@ public class ImsMmTelManager implements RegistrationManager { * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @see #isVoWiFiRoamingSettingEnabled() + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) { try { @@ -796,7 +966,9 @@ public class ImsMmTelManager implements RegistrationManager { * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @see #setVoWiFiSettingEnabled(boolean) + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean isCapable, int mode) { try { @@ -816,6 +988,20 @@ public class ImsMmTelManager implements RegistrationManager { /** * Returns the user's voice over WiFi Roaming mode setting associated with the device. * + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * <li>The caller is the default SMS app for the device.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @return The Voice over WiFi Mode preference set by the user, which can be one of the @@ -825,7 +1011,10 @@ public class ImsMmTelManager implements RegistrationManager { * - {@link #WIFI_MODE_WIFI_PREFERRED} * @see #setVoWiFiSettingEnabled(boolean) */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public @WiFiCallingMode int getVoWiFiModeSetting() { try { return getITelephony().getVoWiFiModeSetting(mSubId); @@ -851,7 +1040,9 @@ public class ImsMmTelManager implements RegistrationManager { * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @see #getVoWiFiModeSetting() + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(@WiFiCallingMode int mode) { try { @@ -880,7 +1071,9 @@ public class ImsMmTelManager implements RegistrationManager { * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @see #setVoWiFiRoamingSettingEnabled(boolean) + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @WiFiCallingMode int getVoWiFiRoamingModeSetting() { try { @@ -909,7 +1102,9 @@ public class ImsMmTelManager implements RegistrationManager { * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @see #getVoWiFiRoamingModeSetting() + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) { try { @@ -936,7 +1131,9 @@ public class ImsMmTelManager implements RegistrationManager { * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @param isEnabled if true RTT should be enabled during calls made on this subscription. + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean isEnabled) { try { @@ -956,12 +1153,29 @@ public class ImsMmTelManager implements RegistrationManager { /** * @return true if TTY over VoLTE is supported * + * <p>This API requires one of the following: + * <ul> + * <li>The caller holds the READ_PRECISE_PHONE_STATE permission.</li> + * <li>If the caller is the device or profile owner, the caller holds the + * {@link Manifest.permission#READ_PRECISE_PHONE_STATE} permission.</li> + * <li>The caller has carrier privileges (see + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges}) on any + * active subscription.</li> + * <li>The caller is the default SMS app for the device.</li> + * </ul> + * <p>The profile owner is an app that owns a managed profile on the device; for more details + * see <a href="https://developer.android.com/work/managed-profiles">Work profiles</a>. + * Access by profile owners is deprecated and will be removed in a future release. + * * @throws IllegalArgumentException if the subscription associated with this operation is not * active (SIM is not inserted, ESIM inactive) or invalid. * @see android.telecom.TelecomManager#getCurrentTtyMode * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL */ - @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). + @RequiresPermission(anyOf = { + android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, + android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public boolean isTtyOverVolteEnabled() { try { return getITelephony().isTtyOverVolteEnabled(mSubId); @@ -988,7 +1202,9 @@ public class ImsMmTelManager implements RegistrationManager { * specified when the service state has been retrieved from the IMS service. * @throws ImsException if the IMS service associated with this subscription is not available or * the IMS service is not available. + * @hide */ + @SystemApi @TestApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull @CallbackExecutor Executor executor, @NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException { diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 666a688e8cc0..aa4f77d09212 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -102,6 +102,101 @@ public class ProvisioningManager { // 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 class ProvisioningManager { 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 class ProvisioningManager { 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 class ProvisioningManager { 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 class ProvisioningManager { 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 class ProvisioningManager { 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/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index 99bb259602e5..a1f6b78ba7c5 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -22,8 +22,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; -import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.Uri; import android.os.Binder; import android.telephony.AccessNetworkConstants; @@ -41,10 +39,7 @@ import java.util.function.Consumer; /** * Manages IMS Service registration state for associated {@link ImsFeature}s. - * @hide */ -@SystemApi -@TestApi public interface RegistrationManager { /** @@ -139,7 +134,6 @@ public interface RegistrationManager { getAccessType(imsRadioTech), info))); } - @Override public void onSubscriberAssociatedUriChanged(Uri[] uris) { if (mLocalCallback == null) return; @@ -225,7 +219,11 @@ public interface RegistrationManager { /** * Registers a {@link RegistrationCallback} with the system. Use - * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed + * @param executor The {@link Executor} that will be used to call the IMS registration state + * callback. + * @param c A callback called on the supplied {@link Executor} that will contain the + * registration state of the IMS service, which will be one of the + * {@see SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. * * When the callback is registered, it will initiate the callback c to be called with the @@ -275,10 +273,10 @@ public interface RegistrationManager { * Gets the Transport Type associated with the current IMS registration. * @param executor The {@link Executor} that will be used to call the transportTypeCallback. * @param transportTypeCallback The transport type associated with the current IMS registration, - * which will be one of following: - * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN}, - * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or - * {@link AccessNetworkConstants#TRANSPORT_TYPE_INVALID}. + * which will be one of following: + * {@see AccessNetworkConstants#TRANSPORT_TYPE_WWAN}, + * {@see AccessNetworkConstants#TRANSPORT_TYPE_WLAN}, or + * {@see AccessNetworkConstants#TRANSPORT_TYPE_INVALID}. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) void getRegistrationTransportType( diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl index d64e67a40201..cc2ebb9b20bd 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl @@ -92,11 +92,11 @@ oneway interface IImsCallSessionListener { /** * 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 acab738737f4..75bd6a7dc648 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.compat.annotation.UnsupportedAppUsage; 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 @@ public class ImsCallSessionImplBase extends IImsCallSession.Stub { @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/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index f14270f99c83..5d102cb4ac06 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -60,15 +60,21 @@ public abstract class ImsFeature { * This feature supports emergency calling over MMTEL. If defined, the framework will try to * place an emergency call over IMS first. If it is not defined, the framework will only use * CSFB for emergency calling. + * @hide */ + @SystemApi @TestApi public static final int FEATURE_EMERGENCY_MMTEL = 0; /** * This feature supports the MMTEL feature. + * @hide */ + @SystemApi @TestApi public static final int FEATURE_MMTEL = 1; /** * This feature supports the RCS feature. + * @hide */ + @SystemApi @TestApi public static final int FEATURE_RCS = 2; /** * Total number of features defined @@ -116,18 +122,24 @@ public abstract class ImsFeature { * This {@link ImsFeature}'s state is unavailable and should not be communicated with. This will * remove all bindings back to the framework. Any attempt to communicate with the framework * during this time will result in an {@link IllegalStateException}. + * @hide */ + @SystemApi @TestApi public static final int STATE_UNAVAILABLE = 0; /** * This {@link ImsFeature} state is initializing and should not be communicated with. This will * remove all bindings back to the framework. Any attempt to communicate with the framework * during this time will result in an {@link IllegalStateException}. + * @hide */ + @SystemApi @TestApi public static final int STATE_INITIALIZING = 1; /** * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods - * until {@link #onFeatureReady()} is called. + * until {@see #onFeatureReady()} is called. + * @hide */ + @SystemApi @TestApi public static final int STATE_READY = 2; /** @@ -155,11 +167,15 @@ public abstract class ImsFeature { /** * The capability was unable to be changed. + * @hide */ + @SystemApi @TestApi public static final int CAPABILITY_ERROR_GENERIC = -1; /** * The capability was able to be changed. + * @hide */ + @SystemApi @TestApi public static final int CAPABILITY_SUCCESS = 0; /** @@ -331,7 +347,9 @@ public abstract class ImsFeature { * * @see SubscriptionManager#getSubscriptionIds(int) for more information on getting the * subscription IDs associated with this slot. + * @hide */ + @SystemApi @TestApi public final int getSlotIndex() { return mSlotId; } @@ -339,7 +357,9 @@ public abstract class ImsFeature { /** * @return The current state of the ImsFeature, set previously by {@link #setFeatureState(int)} * or {@link #STATE_UNAVAILABLE} if it has not been updated yet. + * @hide */ + @SystemApi @TestApi public @ImsState int getFeatureState() { synchronized (mLock) { return mState; @@ -351,7 +371,9 @@ public abstract class ImsFeature { * stop communication, depending on the state sent. * @param state The ImsFeature's state, defined as {@link #STATE_UNAVAILABLE}, * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}. + * @hide */ + @SystemApi @TestApi public final void setFeatureState(@ImsState int state) { synchronized (mLock) { if (mState != state) { diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index 56c87710244c..0d5a979e5894 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -52,14 +52,18 @@ import java.lang.annotation.RetentionPolicy; * * Any class wishing to use MmTelFeature should extend this class and implement all methods that the * service supports. - * @hide */ -@SystemApi -@TestApi public class MmTelFeature extends ImsFeature { private static final String LOG_TAG = "MmTelFeature"; + /** + * @hide + */ + @SystemApi @TestApi + public MmTelFeature() { + } + private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() { @Override @@ -215,11 +219,11 @@ public class MmTelFeature extends ImsFeature { * {@link MmTelCapabilities#CAPABILITY_TYPE_SMS}. * * The capabilities of this MmTelFeature will be set by the framework and can be queried with - * {@link #queryCapabilityStatus()}. + * {@see #queryCapabilityStatus()}. * * This MmTelFeature can then return the status of each of these capabilities (enabled or not) - * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current - * status can also be queried using {@link #queryCapabilityStatus()}. + * by sending a {@see #notifyCapabilitiesStatusChanged} callback to the framework. The current + * status can also be queried using {@see #queryCapabilityStatus()}. * @see #isCapable(int) */ public static class MmTelCapabilities extends Capabilities { @@ -228,13 +232,18 @@ public class MmTelFeature extends ImsFeature { * Create a new empty {@link MmTelCapabilities} instance. * @see #addCapabilities(int) * @see #removeCapabilities(int) + * @hide */ + @SystemApi @TestApi public MmTelCapabilities() { super(); } - /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead.*/ + /**@deprecated Use {@link MmTelCapabilities} to construct a new instance instead. + * @hide + */ @Deprecated + @SystemApi @TestApi public MmTelCapabilities(Capabilities c) { mCapabilities = c.mCapabilities; } @@ -243,11 +252,17 @@ public class MmTelFeature extends ImsFeature { * Create a new {link @MmTelCapabilities} instance with the provided capabilities. * @param capabilities The capabilities that are supported for MmTel in the form of a * bitfield. + * @hide */ + @SystemApi @TestApi public MmTelCapabilities(@MmTelCapability int capabilities) { super(capabilities); } + /** + * @hide + */ + @SystemApi @TestApi @IntDef(flag = true, value = { CAPABILITY_TYPE_VOICE, @@ -278,23 +293,39 @@ public class MmTelFeature extends ImsFeature { */ public static final int CAPABILITY_TYPE_SMS = 1 << 3; + /** + * @hide + */ @Override + @SystemApi @TestApi public final void addCapabilities(@MmTelCapability int capabilities) { super.addCapabilities(capabilities); } + /** + * @hide + */ @Override + @SystemApi @TestApi public final void removeCapabilities(@MmTelCapability int capability) { super.removeCapabilities(capability); } + /** + * @hide + */ @Override + @SystemApi @TestApi public final boolean isCapable(@MmTelCapability int capabilities) { return super.isCapable(capabilities); } + /** + * @hide + */ @NonNull @Override + @SystemApi @TestApi public String toString() { StringBuilder builder = new StringBuilder("MmTel Capabilities - ["); builder.append("Voice: "); @@ -319,8 +350,10 @@ public class MmTelFeature extends ImsFeature { /** * Called when the IMS provider receives an incoming call. * @param c The {@link ImsCallSession} associated with the new call. + * @hide */ @Override + @SystemApi @TestApi public void onIncomingCall(IImsCallSession c, Bundle extras) { } @@ -329,8 +362,10 @@ public class MmTelFeature extends ImsFeature { * Called when the IMS provider implicitly rejects an incoming call during setup. * @param callProfile An {@link ImsCallProfile} with the call details. * @param reason The {@link ImsReasonInfo} reason for call rejection. + * @hide */ @Override + @SystemApi @TestApi public void onRejectedCall(ImsCallProfile callProfile, ImsReasonInfo reason) { } @@ -338,8 +373,10 @@ public class MmTelFeature extends ImsFeature { /** * Updates the Listener when the voice message count for IMS has changed. * @param count an integer representing the new message count. + * @hide */ @Override + @SystemApi @TestApi public void onVoiceMessageCountUpdate(int count) { } @@ -348,14 +385,22 @@ public class MmTelFeature extends ImsFeature { /** * To be returned by {@link #shouldProcessCall(String[])} when the ImsService should process the * outgoing call as IMS. + * @hide */ + @SystemApi @TestApi public static final int PROCESS_CALL_IMS = 0; /** * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should * not process the outgoing call as IMS and should instead use circuit switch. + * @hide */ + @SystemApi @TestApi public static final int PROCESS_CALL_CSFB = 1; + /** + * @hide + */ + @SystemApi @TestApi @IntDef(flag = true, value = { PROCESS_CALL_IMS, @@ -368,7 +413,9 @@ public class MmTelFeature extends ImsFeature { * If the flag is present and true, it indicates that the incoming call is for USSD. * <p> * This is an optional boolean flag. + * @hide */ + @SystemApi @TestApi public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD"; /** @@ -379,7 +426,9 @@ public class MmTelFeature extends ImsFeature { * certain situations. * <p> * This is an optional boolean flag. + * @hide */ + @SystemApi @TestApi public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL"; @@ -388,6 +437,7 @@ public class MmTelFeature extends ImsFeature { /** * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and * notifies the framework. + * @hide */ private void setListener(IImsMmTelListener listener) { synchronized (mLock) { @@ -406,9 +456,11 @@ public class MmTelFeature extends ImsFeature { * Should be a subset of the capabilities that are enabled by the framework in * {@link #changeEnabledCapabilities}. * @return A copy of the current MmTelFeature capability status. + * @hide */ @Override - public final MmTelCapabilities queryCapabilityStatus() { + @SystemApi @TestApi + public @NonNull final MmTelCapabilities queryCapabilityStatus() { return new MmTelCapabilities(super.queryCapabilityStatus()); } @@ -420,7 +472,9 @@ public class MmTelFeature extends ImsFeature { * the status of that capability is disabled. This can happen if the network does not currently * support the capability that is enabled. A capability that is disabled by the framework (via * {@link #changeEnabledCapabilities}) should also show the status as disabled. + * @hide */ + @SystemApi @TestApi public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) { if (c == null) { throw new IllegalArgumentException("MmTelCapabilities must be non-null!"); @@ -433,7 +487,9 @@ public class MmTelFeature extends ImsFeature { * @param c The {@link ImsCallSessionImplBase} of the new incoming call. * @param extras A bundle containing extra parameters related to the call. See * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above. + * @hide */ + @SystemApi @TestApi public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c, @NonNull Bundle extras) { if (c == null || extras == null) { @@ -458,7 +514,9 @@ public class MmTelFeature extends ImsFeature { * @param callProfile The {@link ImsCallProfile} IMS call profile with details. * This can be null if no call information is available for the rejected call. * @param reason The {@link ImsReasonInfo} call rejection reason. + * * @hide */ + @SystemApi @TestApi public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile, @NonNull ImsReasonInfo reason) { if (callProfile == null || reason == null) { @@ -497,7 +555,9 @@ public class MmTelFeature extends ImsFeature { /** * Notify the framework of a change in the Voice Message count. * @link count the new Voice Message count. + * @hide */ + @SystemApi @TestApi public final void notifyVoiceMessageCountUpdate(int count) { synchronized (mLock) { if (mListener == null) { @@ -518,8 +578,10 @@ public class MmTelFeature extends ImsFeature { * status for capability A. * @param capability The capability that we are querying the configuration for. * @return true if the capability is enabled, false otherwise. + * @hide */ @Override + @SystemApi @TestApi public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { // Base implementation - Override to provide functionality @@ -537,8 +599,10 @@ public class MmTelFeature extends ImsFeature { * Enabling/Disabling a capability here indicates that the capability should be registered or * deregistered (depending on the capability change) and become available or unavailable to * the framework. + * * @hide */ @Override + @SystemApi @TestApi public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, @NonNull CapabilityCallbackProxy c) { // Base implementation, no-op @@ -561,7 +625,9 @@ public class MmTelFeature extends ImsFeature { * {@link ImsCallProfile#CALL_TYPE_VS_TX} * {@link ImsCallProfile#CALL_TYPE_VS_RX} * @return a {@link ImsCallProfile} object + * @hide */ + @SystemApi @TestApi public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) { // Base Implementation - Should be overridden return null; @@ -582,7 +648,9 @@ public class MmTelFeature extends ImsFeature { * {@link ImsCallSession} directly. * * @param profile a call profile to make the call + * @hide */ + @SystemApi @TestApi public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) { // Base Implementation - Should be overridden return null; @@ -599,7 +667,9 @@ public class MmTelFeature extends ImsFeature { * call as a conference. * @return a {@link ProcessCallResult} to the framework, which will be used to determine if the * call will be placed over IMS or via CSFB. + * @hide */ + @SystemApi @TestApi public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) { return PROCESS_CALL_IMS; } @@ -632,7 +702,9 @@ public class MmTelFeature extends ImsFeature { /** * @return The {@link ImsUtImplBase} Ut interface implementation for the supplementary service * configuration. + * @hide */ + @SystemApi @TestApi public @NonNull ImsUtImplBase getUt() { // Base Implementation - Should be overridden return new ImsUtImplBase(); @@ -641,7 +713,9 @@ public class MmTelFeature extends ImsFeature { /** * @return The {@link ImsEcbmImplBase} Emergency call-back mode interface for emergency VoLTE * calls that support it. + * @hide */ + @SystemApi @TestApi public @NonNull ImsEcbmImplBase getEcbm() { // Base Implementation - Should be overridden return new ImsEcbmImplBase(); @@ -650,7 +724,9 @@ public class MmTelFeature extends ImsFeature { /** * @return The {@link ImsMultiEndpointImplBase} implementation for implementing Dialog event * package processing for multi-endpoint. + * @hide */ + @SystemApi @TestApi public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() { // Base Implementation - Should be overridden return new ImsMultiEndpointImplBase(); @@ -676,7 +752,9 @@ public class MmTelFeature extends ImsFeature { * // Remote side is dead * } * } + * @hide */ + @SystemApi @TestApi public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) { // Base Implementation - Should be overridden } @@ -710,7 +788,9 @@ public class MmTelFeature extends ImsFeature { * * @return an instance of {@link ImsSmsImplBase} which should be implemented by the IMS * Provider. + * @hide */ + @SystemApi @TestApi public @NonNull ImsSmsImplBase getSmsImplementation() { return new ImsSmsImplBase(); } @@ -719,14 +799,22 @@ public class MmTelFeature extends ImsFeature { return getSmsImplementation().getSmsFormat(); } - /**{@inheritDoc}*/ + /** + * {@inheritDoc} + * @hide + */ @Override + @SystemApi @TestApi public void onFeatureRemoved() { // Base Implementation - Should be overridden } - /**{@inheritDoc}*/ + /** + * {@inheritDoc} + * @hide + */ @Override + @SystemApi @TestApi public void onFeatureReady() { // Base Implementation - Should be overridden } diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index 3ec4f3468497..f13371c1d0fa 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 @@ public class ImsUtImplBase { 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 @@ public class ImsUtImplBase { } /** + * 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 0d86e2b7c2b1..0f6ce13a6ce4 100644 --- a/telephony/java/com/android/ims/ImsConfig.java +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -135,374 +135,596 @@ public class ImsConfig { /** * 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 15f837189843..4a5380e4551b 100644 --- a/telephony/java/com/android/ims/ImsUtInterface.java +++ b/telephony/java/com/android/ims/ImsUtInterface.java @@ -166,6 +166,12 @@ public interface ImsUtInterface { 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 4f97cc5cfb22..302be65070f7 100644 --- a/telephony/java/com/android/ims/internal/IImsUt.aidl +++ b/telephony/java/com/android/ims/internal/IImsUt.aidl @@ -122,4 +122,10 @@ interface IImsUt { */ 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 b4d3ec9fc87d..eef96c4e04a3 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -205,8 +205,6 @@ public class TelephonyIntents { 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 832502cae37d..d0c8024c56fe 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** + * 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 ca03333b99aa..f4a96a67c3b2 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.telephony.uicc.IccUtils; 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 @@ public final class BearerData { 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 @@ public final class BearerData { } } + 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 @@ public final class BearerData { 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 f54da3bf6c6b..4538193818c1 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 com.android.internal.telephony.uicc.IccUtils; 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { 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 @@ public class SmsMessage extends SmsMessageBase { } /** - * 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 @@ public class SmsMessage extends SmsMessageBase { 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 a7328acb73b5..3f311c951c7e 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -75,6 +75,9 @@ public class LinkPropertiesTest { 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 @@ public class LinkPropertiesTest { assertFalse(lp.isIpv6Provisioned()); assertFalse(lp.isPrivateDnsActive()); assertFalse(lp.isWakeOnLanSupported()); + assertNull(lp.getCaptivePortalApiUrl()); + assertNull(lp.getCaptivePortalData()); } private LinkProperties makeTestObject() { @@ -124,6 +129,8 @@ public class LinkPropertiesTest { 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 @@ public class LinkPropertiesTest { 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 @@ public class LinkPropertiesTest { 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 @@ public class LinkPropertiesTest { 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 @@ public class LinkPropertiesTest { 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/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java index 18474a83b368..1c6920986318 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.ConnectivityManager; 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 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { 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 class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { 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 class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { } 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() { @@ -222,7 +222,7 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork { @Override public Network getNetwork() { - return new Network(mNetworkAgent.netId); + return mNetworkAgent.network; } public void expectPreventReconnectReceived(long timeoutMs) { diff --git a/tests/net/java/android/net/CaptivePortalDataTest.kt b/tests/net/java/android/net/CaptivePortalDataTest.kt new file mode 100644 index 000000000000..00714382684f --- /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 000000000000..065add4fc253 --- /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/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index b2d363e27839..783f8d177f4a 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.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; 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.verify; 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.PackageInfo; 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.ResolverParamsParcel; 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.ArrayList; 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 @@ public class ConnectivityServiceTest { @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 @@ public class ConnectivityServiceTest { } @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 @@ -575,7 +610,7 @@ public class ConnectivityServiceTest { } }; - assertEquals(na.netId, nmNetworkCaptor.getValue().netId); + assertEquals(na.network.netId, nmNetworkCaptor.getValue().netId); mNmCallbacks = nmCbCaptor.getValue(); mNmCallbacks.onNetworkMonitorCreated(mNetworkMonitor); @@ -1750,6 +1785,66 @@ public class ConnectivityServiceTest { 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 @@ public class ConnectivityServiceTest { 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 82cb1932d2c1..e863266c4b49 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.INetd; 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 @@ public class LingerMonitorTest { @Mock INetd mNetd; @Mock INetworkManagementService mNMS; @Mock Context mCtx; - @Mock NetworkMisc mMisc; @Mock NetworkNotificationManager mNotifier; @Mock Resources mResources; @@ -358,7 +356,7 @@ public class LingerMonitorTest { 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 b709af1a02f1..9b248878fe96 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.InterfaceConfiguration; 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 @@ public class Nat464XlatTest { static final int NETID = 42; @Mock ConnectivityService mConnectivity; - @Mock NetworkMisc mMisc; @Mock IDnsResolver mDnsResolver; @Mock INetd mNetd; @Mock INetworkManagementService mNms; @@ -72,6 +71,7 @@ public class Nat464XlatTest { TestLooper mLooper; Handler mHandler; + NetworkAgentConfig mAgentConfig = new NetworkAgentConfig(); Nat464Xlat makeNat464Xlat() { return new Nat464Xlat(mNai, mNetd, mDnsResolver, mNms) { @@ -93,7 +93,7 @@ public class Nat464XlatTest { 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 @@ public class Nat464XlatTest { 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 @@ public class Nat464XlatTest { 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 @@ public class Nat464XlatTest { 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); |