diff options
185 files changed, 10629 insertions, 1757 deletions
diff --git a/Android.bp b/Android.bp index 3046b20c7096..a211d2675ec2 100644 --- a/Android.bp +++ b/Android.bp @@ -658,7 +658,10 @@ filegroup { name: "framework-tethering-annotations", srcs: [ "core/java/android/annotation/NonNull.java", + "core/java/android/annotation/Nullable.java", + "core/java/android/annotation/RequiresPermission.java", "core/java/android/annotation/SystemApi.java", + "core/java/android/annotation/TestApi.java", ], } // Build ext.jar @@ -1061,7 +1064,11 @@ metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.x "--hide MissingPermission --hide BroadcastBehavior " + "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " + "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " + - "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.*" + "--force-convert-to-warning-nullability-annotations +*:-android.*:+android.icu.*:-dalvik.* " + + "--api-lint-ignore-prefix android.icu. " + + "--api-lint-ignore-prefix java. " + + "--api-lint-ignore-prefix junit. " + + "--api-lint-ignore-prefix org. " build = [ "StubLibraries.bp", diff --git a/apex/sdkextensions/OWNERS b/apex/sdkextensions/OWNERS index feb274262bef..a6e55228596b 100644 --- a/apex/sdkextensions/OWNERS +++ b/apex/sdkextensions/OWNERS @@ -1 +1,2 @@ +dariofreni@google.com hansson@google.com diff --git a/apex/sdkextensions/framework/Android.bp b/apex/sdkextensions/framework/Android.bp index dd174734df6d..245a96b99148 100644 --- a/apex/sdkextensions/framework/Android.bp +++ b/apex/sdkextensions/framework/Android.bp @@ -32,6 +32,7 @@ java_library { libs: [ "framework-annotations-lib" ], permitted_packages: [ "android.os.ext" ], installable: true, + plugins: ["java_api_finder"], visibility: [ "//frameworks/base/apex/sdkextensions", "//frameworks/base/apex/sdkextensions/testing", diff --git a/api/current.txt b/api/current.txt index 2bcd09788b16..c6c9dde2f086 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 @@ -9872,6 +9873,7 @@ package android.content { field public static final String STORAGE_STATS_SERVICE = "storagestats"; field public static final String SYSTEM_HEALTH_SERVICE = "systemhealth"; field public static final String TELECOM_SERVICE = "telecom"; + field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims"; field public static final String TELEPHONY_SERVICE = "phone"; field public static final String TELEPHONY_SUBSCRIPTION_SERVICE = "telephony_subscription_service"; field public static final String TEXT_CLASSIFICATION_SERVICE = "textclassification"; @@ -9882,6 +9884,7 @@ package android.content { field public static final String USB_SERVICE = "usb"; field public static final String USER_SERVICE = "user"; field public static final String VIBRATOR_SERVICE = "vibrator"; + field public static final String VPN_MANAGEMENT_SERVICE = "vpn_management"; field public static final String WALLPAPER_SERVICE = "wallpaper"; field public static final String WIFI_AWARE_SERVICE = "wifiaware"; field public static final String WIFI_P2P_SERVICE = "wifip2p"; @@ -28691,8 +28694,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 { @@ -28702,21 +28703,44 @@ 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; - } - - public static class ConnectivityDiagnosticsManager.DataStallReport { + method public int describeContents(); + method @NonNull public android.os.PersistableBundle getAdditionalInfo(); + method @NonNull public android.net.LinkProperties getLinkProperties(); + method @NonNull public android.net.Network getNetwork(); + method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities(); + method public long getReportTimestamp(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.ConnectivityReport> CREATOR; + field public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = "networkProbesAttemped"; + field public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = "networkProbesSucceeded"; + field public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; + field public static final int NETWORK_PROBE_DNS = 4; // 0x4 + field public static final int NETWORK_PROBE_FALLBACK = 32; // 0x20 + field public static final int NETWORK_PROBE_HTTP = 8; // 0x8 + field public static final int NETWORK_PROBE_HTTPS = 16; // 0x10 + field public static final int NETWORK_PROBE_PRIVATE_DNS = 64; // 0x40 + field public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; // 0x0 + field public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; // 0x2 + field public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; // 0x3 + field public static final int NETWORK_VALIDATION_RESULT_VALID = 1; // 0x1 + } + + public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable { ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle); - field public final int detectionMethod; - field @NonNull public final android.net.Network network; - field public final long reportTimestamp; - field @NonNull public final android.os.PersistableBundle stallDetails; + method public int describeContents(); + method public int getDetectionMethod(); + method @NonNull public android.net.Network getNetwork(); + method public long getReportTimestamp(); + method @NonNull public android.os.PersistableBundle getStallDetails(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.ConnectivityDiagnosticsManager.DataStallReport> CREATOR; + field public static final int DETECTION_METHOD_DNS_EVENTS = 1; // 0x1 + field public static final int DETECTION_METHOD_TCP_METRICS = 2; // 0x2 + field public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts"; + field public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = "tcpMetricsCollectionPeriodMillis"; + field public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate"; } public class ConnectivityManager { @@ -28859,6 +28883,35 @@ package android.net { field public final int code; } + public final class Ikev2VpnProfile extends android.net.PlatformVpnProfile { + method @NonNull public java.util.List<java.lang.String> getAllowedAlgorithms(); + method public int getMaxMtu(); + method @Nullable public String getPassword(); + method @Nullable public byte[] getPresharedKey(); + method @Nullable public android.net.ProxyInfo getProxyInfo(); + method @Nullable public java.security.PrivateKey getRsaPrivateKey(); + method @NonNull public String getServerAddr(); + method @Nullable public java.security.cert.X509Certificate getServerRootCaCert(); + method @Nullable public java.security.cert.X509Certificate getUserCert(); + method @NonNull public String getUserIdentity(); + method @Nullable public String getUsername(); + method public boolean isBypassable(); + method public boolean isMetered(); + } + + public static final class Ikev2VpnProfile.Builder { + ctor public Ikev2VpnProfile.Builder(@NonNull String, @NonNull String); + method @NonNull public android.net.Ikev2VpnProfile build(); + method @NonNull public android.net.Ikev2VpnProfile.Builder setAllowedAlgorithms(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey, @Nullable java.security.cert.X509Certificate); + method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthPsk(@NonNull byte[]); + method @NonNull public android.net.Ikev2VpnProfile.Builder setAuthUsernamePassword(@NonNull String, @NonNull String, @Nullable java.security.cert.X509Certificate); + method @NonNull public android.net.Ikev2VpnProfile.Builder setBypassable(boolean); + method @NonNull public android.net.Ikev2VpnProfile.Builder setMaxMtu(int); + method @NonNull public android.net.Ikev2VpnProfile.Builder setMetered(boolean); + method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo); + } + public class InetAddresses { method public static boolean isNumericAddress(@NonNull String); method @NonNull public static java.net.InetAddress parseNumericAddress(@NonNull String); @@ -29087,6 +29140,7 @@ package android.net { method public int getLinkDownstreamBandwidthKbps(); method public int getLinkUpstreamBandwidthKbps(); method @Nullable public android.net.NetworkSpecifier getNetworkSpecifier(); + method public int getOwnerUid(); method public int getSignalStrength(); method @Nullable public android.net.TransportInfo getTransportInfo(); method public boolean hasCapability(int); @@ -29096,6 +29150,7 @@ package android.net { method @NonNull public android.net.NetworkCapabilities setLinkDownstreamBandwidthKbps(int); method @NonNull public android.net.NetworkCapabilities setLinkUpstreamBandwidthKbps(int); method @NonNull public android.net.NetworkCapabilities setNetworkSpecifier(@NonNull android.net.NetworkSpecifier); + method public void setOwnerUid(int); method @NonNull public android.net.NetworkCapabilities setSignalStrength(int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR; @@ -29205,6 +29260,14 @@ package android.net { field public String response; } + public abstract class PlatformVpnProfile { + method public final int getType(); + method @NonNull public final String getTypeString(); + field public static final int TYPE_IKEV2_IPSEC_PSK = 7; // 0x7 + field public static final int TYPE_IKEV2_IPSEC_RSA = 8; // 0x8 + field public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; // 0x6 + } + public final class Proxy { ctor public Proxy(); method @Deprecated public static String getDefaultHost(); @@ -29485,6 +29548,13 @@ package android.net { method public String sanitize(String); } + public class VpnManager { + method public void deleteProvisionedVpnProfile(); + method @Nullable public android.content.Intent provisionVpnProfile(@NonNull android.net.PlatformVpnProfile); + method public void startProvisionedVpnProfile(); + method public void stopProvisionedVpnProfile(); + } + public class VpnService extends android.app.Service { ctor public VpnService(); method public final boolean isAlwaysOn(); @@ -35339,7 +35409,7 @@ package android.os { method public boolean isSustainedPerformanceModeSupported(); method public boolean isWakeLockLevelSupported(int); method public android.os.PowerManager.WakeLock newWakeLock(int, String); - method public void reboot(@Nullable String); + method @RequiresPermission(android.Manifest.permission.REBOOT) public void reboot(@Nullable String); method public void removeThermalStatusListener(@NonNull android.os.PowerManager.OnThermalStatusChangedListener); field public static final int ACQUIRE_CAUSES_WAKEUP = 268435456; // 0x10000000 field public static final String ACTION_DEVICE_IDLE_MODE_CHANGED = "android.os.action.DEVICE_IDLE_MODE_CHANGED"; @@ -43509,6 +43579,7 @@ package android.telecom { method public void registerCallback(android.telecom.Call.Callback); method public void registerCallback(android.telecom.Call.Callback, android.os.Handler); method public void reject(boolean, String); + method public void reject(int); method public void removeExtras(java.util.List<java.lang.String>); method public void removeExtras(java.lang.String...); method public void respondToRttRequest(int, boolean); @@ -43524,6 +43595,8 @@ package android.telecom { field public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; field public static final String EXTRA_SILENT_RINGING_REQUESTED = "android.telecom.extra.SILENT_RINGING_REQUESTED"; field public static final String EXTRA_SUGGESTED_PHONE_ACCOUNTS = "android.telecom.extra.SUGGESTED_PHONE_ACCOUNTS"; + field public static final int REJECT_REASON_DECLINED = 1; // 0x1 + field public static final int REJECT_REASON_UNWANTED = 2; // 0x2 field public static final int STATE_ACTIVE = 4; // 0x4 field public static final int STATE_AUDIO_PROCESSING = 12; // 0xc field public static final int STATE_CONNECTING = 9; // 0x9 @@ -43617,6 +43690,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 @@ -43694,6 +43768,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(); @@ -43708,6 +43783,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(); @@ -43716,6 +43793,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(); @@ -43735,6 +43813,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); @@ -43784,6 +43864,7 @@ package android.telecom { method public void onPostDialContinue(boolean); method public void onPullExternalCall(); method public void onReject(); + method public void onReject(int); method public void onReject(String); method public void onSeparate(); method public void onShowIncomingCallUi(); @@ -43892,6 +43973,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 @@ -43964,8 +44046,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; @@ -43985,9 +44069,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); @@ -44102,6 +44190,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 @@ -44297,6 +44386,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(); @@ -44324,6 +44414,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"; @@ -44571,6 +44662,12 @@ package android.telephony { field public static final int BAND_7 = 7; // 0x7 field public static final int BAND_8 = 8; // 0x8 field public static final int BAND_9 = 9; // 0x9 + field public static final int BAND_A = 101; // 0x65 + field public static final int BAND_B = 102; // 0x66 + field public static final int BAND_C = 103; // 0x67 + field public static final int BAND_D = 104; // 0x68 + field public static final int BAND_E = 105; // 0x69 + field public static final int BAND_F = 106; // 0x6a } public final class AvailableNetworkInfo implements android.os.Parcelable { @@ -44802,6 +44899,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"; @@ -45230,8 +45328,32 @@ package android.telephony { public final class PhoneCapability implements android.os.Parcelable { method public int describeContents(); + method @NonNull public java.util.List<java.lang.Integer> getBands(int); + method @NonNull public java.util.List<java.util.List<java.lang.Long>> getConcurrentFeaturesSupport(); + method @NonNull public java.util.List<java.lang.String> getLogicalModemUuids(); + method public int getMaxActiveDedicatedBearers(); + method public int getMaxActiveInternetData(); + method public int getMaxActivePsVoice(); + method public long getPsDataConnectionLingerTimeMillis(); + method @NonNull public java.util.List<android.telephony.SimSlotCapability> getSimSlotCapabilities(); + method public long getSupportedRats(); + method public int getUeCategory(boolean, int); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR; + field public static final long MODEM_FEATURE_3GPP2_REG = 1L; // 0x1L + field public static final long MODEM_FEATURE_3GPP_REG = 2L; // 0x2L + field public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 4L; // 0x4L + field public static final long MODEM_FEATURE_CSIM = 8192L; // 0x2000L + field public static final long MODEM_FEATURE_CS_VOICE_SESSION = 512L; // 0x200L + field public static final long MODEM_FEATURE_DEDICATED_BEARER = 2048L; // 0x800L + field public static final long MODEM_FEATURE_EUTRAN_REG = 32L; // 0x20L + field public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 128L; // 0x80L + field public static final long MODEM_FEATURE_GERAN_REG = 8L; // 0x8L + field public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1024L; // 0x400L + field public static final long MODEM_FEATURE_NETWORK_SCAN = 4096L; // 0x1000L + field public static final long MODEM_FEATURE_NGRAN_REG = 64L; // 0x40L + field public static final long MODEM_FEATURE_PS_VOICE_REG = 256L; // 0x100L + field public static final long MODEM_FEATURE_UTRAN_REG = 16L; // 0x10L } public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher { @@ -45416,6 +45538,18 @@ package android.telephony { field public static final int INVALID = 2147483647; // 0x7fffffff } + public final class SimSlotCapability implements android.os.Parcelable { + method public int describeContents(); + method public int getPhysicalSlotIndex(); + method public int getSlotType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SimSlotCapability> CREATOR; + field public static final int SLOT_TYPE_EUICC = 3; // 0x3 + field public static final int SLOT_TYPE_IUICC = 2; // 0x2 + field public static final int SLOT_TYPE_SOFT_SIM = 4; // 0x4 + field public static final int SLOT_TYPE_UICC = 1; // 0x1 + } + public final class SmsManager { method public String createAppSpecificSmsToken(android.app.PendingIntent); method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent); @@ -45762,6 +45896,7 @@ package android.telephony { 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 @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.PhoneCapability getPhoneCapability(); method @Deprecated public int getPhoneCount(); method public int getPhoneType(); method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription(); @@ -46308,13 +46443,20 @@ package android.telephony.ims { public final class ImsException extends java.lang.Exception { method public int getCode(); + field public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3; // 0x3 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 ImsManager { + method @NonNull public android.telephony.ims.ImsMmTelManager getImsMmTelManager(int); + 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 @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(); @@ -49577,6 +49719,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/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 6a60faa485dc..a92fd515e46e 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -93,6 +93,7 @@ package android { field public static final String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW"; field public static final String INVOKE_CARRIER_SETUP = "android.permission.INVOKE_CARRIER_SETUP"; field public static final String KILL_UID = "android.permission.KILL_UID"; + field public static final String LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = "android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH"; field public static final String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS"; field public static final String LOCK_DEVICE = "android.permission.LOCK_DEVICE"; field public static final String LOOP_RADIO = "android.permission.LOOP_RADIO"; @@ -1440,12 +1441,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"; @@ -1575,6 +1577,7 @@ package android.content { field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; + field public static final String ETHERNET_SERVICE = "ethernet"; field public static final String EUICC_CARD_SERVICE = "euicc_card"; field public static final String HDMI_CONTROL_SERVICE = "hdmi_control"; field public static final String NETD_SERVICE = "netd"; @@ -1589,7 +1592,6 @@ package android.content { field public static final String STATS_MANAGER = "stats"; field public static final String STATUS_BAR_SERVICE = "statusbar"; field public static final String SYSTEM_UPDATE_SERVICE = "system_update"; - field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims"; field public static final String TELEPHONY_REGISTRY_SERVICE = "telephony_registry"; field public static final String TETHERING_SERVICE = "tethering"; field public static final String VR_SERVICE = "vrmanager"; @@ -1640,6 +1642,7 @@ package android.content { field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP"; field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED"; field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED"; + field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED"; field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST"; field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS"; field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE"; @@ -1944,6 +1947,7 @@ package android.content.pm { field @Deprecated public static final int MASK_PERMISSION_FLAGS = 255; // 0xff field public static final int MATCH_ANY_USER = 4194304; // 0x400000 field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 + field public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 536870912; // 0x20000000 field public static final int MATCH_INSTANT = 8388608; // 0x800000 field public static final int RESTRICTION_HIDE_FROM_SUGGESTIONS = 1; // 0x1 field public static final int RESTRICTION_HIDE_NOTIFICATIONS = 2; // 0x2 @@ -3282,10 +3286,12 @@ package android.hardware.usb { method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long); field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED"; field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE"; + field public static final long FUNCTION_NCM = 1024L; // 0x400L field public static final long FUNCTION_NONE = 0L; // 0x0L field public static final long FUNCTION_RNDIS = 32L; // 0x20L field public static final String USB_CONFIGURED = "configured"; field public static final String USB_CONNECTED = "connected"; + field public static final String USB_FUNCTION_NCM = "ncm"; field public static final String USB_FUNCTION_RNDIS = "rndis"; } @@ -4051,8 +4057,8 @@ package android.media.tv { public final class DvbDeviceInfo implements android.os.Parcelable { ctor public DvbDeviceInfo(int, int); method public int describeContents(); - method public int getAdapterId(); - method public int getDeviceId(); + method @IntRange(from=0) public int getAdapterId(); + method @IntRange(from=0) public int getDeviceId(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR; } @@ -4307,48 +4313,87 @@ 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); method @Deprecated @RequiresPermission("android.permission.NETWORK_SETTINGS") public String getCaptivePortalServerUrl(); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener); + method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported(); method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback); method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean); method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, "android.permission.NETWORK_STACK"}) public boolean shouldAvoidBadWifi(); method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int); method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider); - method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); + method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback); field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; field public static final int TETHERING_BLUETOOTH = 2; // 0x2 field public static final int TETHERING_USB = 1; // 0x1 field public static final int TETHERING_WIFI = 0; // 0x0 - field public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd - field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field @Deprecated public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd + field @Deprecated public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field @Deprecated public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb field public static final int TYPE_NONE = -1; // 0xffffffff field @Deprecated public static final int TYPE_WIFI_P2P = 13; // 0xd } - public abstract static class ConnectivityManager.OnStartTetheringCallback { - ctor public ConnectivityManager.OnStartTetheringCallback(); - method public void onTetheringFailed(); - method public void onTetheringStarted(); + @Deprecated public abstract static class ConnectivityManager.OnStartTetheringCallback { + ctor @Deprecated public ConnectivityManager.OnStartTetheringCallback(); + method @Deprecated public void onTetheringFailed(); + method @Deprecated public void onTetheringStarted(); } - public static interface ConnectivityManager.OnTetheringEntitlementResultListener { - method public void onTetheringEntitlementResult(int); + @Deprecated public static interface ConnectivityManager.OnTetheringEntitlementResultListener { + method @Deprecated public void onTetheringEntitlementResult(int); } - public abstract static class ConnectivityManager.OnTetheringEventCallback { - ctor public ConnectivityManager.OnTetheringEventCallback(); - method public void onUpstreamChanged(@Nullable android.net.Network); + @Deprecated public abstract static class ConnectivityManager.OnTetheringEventCallback { + ctor @Deprecated public ConnectivityManager.OnTetheringEventCallback(); + method @Deprecated public void onUpstreamChanged(@Nullable android.net.Network); + } + + public class EthernetManager { + method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull android.net.EthernetManager.TetheredInterfaceCallback); + } + + public static interface EthernetManager.TetheredInterfaceCallback { + method public void onAvailable(@NonNull String); + method public void onUnavailable(); + } + + public static class EthernetManager.TetheredInterfaceRequest { + method public void release(); } public class InvalidPacketException extends java.lang.Exception { @@ -4420,13 +4465,18 @@ package android.net { public class LinkAddress implements android.os.Parcelable { ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long); ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); ctor public LinkAddress(@NonNull String); ctor public LinkAddress(@NonNull String, int, int); + method public long getDeprecationTime(); + method public long getExpirationTime(); method public boolean isGlobalPreferred(); method public boolean isIpv4(); method public boolean isIpv6(); method public boolean isSameAddressAs(@Nullable android.net.LinkAddress); + field public static final long LIFETIME_PERMANENT = 9223372036854775807L; // 0x7fffffffffffffffL + field public static final long LIFETIME_UNKNOWN = -1L; // 0xffffffffffffffffL } public final class LinkProperties implements android.os.Parcelable { @@ -4438,6 +4488,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(); @@ -4451,9 +4503,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); @@ -4501,8 +4556,11 @@ package android.net { public final class NetworkCapabilities implements android.os.Parcelable { method public boolean deduceRestrictedCapability(); + method @NonNull public java.util.List<java.lang.Integer> getAdministratorUids(); + method @Nullable public String getSSID(); method @NonNull public int[] getTransportTypes(); method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); + method public void setAdministratorUids(@NonNull java.util.List<java.lang.Integer>); method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String); method @NonNull public android.net.NetworkCapabilities setTransportInfo(@NonNull android.net.TransportInfo); field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 @@ -4675,17 +4733,103 @@ package android.net { method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress); } - public final class StringNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { - ctor public StringNetworkSpecifier(@NonNull String); - method public int describeContents(); + public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { method public boolean satisfiedBy(android.net.NetworkSpecifier); + } + + public final class TetheredClient implements android.os.Parcelable { + ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int); + method public int describeContents(); + method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses(); + method @NonNull public android.net.MacAddress getMacAddress(); + method public int getTetheringType(); 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; + field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR; } - public final class TelephonyNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable { - method public boolean satisfiedBy(android.net.NetworkSpecifier); + public static final class TetheredClient.AddressInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.LinkAddress getAddress(); + method @Nullable public String getHostname(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR; + } + + public class TetheringManager { + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); + method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); + field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; + field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; + field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_ETHERNET = 5; // 0x5 + field public static final int TETHERING_INVALID = -1; // 0xffffffff + field public static final int TETHERING_NCM = 4; // 0x4 + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field public static final int TETHERING_WIFI_P2P = 3; // 0x3 + field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc + field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd + field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa + field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf + field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe + field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 + field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 + field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 + field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 + field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 + } + + public static interface TetheringManager.OnTetheringEntitlementResultListener { + method public void onTetheringEntitlementResult(int); + } + + public abstract static class TetheringManager.StartTetheringCallback { + ctor public TetheringManager.StartTetheringCallback(); + method public void onTetheringFailed(int); + method public void onTetheringStarted(); + } + + public abstract static class TetheringManager.TetheringEventCallback { + ctor public TetheringManager.TetheringEventCallback(); + method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>); + method public void onError(@NonNull String, int); + method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>); + method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>); + method public void onTetheringSupported(boolean); + method public void onUpstreamChanged(@Nullable android.net.Network); + } + + @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { + ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs(); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs(); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs(); + } + + public static class TetheringManager.TetheringRequest { + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); } public class TrafficStats { @@ -4877,7 +5021,7 @@ package android.net.ipsec.ike { } public final class IkeSessionConfiguration { - ctor public IkeSessionConfiguration(); + method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers(); method @NonNull public String getRemoteApplicationVersion(); method @NonNull public java.util.List<byte[]> getRemoteVendorIDs(); method public boolean isIkeExtensionEnabled(int); @@ -4886,6 +5030,7 @@ package android.net.ipsec.ike { } public final class IkeSessionParams { + method @NonNull public java.util.List<android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest> getConfigurationRequests(); method public long getHardLifetime(); method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig(); method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification(); @@ -4899,6 +5044,8 @@ package android.net.ipsec.ike { public static final class IkeSessionParams.Builder { ctor public IkeSessionParams.Builder(); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(@NonNull java.net.InetAddress); + method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(int); method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal); method @NonNull public android.net.ipsec.ike.IkeSessionParams build(); method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey); @@ -4912,6 +5059,14 @@ package android.net.ipsec.ike { method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket); } + public static interface IkeSessionParams.ConfigRequestIpv4PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest { + method @Nullable public java.net.Inet4Address getAddress(); + } + + public static interface IkeSessionParams.ConfigRequestIpv6PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest { + method @Nullable public java.net.Inet6Address getAddress(); + } + public abstract static class IkeSessionParams.IkeAuthConfig { } @@ -4933,6 +5088,9 @@ package android.net.ipsec.ike { method @NonNull public byte[] getPsk(); } + public static interface IkeSessionParams.IkeConfigRequest { + } + public final class IkeTrafficSelector { ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress); field public final int endPort; @@ -4980,7 +5138,7 @@ package android.net.ipsec.ike { } public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams { - method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest> getConfigurationRequests(); + method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest> getConfigurationRequests(); } public static final class TunnelModeChildSessionParams.Builder { @@ -4997,33 +5155,33 @@ package android.net.ipsec.ike { method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder setLifetime(long, long); } - public static interface TunnelModeChildSessionParams.ConfigRequest { - } - - public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest { + public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { method @Nullable public java.net.Inet4Address getAddress(); } - public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest { + public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { method @Nullable public java.net.Inet4Address getAddress(); } - public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest { + public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { method @Nullable public java.net.Inet4Address getAddress(); } - public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest { + public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { } - public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest { + public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { method @Nullable public java.net.Inet6Address getAddress(); method public int getPrefixLength(); } - public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.ConfigRequest { + public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest { method @Nullable public java.net.Inet6Address getAddress(); } + public static interface TunnelModeChildSessionParams.TunnelModeChildConfigRequest { + } + } package android.net.ipsec.ike.exceptions { @@ -5507,7 +5665,7 @@ package android.net.wifi { method public boolean isPortableHotspotSupported(); method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled(); method public boolean isWifiScannerSupported(); - method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@Nullable java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback); + method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback); method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener); method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener); method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int); @@ -6924,6 +7082,7 @@ package android.provider { field public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size"; field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length"; field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length"; + field public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled"; field public static final String COMPLETED_CATEGORY_PREFIX = "suggested.completed_category."; field public static final String DOZE_ALWAYS_ON = "doze_always_on"; field public static final String HUSH_GESTURE_USED = "hush_gesture_used"; @@ -7043,7 +7202,73 @@ package android.provider { } public static final class Telephony.SimInfo { + field public static final String ACCESS_RULES = "access_rules"; + field public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = "access_rules_from_carrier_configs"; + field public static final String ALLOWED_NETWORK_TYPES = "allowed_network_types"; + field public static final String CARD_ID = "card_id"; + field public static final String CARRIER_ID = "carrier_id"; + field public static final String CARRIER_NAME = "carrier_name"; + field public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; + field public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration"; + field public static final String CB_ALERT_SPEECH = "enable_alert_speech"; + field public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate"; + field public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts"; + field public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts"; + field public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts"; + field public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts"; + field public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts"; + field public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts"; + field public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; + field public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts"; + field public static final String COLOR = "color"; field @NonNull public static final android.net.Uri CONTENT_URI; + field public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules"; + field public static final String DATA_ROAMING = "data_roaming"; + field public static final int DATA_ROAMING_DEFAULT = 0; // 0x0 + field public static final int DATA_ROAMING_DISABLE = 0; // 0x0 + field public static final int DATA_ROAMING_ENABLE = 1; // 0x1 + field public static final String DISPLAY_NAME = "display_name"; + field public static final String EHPLMNS = "ehplmns"; + field public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled"; + field public static final String GROUP_OWNER = "group_owner"; + field public static final String GROUP_UUID = "group_uuid"; + field public static final String HPLMNS = "hplmns"; + field public static final String ICC_ID = "icc_id"; + field public static final String IMSI = "imsi"; + field public static final String IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled"; + field public static final String ISO_COUNTRY_CODE = "iso_country_code"; + field public static final String IS_EMBEDDED = "is_embedded"; + field public static final String IS_OPPORTUNISTIC = "is_opportunistic"; + field public static final String IS_REMOVABLE = "is_removable"; + field public static final String MCC = "mcc"; + field public static final String MCC_STRING = "mcc_string"; + field public static final String MNC = "mnc"; + field public static final String MNC_STRING = "mnc_string"; + field public static final String NAME_SOURCE = "name_source"; + field public static final int NAME_SOURCE_CARRIER = 3; // 0x3 + field public static final int NAME_SOURCE_DEFAULT = 0; // 0x0 + field public static final int NAME_SOURCE_SIM_PNN = 4; // 0x4 + field public static final int NAME_SOURCE_SIM_SPN = 1; // 0x1 + field public static final int NAME_SOURCE_USER_INPUT = 2; // 0x2 + field public static final String NUMBER = "number"; + field public static final String PROFILE_CLASS = "profile_class"; + field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff + field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2 + field public static final int PROFILE_CLASS_PROVISIONING = 1; // 0x1 + field public static final int PROFILE_CLASS_TESTING = 0; // 0x0 + field public static final int PROFILE_CLASS_UNSET = -1; // 0xffffffff + field public static final int SIM_NOT_INSERTED = -1; // 0xffffffff + field public static final String SIM_SLOT_INDEX = "sim_id"; + field public static final String SUBSCRIPTION_TYPE = "subscription_type"; + field public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; // 0x0 + field public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; // 0x1 + field public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled"; + field public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id"; + field public static final String VT_IMS_ENABLED = "vt_ims_enabled"; + field public static final String WFC_IMS_ENABLED = "wfc_ims_enabled"; + field public static final String WFC_IMS_MODE = "wfc_ims_mode"; + field public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled"; + field public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode"; } public static final class Telephony.Sms.Intents { @@ -7068,6 +7293,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 { @@ -8255,7 +8488,7 @@ package android.telephony { } public class CellBroadcastIntents { - method public static void sendOrderedBroadcastForBackgroundReceivers(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle); + method public static void sendSmsCbReceivedBroadcast(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.telephony.SmsCbMessage, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, int); } public abstract class CellBroadcastService extends android.app.Service { @@ -8772,6 +9005,7 @@ package android.telephony { method public int getSleepTimeMillis(); method public long getTimestamp(); method @NonNull public java.util.List<android.telephony.ModemActivityInfo.TransmitPower> getTransmitPowerInfo(); + method public boolean isValid(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR; field public static final int TX_POWER_LEVELS = 5; // 0x5 @@ -8869,6 +9103,7 @@ package android.telephony { method public void onRadioPowerStateChanged(int); method public void onSrvccStateChanged(int); method public void onVoiceActivationStateChanged(int); + field @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 512; // 0x200 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 @@ -9150,6 +9385,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); @@ -9161,6 +9397,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); } @@ -9239,6 +9476,7 @@ package android.telephony { method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void enableVideoCalling(boolean); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getAidForAppType(int); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int); @@ -9312,9 +9550,12 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption(); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig(); method @RequiresPermission(android.Manifest.permission.CONNECTIVITY_INTERNAL) public void resetSettings(); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAlwaysReportSignalStrength(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int); @@ -9341,6 +9582,7 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateTestOtaEmergencyNumberDbFilePath(@NonNull String); field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_ANOMALY_REPORTED = "android.telephony.action.ANOMALY_REPORTED"; field public static final String ACTION_EMERGENCY_ASSISTANCE = "android.telephony.action.EMERGENCY_ASSISTANCE"; + field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED"; field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED"; field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED"; field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED"; @@ -9353,7 +9595,12 @@ package android.telephony { field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION"; field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID"; + field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN"; + field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN"; + field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN"; + field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN"; field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE"; + field public static final String EXTRA_SPN = "android.telephony.extra.SPN"; field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL"; field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING"; field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff @@ -9942,12 +10189,12 @@ package android.telephony.ims { } public class ImsManager { - 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"; } public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { + method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) 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 int getVoWiFiRoamingModeSetting(); @@ -10315,8 +10562,8 @@ package android.telephony.ims { method public int describeContents(); method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags(); method @NonNull public android.net.Uri getContactUri(); - method @Nullable public android.net.Uri getServiceUri(int); - method public boolean isCapable(int); + method @Nullable public android.net.Uri getServiceUri(long); + method public boolean isCapable(long); method public boolean isCapable(@NonNull String); method public void writeToParcel(@NonNull android.os.Parcel, int); field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000 @@ -10338,6 +10585,7 @@ package android.telephony.ims { field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100 field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000 field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000 + field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000 field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000 field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000 field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000 @@ -10346,6 +10594,7 @@ package android.telephony.ims { field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000 field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000 field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800 + field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000 field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400 field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200 field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR; @@ -10353,12 +10602,43 @@ package android.telephony.ims { public static class RcsContactUceCapability.Builder { ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri); - method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(int, @NonNull android.net.Uri); - method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(int); + method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri); + method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long); method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String); method @NonNull public android.telephony.ims.RcsContactUceCapability build(); } + public class RcsUceAdapter { + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; + field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd + field public static final int ERROR_FORBIDDEN = 6; // 0x6 + field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1 + field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb + field public static final int ERROR_LOST_NETWORK = 12; // 0xc + field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5 + field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3 + field public static final int ERROR_NOT_ENABLED = 2; // 0x2 + field public static final int ERROR_NOT_FOUND = 7; // 0x7 + field public static final int ERROR_NOT_REGISTERED = 4; // 0x4 + field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa + field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8 + field public static final int PUBLISH_STATE_200_OK = 1; // 0x1 + field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2 + field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6 + field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4 + field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5 + field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3 + } + + public static class RcsUceAdapter.CapabilitiesCallback { + ctor public RcsUceAdapter.CapabilitiesCallback(); + method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>); + method public void onError(int); + } + } package android.telephony.ims.feature { @@ -10448,6 +10728,8 @@ package android.telephony.ims.feature { public class RcsFeature extends android.telephony.ims.feature.ImsFeature { ctor public RcsFeature(); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); + method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl(); + method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl(); method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities); method public void onFeatureReady(); method public void onFeatureRemoved(); @@ -10620,6 +10902,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); @@ -10639,6 +10922,71 @@ package android.telephony.ims.stub { field public static final int INVALID_RESULT = -1; // 0xffffffff } + public class RcsCapabilityExchange { + ctor public RcsCapabilityExchange(); + method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException; + field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4 + field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2 + field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6 + field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3 + field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7 + field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9 + field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8 + field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb + field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5 + field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa + field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0 + field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1 + } + + public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange { + ctor public RcsPresenceExchangeImplBase(); + method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException; + method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException; + method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException; + method public final void onUnpublish() throws android.telephony.ims.ImsException; + method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int); + method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int); + field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7 + field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9 + field public static final int RESPONSE_FORBIDDEN = 3; // 0x3 + field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2 + field public static final int RESPONSE_NOT_FOUND = 4; // 0x4 + field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1 + field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7 + field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5 + field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8 + field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff + field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6 + field public static final int RESPONSE_SUCCESS = 0; // 0x0 + } + + public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange { + ctor public RcsSipOptionsImplBase(); + method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException; + method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException; + method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int); + method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int); + method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int); + field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5 + field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4 + field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff + field public static final int RESPONSE_NOT_FOUND = 3; // 0x3 + field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2 + field public static final int RESPONSE_SUCCESS = 0; // 0x0 + field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1 + } + } package android.telephony.mbms { diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt index 432a5fd8efec..306b8afaadae 100644 --- a/api/system-lint-baseline.txt +++ b/api/system-lint-baseline.txt @@ -1,4 +1,14 @@ // Baseline format: 1.0 +// Tethering broadcast action / extras cannot change name for backwards compatibility +ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED: + Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED` +ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER: + Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray` +ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER: + Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray` +ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER: + Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray` + ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions(): diff --git a/api/test-current.txt b/api/test-current.txt index 4acb8656a34c..b9d53dbaf5ce 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -653,11 +653,11 @@ package android.content { method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions); field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; + field public static final String ETHERNET_SERVICE = "ethernet"; field public static final String NETWORK_STACK_SERVICE = "network_stack"; field public static final String PERMISSION_SERVICE = "permission"; field public static final String ROLLBACK_SERVICE = "rollback"; field public static final String STATUS_BAR_SERVICE = "statusbar"; - field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims"; field public static final String TEST_NETWORK_SERVICE = "test_network"; } @@ -1361,12 +1361,51 @@ 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"; field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; } + public class EthernetManager { + method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull android.net.EthernetManager.TetheredInterfaceCallback); + } + + public static interface EthernetManager.TetheredInterfaceCallback { + method public void onAvailable(@NonNull String); + method public void onUnavailable(); + } + + public static class EthernetManager.TetheredInterfaceRequest { + method public void release(); + } + public final class IpPrefix implements android.os.Parcelable { ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); ctor public IpPrefix(@NonNull String); @@ -1378,9 +1417,12 @@ package android.net { public class LinkAddress implements android.os.Parcelable { ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int); + ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long); ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); ctor public LinkAddress(@NonNull String); ctor public LinkAddress(@NonNull String, int, int); + method public long getDeprecationTime(); + method public long getExpirationTime(); method public boolean isGlobalPreferred(); method public boolean isIpv4(); method public boolean isIpv6(); @@ -1391,6 +1433,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 +1445,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); @@ -1475,6 +1522,101 @@ package android.net { method public void teardownTestNetwork(@NonNull android.net.Network); } + public final class TetheredClient implements android.os.Parcelable { + ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int); + method public int describeContents(); + method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses(); + method @NonNull public android.net.MacAddress getMacAddress(); + method public int getTetheringType(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR; + } + + public static final class TetheredClient.AddressInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.net.LinkAddress getAddress(); + method @Nullable public String getHostname(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR; + } + + public class TetheringManager { + method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); + method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); + field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; + field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; + field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; + field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; + field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; + field public static final int TETHERING_BLUETOOTH = 2; // 0x2 + field public static final int TETHERING_ETHERNET = 5; // 0x5 + field public static final int TETHERING_INVALID = -1; // 0xffffffff + field public static final int TETHERING_NCM = 4; // 0x4 + field public static final int TETHERING_USB = 1; // 0x1 + field public static final int TETHERING_WIFI = 0; // 0x0 + field public static final int TETHERING_WIFI_P2P = 3; // 0x3 + field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc + field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9 + field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8 + field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd + field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa + field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5 + field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf + field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe + field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 + field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb + field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 + field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 + field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 + field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 + field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 + field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 + } + + public static interface TetheringManager.OnTetheringEntitlementResultListener { + method public void onTetheringEntitlementResult(int); + } + + public abstract static class TetheringManager.StartTetheringCallback { + ctor public TetheringManager.StartTetheringCallback(); + method public void onTetheringFailed(int); + method public void onTetheringStarted(); + } + + public abstract static class TetheringManager.TetheringEventCallback { + ctor public TetheringManager.TetheringEventCallback(); + method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>); + method public void onError(@NonNull String, int); + method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps); + method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>); + method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>); + method public void onTetheringSupported(boolean); + method public void onUpstreamChanged(@Nullable android.net.Network); + } + + @Deprecated public static class TetheringManager.TetheringInterfaceRegexps { + ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs(); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs(); + method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs(); + } + + public static class TetheringManager.TetheringRequest { + } + + public static class TetheringManager.TetheringRequest.Builder { + ctor public TetheringManager.TetheringRequest.Builder(int); + method @NonNull public android.net.TetheringManager.TetheringRequest build(); + method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); + method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean); + method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress); + } + public class TrafficStats { method public static long getLoopbackRxBytes(); method public static long getLoopbackRxPackets(); @@ -2853,6 +2995,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); @@ -3271,12 +3415,12 @@ package android.telephony.ims { } public class ImsManager { - 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"; } public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { + method @Deprecated @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 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 int getVoWiFiRoamingModeSetting(); @@ -3636,6 +3780,87 @@ package android.telephony.ims { method public void onProvisioningStringChanged(int, @NonNull String); } + public final class RcsContactUceCapability implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public java.util.List<java.lang.String> getCapableExtensionTags(); + method @NonNull public android.net.Uri getContactUri(); + method @Nullable public android.net.Uri getServiceUri(long); + method public boolean isCapable(long); + method public boolean isCapable(@NonNull String); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int CAPABILITY_CALL_COMPOSER = 4194304; // 0x400000 + field public static final int CAPABILITY_CHAT_BOT = 67108864; // 0x4000000 + field public static final int CAPABILITY_CHAT_BOT_ROLE = 134217728; // 0x8000000 + field public static final int CAPABILITY_CHAT_SESSION = 2; // 0x2 + field public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = 4; // 0x4 + field public static final int CAPABILITY_CHAT_STANDALONE = 1; // 0x1 + field public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = 4096; // 0x1000 + field public static final int CAPABILITY_FILE_TRANSFER = 8; // 0x8 + field public static final int CAPABILITY_FILE_TRANSFER_HTTP = 64; // 0x40 + field public static final int CAPABILITY_FILE_TRANSFER_SMS = 128; // 0x80 + field public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = 32; // 0x20 + field public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = 16; // 0x10 + field public static final int CAPABILITY_GEOLOCATION_PULL = 131072; // 0x20000 + field public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = 262144; // 0x40000 + field public static final int CAPABILITY_GEOLOCATION_PUSH = 32768; // 0x8000 + field public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = 65536; // 0x10000 + field public static final int CAPABILITY_IMAGE_SHARE = 256; // 0x100 + field public static final int CAPABILITY_IP_VIDEO_CALL = 16384; // 0x4000 + field public static final int CAPABILITY_IP_VOICE_CALL = 8192; // 0x2000 + field public static final int CAPABILITY_MMTEL_CALL_COMPOSER = 1073741824; // 0x40000000 + field public static final int CAPABILITY_PLUG_IN = 268435456; // 0x10000000 + field public static final int CAPABILITY_POST_CALL = 8388608; // 0x800000 + field public static final int CAPABILITY_RCS_VIDEO_CALL = 1048576; // 0x100000 + field public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = 2097152; // 0x200000 + field public static final int CAPABILITY_RCS_VOICE_CALL = 524288; // 0x80000 + field public static final int CAPABILITY_SHARED_MAP = 16777216; // 0x1000000 + field public static final int CAPABILITY_SHARED_SKETCH = 33554432; // 0x2000000 + field public static final int CAPABILITY_SOCIAL_PRESENCE = 2048; // 0x800 + field public static final int CAPABILITY_STANDALONE_CHAT_BOT = 536870912; // 0x20000000 + field public static final int CAPABILITY_VIDEO_SHARE = 1024; // 0x400 + field public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = 512; // 0x200 + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR; + } + + public static class RcsContactUceCapability.Builder { + ctor public RcsContactUceCapability.Builder(@NonNull android.net.Uri); + method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long, @NonNull android.net.Uri); + method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(long); + method @NonNull public android.telephony.ims.RcsContactUceCapability.Builder add(@NonNull String); + method @NonNull public android.telephony.ims.RcsContactUceCapability build(); + } + + public class RcsUceAdapter { + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getUcePublishState() throws android.telephony.ims.ImsException; + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public boolean isUceSettingEnabled() throws android.telephony.ims.ImsException; + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public void requestCapabilities(@NonNull java.util.concurrent.Executor, @NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException; + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; + field public static final int ERROR_ALREADY_IN_QUEUE = 13; // 0xd + field public static final int ERROR_FORBIDDEN = 6; // 0x6 + field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1 + field public static final int ERROR_INSUFFICIENT_MEMORY = 11; // 0xb + field public static final int ERROR_LOST_NETWORK = 12; // 0xc + field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5 + field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3 + field public static final int ERROR_NOT_ENABLED = 2; // 0x2 + field public static final int ERROR_NOT_FOUND = 7; // 0x7 + field public static final int ERROR_NOT_REGISTERED = 4; // 0x4 + field public static final int ERROR_REQUEST_TIMEOUT = 10; // 0xa + field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8 + field public static final int PUBLISH_STATE_200_OK = 1; // 0x1 + field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2 + field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6 + field public static final int PUBLISH_STATE_RCS_PROVISION_ERROR = 4; // 0x4 + field public static final int PUBLISH_STATE_REQUEST_TIMEOUT = 5; // 0x5 + field public static final int PUBLISH_STATE_VOLTE_PROVISION_ERROR = 3; // 0x3 + } + + public static class RcsUceAdapter.CapabilitiesCallback { + ctor public RcsUceAdapter.CapabilitiesCallback(); + method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>); + method public void onError(int); + } + } package android.telephony.ims.feature { @@ -3725,6 +3950,8 @@ package android.telephony.ims.feature { public class RcsFeature extends android.telephony.ims.feature.ImsFeature { ctor public RcsFeature(); method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); + method @NonNull public android.telephony.ims.stub.RcsSipOptionsImplBase getOptionsExchangeImpl(); + method @NonNull public android.telephony.ims.stub.RcsPresenceExchangeImplBase getPresenceExchangeImpl(); method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.RcsFeature.RcsImsCapabilities); method public void onFeatureReady(); method public void onFeatureRemoved(); @@ -3897,6 +4124,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); @@ -3916,6 +4144,71 @@ package android.telephony.ims.stub { field public static final int INVALID_RESULT = -1; // 0xffffffff } + public class RcsCapabilityExchange { + ctor public RcsCapabilityExchange(); + method public final void onCommandUpdate(int, int) throws android.telephony.ims.ImsException; + field public static final int COMMAND_CODE_FETCH_ERROR = 4; // 0x4 + field public static final int COMMAND_CODE_GENERIC_FAILURE = 2; // 0x2 + field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 6; // 0x6 + field public static final int COMMAND_CODE_INVALID_PARAM = 3; // 0x3 + field public static final int COMMAND_CODE_LOST_NETWORK_CONNECTION = 7; // 0x7 + field public static final int COMMAND_CODE_NOT_FOUND = 9; // 0x9 + field public static final int COMMAND_CODE_NOT_SUPPORTED = 8; // 0x8 + field public static final int COMMAND_CODE_NO_CHANGE_IN_CAP = 11; // 0xb + field public static final int COMMAND_CODE_REQUEST_TIMEOUT = 5; // 0x5 + field public static final int COMMAND_CODE_SERVICE_UNAVAILABLE = 10; // 0xa + field public static final int COMMAND_CODE_SERVICE_UNKNOWN = 0; // 0x0 + field public static final int COMMAND_CODE_SUCCESS = 1; // 0x1 + } + + public class RcsPresenceExchangeImplBase extends android.telephony.ims.stub.RcsCapabilityExchange { + ctor public RcsPresenceExchangeImplBase(); + method public final void onCapabilityRequestResponse(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>, int) throws android.telephony.ims.ImsException; + method public final void onNetworkResponse(int, @NonNull String, int) throws android.telephony.ims.ImsException; + method public final void onNotifyUpdateCapabilites(int) throws android.telephony.ims.ImsException; + method public final void onUnpublish() throws android.telephony.ims.ImsException; + method public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, int); + method public void updateCapabilities(@NonNull android.telephony.ims.RcsContactUceCapability, int); + field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 0; // 0x0 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 6; // 0x6 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 5; // 0x5 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_EHRPD = 3; // 0x3 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_HSPAPLUS = 4; // 0x4 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_IWLAN = 8; // 0x8 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_DISABLED = 1; // 0x1 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_LTE_VOPS_ENABLED = 2; // 0x2 + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_DISABLED = 10; // 0xa + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb + field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 7; // 0x7 + field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 9; // 0x9 + field public static final int RESPONSE_FORBIDDEN = 3; // 0x3 + field public static final int RESPONSE_NOT_AUTHORIZED_FOR_PRESENCE = 2; // 0x2 + field public static final int RESPONSE_NOT_FOUND = 4; // 0x4 + field public static final int RESPONSE_NOT_REGISTERED = 1; // 0x1 + field public static final int RESPONSE_SIP_INTERVAL_TOO_SHORT = 7; // 0x7 + field public static final int RESPONSE_SIP_REQUEST_TIMEOUT = 5; // 0x5 + field public static final int RESPONSE_SIP_SERVICE_UNAVAILABLE = 8; // 0x8 + field public static final int RESPONSE_SUBSCRIBE_GENERIC_FAILURE = -1; // 0xffffffff + field public static final int RESPONSE_SUBSCRIBE_TOO_LARGE = 6; // 0x6 + field public static final int RESPONSE_SUCCESS = 0; // 0x0 + } + + public class RcsSipOptionsImplBase extends android.telephony.ims.stub.RcsCapabilityExchange { + ctor public RcsSipOptionsImplBase(); + method public final void onCapabilityRequestResponse(int, @NonNull String, @Nullable android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException; + method public final void onRemoteCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int) throws android.telephony.ims.ImsException; + method public void respondToCapabilityRequest(@NonNull String, @NonNull android.telephony.ims.RcsContactUceCapability, int); + method public void respondToCapabilityRequestWithError(@NonNull android.net.Uri, int, @NonNull String, int); + method public void sendCapabilityRequest(@NonNull android.net.Uri, @NonNull android.telephony.ims.RcsContactUceCapability, int); + field public static final int RESPONSE_BAD_REQUEST = 5; // 0x5 + field public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; // 0x4 + field public static final int RESPONSE_GENERIC_FAILURE = -1; // 0xffffffff + field public static final int RESPONSE_NOT_FOUND = 3; // 0x3 + field public static final int RESPONSE_REQUEST_TIMEOUT = 2; // 0x2 + field public static final int RESPONSE_SUCCESS = 0; // 0x0 + field public static final int RESPONSE_TEMPORARILY_UNAVAILABLE = 1; // 0x1 + } + } package android.telephony.mbms { @@ -4098,138 +4391,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/api/test-lint-baseline.txt b/api/test-lint-baseline.txt index 3af392c05c8f..46f3aeba85bd 100644 --- a/api/test-lint-baseline.txt +++ b/api/test-lint-baseline.txt @@ -5,6 +5,16 @@ AcronymName: android.app.NotificationChannel#setImportanceLockedByOEM(boolean): +// Tethering broadcast action / extras cannot change name for backwards compatibility +ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED: + Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED` +ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER: + Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray` +ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER: + Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray` +ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER: + Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray` + ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_ADDITIONAL_CALL_INFO: Inconsistent extra value; expected `android.telephony.ims.extra.ADDITIONAL_CALL_INFO`, was `AdditionalCallInfo` ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CALL_RAT_TYPE: diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 1987440106f5..97074050448e 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -366,12 +366,8 @@ public final class Telecom extends BaseCommand { } private void runGetMaxPhones() throws RemoteException { - // This assumes the max number of SIMs is 2, which it currently is - if (TelephonyManager.MULTISIM_ALLOWED == mTelephonyManager.isMultiSimSupported()) { - System.out.println("2"); - } else { - System.out.println("1"); - } + // how many logical modems can be potentially active simultaneously + System.out.println(mTelephonyManager.getSupportedModemCount()); } private void runSetEmergencyPhoneAccountPackageFilter() throws RemoteException { diff --git a/core/java/android/annotation/SystemApi.java b/core/java/android/annotation/SystemApi.java index 2cb93e4f8cea..ecbfed96cefd 100644 --- a/core/java/android/annotation/SystemApi.java +++ b/core/java/android/annotation/SystemApi.java @@ -23,6 +23,7 @@ import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PACKAGE; import static java.lang.annotation.ElementType.TYPE; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -40,39 +41,48 @@ import java.lang.annotation.Target; */ @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE}) @Retention(RetentionPolicy.RUNTIME) +@Repeatable(SystemApi.Container.class) // TODO(b/146727827): make this non-repeatable public @interface SystemApi { enum Client { /** * Specifies that the intended clients of a SystemApi are privileged apps. - * This is the default value for {@link #client}. This implies - * MODULE_APPS and MODULE_LIBRARIES as well, which means that APIs will also - * be available to module apps and jars. + * This is the default value for {@link #client}. + * TODO Update the javadoc according to the final spec */ PRIVILEGED_APPS, /** - * Specifies that the intended clients of a SystemApi are modules implemented - * as apps, like the NetworkStack app. This implies MODULE_LIBRARIES as well, - * which means that APIs will also be available to module jars. + * DO NOT USE. Use PRIVILEGED_APPS instead. + * (This would provide no further protection over PRIVILEGED_APPS; do not rely on it) + * @deprecated Use #PRIVILEGED_APPS instead */ + @Deprecated MODULE_APPS, /** * Specifies that the intended clients of a SystemApi are modules implemented * as libraries, like the conscrypt.jar in the conscrypt APEX. + * TODO Update the javadoc according to the final spec */ - MODULE_LIBRARIES - } + MODULE_LIBRARIES, - enum Process { /** - * Specifies that the SystemAPI is available in every Java processes. - * This is the default value for {@link #process}. + * Specifies that the system API is available only in the system server process. + * Use this to expose APIs from code loaded by the system server process <em>but</em> + * not in <pre>BOOTCLASSPATH</pre>. + * TODO(b/148177503) Update "services-stubs" and actually use it. */ + SYSTEM_SERVER + } + + /** @deprecated do not use */ + @Deprecated + enum Process { + /** @deprecated do not use */ ALL, /** - * Specifies that the SystemAPI is available only in the system server process. + * @deprecated use Client#SYSTEM_SERVER instead */ SYSTEM_SERVER } @@ -83,7 +93,18 @@ public @interface SystemApi { Client client() default android.annotation.SystemApi.Client.PRIVILEGED_APPS; /** - * The process(es) that this SystemAPI is available + * @deprecated use Client#SYSTEM_SERVER instead for system_server APIs */ + @Deprecated Process process() default android.annotation.SystemApi.Process.ALL; + + + /** + * Container for {@link SystemApi} that allows it to be applied repeatedly to types. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(TYPE) + @interface Container { + SystemApi[] value(); + } } diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 88976e182e6d..81f6d28db9fe 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; @@ -112,6 +113,7 @@ import android.net.NetworkScoreManager; import android.net.NetworkWatchlistManager; import android.net.TestNetworkManager; import android.net.TetheringManager; +import android.net.VpnManager; import android.net.lowpan.ILowpanManager; import android.net.lowpan.LowpanManager; import android.net.nsd.INsdManager; @@ -369,6 +371,27 @@ final class SystemServiceRegistry { return new IpSecManager(ctx, service); }}); + registerService(Context.VPN_MANAGEMENT_SERVICE, VpnManager.class, + new CachedServiceFetcher<VpnManager>() { + @Override + public VpnManager createService(ContextImpl ctx) throws ServiceNotFoundException { + IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE); + IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); + return new VpnManager(ctx, service); + }}); + + registerService(Context.CONNECTIVITY_DIAGNOSTICS_SERVICE, + ConnectivityDiagnosticsManager.class, + new CachedServiceFetcher<ConnectivityDiagnosticsManager>() { + @Override + public ConnectivityDiagnosticsManager createService(ContextImpl ctx) + throws ServiceNotFoundException { + // ConnectivityDiagnosticsManager is backed by ConnectivityService + IBinder b = ServiceManager.getServiceOrThrow(Context.CONNECTIVITY_SERVICE); + IConnectivityManager service = IConnectivityManager.Stub.asInterface(b); + return new ConnectivityDiagnosticsManager(ctx, service); + }}); + registerService( Context.TEST_NETWORK_SERVICE, TestNetworkManager.class, diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java index 8993de0939e6..ee2cc6d14712 100755 --- a/core/java/android/bluetooth/BluetoothA2dpSink.java +++ b/core/java/android/bluetooth/BluetoothA2dpSink.java @@ -297,7 +297,8 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -345,7 +346,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothA2dpSink service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 1869c6fa7621..8415ecd38b75 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; } @@ -3235,18 +3236,6 @@ public final class BluetoothAdapter { } /** - * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new - * API name, listenUsingL2capChannel. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothServerSocket listenUsingL2capCoc(int transport) - throws IOException { - Log.e(TAG, "listenUsingL2capCoc: PLEASE USE THE OFFICIAL API, listenUsingL2capChannel"); - return listenUsingL2capChannel(); - } - - /** * Create an insecure L2CAP Connection-oriented Channel (CoC) {@link BluetoothServerSocket} and * assign a dynamic PSM value. This socket can be used to listen for incoming connections. The * supported Bluetooth transport is LE only. @@ -3293,19 +3282,6 @@ public final class BluetoothAdapter { } /** - * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new - * API name, listenUsingInsecureL2capChannel. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothServerSocket listenUsingInsecureL2capCoc(int transport) - throws IOException { - Log.e(TAG, "listenUsingInsecureL2capCoc: PLEASE USE THE OFFICIAL API, " - + "listenUsingInsecureL2capChannel"); - return listenUsingInsecureL2capChannel(); - } - - /** * Register a {@link #OnMetadataChangedListener} to receive update about metadata * changes for this {@link BluetoothDevice}. * Registration must be done when Bluetooth is ON and will last until diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 9fe4dd66b874..12dc814c3416 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -2172,17 +2172,6 @@ public final class BluetoothDevice implements Parcelable { } /** - * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new - * API name, createL2capChannel. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothSocket createL2capCocSocket(int transport, int psm) throws IOException { - Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createL2capChannel"); - return createL2capChannel(psm); - } - - /** * Create a Bluetooth L2CAP Connection-oriented Channel (CoC) {@link BluetoothSocket} that can * be used to start a secure outgoing connection to the remote device with the same dynamic * protocol/service multiplexer (PSM) value. The supported Bluetooth transport is LE only. @@ -2213,17 +2202,6 @@ public final class BluetoothDevice implements Parcelable { } /** - * TODO: Remove this hidden method once all the SL4A and other tests are updated to use the new - * API name, createInsecureL2capChannel. - * @hide - */ - @RequiresPermission(Manifest.permission.BLUETOOTH) - public BluetoothSocket createInsecureL2capCocSocket(int transport, int psm) throws IOException { - Log.e(TAG, "createL2capCocSocket: PLEASE USE THE OFFICIAL API, createInsecureL2capChannel"); - return createInsecureL2capChannel(psm); - } - - /** * Set a keyed metadata of this {@link BluetoothDevice} to a * {@link String} value. * Only bonded devices's metadata will be persisted across Bluetooth diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java index 6de1ffb6344e..fbda9e9d6dd7 100644 --- a/core/java/android/bluetooth/BluetoothHeadsetClient.java +++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java @@ -584,7 +584,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHeadsetClient service = getService(); @@ -633,7 +634,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHeadsetClient service = getService(); diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java index c1233b800a51..26e3e271bffa 100644 --- a/core/java/android/bluetooth/BluetoothHidHost.java +++ b/core/java/android/bluetooth/BluetoothHidHost.java @@ -416,7 +416,8 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -464,7 +465,7 @@ public final class BluetoothHidHost implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothHidHost service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java index 467470674286..1c62faa97ee6 100644 --- a/core/java/android/bluetooth/BluetoothMap.java +++ b/core/java/android/bluetooth/BluetoothMap.java @@ -340,7 +340,8 @@ public final class BluetoothMap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(@Nullable BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(@Nullable BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -388,7 +389,7 @@ public final class BluetoothMap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(@Nullable BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothMap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index 0aa5aac5d8f6..8d2aaddd38d2 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -271,7 +271,8 @@ public final class BluetoothMapClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) Log.d(TAG, "setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -319,7 +320,7 @@ public final class BluetoothMapClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) Log.d(TAG, "getConnectionPolicy(" + device + ")"); final IBluetoothMapClient service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 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/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java index 9618ba01572e..9563c68ce657 100644 --- a/core/java/android/bluetooth/BluetoothPbapClient.java +++ b/core/java/android/bluetooth/BluetoothPbapClient.java @@ -271,7 +271,8 @@ public final class BluetoothPbapClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) { log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); } @@ -323,7 +324,7 @@ public final class BluetoothPbapClient implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) { log("getConnectionPolicy(" + device + ")"); } diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java index 8bf1b588c89a..bfc3a4d25c23 100644 --- a/core/java/android/bluetooth/BluetoothSap.java +++ b/core/java/android/bluetooth/BluetoothSap.java @@ -330,7 +330,8 @@ public final class BluetoothSap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN) - public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { + public boolean setConnectionPolicy(BluetoothDevice device, + @ConnectionPolicy int connectionPolicy) { if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")"); final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { @@ -378,7 +379,7 @@ public final class BluetoothSap implements BluetoothProfile { */ @SystemApi @RequiresPermission(Manifest.permission.BLUETOOTH) - public int getConnectionPolicy(BluetoothDevice device) { + public @ConnectionPolicy int getConnectionPolicy(BluetoothDevice device) { if (VDBG) log("getConnectionPolicy(" + device + ")"); final IBluetoothSap service = getService(); if (service != null && isEnabled() && isValidDevice(device)) { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index cd4af968ebfc..c3c7f972066b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3296,6 +3296,7 @@ public abstract class Context { CONNECTIVITY_SERVICE, //@hide: IP_MEMORY_STORE_SERVICE, IPSEC_SERVICE, + VPN_MANAGEMENT_SERVICE, TEST_NETWORK_SERVICE, //@hide: UPDATE_LOCK_SERVICE, //@hide: NETWORKMANAGEMENT_SERVICE, @@ -3880,6 +3881,24 @@ public abstract class Context { public static final String IPSEC_SERVICE = "ipsec"; /** + * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.VpnManager} to + * manage profiles for the platform built-in VPN. + * + * @see #getSystemService(String) + */ + public static final String VPN_MANAGEMENT_SERVICE = "vpn_management"; + + /** + * Use with {@link #getSystemService(String)} to retrieve a {@link + * android.net.ConnectivityDiagnosticsManager} for performing network connectivity diagnostics + * as well as receiving network connectivity information from the system. + * + * @see #getSystemService(String) + * @see android.net.ConnectivityDiagnosticsManager + */ + public static final String CONNECTIVITY_DIAGNOSTICS_SERVICE = "connectivity_diagnostics"; + + /** * Use with {@link #getSystemService(String)} to retrieve a {@link * android.net.TestNetworkManager} for building TUNs and limited-use Networks * @@ -4003,16 +4022,16 @@ public abstract class Context { public static final String LOWPAN_SERVICE = "lowpan"; /** - * Use with {@link #getSystemService(String)} to retrieve a {@link - * android.net.EthernetManager} for handling management of - * Ethernet access. + * Use with {@link #getSystemService(String)} to retrieve a {@link android.net.EthernetManager} + * for handling management of Ethernet access. * * @see #getSystemService(String) * @see android.net.EthernetManager * * @hide */ - @UnsupportedAppUsage + @SystemApi + @TestApi public static final String ETHERNET_SERVICE = "ethernet"; /** @@ -4835,10 +4854,7 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve an * {@link android.telephony.ims.ImsManager}. - * @hide */ - @SystemApi - @TestApi public static final String TELEPHONY_IMS_SERVICE = "telephony_ims"; /** diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 9cba7aab3c87..d859a3af73a2 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3619,7 +3619,9 @@ public class Intent implements Parcelable, Cloneable { * {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast. * @hide */ - @UnsupportedAppUsage + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + @SystemApi public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED"; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 0ae00fb68d84..93b90b410c1c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -545,9 +545,10 @@ public abstract class PackageManager { public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO; /** - * Internal flag used to indicate that a package is a hidden system app. + * Internal {@link PackageInfo} flag used to indicate that a package is a hidden system app. * @hide */ + @SystemApi public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS = 0x20000000; /** diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index 67fdda37ed3b..b0d0b4c16873 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -262,6 +262,15 @@ public class UsbManager { public static final String USB_FUNCTION_ACCESSORY = "accessory"; /** + * Name of the NCM USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + * + * {@hide} + */ + @SystemApi + public static final String USB_FUNCTION_NCM = "ncm"; + + /** * Name of extra for {@link #ACTION_USB_PORT_CHANGED} * containing the {@link UsbPort} object for the port. * @@ -367,8 +376,15 @@ public class UsbManager { */ public static final long FUNCTION_ADB = GadgetFunction.ADB; + /** + * Code for the ncm source usb function. + * {@hide} + */ + @SystemApi + public static final long FUNCTION_NCM = 1 << 10; + private static final long SETTABLE_FUNCTIONS = FUNCTION_MTP | FUNCTION_PTP | FUNCTION_RNDIS - | FUNCTION_MIDI; + | FUNCTION_MIDI | FUNCTION_NCM; private static final Map<String, Long> FUNCTION_NAME_TO_CODE = new HashMap<>(); @@ -380,6 +396,7 @@ public class UsbManager { FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ACCESSORY, FUNCTION_ACCESSORY); FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_AUDIO_SOURCE, FUNCTION_AUDIO_SOURCE); FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_ADB, FUNCTION_ADB); + FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_NCM, FUNCTION_NCM); } private final Context mContext; @@ -935,6 +952,9 @@ public class UsbManager { if ((functions & FUNCTION_AUDIO_SOURCE) != 0) { joiner.add(UsbManager.USB_FUNCTION_AUDIO_SOURCE); } + if ((functions & FUNCTION_NCM) != 0) { + joiner.add(UsbManager.USB_FUNCTION_NCM); + } if ((functions & FUNCTION_ADB) != 0) { joiner.add(UsbManager.USB_FUNCTION_ADB); } 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..b13e4b72aa22 100644 --- a/core/java/android/net/ConnectivityDiagnosticsManager.java +++ b/core/java/android/net/ConnectivityDiagnosticsManager.java @@ -18,10 +18,20 @@ package android.net; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; +import android.content.Context; +import android.os.Binder; +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 +57,169 @@ 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 overall status of the network is that it is invalid; it neither provides + * connectivity nor has been exempted from validation. + */ + public static final int NETWORK_VALIDATION_RESULT_INVALID = 0; + + /** + * The overall status of the network is that it is valid, this may be because it provides + * full Internet access (all probes succeeded), or because other properties of the network + * caused probes not to be run. + */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID + public static final int NETWORK_VALIDATION_RESULT_VALID = 1; + + /** + * The overall status of the network is that it provides partial connectivity; some + * probed services succeeded but others failed. + */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; + public static final int NETWORK_VALIDATION_RESULT_PARTIALLY_VALID = 2; + + /** + * Due to the properties of the network, validation was not performed. + */ + public static final int NETWORK_VALIDATION_RESULT_SKIPPED = 3; + + /** @hide */ + @IntDef( + prefix = {"NETWORK_VALIDATION_RESULT_"}, + value = { + NETWORK_VALIDATION_RESULT_INVALID, + NETWORK_VALIDATION_RESULT_VALID, + NETWORK_VALIDATION_RESULT_PARTIALLY_VALID, + NETWORK_VALIDATION_RESULT_SKIPPED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NetworkValidationResult {} + + /** + * The overall validation result for the Network being reported on. + * + * <p>The possible values for this key are: + * {@link #NETWORK_VALIDATION_RESULT_INVALID}, + * {@link #NETWORK_VALIDATION_RESULT_VALID}, + * {@link #NETWORK_VALIDATION_RESULT_PARTIALLY_VALID}, + * {@link #NETWORK_VALIDATION_RESULT_SKIPPED}. + * + * @see android.net.NetworkCapabilities#CAPABILITY_VALIDATED + */ + @NetworkValidationResult + public static final String KEY_NETWORK_VALIDATION_RESULT = "networkValidationResult"; + + /** DNS probe. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS + public static final int NETWORK_PROBE_DNS = 0x04; + + /** HTTP probe. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP + public static final int NETWORK_PROBE_HTTP = 0x08; + + /** HTTPS probe. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; + public static final int NETWORK_PROBE_HTTPS = 0x10; + + /** Captive portal fallback probe. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_FALLBACK + public static final int NETWORK_PROBE_FALLBACK = 0x20; + + /** Private DNS (DNS over TLS) probd. */ + // TODO: link to INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS + public static final int NETWORK_PROBE_PRIVATE_DNS = 0x40; + + /** @hide */ + @IntDef( + prefix = {"NETWORK_PROBE_"}, + value = { + NETWORK_PROBE_DNS, + NETWORK_PROBE_HTTP, + NETWORK_PROBE_HTTPS, + NETWORK_PROBE_FALLBACK, + NETWORK_PROBE_PRIVATE_DNS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NetworkProbe {} + + /** + * A bitmask of network validation probes that succeeded. + * + * <p>The possible bits values reported by this key are: + * {@link #NETWORK_PROBE_DNS}, + * {@link #NETWORK_PROBE_HTTP}, + * {@link #NETWORK_PROBE_HTTPS}, + * {@link #NETWORK_PROBE_FALLBACK}, + * {@link #NETWORK_PROBE_PRIVATE_DNS}. + */ + @NetworkProbe + public static final String KEY_NETWORK_PROBES_SUCCEEDED_BITMASK = + "networkProbesSucceeded"; + + /** + * A bitmask of network validation probes that were attempted. + * + * <p>These probes may have failed or may be incomplete at the time of this report. + * + * <p>The possible bits values reported by this key are: + * {@link #NETWORK_PROBE_DNS}, + * {@link #NETWORK_PROBE_HTTP}, + * {@link #NETWORK_PROBE_HTTPS}, + * {@link #NETWORK_PROBE_FALLBACK}, + * {@link #NETWORK_PROBE_PRIVATE_DNS}. + */ + @NetworkProbe + public static final String KEY_NETWORK_PROBES_ATTEMPTED_BITMASK = + "networkProbesAttemped"; + + /** @hide */ + @StringDef(prefix = {"KEY_"}, value = { + KEY_NETWORK_VALIDATION_RESULT, KEY_NETWORK_PROBES_SUCCEEDED_BITMASK, + KEY_NETWORK_PROBES_ATTEMPTED_BITMASK}) + @Retention(RetentionPolicy.SOURCE) + public @interface ConnectivityReportBundleKeys {} + /** The Network for which this ConnectivityReport applied */ - @NonNull public final Network network; + @NonNull private final Network mNetwork; /** * The timestamp for the report. The timestamp is taken from {@link * System#currentTimeMillis}. */ - public final long reportTimestamp; + private final long mReportTimestamp; /** LinkProperties available on the Network at the reported timestamp */ - @NonNull public final LinkProperties linkProperties; + @NonNull private final LinkProperties mLinkProperties; /** NetworkCapabilities available on the Network at the reported timestamp */ - @NonNull public final NetworkCapabilities networkCapabilities; + @NonNull private final NetworkCapabilities mNetworkCapabilities; /** PersistableBundle that may contain additional info about the report */ - @NonNull public final PersistableBundle additionalInfo; + @NonNull private final PersistableBundle mAdditionalInfo; /** * Constructor for ConnectivityReport. @@ -101,30 +242,191 @@ 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<ConnectivityReport>() { + public ConnectivityReport createFromParcel(Parcel in) { + return new ConnectivityReport( + in.readParcelable(null), + in.readLong(), + in.readParcelable(null), + in.readParcelable(null), + in.readParcelable(null)); + } + + public ConnectivityReport[] newArray(int size) { + return new ConnectivityReport[size]; + } + }; } /** Class that includes information for a suspected data stall on a specific Network */ - public static class DataStallReport { + public static final class DataStallReport implements Parcelable { + public static final int DETECTION_METHOD_DNS_EVENTS = 1; + public static final int DETECTION_METHOD_TCP_METRICS = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef( + prefix = {"DETECTION_METHOD_"}, + value = {DETECTION_METHOD_DNS_EVENTS, DETECTION_METHOD_TCP_METRICS}) + public @interface DetectionMethod {} + + /** + * This key represents the period in milliseconds over which other included TCP metrics + * were measured. + * + * <p>This key will be included if the data stall detection method is + * {@link #DETECTION_METHOD_TCP_METRICS}. + * + * <p>This value is an int. + */ + public static final String KEY_TCP_METRICS_COLLECTION_PERIOD_MILLIS = + "tcpMetricsCollectionPeriodMillis"; + + /** + * This key represents the fail rate of TCP packets when the suspected data stall was + * detected. + * + * <p>This key will be included if the data stall detection method is + * {@link #DETECTION_METHOD_TCP_METRICS}. + * + * <p>This value is an int percentage between 0 and 100. + */ + public static final String KEY_TCP_PACKET_FAIL_RATE = "tcpPacketFailRate"; + + /** + * This key represents the consecutive number of DNS timeouts that have occurred. + * + * <p>The consecutive count will be reset any time a DNS response is received. + * + * <p>This key will be included if the data stall detection method is + * {@link #DETECTION_METHOD_DNS_EVENTS}. + * + * <p>This value is an int. + */ + public static final String KEY_DNS_CONSECUTIVE_TIMEOUTS = "dnsConsecutiveTimeouts"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = {"KEY_"}, value = { + KEY_TCP_PACKET_FAIL_RATE, + KEY_DNS_CONSECUTIVE_TIMEOUTS + }) + public @interface DataStallReportBundleKeys {} + /** The Network for which this DataStallReport applied */ - @NonNull public final Network network; + @NonNull private final Network mNetwork; /** * The timestamp for the report. The timestamp is taken from {@link * System#currentTimeMillis}. */ - public final long reportTimestamp; + private long mReportTimestamp; /** The detection method used to identify the suspected data stall */ - @DetectionMethod public final int detectionMethod; + @DetectionMethod private final int mDetectionMethod; /** PersistableBundle that may contain additional information on the suspected data stall */ - @NonNull public final PersistableBundle stallDetails; + @NonNull private final PersistableBundle mStallDetails; /** * Constructor for DataStallReport. @@ -143,10 +445,150 @@ 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. + * + * <p>Gets a bundle with details about the suspected data stall including information + * specific to the monitoring method that detected the data stall. + * + * @return PersistableBundle that may contain additional information on the suspected data + * stall + */ + @NonNull + public PersistableBundle getStallDetails() { + return new PersistableBundle(mStallDetails); + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (!(o instanceof DataStallReport)) return false; + final DataStallReport that = (DataStallReport) o; + + // PersistableBundle is optimized to avoid unparcelling data unless fields are + // referenced. Because of this, use {@link ConnectivityDiagnosticsManager#equals} over + // {@link PersistableBundle#kindofEquals}. + return mReportTimestamp == that.mReportTimestamp + && mDetectionMethod == that.mDetectionMethod + && mNetwork.equals(that.mNetwork) + && persistableBundleEquals(mStallDetails, that.mStallDetails); + } + + @Override + public int hashCode() { + return Objects.hash(mNetwork, mReportTimestamp, mDetectionMethod, mStallDetails); + } + + /** {@inheritDoc} */ + @Override + public int describeContents() { + return 0; + } + + /** {@inheritDoc} */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mNetwork, flags); + dest.writeLong(mReportTimestamp); + dest.writeInt(mDetectionMethod); + dest.writeParcelable(mStallDetails, flags); + } + + /** Implement the Parcelable interface */ + public static final @NonNull Creator<DataStallReport> CREATOR = + new Creator<DataStallReport>() { + public DataStallReport createFromParcel(Parcel in) { + return new DataStallReport( + in.readParcelable(null), + in.readLong(), + in.readInt(), + in.readParcelable(null)); + } + + public DataStallReport[] newArray(int size) { + return new DataStallReport[size]; + } + }; + } + + /** @hide */ + @VisibleForTesting + public static class ConnectivityDiagnosticsBinder + extends IConnectivityDiagnosticsCallback.Stub { + @NonNull private final ConnectivityDiagnosticsCallback mCb; + @NonNull private final Executor mExecutor; + + /** @hide */ + @VisibleForTesting + public ConnectivityDiagnosticsBinder( + @NonNull ConnectivityDiagnosticsCallback cb, @NonNull Executor executor) { + this.mCb = cb; + this.mExecutor = executor; + } + + /** @hide */ + @VisibleForTesting + public void onConnectivityReport(@NonNull ConnectivityReport report) { + Binder.withCleanCallingIdentity(() -> { + mExecutor.execute(() -> { + mCb.onConnectivityReport(report); + }); + }); + } + + /** @hide */ + @VisibleForTesting + public void onDataStallSuspected(@NonNull DataStallReport report) { + Binder.withCleanCallingIdentity(() -> { + mExecutor.execute(() -> { + mCb.onDataStallSuspected(report); + }); + }); + } + + /** @hide */ + @VisibleForTesting + public void onNetworkConnectivityReported( + @NonNull Network network, boolean hasConnectivity) { + Binder.withCleanCallingIdentity(() -> { + mExecutor.execute(() -> { + mCb.onNetworkConnectivityReported(network, hasConnectivity); + }); + }); } } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 11c1a9c32d8a..94eda01410da 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -33,6 +33,9 @@ import android.content.Context; import android.content.Intent; import android.net.IpSecManager.UdpEncapsulationSocket; import android.net.SocketKeepalive.Callback; +import android.net.TetheringManager.StartTetheringCallback; +import android.net.TetheringManager.TetheringEventCallback; +import android.net.TetheringManager.TetheringRequest; import android.os.Binder; import android.os.Build; import android.os.Build.VERSION_CODES; @@ -58,6 +61,7 @@ import android.util.ArrayMap; import android.util.Log; import android.util.SparseIntArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.internal.util.Protocol; @@ -75,6 +79,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -484,34 +489,35 @@ public class ConnectivityManager { * enable if any. * @hide */ - public static final String EXTRA_ADD_TETHER_TYPE = TetheringManager.EXTRA_ADD_TETHER_TYPE; + public static final String EXTRA_ADD_TETHER_TYPE = TetheringConstants.EXTRA_ADD_TETHER_TYPE; /** * Extra used for communicating with the TetherService. Includes the type of tethering for * which to cancel provisioning. * @hide */ - public static final String EXTRA_REM_TETHER_TYPE = TetheringManager.EXTRA_REM_TETHER_TYPE; + public static final String EXTRA_REM_TETHER_TYPE = TetheringConstants.EXTRA_REM_TETHER_TYPE; /** * Extra used for communicating with the TetherService. True to schedule a recheck of tether * provisioning. * @hide */ - public static final String EXTRA_SET_ALARM = TetheringManager.EXTRA_SET_ALARM; + public static final String EXTRA_SET_ALARM = TetheringConstants.EXTRA_SET_ALARM; /** * Tells the TetherService to run a provision check now. * @hide */ - public static final String EXTRA_RUN_PROVISION = TetheringManager.EXTRA_RUN_PROVISION; + public static final String EXTRA_RUN_PROVISION = TetheringConstants.EXTRA_RUN_PROVISION; /** * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} * which will receive provisioning results. Can be left empty. * @hide */ - public static final String EXTRA_PROVISION_CALLBACK = TetheringManager.EXTRA_PROVISION_CALLBACK; + public static final String EXTRA_PROVISION_CALLBACK = + TetheringConstants.EXTRA_PROVISION_CALLBACK; /** * The absence of a connection type. @@ -2369,10 +2375,12 @@ public class ConnectivityManager { * * @return an array of 0 or more Strings of tetherable interface names. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfacesChanged(List)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetherableIfaces() { return getTetheringManager().getTetherableIfaces(); } @@ -2382,10 +2390,12 @@ public class ConnectivityManager { * * @return an array of 0 or more String of currently tethered interface names. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfacesChanged(List)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetheredIfaces() { return getTetheringManager().getTetheredIfaces(); } @@ -2401,10 +2411,12 @@ public class ConnectivityManager { * @return an array of 0 or more String indicating the interface names * which failed to tether. * + * @deprecated Use {@link TetheringEventCallback#onError(String, int)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetheringErroredIfaces() { return getTetheringManager().getTetheringErroredIfaces(); } @@ -2413,9 +2425,11 @@ public class ConnectivityManager { * Get the set of tethered dhcp ranges. * * @return an array of 0 or more {@code String} of tethered dhcp ranges. + * @deprecated This API just return the default value which is not used in DhcpServer. * {@hide} */ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) + @Deprecated public String[] getTetheredDhcpRanges() { return getTetheringManager().getTetheredDhcpRanges(); } @@ -2441,10 +2455,12 @@ public class ConnectivityManager { * * @param iface the interface name to tether. * @return error a {@code TETHER_ERROR} value indicating success or failure type + * @deprecated Use {@link TetheringManager#startTethering} instead * * {@hide} */ @UnsupportedAppUsage + @Deprecated public int tether(String iface) { return getTetheringManager().tether(iface); } @@ -2468,6 +2484,7 @@ public class ConnectivityManager { * {@hide} */ @UnsupportedAppUsage + @Deprecated public int untether(String iface) { return getTetheringManager().untether(iface); } @@ -2488,6 +2505,7 @@ public class ConnectivityManager { * * @return a boolean - {@code true} indicating Tethering is supported. * + * @deprecated Use {@link TetheringEventCallback#onTetheringSupported(boolean)} instead. * {@hide} */ @SystemApi @@ -2499,9 +2517,12 @@ public class ConnectivityManager { /** * Callback for use with {@link #startTethering} to find out whether tethering succeeded. + * + * @deprecated Use {@link TetheringManager.StartTetheringCallback} instead. * @hide */ @SystemApi + @Deprecated public static abstract class OnStartTetheringCallback { /** * Called when tethering has been successfully started. @@ -2518,9 +2539,12 @@ public class ConnectivityManager { * Convenient overload for * {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null * handler to run on the current thread's {@link Looper}. + * + * @deprecated Use {@link TetheringManager#startTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback) { @@ -2544,26 +2568,44 @@ public class ConnectivityManager { * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller * of the result of trying to tether. * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * + * @deprecated Use {@link TetheringManager#startTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback, Handler handler) { Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null."); - ResultReceiver wrappedCallback = new ResultReceiver(handler) { + final Executor executor = new Executor() { @Override - protected void onReceiveResult(int resultCode, Bundle resultData) { - if (resultCode == TETHER_ERROR_NO_ERROR) { - callback.onTetheringStarted(); + public void execute(Runnable command) { + if (handler == null) { + command.run(); } else { - callback.onTetheringFailed(); + handler.post(command); } } }; - getTetheringManager().startTethering(type, wrappedCallback, showProvisioningUi); + final StartTetheringCallback tetheringCallback = new StartTetheringCallback() { + @Override + public void onTetheringStarted() { + callback.onTetheringStarted(); + } + + @Override + public void onTetheringFailed(final int resultCode) { + callback.onTetheringFailed(); + } + }; + + final TetheringRequest request = new TetheringRequest.Builder(type) + .setSilentProvisioning(!showProvisioningUi).build(); + + getTetheringManager().startTethering(request, executor, tetheringCallback); } /** @@ -2574,9 +2616,12 @@ public class ConnectivityManager { * {@link ConnectivityManager.TETHERING_WIFI}, * {@link ConnectivityManager.TETHERING_USB}, or * {@link ConnectivityManager.TETHERING_BLUETOOTH}. + * + * @deprecated Use {@link TetheringManager#stopTethering} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int type) { getTetheringManager().stopTethering(type); @@ -2586,9 +2631,11 @@ public class ConnectivityManager { * Callback for use with {@link registerTetheringEventCallback} to find out tethering * upstream status. * - *@hide + * @deprecated Use {@link TetheringManager#OnTetheringEventCallback} instead. + * @hide */ @SystemApi + @Deprecated public abstract static class OnTetheringEventCallback { /** @@ -2601,6 +2648,10 @@ public class ConnectivityManager { public void onUpstreamChanged(@Nullable Network network) {} } + @GuardedBy("mTetheringEventCallbacks") + private final ArrayMap<OnTetheringEventCallback, TetheringEventCallback> + mTetheringEventCallbacks = new ArrayMap<>(); + /** * Start listening to tethering change events. Any new added callback will receive the last * tethering status right away. If callback is registered when tethering has no upstream or @@ -2609,16 +2660,30 @@ public class ConnectivityManager { * * @param executor the executor on which callback will be invoked. * @param callback the callback to be called when tethering has change events. + * + * @deprecated Use {@link TetheringManager#registerTetheringEventCallback} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback( @NonNull @CallbackExecutor Executor executor, @NonNull final OnTetheringEventCallback callback) { Preconditions.checkNotNull(callback, "OnTetheringEventCallback cannot be null."); - getTetheringManager().registerTetheringEventCallback(executor, callback); + final TetheringEventCallback tetherCallback = + new TetheringEventCallback() { + @Override + public void onUpstreamChanged(@Nullable Network network) { + callback.onUpstreamChanged(network); + } + }; + + synchronized (mTetheringEventCallbacks) { + mTetheringEventCallbacks.put(callback, tetherCallback); + getTetheringManager().registerTetheringEventCallback(executor, tetherCallback); + } } /** @@ -2626,13 +2691,21 @@ public class ConnectivityManager { * {@link #registerTetheringEventCallback}. * * @param callback previously registered callback. + * + * @deprecated Use {@link TetheringManager#unregisterTetheringEventCallback} instead. * @hide */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback( @NonNull final OnTetheringEventCallback callback) { - getTetheringManager().unregisterTetheringEventCallback(callback); + Objects.requireNonNull(callback, "The callback must be non-null"); + synchronized (mTetheringEventCallbacks) { + final TetheringEventCallback tetherCallback = + mTetheringEventCallbacks.remove(callback); + getTetheringManager().unregisterTetheringEventCallback(tetherCallback); + } } @@ -2644,10 +2717,12 @@ public class ConnectivityManager { * @return an array of 0 or more regular expression Strings defining * what interfaces are considered tetherable usb interfaces. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetherableUsbRegexs() { return getTetheringManager().getTetherableUsbRegexs(); } @@ -2660,10 +2735,12 @@ public class ConnectivityManager { * @return an array of 0 or more regular expression Strings defining * what interfaces are considered tetherable wifi interfaces. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetherableWifiRegexs() { return getTetheringManager().getTetherableWifiRegexs(); } @@ -2676,10 +2753,13 @@ public class ConnectivityManager { * @return an array of 0 or more regular expression Strings defining * what interfaces are considered tetherable bluetooth interfaces. * + * @deprecated Use {@link TetheringEventCallback#onTetherableInterfaceRegexpsChanged( + *TetheringManager.TetheringInterfaceRegexps)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public String[] getTetherableBluetoothRegexs() { return getTetheringManager().getTetherableBluetoothRegexs(); } @@ -2698,45 +2778,114 @@ public class ConnectivityManager { * * @param enable a boolean - {@code true} to enable tethering * @return error a {@code TETHER_ERROR} value indicating success or failure type + * @deprecated Use {@link TetheringManager#startTethering} instead * * {@hide} */ @UnsupportedAppUsage + @Deprecated public int setUsbTethering(boolean enable) { return getTetheringManager().setUsbTethering(enable); } - /** {@hide} */ + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_NO_ERROR}. + * {@hide} + */ @SystemApi - public static final int TETHER_ERROR_NO_ERROR = 0; - /** {@hide} */ - public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; - /** {@hide} */ - public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; - /** {@hide} */ - public static final int TETHER_ERROR_UNSUPPORTED = 3; - /** {@hide} */ - public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; - /** {@hide} */ - public static final int TETHER_ERROR_MASTER_ERROR = 5; - /** {@hide} */ - public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; - /** {@hide} */ - public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; - /** {@hide} */ - public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; - /** {@hide} */ - public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; - /** {@hide} */ - public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; - /** {@hide} */ + @Deprecated + public static final int TETHER_ERROR_NO_ERROR = TetheringManager.TETHER_ERROR_NO_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNKNOWN_IFACE}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNKNOWN_IFACE = + TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_SERVICE_UNAVAIL}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_SERVICE_UNAVAIL = + TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNSUPPORTED}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNSUPPORTED = TetheringManager.TETHER_ERROR_UNSUPPORTED; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNAVAIL_IFACE}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNAVAIL_IFACE = + TetheringManager.TETHER_ERROR_UNAVAIL_IFACE; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_MASTER_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_MASTER_ERROR = TetheringManager.TETHER_ERROR_MASTER_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_TETHER_IFACE_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_TETHER_IFACE_ERROR = + TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_UNTETHER_IFACE_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = + TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENABLE_NAT_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_ENABLE_NAT_ERROR = + TetheringManager.TETHER_ERROR_ENABLE_NAT_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_DISABLE_NAT_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_DISABLE_NAT_ERROR = + TetheringManager.TETHER_ERROR_DISABLE_NAT_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_IFACE_CFG_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_IFACE_CFG_ERROR = + TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_PROVISION_FAILED}. + * {@hide} + */ @SystemApi - public static final int TETHER_ERROR_PROVISION_FAILED = 11; - /** {@hide} */ - public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; - /** {@hide} */ + @Deprecated + public static final int TETHER_ERROR_PROVISION_FAILED = + TetheringManager.TETHER_ERROR_PROVISION_FAILED; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_DHCPSERVER_ERROR}. + * {@hide} + */ + @Deprecated + public static final int TETHER_ERROR_DHCPSERVER_ERROR = + TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR; + /** + * @deprecated Use {@link TetheringManager#TETHER_ERROR_ENTITLEMENT_UNKNOWN}. + * {@hide} + */ @SystemApi - public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; + @Deprecated + public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = + TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; /** * Get a more detailed error code after a Tethering or Untethering @@ -2746,10 +2895,12 @@ public class ConnectivityManager { * @return error The error code of the last error tethering or untethering the named * interface * + * @deprecated Use {@link TetheringEventCallback#onError(String, int)} instead. * {@hide} */ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) @UnsupportedAppUsage + @Deprecated public int getLastTetherError(String iface) { return getTetheringManager().getLastTetherError(iface); } @@ -2767,9 +2918,12 @@ public class ConnectivityManager { /** * Callback for use with {@link #getLatestTetheringEntitlementResult} to find out whether * entitlement succeeded. + * + * @deprecated Use {@link TetheringManager#OnTetheringEntitlementResultListener} instead. * @hide */ @SystemApi + @Deprecated public interface OnTetheringEntitlementResultListener { /** * Called to notify entitlement result. @@ -2799,9 +2953,11 @@ public class ConnectivityManager { * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to * notify the caller of the result of entitlement check. The listener may be called zero * or one time. + * @deprecated Use {@link TetheringManager#requestLatestTetheringEntitlementResult} instead. * {@hide} */ @SystemApi + @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int type, boolean showEntitlementUi, @NonNull @CallbackExecutor Executor executor, diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java index fd015b4fe52c..a3899b705c1b 100644 --- a/core/java/android/net/EthernetManager.java +++ b/core/java/android/net/EthernetManager.java @@ -16,7 +16,10 @@ package android.net; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Handler; @@ -24,12 +27,15 @@ import android.os.Message; import android.os.RemoteException; import java.util.ArrayList; +import java.util.Objects; /** * A class representing the IP configuration of the Ethernet network. * * @hide */ +@SystemApi +@TestApi @SystemService(Context.ETHERNET_SERVICE) public class EthernetManager { private static final String TAG = "EthernetManager"; @@ -37,7 +43,7 @@ public class EthernetManager { private final Context mContext; private final IEthernetManager mService; - private final Handler mHandler = new Handler() { + private final Handler mHandler = new Handler(ConnectivityThread.getInstanceLooper()) { @Override public void handleMessage(Message msg) { if (msg.what == MSG_AVAILABILITY_CHANGED) { @@ -60,12 +66,14 @@ public class EthernetManager { /** * A listener interface to receive notification on changes in Ethernet. + * @hide */ public interface Listener { /** * Called when Ethernet port's availability is changed. * @param iface Ethernet interface name * @param isAvailable {@code true} if Ethernet port exists. + * @hide */ @UnsupportedAppUsage void onAvailabilityChanged(String iface, boolean isAvailable); @@ -76,6 +84,7 @@ public class EthernetManager { * Applications will almost always want to use * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve * the standard {@link android.content.Context#ETHERNET_SERVICE Context.ETHERNET_SERVICE}. + * @hide */ public EthernetManager(Context context, IEthernetManager service) { mContext = context; @@ -85,6 +94,7 @@ public class EthernetManager { /** * Get Ethernet configuration. * @return the Ethernet Configuration, contained in {@link IpConfiguration}. + * @hide */ @UnsupportedAppUsage public IpConfiguration getConfiguration(String iface) { @@ -97,6 +107,7 @@ public class EthernetManager { /** * Set Ethernet configuration. + * @hide */ @UnsupportedAppUsage public void setConfiguration(String iface, IpConfiguration config) { @@ -109,6 +120,7 @@ public class EthernetManager { /** * Indicates whether the system currently has one or more Ethernet interfaces. + * @hide */ @UnsupportedAppUsage public boolean isAvailable() { @@ -119,6 +131,7 @@ public class EthernetManager { * Indicates whether the system has given interface. * * @param iface Ethernet interface name + * @hide */ @UnsupportedAppUsage public boolean isAvailable(String iface) { @@ -133,6 +146,7 @@ public class EthernetManager { * Adds a listener. * @param listener A {@link Listener} to add. * @throws IllegalArgumentException If the listener is null. + * @hide */ @UnsupportedAppUsage public void addListener(Listener listener) { @@ -151,6 +165,7 @@ public class EthernetManager { /** * Returns an array of available Ethernet interface names. + * @hide */ @UnsupportedAppUsage public String[] getAvailableInterfaces() { @@ -165,6 +180,7 @@ public class EthernetManager { * Removes a listener. * @param listener A {@link Listener} to remove. * @throws IllegalArgumentException If the listener is null. + * @hide */ @UnsupportedAppUsage public void removeListener(Listener listener) { @@ -180,4 +196,78 @@ public class EthernetManager { } } } + + /** + * A request for a tethered interface. + */ + public static class TetheredInterfaceRequest { + private final IEthernetManager mService; + private final ITetheredInterfaceCallback mCb; + + private TetheredInterfaceRequest(@NonNull IEthernetManager service, + @NonNull ITetheredInterfaceCallback cb) { + this.mService = service; + this.mCb = cb; + } + + /** + * Release the request, causing the interface to revert back from tethering mode if there + * is no other requestor. + */ + public void release() { + try { + mService.releaseTetheredInterface(mCb); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + } + + /** + * Callback for {@link #requestTetheredInterface(TetheredInterfaceCallback)}. + */ + public interface TetheredInterfaceCallback { + /** + * Called when the tethered interface is available. + * @param iface The name of the interface. + */ + void onAvailable(@NonNull String iface); + + /** + * Called when the tethered interface is now unavailable. + */ + void onUnavailable(); + } + + /** + * Request a tethered interface in tethering mode. + * + * <p>When this method is called and there is at least one ethernet interface available, the + * system will designate one to act as a tethered interface. If there is already a tethered + * interface, the existing interface will be used. + * @param callback A callback to be called once the request has been fulfilled. + */ + @NonNull + public TetheredInterfaceRequest requestTetheredInterface( + @NonNull TetheredInterfaceCallback callback) { + Objects.requireNonNull(callback, "Callback must be non-null"); + final ITetheredInterfaceCallback cbInternal = new ITetheredInterfaceCallback.Stub() { + @Override + public void onAvailable(String iface) { + callback.onAvailable(iface); + } + + @Override + public void onUnavailable() { + callback.onUnavailable(); + } + }; + + try { + mService.requestTetheredInterface(cbInternal); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + return new TetheredInterfaceRequest(mService, cbInternal); + } } diff --git a/core/java/android/net/IConnectivityDiagnosticsCallback.aidl b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl new file mode 100644 index 000000000000..3a161bfabfd2 --- /dev/null +++ b/core/java/android/net/IConnectivityDiagnosticsCallback.aidl @@ -0,0 +1,28 @@ +/** + * + * 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.net.ConnectivityDiagnosticsManager; +import android.net.Network; + +/** @hide */ +oneway interface IConnectivityDiagnosticsCallback { + void onConnectivityReport(in ConnectivityDiagnosticsManager.ConnectivityReport report); + void onDataStallSuspected(in ConnectivityDiagnosticsManager.DataStallReport report); + void onNetworkConnectivityReported(in Network n, boolean hasConnectivity); +}
\ No newline at end of file diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 186196bd31c7..3e9e7faccb02 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -18,6 +18,7 @@ package android.net; import android.app.PendingIntent; import android.net.ConnectionInfo; +import android.net.IConnectivityDiagnosticsCallback; import android.net.LinkProperties; import android.net.Network; import android.net.NetworkAgentConfig; @@ -211,5 +212,9 @@ interface IConnectivityManager boolean isCallerCurrentAlwaysOnVpnApp(); boolean isCallerCurrentAlwaysOnVpnLockdownApp(); + void registerConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback, + in NetworkRequest request); + void unregisterConnectivityDiagnosticsCallback(in IConnectivityDiagnosticsCallback callback); + IBinder startOrGetTestNetworkService(); } diff --git a/core/java/android/net/IEthernetManager.aidl b/core/java/android/net/IEthernetManager.aidl index 94960b51d329..ccc6e352098f 100644 --- a/core/java/android/net/IEthernetManager.aidl +++ b/core/java/android/net/IEthernetManager.aidl @@ -18,6 +18,7 @@ package android.net; import android.net.IpConfiguration; import android.net.IEthernetServiceListener; +import android.net.ITetheredInterfaceCallback; /** * Interface that answers queries about, and allows changing @@ -32,4 +33,6 @@ interface IEthernetManager boolean isAvailable(String iface); void addListener(in IEthernetServiceListener listener); void removeListener(in IEthernetServiceListener listener); + void requestTetheredInterface(in ITetheredInterfaceCallback callback); + void releaseTetheredInterface(in ITetheredInterfaceCallback callback); } diff --git a/core/java/android/net/ITetheredInterfaceCallback.aidl b/core/java/android/net/ITetheredInterfaceCallback.aidl new file mode 100644 index 000000000000..e3d075988c8a --- /dev/null +++ b/core/java/android/net/ITetheredInterfaceCallback.aidl @@ -0,0 +1,23 @@ +/* + * 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; + +/** @hide */ +interface ITetheredInterfaceCallback { + void onAvailable(in String iface); + void onUnavailable(); +}
\ No newline at end of file diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java new file mode 100644 index 000000000000..42b4da14d879 --- /dev/null +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -0,0 +1,728 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK; +import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA; +import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; + +import static com.android.internal.annotations.VisibleForTesting.Visibility; +import static com.android.internal.util.Preconditions.checkStringNotEmpty; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.security.Credentials; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.net.VpnProfile; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +/** + * The Ikev2VpnProfile is a configuration for the platform setup of IKEv2/IPsec VPNs. + * + * <p>Together with VpnManager, this allows apps to provision IKEv2/IPsec VPNs that do not require + * the VPN app to constantly run in the background. + * + * @see VpnManager + * @see <a href="https://tools.ietf.org/html/rfc7296#section-3.3.2">RFC 7296 - Internet Key + * Exchange, Version 2 (IKEv2)</a> + */ +public final class Ikev2VpnProfile extends PlatformVpnProfile { + private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s"; + private static final String EMPTY_CERT = ""; + + @NonNull private final String mServerAddr; + @NonNull private final String mUserIdentity; + + // PSK authentication + @Nullable private final byte[] mPresharedKey; + + // Username/Password, RSA authentication + @Nullable private final X509Certificate mServerRootCaCert; + + // Username/Password authentication + @Nullable private final String mUsername; + @Nullable private final String mPassword; + + // RSA Certificate authentication + @Nullable private final PrivateKey mRsaPrivateKey; + @Nullable private final X509Certificate mUserCert; + + @Nullable private final ProxyInfo mProxyInfo; + @NonNull private final List<String> mAllowedAlgorithms; + private final boolean mIsBypassable; // Defaults in builder + private final boolean mIsMetered; // Defaults in builder + private final int mMaxMtu; // Defaults in builder + + private Ikev2VpnProfile( + int type, + @NonNull String serverAddr, + @NonNull String userIdentity, + @Nullable byte[] presharedKey, + @Nullable X509Certificate serverRootCaCert, + @Nullable String username, + @Nullable String password, + @Nullable PrivateKey rsaPrivateKey, + @Nullable X509Certificate userCert, + @Nullable ProxyInfo proxyInfo, + @NonNull List<String> allowedAlgorithms, + boolean isBypassable, + boolean isMetered, + int maxMtu) { + super(type); + + checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "Server address"); + checkNotNull(userIdentity, MISSING_PARAM_MSG_TMPL, "User Identity"); + checkNotNull(allowedAlgorithms, MISSING_PARAM_MSG_TMPL, "Allowed Algorithms"); + + mServerAddr = serverAddr; + mUserIdentity = userIdentity; + mPresharedKey = + presharedKey == null ? null : Arrays.copyOf(presharedKey, presharedKey.length); + mServerRootCaCert = serverRootCaCert; + mUsername = username; + mPassword = password; + mRsaPrivateKey = rsaPrivateKey; + mUserCert = userCert; + mProxyInfo = new ProxyInfo(proxyInfo); + + // UnmodifiableList doesn't make a defensive copy by default. + mAllowedAlgorithms = Collections.unmodifiableList(new ArrayList<>(allowedAlgorithms)); + + mIsBypassable = isBypassable; + mIsMetered = isMetered; + mMaxMtu = maxMtu; + + validate(); + } + + private void validate() { + // Server Address not validated except to check an address was provided. This allows for + // dual-stack servers and hostname based addresses. + checkStringNotEmpty(mServerAddr, MISSING_PARAM_MSG_TMPL, "Server Address"); + checkStringNotEmpty(mUserIdentity, MISSING_PARAM_MSG_TMPL, "User Identity"); + + // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6 + // networks, the VPN must provide a link fulfilling the stricter of the two conditions + // (at least that of the IPv6 MTU). + if (mMaxMtu < LinkProperties.MIN_MTU_V6) { + throw new IllegalArgumentException( + "Max MTU must be at least" + LinkProperties.MIN_MTU_V6); + } + + switch (mType) { + case TYPE_IKEV2_IPSEC_USER_PASS: + checkNotNull(mUsername, MISSING_PARAM_MSG_TMPL, "Username"); + checkNotNull(mPassword, MISSING_PARAM_MSG_TMPL, "Password"); + + if (mServerRootCaCert != null) checkCert(mServerRootCaCert); + + break; + case TYPE_IKEV2_IPSEC_PSK: + checkNotNull(mPresharedKey, MISSING_PARAM_MSG_TMPL, "Preshared Key"); + break; + case TYPE_IKEV2_IPSEC_RSA: + checkNotNull(mUserCert, MISSING_PARAM_MSG_TMPL, "User cert"); + checkNotNull(mRsaPrivateKey, MISSING_PARAM_MSG_TMPL, "RSA Private key"); + + checkCert(mUserCert); + if (mServerRootCaCert != null) checkCert(mServerRootCaCert); + + break; + default: + throw new IllegalArgumentException("Invalid auth method set"); + } + + VpnProfile.validateAllowedAlgorithms(mAllowedAlgorithms); + } + + /** Retrieves the server address string. */ + @NonNull + public String getServerAddr() { + return mServerAddr; + } + + /** Retrieves the user identity. */ + @NonNull + public String getUserIdentity() { + return mUserIdentity; + } + + /** + * Retrieves the pre-shared key. + * + * <p>May be null if the profile is not using Pre-shared key authentication. + */ + @Nullable + public byte[] getPresharedKey() { + return mPresharedKey == null ? null : Arrays.copyOf(mPresharedKey, mPresharedKey.length); + } + + /** + * Retrieves the certificate for the server's root CA. + * + * <p>May be null if the profile is not using RSA Digital Signature Authentication or + * Username/Password authentication + */ + @Nullable + public X509Certificate getServerRootCaCert() { + return mServerRootCaCert; + } + + /** + * Retrieves the username. + * + * <p>May be null if the profile is not using Username/Password authentication + */ + @Nullable + public String getUsername() { + return mUsername; + } + + /** + * Retrieves the password. + * + * <p>May be null if the profile is not using Username/Password authentication + */ + @Nullable + public String getPassword() { + return mPassword; + } + + /** + * Retrieves the RSA private key. + * + * <p>May be null if the profile is not using RSA Digital Signature authentication + */ + @Nullable + public PrivateKey getRsaPrivateKey() { + return mRsaPrivateKey; + } + + /** Retrieves the user certificate, if any was set. */ + @Nullable + public X509Certificate getUserCert() { + return mUserCert; + } + + /** Retrieves the proxy information if any was set */ + @Nullable + public ProxyInfo getProxyInfo() { + return mProxyInfo; + } + + /** Returns all the algorithms allowed by this VPN profile. */ + @NonNull + public List<String> getAllowedAlgorithms() { + return mAllowedAlgorithms; + } + + /** Returns whether or not the VPN profile should be bypassable. */ + public boolean isBypassable() { + return mIsBypassable; + } + + /** Returns whether or not the VPN profile should be always considered metered. */ + public boolean isMetered() { + return mIsMetered; + } + + /** Retrieves the maximum MTU set for this VPN profile. */ + public int getMaxMtu() { + return mMaxMtu; + } + + @Override + public int hashCode() { + return Objects.hash( + mType, + mServerAddr, + mUserIdentity, + Arrays.hashCode(mPresharedKey), + mServerRootCaCert, + mUsername, + mPassword, + mRsaPrivateKey, + mUserCert, + mProxyInfo, + mAllowedAlgorithms, + mIsBypassable, + mIsMetered, + mMaxMtu); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof Ikev2VpnProfile)) { + return false; + } + + final Ikev2VpnProfile other = (Ikev2VpnProfile) obj; + return mType == other.mType + && Objects.equals(mServerAddr, other.mServerAddr) + && Objects.equals(mUserIdentity, other.mUserIdentity) + && Arrays.equals(mPresharedKey, other.mPresharedKey) + && Objects.equals(mServerRootCaCert, other.mServerRootCaCert) + && Objects.equals(mUsername, other.mUsername) + && Objects.equals(mPassword, other.mPassword) + && Objects.equals(mRsaPrivateKey, other.mRsaPrivateKey) + && Objects.equals(mUserCert, other.mUserCert) + && Objects.equals(mProxyInfo, other.mProxyInfo) + && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms) + && mIsBypassable == other.mIsBypassable + && mIsMetered == other.mIsMetered + && mMaxMtu == other.mMaxMtu; + } + + /** + * Builds a VpnProfile instance for internal use, based on the stored IKEv2/IPsec parameters. + * + * <p>Redundant authentication information (from previous calls to other setAuth* methods) will + * be discarded. + * + * @hide + */ + @NonNull + public VpnProfile toVpnProfile() throws IOException, GeneralSecurityException { + final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */); + profile.type = mType; + profile.server = mServerAddr; + profile.ipsecIdentifier = mUserIdentity; + profile.proxy = mProxyInfo; + profile.setAllowedAlgorithms(mAllowedAlgorithms); + profile.isBypassable = mIsBypassable; + profile.isMetered = mIsMetered; + profile.maxMtu = mMaxMtu; + profile.areAuthParamsInline = true; + profile.saveLogin = true; + + switch (mType) { + case TYPE_IKEV2_IPSEC_USER_PASS: + profile.username = mUsername; + profile.password = mPassword; + profile.ipsecCaCert = + mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert); + break; + case TYPE_IKEV2_IPSEC_PSK: + profile.ipsecSecret = encodeForIpsecSecret(mPresharedKey); + break; + case TYPE_IKEV2_IPSEC_RSA: + profile.ipsecUserCert = certificateToPemString(mUserCert); + profile.ipsecSecret = encodeForIpsecSecret(mRsaPrivateKey.getEncoded()); + profile.ipsecCaCert = + mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert); + break; + default: + throw new IllegalArgumentException("Invalid auth method set"); + } + + return profile; + } + + /** + * Constructs a Ikev2VpnProfile from an internal-use VpnProfile instance. + * + * <p>Redundant authentication information (not related to profile type) will be discarded. + * + * @hide + */ + @NonNull + public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile) + throws IOException, GeneralSecurityException { + final Builder builder = new Builder(profile.server, profile.ipsecIdentifier); + builder.setProxy(profile.proxy); + builder.setAllowedAlgorithms(profile.getAllowedAlgorithms()); + builder.setBypassable(profile.isBypassable); + builder.setMetered(profile.isMetered); + builder.setMaxMtu(profile.maxMtu); + + switch (profile.type) { + case TYPE_IKEV2_IPSEC_USER_PASS: + builder.setAuthUsernamePassword( + profile.username, + profile.password, + certificateFromPemString(profile.ipsecCaCert)); + break; + case TYPE_IKEV2_IPSEC_PSK: + builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret)); + break; + case TYPE_IKEV2_IPSEC_RSA: + final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert); + final PrivateKey key = getPrivateKey(profile.ipsecSecret); + final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert); + builder.setAuthDigitalSignature(userCert, key, serverRootCa); + break; + default: + throw new IllegalArgumentException("Invalid auth method set"); + } + + return builder.build(); + } + + /** + * Converts a X509 Certificate to a PEM-formatted string. + * + * <p>Must be public due to runtime-package restrictions. + * + * @hide + */ + @NonNull + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static String certificateToPemString(@Nullable X509Certificate cert) + throws IOException, CertificateEncodingException { + if (cert == null) { + return EMPTY_CERT; + } + + // Credentials.convertToPem outputs ASCII bytes. + return new String(Credentials.convertToPem(cert), StandardCharsets.US_ASCII); + } + + /** + * Decodes the provided Certificate(s). + * + * <p>Will use the first one if the certStr encodes more than one certificate. + */ + @Nullable + private static X509Certificate certificateFromPemString(@Nullable String certStr) + throws CertificateException { + if (certStr == null || EMPTY_CERT.equals(certStr)) { + return null; + } + + try { + final List<X509Certificate> certs = + Credentials.convertFromPem(certStr.getBytes(StandardCharsets.US_ASCII)); + return certs.isEmpty() ? null : certs.get(0); + } catch (IOException e) { + throw new CertificateException(e); + } + } + + /** @hide */ + @NonNull + @VisibleForTesting(visibility = Visibility.PRIVATE) + public static String encodeForIpsecSecret(@NonNull byte[] secret) { + checkNotNull(secret, MISSING_PARAM_MSG_TMPL, "secret"); + + return Base64.getEncoder().encodeToString(secret); + } + + @NonNull + private static byte[] decodeFromIpsecSecret(@NonNull String encoded) { + checkNotNull(encoded, MISSING_PARAM_MSG_TMPL, "encoded"); + + return Base64.getDecoder().decode(encoded); + } + + @NonNull + private static PrivateKey getPrivateKey(@NonNull String keyStr) + throws InvalidKeySpecException, NoSuchAlgorithmException { + final PKCS8EncodedKeySpec privateKeySpec = + new PKCS8EncodedKeySpec(decodeFromIpsecSecret(keyStr)); + final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + return keyFactory.generatePrivate(privateKeySpec); + } + + private static void checkCert(@NonNull X509Certificate cert) { + try { + certificateToPemString(cert); + } catch (GeneralSecurityException | IOException e) { + throw new IllegalArgumentException("Certificate could not be encoded"); + } + } + + private static @NonNull <T> T checkNotNull( + final T reference, final String messageTemplate, final Object... messageArgs) { + return Objects.requireNonNull(reference, String.format(messageTemplate, messageArgs)); + } + + /** A incremental builder for IKEv2 VPN profiles */ + public static final class Builder { + private int mType = -1; + @NonNull private final String mServerAddr; + @NonNull private final String mUserIdentity; + + // PSK authentication + @Nullable private byte[] mPresharedKey; + + // Username/Password, RSA authentication + @Nullable private X509Certificate mServerRootCaCert; + + // Username/Password authentication + @Nullable private String mUsername; + @Nullable private String mPassword; + + // RSA Certificate authentication + @Nullable private PrivateKey mRsaPrivateKey; + @Nullable private X509Certificate mUserCert; + + @Nullable private ProxyInfo mProxyInfo; + @NonNull private List<String> mAllowedAlgorithms = new ArrayList<>(); + private boolean mIsBypassable = false; + private boolean mIsMetered = true; + private int mMaxMtu = 1360; + + /** + * Creates a new builder with the basic parameters of an IKEv2/IPsec VPN. + * + * @param serverAddr the server that the VPN should connect to + * @param identity the identity string to be used for IKEv2 authentication + */ + public Builder(@NonNull String serverAddr, @NonNull String identity) { + checkNotNull(serverAddr, MISSING_PARAM_MSG_TMPL, "serverAddr"); + checkNotNull(identity, MISSING_PARAM_MSG_TMPL, "identity"); + + mServerAddr = serverAddr; + mUserIdentity = identity; + } + + private void resetAuthParams() { + mPresharedKey = null; + mServerRootCaCert = null; + mUsername = null; + mPassword = null; + mRsaPrivateKey = null; + mUserCert = null; + } + + /** + * Set the IKEv2 authentication to use the provided username/password. + * + * <p>Setting this will configure IKEv2 authentication using EAP-MSCHAPv2. Only one + * authentication method may be set. This method will overwrite any previously set + * authentication method. + * + * @param user the username to be used for EAP-MSCHAPv2 authentication + * @param pass the password to be used for EAP-MSCHAPv2 authentication + * @param serverRootCa the root certificate to be used for verifying the identity of the + * server + * @return this {@link Builder} object to facilitate chaining of method calls + * @throws IllegalArgumentException if any of the certificates were invalid or of an + * unrecognized format + */ + @NonNull + public Builder setAuthUsernamePassword( + @NonNull String user, + @NonNull String pass, + @Nullable X509Certificate serverRootCa) { + checkNotNull(user, MISSING_PARAM_MSG_TMPL, "user"); + checkNotNull(pass, MISSING_PARAM_MSG_TMPL, "pass"); + + // Test to make sure all auth params can be encoded safely. + if (serverRootCa != null) checkCert(serverRootCa); + + resetAuthParams(); + mUsername = user; + mPassword = pass; + mServerRootCaCert = serverRootCa; + mType = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; + return this; + } + + /** + * Set the IKEv2 authentication to use Digital Signature Authentication with the given key. + * + * <p>Setting this will configure IKEv2 authentication using a Digital Signature scheme. + * Only one authentication method may be set. This method will overwrite any previously set + * authentication method. + * + * @param userCert the username to be used for RSA Digital signiture authentication + * @param key the PrivateKey instance associated with the user ceritificate, used for + * constructing the signature + * @param serverRootCa the root certificate to be used for verifying the identity of the + * server + * @return this {@link Builder} object to facilitate chaining of method calls + * @throws IllegalArgumentException if any of the certificates were invalid or of an + * unrecognized format + */ + @NonNull + public Builder setAuthDigitalSignature( + @NonNull X509Certificate userCert, + @NonNull PrivateKey key, + @Nullable X509Certificate serverRootCa) { + checkNotNull(userCert, MISSING_PARAM_MSG_TMPL, "userCert"); + checkNotNull(key, MISSING_PARAM_MSG_TMPL, "key"); + + // Test to make sure all auth params can be encoded safely. + checkCert(userCert); + if (serverRootCa != null) checkCert(serverRootCa); + + resetAuthParams(); + mUserCert = userCert; + mRsaPrivateKey = key; + mServerRootCaCert = serverRootCa; + mType = VpnProfile.TYPE_IKEV2_IPSEC_RSA; + return this; + } + + /** + * Set the IKEv2 authentication to use Preshared keys. + * + * <p>Setting this will configure IKEv2 authentication using a Preshared Key. Only one + * authentication method may be set. This method will overwrite any previously set + * authentication method. + * + * @param psk the key to be used for Pre-Shared Key authentication + * @return this {@link Builder} object to facilitate chaining of method calls + */ + @NonNull + public Builder setAuthPsk(@NonNull byte[] psk) { + checkNotNull(psk, MISSING_PARAM_MSG_TMPL, "psk"); + + resetAuthParams(); + mPresharedKey = psk; + mType = VpnProfile.TYPE_IKEV2_IPSEC_PSK; + return this; + } + + /** + * Sets whether apps can bypass this VPN connection. + * + * <p>By default, all traffic from apps are forwarded through the VPN interface and it is + * not possible for unprivileged apps to side-step the VPN. If a VPN is set to bypassable, + * apps may use methods such as {@link Network#getSocketFactory} or {@link + * Network#openConnection} to instead send/receive directly over the underlying network or + * any other network they have permissions for. + * + * @param isBypassable Whether or not the VPN should be considered bypassable. Defaults to + * {@code false}. + * @return this {@link Builder} object to facilitate chaining of method calls + */ + @NonNull + public Builder setBypassable(boolean isBypassable) { + mIsBypassable = isBypassable; + return this; + } + + /** + * Sets a proxy for the VPN network. + * + * <p>Note that this proxy is only a recommendation and it may be ignored by apps. + * + * @param proxy the ProxyInfo to be set for the VPN network + * @return this {@link Builder} object to facilitate chaining of method calls + */ + @NonNull + public Builder setProxy(@Nullable ProxyInfo proxy) { + mProxyInfo = proxy; + return this; + } + + /** + * Set the upper bound of the maximum transmission unit (MTU) of the VPN interface. + * + * <p>If it is not set, a safe value will be used. Additionally, the actual link MTU will be + * dynamically calculated/updated based on the underlying link's mtu. + * + * @param mtu the MTU (in bytes) of the VPN interface + * @return this {@link Builder} object to facilitate chaining of method calls + * @throws IllegalArgumentException if the value is not at least the minimum IPv6 MTU (1280) + */ + @NonNull + public Builder setMaxMtu(int mtu) { + // IPv6 MTU is greater; since profiles may be started by the system on IPv4 and IPv6 + // networks, the VPN must provide a link fulfilling the stricter of the two conditions + // (at least that of the IPv6 MTU). + if (mtu < LinkProperties.MIN_MTU_V6) { + throw new IllegalArgumentException( + "Max MTU must be at least " + LinkProperties.MIN_MTU_V6); + } + mMaxMtu = mtu; + return this; + } + + /** + * Marks the VPN network as metered. + * + * <p>A VPN network is classified as metered when the user is sensitive to heavy data usage + * due to monetary costs and/or data limitations. In such cases, you should set this to + * {@code true} so that apps on the system can avoid doing large data transfers. Otherwise, + * set this to {@code false}. Doing so would cause VPN network to inherit its meteredness + * from the underlying network. + * + * @param isMetered {@code true} if the VPN network should be treated as metered regardless + * of underlying network meteredness. Defaults to {@code true}. + * @return this {@link Builder} object to facilitate chaining of method calls + * @see NetworkCapabilities.NET_CAPABILITY_NOT_METERED + */ + @NonNull + public Builder setMetered(boolean isMetered) { + mIsMetered = isMetered; + return this; + } + + /** + * Sets the allowable set of IPsec algorithms + * + * <p>A list of allowed IPsec algorithms as defined in {@link IpSecAlgorithm} + * + * @param algorithmNames the list of supported IPsec algorithms + * @return this {@link Builder} object to facilitate chaining of method calls + * @see IpSecAlgorithm + */ + @NonNull + public Builder setAllowedAlgorithms(@NonNull List<String> algorithmNames) { + checkNotNull(algorithmNames, MISSING_PARAM_MSG_TMPL, "algorithmNames"); + VpnProfile.validateAllowedAlgorithms(algorithmNames); + + mAllowedAlgorithms = algorithmNames; + return this; + } + + /** + * Validates, builds and provisions the VpnProfile. + * + * @throws IllegalArgumentException if any of the required keys or values were invalid + */ + @NonNull + public Ikev2VpnProfile build() { + return new Ikev2VpnProfile( + mType, + mServerAddr, + mUserIdentity, + mPresharedKey, + mServerRootCaCert, + mUsername, + mPassword, + mRsaPrivateKey, + mUserCert, + mProxyInfo, + mAllowedAlgorithms, + mIsBypassable, + mIsMetered, + mMaxMtu); + } + } +} diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index bf8b38fc7f84..a9d7f17017c4 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -19,6 +19,7 @@ package android.net; import static android.system.OsConstants.IFA_F_DADFAILED; import static android.system.OsConstants.IFA_F_DEPRECATED; import static android.system.OsConstants.IFA_F_OPTIMISTIC; +import static android.system.OsConstants.IFA_F_PERMANENT; import static android.system.OsConstants.IFA_F_TENTATIVE; import static android.system.OsConstants.RT_SCOPE_HOST; import static android.system.OsConstants.RT_SCOPE_LINK; @@ -34,6 +35,7 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.SystemClock; import android.util.Pair; import java.net.Inet4Address; @@ -41,6 +43,7 @@ import java.net.Inet6Address; import java.net.InetAddress; import java.net.InterfaceAddress; import java.net.UnknownHostException; +import java.util.Objects; /** * Identifies an IP address on a network link. @@ -58,6 +61,21 @@ import java.net.UnknownHostException; * </ul> */ public class LinkAddress implements Parcelable { + + /** + * Indicates the deprecation or expiration time is unknown + * @hide + */ + @SystemApi + public static final long LIFETIME_UNKNOWN = -1; + + /** + * Indicates this address is permanent. + * @hide + */ + @SystemApi + public static final long LIFETIME_PERMANENT = Long.MAX_VALUE; + /** * IPv4 or IPv6 address. */ @@ -71,7 +89,9 @@ public class LinkAddress implements Parcelable { private int prefixLength; /** - * Address flags. A bitmask of IFA_F_* values. + * Address flags. A bitmask of {@code IFA_F_*} values. Note that {@link #getFlags()} may not + * return these exact values. For example, it may set or clear the {@code IFA_F_DEPRECATED} + * flag depending on the current preferred lifetime. */ private int flags; @@ -81,6 +101,23 @@ public class LinkAddress implements Parcelable { private int scope; /** + * The time, as reported by {@link SystemClock#elapsedRealtime}, when this LinkAddress will be + * or was deprecated. At the time existing connections can still use this address until it + * expires, but new connections should use the new address. {@link #LIFETIME_UNKNOWN} indicates + * this information is not available. {@link #LIFETIME_PERMANENT} indicates this + * {@link LinkAddress} will never be deprecated. + */ + private long deprecationTime; + + /** + * The time, as reported by {@link SystemClock#elapsedRealtime}, when this {@link LinkAddress} + * will expire and be removed from the interface. {@link #LIFETIME_UNKNOWN} indicates this + * information is not available. {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} + * will never expire. + */ + private long expirationTime; + + /** * Utility function to determines the scope of a unicast address. Per RFC 4291 section 2.5 and * RFC 6724 section 3.2. * @hide @@ -152,7 +189,8 @@ public class LinkAddress implements Parcelable { /** * Utility function for the constructors. */ - private void init(InetAddress address, int prefixLength, int flags, int scope) { + private void init(InetAddress address, int prefixLength, int flags, int scope, + long deprecationTime, long expirationTime) { if (address == null || address.isMulticastAddress() || prefixLength < 0 || @@ -161,15 +199,42 @@ public class LinkAddress implements Parcelable { throw new IllegalArgumentException("Bad LinkAddress params " + address + "/" + prefixLength); } + + // deprecation time and expiration time must be both provided, or neither. + if ((deprecationTime == LIFETIME_UNKNOWN) != (expirationTime == LIFETIME_UNKNOWN)) { + throw new IllegalArgumentException( + "Must not specify only one of deprecation time and expiration time"); + } + + // deprecation time needs to be a positive value. + if (deprecationTime != LIFETIME_UNKNOWN && deprecationTime < 0) { + throw new IllegalArgumentException("invalid deprecation time " + deprecationTime); + } + + // expiration time needs to be a positive value. + if (expirationTime != LIFETIME_UNKNOWN && expirationTime < 0) { + throw new IllegalArgumentException("invalid expiration time " + expirationTime); + } + + // expiration time can't be earlier than deprecation time + if (deprecationTime != LIFETIME_UNKNOWN && expirationTime != LIFETIME_UNKNOWN + && expirationTime < deprecationTime) { + throw new IllegalArgumentException("expiration earlier than deprecation (" + + deprecationTime + ", " + expirationTime + ")"); + } + this.address = address; this.prefixLength = prefixLength; this.flags = flags; this.scope = scope; + this.deprecationTime = deprecationTime; + this.expirationTime = expirationTime; } /** * Constructs a new {@code LinkAddress} from an {@code InetAddress} and prefix length, with * the specified flags and scope. Flags and scope are not checked for validity. + * * @param address The IP address. * @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). * @param flags A bitmask of {@code IFA_F_*} values representing properties of the address. @@ -181,7 +246,39 @@ public class LinkAddress implements Parcelable { @TestApi public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength, int flags, int scope) { - init(address, prefixLength, flags, scope); + init(address, prefixLength, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN); + } + + /** + * Constructs a new {@code LinkAddress} from an {@code InetAddress}, prefix length, with + * the specified flags, scope, deprecation time, and expiration time. Flags and scope are not + * checked for validity. The value of the {@code IFA_F_DEPRECATED} and {@code IFA_F_PERMANENT} + * flag will be adjusted based on the passed-in lifetimes. + * + * @param address The IP address. + * @param prefixLength The prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). + * @param flags A bitmask of {@code IFA_F_*} values representing properties of the address. + * @param scope An integer defining the scope in which the address is unique (e.g., + * {@link OsConstants#RT_SCOPE_LINK} or {@link OsConstants#RT_SCOPE_SITE}). + * @param deprecationTime The time, as reported by {@link SystemClock#elapsedRealtime}, when + * this {@link LinkAddress} will be or was deprecated. At the time + * existing connections can still use this address until it expires, but + * new connections should use the new address. {@link #LIFETIME_UNKNOWN} + * indicates this information is not available. + * {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} will + * never be deprecated. + * @param expirationTime The time, as reported by {@link SystemClock#elapsedRealtime}, when this + * {@link LinkAddress} will expire and be removed from the interface. + * {@link #LIFETIME_UNKNOWN} indicates this information is not available. + * {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} will + * never expire. + * @hide + */ + @SystemApi + @TestApi + public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength, + int flags, int scope, long deprecationTime, long expirationTime) { + init(address, prefixLength, flags, scope, deprecationTime, expirationTime); } /** @@ -237,7 +334,7 @@ public class LinkAddress implements Parcelable { // This may throw an IllegalArgumentException; catching it is the caller's responsibility. // TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24". Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address); - init(ipAndMask.first, ipAndMask.second, flags, scope); + init(ipAndMask.first, ipAndMask.second, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN); } /** @@ -265,10 +362,12 @@ public class LinkAddress implements Parcelable { return false; } LinkAddress linkAddress = (LinkAddress) obj; - return this.address.equals(linkAddress.address) && - this.prefixLength == linkAddress.prefixLength && - this.flags == linkAddress.flags && - this.scope == linkAddress.scope; + return this.address.equals(linkAddress.address) + && this.prefixLength == linkAddress.prefixLength + && this.flags == linkAddress.flags + && this.scope == linkAddress.scope + && this.deprecationTime == linkAddress.deprecationTime + && this.expirationTime == linkAddress.expirationTime; } /** @@ -276,7 +375,7 @@ public class LinkAddress implements Parcelable { */ @Override public int hashCode() { - return address.hashCode() + 11 * prefixLength + 19 * flags + 43 * scope; + return Objects.hash(address, prefixLength, flags, scope, deprecationTime, expirationTime); } /** @@ -329,6 +428,25 @@ public class LinkAddress implements Parcelable { * Returns the flags of this {@code LinkAddress}. */ public int getFlags() { + int flags = this.flags; + if (deprecationTime != LIFETIME_UNKNOWN) { + if (SystemClock.elapsedRealtime() >= deprecationTime) { + flags |= IFA_F_DEPRECATED; + } else { + // If deprecation time is in the future, or permanent. + flags &= ~IFA_F_DEPRECATED; + } + } + + if (expirationTime == LIFETIME_PERMANENT) { + flags |= IFA_F_PERMANENT; + } else if (expirationTime != LIFETIME_UNKNOWN) { + // If we know this address expired or will expire in the future, then this address + // should not be permanent. + flags &= ~IFA_F_PERMANENT; + } + + // Do no touch the original flags. Return the adjusted flags here. return flags; } @@ -340,7 +458,42 @@ public class LinkAddress implements Parcelable { } /** - * Returns true if this {@code LinkAddress} is global scope and preferred. + * Get the deprecation time, as reported by {@link SystemClock#elapsedRealtime}, when this + * {@link LinkAddress} will be or was deprecated. At the time existing connections can still use + * this address until it expires, but new connections should use the new address. + * + * @return The deprecation time in milliseconds. {@link #LIFETIME_UNKNOWN} indicates this + * information is not available. {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} + * will never be deprecated. + * + * @hide + */ + @SystemApi + @TestApi + public long getDeprecationTime() { + return deprecationTime; + } + + /** + * Get the expiration time, as reported by {@link SystemClock#elapsedRealtime}, when this + * {@link LinkAddress} will expire and be removed from the interface. + * + * @return The expiration time in milliseconds. {@link #LIFETIME_UNKNOWN} indicates this + * information is not available. {@link #LIFETIME_PERMANENT} indicates this {@link LinkAddress} + * will never expire. + * + * @hide + */ + @SystemApi + @TestApi + public long getExpirationTime() { + return expirationTime; + } + + /** + * Returns true if this {@code LinkAddress} is global scope and preferred (i.e., not currently + * deprecated). + * * @hide */ @TestApi @@ -352,6 +505,7 @@ public class LinkAddress implements Parcelable { * state has cleared either DAD has succeeded or failed, and both * flags are cleared regardless). */ + int flags = getFlags(); return (scope == RT_SCOPE_UNIVERSE && !isIpv6ULA() && (flags & (IFA_F_DADFAILED | IFA_F_DEPRECATED)) == 0L @@ -373,6 +527,8 @@ public class LinkAddress implements Parcelable { dest.writeInt(prefixLength); dest.writeInt(this.flags); dest.writeInt(scope); + dest.writeLong(deprecationTime); + dest.writeLong(expirationTime); } /** @@ -392,7 +548,10 @@ public class LinkAddress implements Parcelable { int prefixLength = in.readInt(); int flags = in.readInt(); int scope = in.readInt(); - return new LinkAddress(address, prefixLength, flags, scope); + long deprecationTime = in.readLong(); + long expirationTime = in.readLong(); + return new LinkAddress(address, prefixLength, flags, scope, deprecationTime, + expirationTime); } public LinkAddress[] newArray(int size) { diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 2792c564988a..d25ee0e69e88 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -70,9 +70,18 @@ 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; + /* package-visibility - Used in other files (such as Ikev2VpnProfile) as minimum iface MTU. */ + static final int MIN_MTU_V6 = 1280; private static final int MAX_MTU = 10000; private static final int INET6_ADDR_LENGTH = 16; @@ -174,6 +183,7 @@ public final class LinkProperties implements Parcelable { * Constructs a new {@code LinkProperties} with default values. */ public LinkProperties() { + mParcelSensitiveFields = false; } /** @@ -182,26 +192,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 +876,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 +896,8 @@ public final class LinkProperties implements Parcelable { mTcpBufferSizes = null; mNat64Prefix = null; mWakeOnLanSupported = false; + mCaptivePortalApiUrl = null; + mCaptivePortalData = null; } /** @@ -945,6 +968,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 +1510,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 +1552,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 +1657,9 @@ public final class LinkProperties implements Parcelable { && isIdenticalMtu(target) && isIdenticalTcpBufferSizes(target) && isIdenticalNat64Prefix(target) - && isIdenticalWakeOnLan(target); + && isIdenticalWakeOnLan(target) + && isIdenticalCaptivePortalApiUrl(target) + && isIdenticalCaptivePortalData(target); } /** @@ -1655,7 +1777,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 +1817,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 +1910,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/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index f43385d1f2e0..f94bdb767c02 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -26,6 +26,7 @@ import android.net.ConnectivityManager.NetworkCallback; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; +import android.os.Process; import android.util.ArraySet; import android.util.proto.ProtoOutputStream; @@ -35,6 +36,9 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.StringJoiner; @@ -55,7 +59,6 @@ import java.util.StringJoiner; */ public final class NetworkCapabilities implements Parcelable { private static final String TAG = "NetworkCapabilities"; - private static final int INVALID_UID = -1; // Set to true when private DNS is broken. private boolean mPrivateDnsBroken; @@ -82,7 +85,8 @@ public final class NetworkCapabilities implements Parcelable { mTransportInfo = null; mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; mUids = null; - mEstablishingVpnAppUid = INVALID_UID; + mAdministratorUids.clear(); + mOwnerUid = Process.INVALID_UID; mSSID = null; mPrivateDnsBroken = false; } @@ -100,7 +104,8 @@ public final class NetworkCapabilities implements Parcelable { mTransportInfo = nc.mTransportInfo; mSignalStrength = nc.mSignalStrength; setUids(nc.mUids); // Will make the defensive copy - mEstablishingVpnAppUid = nc.mEstablishingVpnAppUid; + setAdministratorUids(nc.mAdministratorUids); + mOwnerUid = nc.mOwnerUid; mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities; mSSID = nc.mSSID; mPrivateDnsBroken = nc.mPrivateDnsBroken; @@ -805,31 +810,76 @@ public final class NetworkCapabilities implements Parcelable { } /** - * UID of the app that manages this network, or INVALID_UID if none/unknown. + * UID of the app that owns this network, or INVALID_UID if none/unknown. * - * This field keeps track of the UID of the app that created this network and is in charge - * of managing it. In the practice, it is used to store the UID of VPN apps so it is named - * accordingly, but it may be renamed if other mechanisms are offered for third party apps - * to create networks. + * <p>This field keeps track of the UID of the app that created this network and is in charge of + * its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running + * VPN, or Carrier Service app managing a cellular data connection. + */ + private int mOwnerUid = Process.INVALID_UID; + + /** + * Set the UID of the owner app. + */ + public void setOwnerUid(final int uid) { + mOwnerUid = uid; + } + + /** + * Retrieves the UID of the owner app. + */ + public int getOwnerUid() { + return mOwnerUid; + } + + /** + * UIDs of packages that are administrators of this network, or empty if none. * - * Because this field is only used in the services side (and to avoid apps being able to - * set this to whatever they want), this field is not parcelled and will not be conserved - * across the IPC boundary. - * @hide + * <p>This field tracks the UIDs of packages that have permission to manage this network. + * + * <p>Network owners will also be listed as administrators. + * + * <p>For NetworkCapability instances being sent from the System Server, this value MUST be + * empty unless the destination is 1) the System Server, or 2) Telephony. In either case, the + * receiving entity must have the ACCESS_FINE_LOCATION permission and target R+. */ - private int mEstablishingVpnAppUid = INVALID_UID; + private final List<Integer> mAdministratorUids = new ArrayList<>(); /** - * Set the UID of the managing app. + * Sets the list of UIDs that are administrators of this network. + * + * <p>UIDs included in administratorUids gain administrator privileges over this Network. + * Examples of UIDs that should be included in administratorUids are: + * <ul> + * <li>Carrier apps with privileges for the relevant subscription + * <li>Active VPN apps + * <li>Other application groups with a particular Network-related role + * </ul> + * + * <p>In general, user-supplied networks (such as WiFi networks) do not have an administrator. + * + * <p>An app is granted owner privileges over Networks that it supplies. Owner privileges + * implicitly include administrator privileges. + * + * @param administratorUids the UIDs to be set as administrators of this Network. * @hide */ - public void setEstablishingVpnAppUid(final int uid) { - mEstablishingVpnAppUid = uid; + @SystemApi + public void setAdministratorUids(@NonNull final List<Integer> administratorUids) { + mAdministratorUids.clear(); + mAdministratorUids.addAll(administratorUids); } - /** @hide */ - public int getEstablishingVpnAppUid() { - return mEstablishingVpnAppUid; + /** + * Retrieves the list of UIDs that are administrators of this Network. + * + * @return the List of UIDs that are administrators of this Network + * @hide + */ + @NonNull + @SystemApi + public List<Integer> getAdministratorUids() { + return Collections.unmodifiableList(mAdministratorUids); } /** @@ -1102,7 +1152,7 @@ public final class NetworkCapabilities implements Parcelable { * member is null, then the network is not restricted by app UID. If it's an empty list, then * it means nobody can use it. * As a special exception, the app managing this network (as identified by its UID stored in - * mEstablishingVpnAppUid) can always see this network. This is embodied by a special check in + * mOwnerUid) can always see this network. This is embodied by a special check in * satisfiedByUids. That still does not mean the network necessarily <strong>applies</strong> * to the app that manages it as determined by #appliesToUid. * <p> @@ -1209,7 +1259,7 @@ public final class NetworkCapabilities implements Parcelable { * in the passed nc (representing the UIDs that this network is available to). * <p> * As a special exception, the UID that created the passed network (as represented by its - * mEstablishingVpnAppUid field) always satisfies a NetworkRequest requiring it (of LISTEN + * mOwnerUid field) always satisfies a NetworkRequest requiring it (of LISTEN * or REQUEST types alike), even if the network does not apply to it. That is so a VPN app * can see its own network when it listens for it. * <p> @@ -1220,7 +1270,7 @@ public final class NetworkCapabilities implements Parcelable { public boolean satisfiedByUids(@NonNull NetworkCapabilities nc) { if (null == nc.mUids || null == mUids) return true; // The network satisfies everything. for (UidRange requiredRange : mUids) { - if (requiredRange.contains(nc.mEstablishingVpnAppUid)) return true; + if (requiredRange.contains(nc.mOwnerUid)) return true; if (!nc.appliesToUidRange(requiredRange)) { return false; } @@ -1283,6 +1333,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; } @@ -1470,6 +1521,7 @@ public final class NetworkCapabilities implements Parcelable { public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mNetworkCapabilities); @@ -1483,6 +1535,8 @@ public final class NetworkCapabilities implements Parcelable { dest.writeArraySet(mUids); dest.writeString(mSSID); dest.writeBoolean(mPrivateDnsBroken); + dest.writeList(mAdministratorUids); + dest.writeInt(mOwnerUid); } public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR = @@ -1503,6 +1557,8 @@ public final class NetworkCapabilities implements Parcelable { null /* ClassLoader, null for default */); netCap.mSSID = in.readString(); netCap.mPrivateDnsBroken = in.readBoolean(); + netCap.setAdministratorUids(in.readArrayList(null)); + netCap.mOwnerUid = in.readInt(); return netCap; } @Override @@ -1552,8 +1608,12 @@ public final class NetworkCapabilities implements Parcelable { sb.append(" Uids: <").append(mUids).append(">"); } } - if (mEstablishingVpnAppUid != INVALID_UID) { - sb.append(" EstablishingAppUid: ").append(mEstablishingVpnAppUid); + if (mOwnerUid != Process.INVALID_UID) { + sb.append(" OwnerUid: ").append(mOwnerUid); + } + + if (!mAdministratorUids.isEmpty()) { + sb.append(" AdministratorUids: ").append(mAdministratorUids); } if (null != mSSID) { diff --git a/core/java/android/net/PlatformVpnProfile.java b/core/java/android/net/PlatformVpnProfile.java new file mode 100644 index 000000000000..fbae63707be2 --- /dev/null +++ b/core/java/android/net/PlatformVpnProfile.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_PSK; +import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_RSA; +import static android.net.PlatformVpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; + +import android.annotation.IntDef; +import android.annotation.NonNull; + +import com.android.internal.net.VpnProfile; + +import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.security.GeneralSecurityException; + +/** + * PlatformVpnProfile represents a configuration for a platform-based VPN implementation. + * + * <p>Platform-based VPNs allow VPN applications to provide configuration and authentication options + * to leverage the Android OS' implementations of well-defined control plane (authentication, key + * negotiation) and data plane (per-packet encryption) protocols to simplify the creation of VPN + * tunnels. In contrast, {@link VpnService} based VPNs must implement both the control and data + * planes on a per-app basis. + * + * @see Ikev2VpnProfile + */ +public abstract class PlatformVpnProfile { + /** + * Alias to platform VPN related types from VpnProfile, for API use. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + TYPE_IKEV2_IPSEC_USER_PASS, + TYPE_IKEV2_IPSEC_PSK, + TYPE_IKEV2_IPSEC_RSA, + }) + public static @interface PlatformVpnType {} + + public static final int TYPE_IKEV2_IPSEC_USER_PASS = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; + public static final int TYPE_IKEV2_IPSEC_PSK = VpnProfile.TYPE_IKEV2_IPSEC_PSK; + public static final int TYPE_IKEV2_IPSEC_RSA = VpnProfile.TYPE_IKEV2_IPSEC_RSA; + + /** @hide */ + @PlatformVpnType protected final int mType; + + /** @hide */ + PlatformVpnProfile(@PlatformVpnType int type) { + mType = type; + } + /** Returns the profile integer type. */ + @PlatformVpnType + public final int getType() { + return mType; + } + + /** Returns a type string describing the VPN profile type */ + @NonNull + public final String getTypeString() { + switch (mType) { + case TYPE_IKEV2_IPSEC_USER_PASS: + return "IKEv2/IPsec Username/Password"; + case TYPE_IKEV2_IPSEC_PSK: + return "IKEv2/IPsec Preshared key"; + case TYPE_IKEV2_IPSEC_RSA: + return "IKEv2/IPsec RSA Digital Signature"; + default: + return "Unknown VPN profile type"; + } + } + + /** @hide */ + @NonNull + public abstract VpnProfile toVpnProfile() throws IOException, GeneralSecurityException; + + /** @hide */ + @NonNull + public static PlatformVpnProfile fromVpnProfile(@NonNull VpnProfile profile) + throws IOException, GeneralSecurityException { + switch (profile.type) { + case TYPE_IKEV2_IPSEC_USER_PASS: // fallthrough + case TYPE_IKEV2_IPSEC_PSK: // fallthrough + case TYPE_IKEV2_IPSEC_RSA: + return Ikev2VpnProfile.fromVpnProfile(profile); + default: + throw new IllegalArgumentException("Unknown VPN Profile type"); + } + } +} diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java index 83dbc637fb65..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; @@ -27,7 +26,6 @@ import com.android.internal.util.Preconditions; import java.util.Objects; /** @hide */ -@SystemApi public final class StringNetworkSpecifier extends NetworkSpecifier implements Parcelable { /** * Arbitrary string used to pass (additional) information to the network factory. diff --git a/core/java/android/net/Uri.aidl b/core/java/android/net/Uri.aidl index 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/net/VpnManager.java b/core/java/android/net/VpnManager.java new file mode 100644 index 000000000000..f95807a14f00 --- /dev/null +++ b/core/java/android/net/VpnManager.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static com.android.internal.util.Preconditions.checkNotNull; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.Intent; + +/** + * This class provides an interface for apps to manage platform VPN profiles + * + * <p>Apps can use this API to provide profiles with which the platform can set up a VPN without + * further app intermediation. When a VPN profile is present and the app is selected as an always-on + * VPN, the platform will directly trigger the negotiation of the VPN without starting or waking the + * app (unlike VpnService). + * + * <p>VPN apps using supported protocols should preferentially use this API over the {@link + * VpnService} API for ease-of-development and reduced maintainance burden. This also give the user + * the guarantee that VPN network traffic is not subjected to on-device packet interception. + * + * @see Ikev2VpnProfile + */ +public class VpnManager { + @NonNull private final Context mContext; + @NonNull private final IConnectivityManager mService; + + /** + * Create an instance of the VpnManger with the given context. + * + * <p>Internal only. Applications are expected to obtain an instance of the VpnManager via the + * {@link Context.getSystemService()} method call. + * + * @hide + */ + public VpnManager(@NonNull Context ctx, @NonNull IConnectivityManager service) { + mContext = checkNotNull(ctx, "missing Context"); + mService = checkNotNull(service, "missing IConnectivityManager"); + } + + /** + * Install a VpnProfile configuration keyed on the calling app's package name. + * + * @param profile the PlatformVpnProfile provided by this package. Will override any previous + * PlatformVpnProfile stored for this package. + * @return an intent to request user consent if needed (null otherwise). + */ + @Nullable + public Intent provisionVpnProfile(@NonNull PlatformVpnProfile profile) { + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** Delete the VPN profile configuration that was provisioned by the calling app */ + public void deleteProvisionedVpnProfile() { + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** + * Request the startup of a previously provisioned VPN. + * + * @throws SecurityException exception if user or device settings prevent this VPN from being + * setup, or if user consent has not been granted + */ + public void startProvisionedVpnProfile() { + throw new UnsupportedOperationException("Not yet implemented"); + } + + /** Tear down the VPN provided by the calling app (if any) */ + public void stopProvisionedVpnProfile() { + throw new UnsupportedOperationException("Not yet implemented"); + } +} diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index febc36cbe47b..68c2764a78d7 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1409,6 +1409,7 @@ public final class PowerManager { * @throws UnsupportedOperationException if userspace reboot was requested on a device that * doesn't support it. */ + @RequiresPermission(permission.REBOOT) public void reboot(@Nullable String reason) { if (REBOOT_USERSPACE.equals(reason) && !isRebootingUserspaceSupported()) { throw new UnsupportedOperationException( @@ -1428,6 +1429,7 @@ public final class PowerManager { * </p> * @hide */ + @RequiresPermission(permission.REBOOT) public void rebootSafeMode() { try { mService.rebootSafeMode(false, true); diff --git a/core/java/android/os/storage/IStorageManager.aidl b/core/java/android/os/storage/IStorageManager.aidl index 92fecaddff22..bbc936d76e1c 100644 --- a/core/java/android/os/storage/IStorageManager.aidl +++ b/core/java/android/os/storage/IStorageManager.aidl @@ -193,4 +193,5 @@ interface IStorageManager { void startCheckpoint(int numTries) = 85; boolean needsCheckpoint() = 86; void abortChanges(in String message, boolean retry) = 87; + void clearUserKeyAuth(int userId, int serialNumber, in byte[] token, in byte[] secret) = 88; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 1f3ae09e9216..b22db2e24624 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -81,7 +81,6 @@ import android.os.ServiceManager; import android.os.UserHandle; import android.provider.SettingsValidators.Validator; import android.speech.tts.TextToSpeech; -import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.util.AndroidException; import android.util.ArrayMap; @@ -8571,6 +8570,7 @@ public final class Settings { * * @hide */ + @SystemApi public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled"; /** @@ -13320,16 +13320,17 @@ public final class Settings { /** * Whether the Volte is enabled. If this setting is not set then we use the Carrier Config - * value {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. + * value + * {@link android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. * <p> * Type: int (0 for false, 1 for true) * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#ENHANCED_4G_MODE_ENABLED} + * @deprecated Use {@link android.provider.Telephony.SimInfo#ENHANCED_4G_MODE_ENABLED} * instead. */ @Deprecated public static final String ENHANCED_4G_MODE_ENABLED = - SubscriptionManager.ENHANCED_4G_MODE_ENABLED; + Telephony.SimInfo.ENHANCED_4G_MODE_ENABLED; /** * Whether VT (Video Telephony over IMS) is enabled @@ -13337,10 +13338,10 @@ public final class Settings { * Type: int (0 for false, 1 for true) * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#VT_IMS_ENABLED} instead. + * @deprecated Use {@link android.provider.Telephony.SimInfo#VT_IMS_ENABLED} instead. */ @Deprecated - public static final String VT_IMS_ENABLED = SubscriptionManager.VT_IMS_ENABLED; + public static final String VT_IMS_ENABLED = Telephony.SimInfo.VT_IMS_ENABLED; /** * Whether WFC is enabled @@ -13348,10 +13349,10 @@ public final class Settings { * Type: int (0 for false, 1 for true) * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ENABLED} instead. + * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ENABLED} instead. */ @Deprecated - public static final String WFC_IMS_ENABLED = SubscriptionManager.WFC_IMS_ENABLED; + public static final String WFC_IMS_ENABLED = Telephony.SimInfo.WFC_IMS_ENABLED; /** * WFC mode on home/non-roaming network. @@ -13359,10 +13360,10 @@ public final class Settings { * Type: int - 2=Wi-Fi preferred, 1=Cellular preferred, 0=Wi-Fi only * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_MODE} instead. + * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_MODE} instead. */ @Deprecated - public static final String WFC_IMS_MODE = SubscriptionManager.WFC_IMS_MODE; + public static final String WFC_IMS_MODE = Telephony.SimInfo.WFC_IMS_MODE; /** * WFC mode on roaming network. @@ -13370,11 +13371,11 @@ public final class Settings { * Type: int - see {@link #WFC_IMS_MODE} for values * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_MODE} + * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_MODE} * instead. */ @Deprecated - public static final String WFC_IMS_ROAMING_MODE = SubscriptionManager.WFC_IMS_ROAMING_MODE; + public static final String WFC_IMS_ROAMING_MODE = Telephony.SimInfo.WFC_IMS_ROAMING_MODE; /** * Whether WFC roaming is enabled @@ -13382,12 +13383,12 @@ public final class Settings { * Type: int (0 for false, 1 for true) * * @hide - * @deprecated Use {@link android.telephony.SubscriptionManager#WFC_IMS_ROAMING_ENABLED} + * @deprecated Use {@link android.provider.Telephony.SimInfo#WFC_IMS_ROAMING_ENABLED} * instead */ @Deprecated public static final String WFC_IMS_ROAMING_ENABLED = - SubscriptionManager.WFC_IMS_ROAMING_ENABLED; + Telephony.SimInfo.WFC_IMS_ROAMING_ENABLED; /** * Whether user can enable/disable LTE as a preferred network. A carrier might control diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 90ac821363b2..1611d28b1be2 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -37,11 +37,13 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Parcel; +import android.telephony.CarrierConfigManager; import android.telephony.Rlog; import android.telephony.ServiceState; import android.telephony.SmsMessage; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.telephony.UiccAccessRule; import android.text.TextUtils; import android.util.Patterns; @@ -4928,5 +4930,414 @@ public final class Telephony { */ @NonNull public static final Uri CONTENT_URI = Uri.parse("content://telephony/siminfo"); + + /** + * TelephonyProvider unique key column name is the subscription id. + * <P>Type: TEXT (String)</P> + */ + public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id"; + + /** + * TelephonyProvider column name for a unique identifier for the subscription within the + * specific subscription type. For example, it contains SIM ICC Identifier subscriptions + * on Local SIMs. and Mac-address for Remote-SIM Subscriptions for Bluetooth devices. + * <P>Type: TEXT (String)</P> + */ + public static final String ICC_ID = "icc_id"; + + /** + * TelephonyProvider column name for user SIM_SlOT_INDEX + * <P>Type: INTEGER (int)</P> + */ + public static final String SIM_SLOT_INDEX = "sim_id"; + + /** + * SIM is not inserted + */ + public static final int SIM_NOT_INSERTED = -1; + + /** + * TelephonyProvider column name Subscription-type. + * <P>Type: INTEGER (int)</P> {@link #SUBSCRIPTION_TYPE_LOCAL_SIM} for Local-SIM + * Subscriptions, {@link #SUBSCRIPTION_TYPE_REMOTE_SIM} for Remote-SIM Subscriptions. + * Default value is 0. + */ + public static final String SUBSCRIPTION_TYPE = "subscription_type"; + + /** + * This constant is to designate a subscription as a Local-SIM Subscription. + * <p> A Local-SIM can be a physical SIM inserted into a sim-slot in the device, or eSIM on + * the device. + * </p> + */ + public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; + + /** + * This constant is to designate a subscription as a Remote-SIM Subscription. + * <p> + * A Remote-SIM subscription is for a SIM on a phone connected to this device via some + * connectivity mechanism, for example bluetooth. Similar to Local SIM, this subscription + * can be used for SMS, Voice and data by proxying data through the connected device. + * Certain data of the SIM, such as IMEI, are not accessible for Remote SIMs. + * </p> + * + * <p> + * A Remote-SIM is available only as long the phone stays connected to this device. + * When the phone disconnects, Remote-SIM subscription is removed from this device and is + * no longer known. All data associated with the subscription, such as stored SMS, call + * logs, contacts etc, are removed from this device. + * </p> + * + * <p> + * If the phone re-connects to this device, a new Remote-SIM subscription is created for + * the phone. The Subscription Id associated with the new subscription is different from + * the Subscription Id of the previous Remote-SIM subscription created (and removed) for the + * phone; i.e., new Remote-SIM subscription treats the reconnected phone as a Remote-SIM + * that was never seen before. + * </p> + */ + public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; + + /** + * TelephonyProvider column name data_enabled_override_rules. + * It's a list of rules for overriding data enabled settings. The syntax is + * For example, "mms=nonDefault" indicates enabling data for mms in non-default + * subscription. + * "default=nonDefault&inVoiceCall" indicates enabling data for internet in non-default + * subscription and while is in voice call. + * + * Default value is empty string. + */ + public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules"; + + /** + * TelephonyProvider column name for user displayed name. + * <P>Type: TEXT (String)</P> + */ + public static final String DISPLAY_NAME = "display_name"; + + /** + * TelephonyProvider column name for the service provider name for the SIM. + * <P>Type: TEXT (String)</P> + */ + public static final String CARRIER_NAME = "carrier_name"; + + /** + * TelephonyProvider column name for source of the user displayed name. + * <P>Type: INT (int)</P> with one of the NAME_SOURCE_XXXX values below + */ + public static final String NAME_SOURCE = "name_source"; + + /** The name_source is the default, which is from the carrier id. */ + public static final int NAME_SOURCE_DEFAULT = 0; + + /** + * The name_source is from SIM EF_SPN. + */ + public static final int NAME_SOURCE_SIM_SPN = 1; + + /** + * The name_source is from user input + */ + public static final int NAME_SOURCE_USER_INPUT = 2; + + /** + * The name_source is carrier (carrier app, carrier config, etc.) + */ + public static final int NAME_SOURCE_CARRIER = 3; + + /** + * The name_source is from SIM EF_PNN. + */ + public static final int NAME_SOURCE_SIM_PNN = 4; + + /** + * TelephonyProvider column name for the color of a SIM. + * <P>Type: INTEGER (int)</P> + */ + public static final String COLOR = "color"; + + /** TelephonyProvider column name for the default color of a SIM {@hide} */ + public static final int COLOR_DEFAULT = 0; + + /** + * TelephonyProvider column name for the phone number of a SIM. + * <P>Type: TEXT (String)</P> + */ + public static final String NUMBER = "number"; + + /** + * TelephonyProvider column name for the number display format of a SIM. + * <P>Type: INTEGER (int)</P> + * @hide + */ + public static final String DISPLAY_NUMBER_FORMAT = "display_number_format"; + + /** + * TelephonyProvider column name for the default display format of a SIM + * @hide + */ + public static final int DISPLAY_NUMBER_DEFAULT = 1; + + /** + * TelephonyProvider column name for whether data roaming is enabled. + * <P>Type: INTEGER (int)</P> + */ + public static final String DATA_ROAMING = "data_roaming"; + + /** Indicates that data roaming is enabled for a subscription */ + public static final int DATA_ROAMING_ENABLE = 1; + + /** Indicates that data roaming is disabled for a subscription */ + public static final int DATA_ROAMING_DISABLE = 0; + + /** TelephonyProvider column name for default data roaming setting: disable */ + public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE; + + /** + * TelephonyProvider column name for subscription carrier id. + * @see TelephonyManager#getSimCarrierId() + * <p>Type: INTEGER (int) </p> + */ + public static final String CARRIER_ID = "carrier_id"; + + /** + * A comma-separated list of EHPLMNs associated with the subscription + * <P>Type: TEXT (String)</P> + */ + public static final String EHPLMNS = "ehplmns"; + + /** + * A comma-separated list of HPLMNs associated with the subscription + * <P>Type: TEXT (String)</P> + */ + public static final String HPLMNS = "hplmns"; + + /** + * TelephonyProvider column name for the MCC associated with a SIM, stored as a string. + * <P>Type: TEXT (String)</P> + */ + public static final String MCC_STRING = "mcc_string"; + + /** + * TelephonyProvider column name for the MNC associated with a SIM, stored as a string. + * <P>Type: TEXT (String)</P> + */ + public static final String MNC_STRING = "mnc_string"; + + /** + * TelephonyProvider column name for the MCC associated with a SIM. + * <P>Type: INTEGER (int)</P> + */ + public static final String MCC = "mcc"; + + /** + * TelephonyProvider column name for the MNC associated with a SIM. + * <P>Type: INTEGER (int)</P> + */ + public static final String MNC = "mnc"; + + /** + * TelephonyProvider column name for the iso country code associated with a SIM. + * <P>Type: TEXT (String)</P> + */ + public static final String ISO_COUNTRY_CODE = "iso_country_code"; + + /** + * TelephonyProvider column name for the sim provisioning status associated with a SIM. + * <P>Type: INTEGER (int)</P> + * @hide + */ + public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status"; + + /** The sim is provisioned {@hide} */ + public static final int SIM_PROVISIONED = 0; + + /** + * TelephonyProvider column name for whether a subscription is embedded (that is, present on + * an eSIM). + * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded. + */ + public static final String IS_EMBEDDED = "is_embedded"; + + /** + * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of + * the current enabled profile on the card, while for eUICC card it is the EID of the card. + * <P>Type: TEXT (String)</P> + */ + public static final String CARD_ID = "card_id"; + + /** + * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from + * {@link UiccAccessRule#encodeRules}. Only present if {@link #IS_EMBEDDED} is 1. + * <p>TYPE: BLOB + */ + public static final String ACCESS_RULES = "access_rules"; + + /** + * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from + * {@link UiccAccessRule#encodeRules} but for the rules that come from CarrierConfigs. + * Only present if there are access rules in CarrierConfigs + * <p>TYPE: BLOB + */ + public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = + "access_rules_from_carrier_configs"; + + /** + * TelephonyProvider column name identifying whether an embedded subscription is on a + * removable card. Such subscriptions are marked inaccessible as soon as the current card + * is removed. Otherwise, they will remain accessible unless explicitly deleted. Only + * present if {@link #IS_EMBEDDED} is 1. + * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable. + */ + public static final String IS_REMOVABLE = "is_removable"; + + /** TelephonyProvider column name for extreme threat in CB settings */ + public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts"; + + /** TelephonyProvider column name for severe threat in CB settings */ + public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts"; + + /** TelephonyProvider column name for amber alert in CB settings */ + public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts"; + + /** TelephonyProvider column name for emergency alert in CB settings */ + public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts"; + + /** TelephonyProvider column name for alert sound duration in CB settings */ + public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration"; + + /** TelephonyProvider column name for alert reminder interval in CB settings */ + public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; + + /** TelephonyProvider column name for enabling vibrate in CB settings */ + public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate"; + + /** TelephonyProvider column name for enabling alert speech in CB settings */ + public static final String CB_ALERT_SPEECH = "enable_alert_speech"; + + /** TelephonyProvider column name for ETWS test alert in CB settings */ + public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts"; + + /** TelephonyProvider column name for enable channel50 alert in CB settings */ + public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts"; + + /** TelephonyProvider column name for CMAS test alert in CB settings */ + public static final String CB_CMAS_TEST_ALERT = "enable_cmas_test_alerts"; + + /** TelephonyProvider column name for Opt out dialog in CB settings */ + public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; + + /** + * TelephonyProvider column name for enable Volte. + * + * If this setting is not initialized (set to -1) then we use the Carrier Config value + * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. + */ + public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled"; + + /** TelephonyProvider column name for enable VT (Video Telephony over IMS) */ + public static final String VT_IMS_ENABLED = "vt_ims_enabled"; + + /** TelephonyProvider column name for enable Wifi calling */ + public static final String WFC_IMS_ENABLED = "wfc_ims_enabled"; + + /** TelephonyProvider column name for Wifi calling mode */ + public static final String WFC_IMS_MODE = "wfc_ims_mode"; + + /** TelephonyProvider column name for Wifi calling mode in roaming */ + public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode"; + + /** TelephonyProvider column name for enable Wifi calling in roaming */ + public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled"; + + /** + * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this + * subscription. + */ + public static final String IMS_RCS_UCE_ENABLED = "ims_rcs_uce_enabled"; + + /** + * TelephonyProvider column name for whether a subscription is opportunistic, that is, + * whether the network it connects to is limited in functionality or coverage. + * For example, CBRS. + * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic. + */ + public static final String IS_OPPORTUNISTIC = "is_opportunistic"; + + /** + * TelephonyProvider column name for group ID. Subscriptions with same group ID + * are considered bundled together, and should behave as a single subscription at + * certain scenarios. + */ + public static final String GROUP_UUID = "group_uuid"; + + /** + * TelephonyProvider column name for group owner. It's the package name who created + * the subscription group. + */ + public static final String GROUP_OWNER = "group_owner"; + + /** + * TelephonyProvider column name for whether a subscription is metered or not, that is, + * whether the network it connects to charges for subscription or not. For example, paid + * CBRS or unpaid. + * @hide + */ + public static final String IS_METERED = "is_metered"; + + /** + * TelephonyProvider column name for the profile class of a subscription + * Only present if {@link #IS_EMBEDDED} is 1. + * <P>Type: INTEGER (int)</P> + */ + public static final String PROFILE_CLASS = "profile_class"; + + /** + * A testing profile can be pre-loaded or downloaded onto + * the eUICC and provides connectivity to test equipment + * for the purpose of testing the device and the eUICC. It + * is not intended to store any operator credentials. + */ + public static final int PROFILE_CLASS_TESTING = 0; + + /** + * A provisioning profile is pre-loaded onto the eUICC and + * provides connectivity to a mobile network solely for the + * purpose of provisioning profiles. + */ + public static final int PROFILE_CLASS_PROVISIONING = 1; + + /** + * An operational profile can be pre-loaded or downloaded + * onto the eUICC and provides services provided by the + * operator. + */ + public static final int PROFILE_CLASS_OPERATIONAL = 2; + + /** + * The profile class is unset. This occurs when profile class + * info is not available. The subscription either has no profile + * metadata or the profile metadata did not encode profile class. + */ + public static final int PROFILE_CLASS_UNSET = -1; + + /** Default profile class */ + public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET; + + /** + * IMSI (International Mobile Subscriber Identity). + * <P>Type: TEXT </P> + */ + public static final String IMSI = "imsi"; + + /** Whether uicc applications is set to be enabled or disabled. By default it's enabled. */ + public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled"; + + /** + * TelephonyProvider column name for allowed network types. Indicate which network types + * are allowed. Default is -1. + */ + public static final String ALLOWED_NETWORK_TYPES = "allowed_network_types"; } } 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/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java index 5330cffee3db..f67af85d00e3 100644 --- a/core/java/android/security/ConfirmationPrompt.java +++ b/core/java/android/security/ConfirmationPrompt.java @@ -212,20 +212,16 @@ public class ConfirmationPrompt { private int getUiOptionsAsFlags() { int uiOptionsAsFlags = 0; - try { - ContentResolver contentResolver = mContext.getContentResolver(); - int inversionEnabled = Settings.Secure.getInt(contentResolver, - Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); - if (inversionEnabled == 1) { - uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG; - } - float fontScale = Settings.System.getFloat(contentResolver, - Settings.System.FONT_SCALE); - if (fontScale > 1.0) { - uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG; - } - } catch (SettingNotFoundException e) { - Log.w(TAG, "Unexpected SettingNotFoundException"); + ContentResolver contentResolver = mContext.getContentResolver(); + int inversionEnabled = Settings.Secure.getInt(contentResolver, + Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0); + if (inversionEnabled == 1) { + uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_INVERTED_FLAG; + } + float fontScale = Settings.System.getFloat(contentResolver, + Settings.System.FONT_SCALE, (float) 1.0); + if (fontScale > 1.0) { + uiOptionsAsFlags |= UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG; } return uiOptionsAsFlags; } diff --git a/core/java/android/telephony/CellBroadcastIntents.java b/core/java/android/telephony/CellBroadcastIntents.java index 8446253c6302..2e0810835a52 100644 --- a/core/java/android/telephony/CellBroadcastIntents.java +++ b/core/java/android/telephony/CellBroadcastIntents.java @@ -16,22 +16,23 @@ package android.telephony; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; -import android.os.Bundle; import android.os.Handler; import android.os.UserHandle; +import android.provider.Telephony; /** * A static helper class used to send Intents with prepopulated flags. * <p> - * This is intended to be used by the CellBroadcastService and will throw a security exception if - * used from a UID besides the network stack UID. + * This is intended to be used by the CellBroadcastService and does nothing if the caller does not + * have permission to broadcast {@link Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION}. * * @hide */ @@ -39,6 +40,8 @@ import android.os.UserHandle; public class CellBroadcastIntents { private static final String LOG_TAG = "CellBroadcastIntents"; + private static final String EXTRA_MESSAGE = "message"; + /** * @hide */ @@ -46,50 +49,71 @@ public class CellBroadcastIntents { } /** - * Returns an intent which can be received by background BroadcastReceivers. This is only - * intended to be used by the CellBroadcastService and will throw a security exception if called - * from another UID. + * Broadcasts an SMS_CB_RECEIVED_ACTION intent which can be received by background + * BroadcastReceivers. This is only intended to be used by the CellBroadcastService and will + * do nothing if the caller does not have permission to broadcast + * {@link Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION}. * * @param context The context from which to send the broadcast * @param user The user from which to send the broadcast - * @param intent The Intent to broadcast; all receivers matching this Intent will - * receive the broadcast. - * @param receiverPermission String naming a permissions that a receiver must hold in order to - * receive your broadcast. If null, no permission is required. - * @param receiverAppOp The app op associated with the broadcast. If null, no appOp is - * required. If both receiverAppOp and receiverPermission are - * non-null, a receiver must have both of them to receive the - * broadcast + * @param smsCbMessage The SmsCbMessage to include with the intent * @param resultReceiver Your own BroadcastReceiver to treat as the final receiver of the * broadcast. * @param scheduler A custom Handler with which to schedule the resultReceiver * callback; if null it will be scheduled in the Context's main * thread. * @param initialCode An initial value for the result code. Often Activity.RESULT_OK. - * @param initialData An initial value for the result data. Often null. - * @param initialExtras An initial value for the result extras. Often null. + * @param slotIndex The slot index to include in the intent */ - public static void sendOrderedBroadcastForBackgroundReceivers(@NonNull Context context, - @Nullable UserHandle user, @NonNull Intent intent, @Nullable String receiverPermission, - @Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver, - @Nullable Handler scheduler, int initialCode, @Nullable String initialData, - @Nullable Bundle initialExtras) { - int status = context.checkCallingOrSelfPermission( - "android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS"); - if (status == PackageManager.PERMISSION_DENIED) { - throw new SecurityException( - "Caller does not have permission to send broadcast for background receivers"); - } - Intent backgroundIntent = new Intent(intent); + public static void sendSmsCbReceivedBroadcast(@NonNull Context context, + @Nullable UserHandle user, @NonNull SmsCbMessage smsCbMessage, + @Nullable BroadcastReceiver resultReceiver, @Nullable Handler scheduler, + int initialCode, int slotIndex) { + Intent backgroundIntent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION); + backgroundIntent.putExtra(EXTRA_MESSAGE, smsCbMessage); backgroundIntent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); + putPhoneIdAndSubIdExtra(context, backgroundIntent, slotIndex); + + String receiverPermission = Manifest.permission.RECEIVE_SMS; + String receiverAppOp = AppOpsManager.OPSTR_RECEIVE_SMS; if (user != null) { context.createContextAsUser(user, 0).sendOrderedBroadcast(backgroundIntent, receiverPermission, receiverAppOp, resultReceiver, scheduler, initialCode, - initialData, initialExtras); + null, null); } else { context.sendOrderedBroadcast(backgroundIntent, receiverPermission, - receiverAppOp, resultReceiver, scheduler, initialCode, initialData, - initialExtras); + receiverAppOp, resultReceiver, scheduler, initialCode, null, null); + } + } + + /** + * Put the phone ID and sub ID into an intent as extras. + */ + private static void putPhoneIdAndSubIdExtra(Context context, Intent intent, int phoneId) { + int subId = getSubIdForPhone(context, phoneId); + if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + intent.putExtra("subscription", subId); + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); + } + intent.putExtra("phone", phoneId); + intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, phoneId); + } + + /** + * Get the subscription ID for a phone ID, or INVALID_SUBSCRIPTION_ID if the phone does not + * have an active sub + * @param phoneId the phoneId to use + * @return the associated sub id + */ + private static int getSubIdForPhone(Context context, int phoneId) { + SubscriptionManager subMan = + (SubscriptionManager) context.getSystemService( + Context.TELEPHONY_SUBSCRIPTION_SERVICE); + int[] subIds = subMan.getSubscriptionIds(phoneId); + if (subIds != null) { + return subIds[0]; + } else { + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; } } } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index f886cf58b850..03e7361159e1 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -168,6 +168,16 @@ public class PhoneStateListener { public static final int LISTEN_SIGNAL_STRENGTHS = 0x00000100; /** + * Listen for always reported changes of the network signal strengths (cellular), + * even in some situations such as the screen of the device is off. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) + @SystemApi + public static final int LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH = 0x00000200; + + /** * Listen for changes to observed cell info. * * @see #onCellInfoChanged 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/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java index 4bb012aac769..bbae0273ef4e 100644 --- a/core/java/com/android/internal/net/VpnProfile.java +++ b/core/java/com/android/internal/net/VpnProfile.java @@ -16,6 +16,7 @@ package com.android.internal.net; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.net.ProxyInfo; import android.os.Build; @@ -23,21 +24,34 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import com.android.internal.annotations.VisibleForTesting; + import java.net.InetAddress; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; /** - * Parcel-like entity class for VPN profiles. To keep things simple, all - * fields are package private. Methods are provided for serialization, so - * storage can be implemented easily. Two rules are set for this class. - * First, all fields must be kept non-null. Second, always make a copy - * using clone() before modifying. + * Profile storage class for a platform VPN. + * + * <p>This class supports both the Legacy VPN, as well as application-configurable platform VPNs + * (such as IKEv2/IPsec). + * + * <p>This class is serialized and deserialized via the {@link #encode()} and {@link #decode()} + * functions for persistent storage in the Android Keystore. The encoding is entirely custom, but + * must be kept for backward compatibility for devices upgrading between Android versions. * * @hide */ -public class VpnProfile implements Cloneable, Parcelable { +public final class VpnProfile implements Cloneable, Parcelable { private static final String TAG = "VpnProfile"; + @VisibleForTesting static final String VALUE_DELIMITER = "\0"; + @VisibleForTesting static final String LIST_DELIMITER = ","; + // Match these constants with R.array.vpn_types. public static final int TYPE_PPTP = 0; public static final int TYPE_L2TP_IPSEC_PSK = 1; @@ -45,39 +59,85 @@ public class VpnProfile implements Cloneable, Parcelable { public static final int TYPE_IPSEC_XAUTH_PSK = 3; public static final int TYPE_IPSEC_XAUTH_RSA = 4; public static final int TYPE_IPSEC_HYBRID_RSA = 5; - public static final int TYPE_MAX = 5; + public static final int TYPE_IKEV2_IPSEC_USER_PASS = 6; + public static final int TYPE_IKEV2_IPSEC_PSK = 7; + public static final int TYPE_IKEV2_IPSEC_RSA = 8; + public static final int TYPE_MAX = 8; // Match these constants with R.array.vpn_proxy_settings. public static final int PROXY_NONE = 0; public static final int PROXY_MANUAL = 1; + private static final String ENCODED_NULL_PROXY_INFO = "\0\0\0\0"; + // Entity fields. @UnsupportedAppUsage - public final String key; // -1 + public final String key; // -1 + @UnsupportedAppUsage - public String name = ""; // 0 + public String name = ""; // 0 + @UnsupportedAppUsage - public int type = TYPE_PPTP; // 1 + public int type = TYPE_PPTP; // 1 + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public String server = ""; // 2 + public String server = ""; // 2 + @UnsupportedAppUsage - public String username = ""; // 3 - public String password = ""; // 4 - public String dnsServers = ""; // 5 - public String searchDomains = ""; // 6 - public String routes = ""; // 7 - public boolean mppe = true; // 8 - public String l2tpSecret = ""; // 9 - public String ipsecIdentifier = "";// 10 - public String ipsecSecret = ""; // 11 - public String ipsecUserCert = ""; // 12 - public String ipsecCaCert = ""; // 13 - public String ipsecServerCert = "";// 14 - public ProxyInfo proxy = null; // 15~18 + public String username = ""; // 3 + public String password = ""; // 4 + public String dnsServers = ""; // 5 + public String searchDomains = ""; // 6 + public String routes = ""; // 7 + public boolean mppe = true; // 8 + public String l2tpSecret = ""; // 9 + public String ipsecIdentifier = ""; // 10 + + /** + * The RSA private key or pre-shared key used for authentication. + * + * <p>If areAuthParamsInline is {@code true}, this String will be either: + * + * <ul> + * <li>If this is an IKEv2 RSA profile: a PKCS#8 encoded {@link java.security.PrivateKey} + * <li>If this is an IKEv2 PSK profile: a string value representing the PSK. + * </ul> + */ + public String ipsecSecret = ""; // 11 + + /** + * The RSA certificate to be used for digital signature authentication. + * + * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link + * java.security.X509Certificate} + */ + public String ipsecUserCert = ""; // 12 + + /** + * The RSA certificate that should be used to verify the server's end/target certificate. + * + * <p>If areAuthParamsInline is {@code true}, this String will be a pem-encoded {@link + * java.security.X509Certificate} + */ + public String ipsecCaCert = ""; // 13 + public String ipsecServerCert = ""; // 14 + public ProxyInfo proxy = null; // 15~18 + + /** + * The list of allowable algorithms. + * + * <p>This list is validated in the setter to ensure that encoding characters (list, value + * delimiters) are not present in the algorithm names. See {@link #validateAllowedAlgorithms()} + */ + private List<String> mAllowedAlgorithms = new ArrayList<>(); // 19 + public boolean isBypassable = false; // 20 + public boolean isMetered = false; // 21 + public int maxMtu = 1400; // 22 + public boolean areAuthParamsInline = false; // 23 // Helper fields. @UnsupportedAppUsage - public boolean saveLogin = false; + public transient boolean saveLogin = false; public VpnProfile(String key) { this.key = key; @@ -103,6 +163,34 @@ public class VpnProfile implements Cloneable, Parcelable { ipsecServerCert = in.readString(); saveLogin = in.readInt() != 0; proxy = in.readParcelable(null); + mAllowedAlgorithms = new ArrayList<>(); + in.readList(mAllowedAlgorithms, null); + isBypassable = in.readBoolean(); + isMetered = in.readBoolean(); + maxMtu = in.readInt(); + areAuthParamsInline = in.readBoolean(); + } + + /** + * Retrieves the list of allowed algorithms. + * + * <p>The contained elements are as listed in {@link IpSecAlgorithm} + */ + public List<String> getAllowedAlgorithms() { + return Collections.unmodifiableList(mAllowedAlgorithms); + } + + /** + * Validates and sets the list of algorithms that can be used for the IPsec transforms. + * + * @param allowedAlgorithms the list of allowable algorithms, as listed in {@link + * IpSecAlgorithm}. + * @throws IllegalArgumentException if any delimiters are used in algorithm names. See {@link + * #VALUE_DELIMITER} and {@link LIST_DELIMITER}. + */ + public void setAllowedAlgorithms(List<String> allowedAlgorithms) { + validateAllowedAlgorithms(allowedAlgorithms); + mAllowedAlgorithms = allowedAlgorithms; } @Override @@ -125,8 +213,18 @@ public class VpnProfile implements Cloneable, Parcelable { out.writeString(ipsecServerCert); out.writeInt(saveLogin ? 1 : 0); out.writeParcelable(proxy, flags); + out.writeList(mAllowedAlgorithms); + out.writeBoolean(isBypassable); + out.writeBoolean(isMetered); + out.writeInt(maxMtu); + out.writeBoolean(areAuthParamsInline); } + /** + * Decodes a VpnProfile instance from the encoded byte array. + * + * <p>See {@link #encode()} + */ @UnsupportedAppUsage public static VpnProfile decode(String key, byte[] value) { try { @@ -134,9 +232,11 @@ public class VpnProfile implements Cloneable, Parcelable { return null; } - String[] values = new String(value, StandardCharsets.UTF_8).split("\0", -1); - // There can be 14 - 19 Bytes in values.length. - if (values.length < 14 || values.length > 19) { + String[] values = new String(value, StandardCharsets.UTF_8).split(VALUE_DELIMITER, -1); + // Acceptable numbers of values are: + // 14-19: Standard profile, with option for serverCert, proxy + // 24: Standard profile with serverCert, proxy and platform-VPN parameters. + if ((values.length < 14 || values.length > 19) && values.length != 24) { return null; } @@ -164,13 +264,23 @@ public class VpnProfile implements Cloneable, Parcelable { String port = (values.length > 16) ? values[16] : ""; String exclList = (values.length > 17) ? values[17] : ""; String pacFileUrl = (values.length > 18) ? values[18] : ""; - if (pacFileUrl.isEmpty()) { + if (!host.isEmpty() || !port.isEmpty() || !exclList.isEmpty()) { profile.proxy = new ProxyInfo(host, port.isEmpty() ? 0 : Integer.parseInt(port), exclList); - } else { + } else if (!pacFileUrl.isEmpty()) { profile.proxy = new ProxyInfo(pacFileUrl); } - } // else profle.proxy = null + } // else profile.proxy = null + + // Either all must be present, or none must be. + if (values.length >= 24) { + profile.mAllowedAlgorithms = Arrays.asList(values[19].split(LIST_DELIMITER)); + profile.isBypassable = Boolean.parseBoolean(values[20]); + profile.isMetered = Boolean.parseBoolean(values[21]); + profile.maxMtu = Integer.parseInt(values[22]); + profile.areAuthParamsInline = Boolean.parseBoolean(values[23]); + } + profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty(); return profile; } catch (Exception e) { @@ -179,36 +289,52 @@ public class VpnProfile implements Cloneable, Parcelable { return null; } + /** + * Encodes a VpnProfile instance to a byte array for storage. + * + * <p>See {@link #decode(String, byte[])} + */ public byte[] encode() { StringBuilder builder = new StringBuilder(name); - builder.append('\0').append(type); - builder.append('\0').append(server); - builder.append('\0').append(saveLogin ? username : ""); - builder.append('\0').append(saveLogin ? password : ""); - builder.append('\0').append(dnsServers); - builder.append('\0').append(searchDomains); - builder.append('\0').append(routes); - builder.append('\0').append(mppe); - builder.append('\0').append(l2tpSecret); - builder.append('\0').append(ipsecIdentifier); - builder.append('\0').append(ipsecSecret); - builder.append('\0').append(ipsecUserCert); - builder.append('\0').append(ipsecCaCert); - builder.append('\0').append(ipsecServerCert); + builder.append(VALUE_DELIMITER).append(type); + builder.append(VALUE_DELIMITER).append(server); + builder.append(VALUE_DELIMITER).append(saveLogin ? username : ""); + builder.append(VALUE_DELIMITER).append(saveLogin ? password : ""); + builder.append(VALUE_DELIMITER).append(dnsServers); + builder.append(VALUE_DELIMITER).append(searchDomains); + builder.append(VALUE_DELIMITER).append(routes); + builder.append(VALUE_DELIMITER).append(mppe); + builder.append(VALUE_DELIMITER).append(l2tpSecret); + builder.append(VALUE_DELIMITER).append(ipsecIdentifier); + builder.append(VALUE_DELIMITER).append(ipsecSecret); + builder.append(VALUE_DELIMITER).append(ipsecUserCert); + builder.append(VALUE_DELIMITER).append(ipsecCaCert); + builder.append(VALUE_DELIMITER).append(ipsecServerCert); if (proxy != null) { - builder.append('\0').append(proxy.getHost() != null ? proxy.getHost() : ""); - builder.append('\0').append(proxy.getPort()); - builder.append('\0').append(proxy.getExclusionListAsString() != null ? - proxy.getExclusionListAsString() : ""); - builder.append('\0').append(proxy.getPacFileUrl().toString()); + builder.append(VALUE_DELIMITER).append(proxy.getHost() != null ? proxy.getHost() : ""); + builder.append(VALUE_DELIMITER).append(proxy.getPort()); + builder.append(VALUE_DELIMITER) + .append( + proxy.getExclusionListAsString() != null + ? proxy.getExclusionListAsString() + : ""); + builder.append(VALUE_DELIMITER).append(proxy.getPacFileUrl().toString()); + } else { + builder.append(ENCODED_NULL_PROXY_INFO); } + + builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, mAllowedAlgorithms)); + builder.append(VALUE_DELIMITER).append(isBypassable); + builder.append(VALUE_DELIMITER).append(isMetered); + builder.append(VALUE_DELIMITER).append(maxMtu); + builder.append(VALUE_DELIMITER).append(areAuthParamsInline); + return builder.toString().getBytes(StandardCharsets.UTF_8); } /** - * Tests if profile is valid for lockdown, which requires IPv4 address for - * both server and DNS. Server hostnames would require using DNS before - * connection. + * Tests if profile is valid for lockdown, which requires IPv4 address for both server and DNS. + * Server hostnames would require using DNS before connection. */ public boolean isValidLockdownProfile() { return isTypeValidForLockdown() @@ -238,10 +364,7 @@ public class VpnProfile implements Cloneable, Parcelable { return !TextUtils.isEmpty(dnsServers); } - /** - * Returns {@code true} if all DNS servers have numeric addresses, - * e.g. 8.8.8.8 - */ + /** Returns {@code true} if all DNS servers have numeric addresses, e.g. 8.8.8.8 */ public boolean areDnsAddressesNumeric() { try { for (String dnsServer : dnsServers.split(" +")) { @@ -253,6 +376,62 @@ public class VpnProfile implements Cloneable, Parcelable { return true; } + /** + * Validates that the provided list of algorithms does not contain illegal characters. + * + * @param allowedAlgorithms The list to be validated + */ + public static void validateAllowedAlgorithms(List<String> allowedAlgorithms) { + for (final String alg : allowedAlgorithms) { + if (alg.contains(VALUE_DELIMITER) || alg.contains(LIST_DELIMITER)) { + throw new IllegalArgumentException( + "Algorithm contained illegal ('\0' or ',') character"); + } + } + } + + /** Generates a hashcode over the VpnProfile. */ + @Override + public int hashCode() { + return Objects.hash( + key, type, server, username, password, dnsServers, searchDomains, routes, mppe, + l2tpSecret, ipsecIdentifier, ipsecSecret, ipsecUserCert, ipsecCaCert, ipsecServerCert, + proxy, mAllowedAlgorithms, isBypassable, isMetered, maxMtu, areAuthParamsInline); + } + + /** Checks VPN profiles for interior equality. */ + @Override + public boolean equals(Object obj) { + if (!(obj instanceof VpnProfile)) { + return false; + } + + final VpnProfile other = (VpnProfile) obj; + return Objects.equals(key, other.key) + && Objects.equals(name, other.name) + && type == other.type + && Objects.equals(server, other.server) + && Objects.equals(username, other.username) + && Objects.equals(password, other.password) + && Objects.equals(dnsServers, other.dnsServers) + && Objects.equals(searchDomains, other.searchDomains) + && Objects.equals(routes, other.routes) + && mppe == other.mppe + && Objects.equals(l2tpSecret, other.l2tpSecret) + && Objects.equals(ipsecIdentifier, other.ipsecIdentifier) + && Objects.equals(ipsecSecret, other.ipsecSecret) + && Objects.equals(ipsecUserCert, other.ipsecUserCert) + && Objects.equals(ipsecCaCert, other.ipsecCaCert) + && Objects.equals(ipsecServerCert, other.ipsecServerCert) + && Objects.equals(proxy, other.proxy) + && Objects.equals(mAllowedAlgorithms, other.mAllowedAlgorithms) + && isBypassable == other.isBypassable + && isMetered == other.isMetered + && maxMtu == other.maxMtu + && areAuthParamsInline == other.areAuthParamsInline; + } + + @NonNull public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() { @Override public VpnProfile createFromParcel(Parcel in) { diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 13d0c5c831b6..7adb27cd9e36 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -19,6 +19,8 @@ package com.android.internal.os; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ApplicationErrorReport; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.type.DefaultMimeMapFactory; import android.os.Build; @@ -34,6 +36,7 @@ import android.util.Slog; import com.android.internal.logging.AndroidConfig; import com.android.server.NetworkManagementSocketTagger; +import dalvik.annotation.compat.VersionCodes; import dalvik.system.RuntimeHooks; import dalvik.system.ThreadPrioritySetter; import dalvik.system.VMRuntime; @@ -64,8 +67,17 @@ public class RuntimeInit { private static volatile boolean mCrashing = false; + /* + * Native heap allocations will now have a non-zero tag in the most significant byte. + * See {@linktourl https://source.android.com/devices/tech/debug/tagged-pointers}. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = VersionCodes.Q) + private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id. + private static final native void nativeFinishInit(); private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup); + private static native void nativeDisableHeapPointerTagging(); private static int Clog_e(String tag, String msg, Throwable tr) { return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr); @@ -398,6 +410,20 @@ public class RuntimeInit { if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!"); } + private static void maybeDisableHeapPointerTagging(long[] disabledCompatChanges) { + // Heap tagging needs to be disabled before any additional threads are created, but the + // AppCompat framework is not initialized enough at this point. + // Check if the change is enabled manually. + if (disabledCompatChanges != null) { + for (int i = 0; i < disabledCompatChanges.length; i++) { + if (disabledCompatChanges[i] == NATIVE_HEAP_POINTER_TAGGING) { + nativeDisableHeapPointerTagging(); + break; + } + } + } + } + protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges, String[] argv, ClassLoader classLoader) { // If the application calls System.exit(), terminate the process @@ -410,6 +436,8 @@ public class RuntimeInit { VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion); VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges); + maybeDisableHeapPointerTagging(disabledCompatChanges); + final Arguments args = new Arguments(argv); // The end of of the RuntimeInit event (see #zygoteInit). diff --git a/core/java/com/android/internal/util/ConnectivityUtil.java b/core/java/com/android/internal/util/LocationPermissionChecker.java index b1d4fa0d3fd3..dc318b45a7e2 100644 --- a/core/java/com/android/internal/util/ConnectivityUtil.java +++ b/core/java/com/android/internal/util/LocationPermissionChecker.java @@ -33,28 +33,59 @@ import com.android.internal.annotations.VisibleForTesting; /** - * Utility methods for common functionality using by different networks. + * The methods used for location permission and location mode checking. * * @hide */ -public class ConnectivityUtil { +public class LocationPermissionChecker { - private static final String TAG = "ConnectivityUtil"; + private static final String TAG = "LocationPermissionChecker"; private final Context mContext; - private final AppOpsManager mAppOps; + private final AppOpsManager mAppOpsManager; private final UserManager mUserManager; + private final LocationManager mLocationManager; - public ConnectivityUtil(Context context) { + public LocationPermissionChecker(Context context) { mContext = context; - mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + mLocationManager = + (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); } /** - * API to determine if the caller has fine/coarse location permission (depending on - * config/targetSDK level) and the location mode is enabled for the user. SecurityException is - * thrown if the caller has no permission or the location mode is disabled. + * Check location permission granted by the caller. + * + * This API check if the location mode enabled for the caller and the caller has + * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION. + * + * @param pkgName package name of the application requesting access + * @param featureId The feature in the package + * @param uid The uid of the package + * @param message A message describing why the permission was checked. Only needed if this is + * not inside of a two-way binder call from the data receiver + * + * @return {@code true} returns if the caller has location permission and the location mode is + * enabled. + */ + public boolean checkLocationPermission(String pkgName, @Nullable String featureId, + int uid, @Nullable String message) { + try { + enforceLocationPermission(pkgName, featureId, uid, message); + return true; + } catch (SecurityException e) { + return false; + } + } + + /** + * Enforce the caller has location permission. + * + * This API determines if the location mode enabled for the caller and the caller has + * ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION. + * SecurityException is thrown if the caller has no permission or the location mode is disabled. + * * @param pkgName package name of the application requesting access * @param featureId The feature in the package * @param uid The uid of the package @@ -62,31 +93,21 @@ public class ConnectivityUtil { * not inside of a two-way binder call from the data receiver */ public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid, - @Nullable String message) - throws SecurityException { + @Nullable String message) throws SecurityException { + checkPackage(uid, pkgName); // Location mode must be enabled if (!isLocationModeEnabled()) { - // Location mode is disabled, scan results cannot be returned throw new SecurityException("Location mode is disabled for the device"); } // LocationAccess by App: caller must have Coarse/Fine Location permission to have access to // location information. - boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId, - uid, /* coarseForTargetSdkLessThanQ */ true, message); - - // If neither caller or app has location access, there is no need to check - // any other permissions. Deny access to scan results. - if (!canAppPackageUseLocation) { + if (!checkCallersLocationPermission(pkgName, featureId, + uid, /* coarseForTargetSdkLessThanQ */ true, message)) { throw new SecurityException("UID " + uid + " has no location permission"); } - // If the User or profile is current, permission is granted - // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. - if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) { - throw new SecurityException("UID " + uid + " profile not permitted"); - } } /** @@ -104,6 +125,7 @@ public class ConnectivityUtil { */ public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId, int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) { + boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid); String permissionType = Manifest.permission.ACCESS_FINE_LOCATION; @@ -111,8 +133,7 @@ public class ConnectivityUtil { // Having FINE permission implies having COARSE permission (but not the reverse) permissionType = Manifest.permission.ACCESS_COARSE_LOCATION; } - if (getUidPermission(permissionType, uid) - == PackageManager.PERMISSION_DENIED) { + if (getUidPermission(permissionType, uid) == PackageManager.PERMISSION_DENIED) { return false; } @@ -134,10 +155,8 @@ public class ConnectivityUtil { * Retrieves a handle to LocationManager (if not already done) and check if location is enabled. */ public boolean isLocationModeEnabled() { - LocationManager locationManager = - (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); try { - return locationManager.isLocationEnabledForUser(UserHandle.of( + return mLocationManager.isLocationEnabledForUser(UserHandle.of( getCurrentUser())); } catch (Exception e) { Log.e(TAG, "Failure to get location mode via API, falling back to settings", e); @@ -166,28 +185,15 @@ public class ConnectivityUtil { private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId, int uid, @Nullable String message) { - return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED; + return mAppOpsManager.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED; } - private void checkPackage(int uid, String pkgName) throws SecurityException { + private void checkPackage(int uid, String pkgName) + throws SecurityException { if (pkgName == null) { throw new SecurityException("Checking UID " + uid + " but Package Name is Null"); } - mAppOps.checkPackage(uid, pkgName); - } - - private boolean isCurrentProfile(int uid) { - UserHandle currentUser = UserHandle.of(getCurrentUser()); - UserHandle callingUser = UserHandle.getUserHandleForUid(uid); - return currentUser.equals(callingUser) - || mUserManager.isSameProfileGroup( - currentUser.getIdentifier(), callingUser.getIdentifier()); - } - - private boolean checkInteractAcrossUsersFull(int uid) { - return getUidPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid) - == PackageManager.PERMISSION_GRANTED; + mAppOpsManager.checkPackage(uid, pkgName); } @VisibleForTesting diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 8e0e1c6be688..551541e4329a 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -47,6 +47,7 @@ #include <signal.h> #include <dirent.h> #include <assert.h> +#include <bionic/malloc.h> #include <string> #include <vector> @@ -279,6 +280,14 @@ static void com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup(JNIE gCurRuntime->setExitWithoutCleanup(exitWithoutCleanup); } +static void com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging( + JNIEnv* env, jobject clazz) { + HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_NONE; + if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level))) { + ALOGE("ERROR: could not disable heap pointer tagging\n"); + } +} + /* * JNI registration. */ @@ -286,10 +295,12 @@ static void com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup(JNIE int register_com_android_internal_os_RuntimeInit(JNIEnv* env) { const JNINativeMethod methods[] = { - { "nativeFinishInit", "()V", - (void*) com_android_internal_os_RuntimeInit_nativeFinishInit }, - { "nativeSetExitWithoutCleanup", "(Z)V", - (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup }, + {"nativeFinishInit", "()V", + (void*)com_android_internal_os_RuntimeInit_nativeFinishInit}, + {"nativeSetExitWithoutCleanup", "(Z)V", + (void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup}, + {"nativeDisableHeapPointerTagging", "()V", + (void*)com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging}, }; return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit", methods, NELEM(methods)); diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index 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/proto/OWNERS b/core/proto/OWNERS index 74ced8921799..4892faaceafe 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -14,6 +14,7 @@ per-file settings_enums.proto=tmfang@google.com # Frameworks ogunwale@google.com jjaggi@google.com +per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com # Launcher hyunyoungs@google.com diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5c204aba49a1..6960aefd09cf 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2082,6 +2082,11 @@ <permission android:name="android.permission.READ_ACTIVE_EMERGENCY_SESSION" android:protectionLevel="signature" /> + <!-- @SystemApi Allows listen permission to always reported signal strength. + @hide Used internally. --> + <permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" + android:protectionLevel="signature|telephony" /> + <!-- @SystemApi Protects the ability to register any PhoneAccount with PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount corresponds to a device SIM. @@ -4695,6 +4700,19 @@ <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" android:protectionLevel="signature|installer" /> + <!-- Allows an app to log compat change usage. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.LOG_COMPAT_CHANGE" + android:protectionLevel="signature" /> + <!-- Allows an app to read compat change config. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" + android:protectionLevel="signature" /> + <!-- Allows an app to override compat change config. + @hide <p>Not for use by third-party applications.</p> --> + <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG" + android:protectionLevel="signature" /> + <!-- Allows input events to be monitored. Very dangerous! @hide --> <permission android:name="android.permission.MONITOR_INPUT" android:protectionLevel="signature" /> diff --git a/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java index 556471260141..6a0a3dcf3b68 100644 --- a/core/tests/utiltests/src/com/android/internal/util/ConnectivityUtilTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/LocationPermissionCheckerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.Manifest; +import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -47,8 +48,8 @@ import org.mockito.stubbing.Answer; import java.util.HashMap; -/** Unit tests for {@link ConnectivityUtil}. */ -public class ConnectivityUtilTest { +/** Unit tests for {@link LocationPermissionChecker}. */ +public class LocationPermissionCheckerTest { public static final String TAG = "ConnectivityUtilTest"; @@ -84,18 +85,7 @@ public class ConnectivityUtilTest { private boolean mThrowSecurityException; private Answer<Integer> mReturnPermission; private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>(); - - private class TestConnectivityUtil extends ConnectivityUtil { - - TestConnectivityUtil(Context context) { - super(context); - } - - @Override - protected int getCurrentUser() { - return mCurrentUser; - } - } + private LocationPermissionChecker mChecker; @Before public void setUp() { @@ -140,11 +130,12 @@ public class ConnectivityUtilTest { mThrowSecurityException = true; mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M; mIsLocationEnabled = false; - mCurrentUser = UserHandle.USER_SYSTEM; + mCurrentUser = ActivityManager.getCurrentUser(); mCoarseLocationPermission = PackageManager.PERMISSION_DENIED; mFineLocationPermission = PackageManager.PERMISSION_DENIED; mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED; mAllowFineLocationApps = AppOpsManager.MODE_ERRORED; + mChecker = new LocationPermissionChecker(mMockContext); } private void setupMockInterface() { @@ -188,8 +179,7 @@ public class ConnectivityUtilTest { mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; mUid = mCurrentUser; setupTestCase(); - new TestConnectivityUtil(mMockContext) - .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); + mChecker.enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); } @Test @@ -202,8 +192,7 @@ public class ConnectivityUtilTest { mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED; mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; setupTestCase(); - new TestConnectivityUtil(mMockContext) - .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); + mChecker.enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null); } @Test @@ -216,22 +205,8 @@ public class ConnectivityUtilTest { setupTestCase(); assertThrows(SecurityException.class, - () -> new TestConnectivityUtil(mMockContext) - .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); - } - - @Test - public void testenforceCanAccessScanResults_UserOrProfileNotCurrent() throws Exception { - mIsLocationEnabled = true; - mThrowSecurityException = false; - mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED; - mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED; - mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED; - setupTestCase(); - - assertThrows(SecurityException.class, - () -> new TestConnectivityUtil(mMockContext) - .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + () -> mChecker.enforceLocationPermission( + TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); } @Test @@ -240,8 +215,8 @@ public class ConnectivityUtilTest { mIsLocationEnabled = true; setupTestCase(); assertThrows(SecurityException.class, - () -> new TestConnectivityUtil(mMockContext) - .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + () -> mChecker.enforceLocationPermission( + TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); } @Test @@ -255,8 +230,8 @@ public class ConnectivityUtilTest { setupTestCase(); assertThrows(SecurityException.class, - () -> new TestConnectivityUtil(mMockContext) - .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + () -> mChecker.enforceLocationPermission( + TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString()); } @@ -271,8 +246,8 @@ public class ConnectivityUtilTest { setupTestCase(); assertThrows(SecurityException.class, - () -> new TestConnectivityUtil(mMockContext) - .enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); + () -> mChecker.enforceLocationPermission( + TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null)); } private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) { diff --git a/media/java/android/media/tv/DvbDeviceInfo.java b/media/java/android/media/tv/DvbDeviceInfo.java index 96c852812a74..54fc39e9db9e 100644 --- a/media/java/android/media/tv/DvbDeviceInfo.java +++ b/media/java/android/media/tv/DvbDeviceInfo.java @@ -16,6 +16,7 @@ package android.media.tv; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; @@ -23,9 +24,13 @@ import android.os.Parcelable; import android.util.Log; /** - * Simple container for information about DVB device. - * Not for third-party developers. + * A digital video broadcasting (DVB) device. * + * <p> Simple wrapper around a <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB + * v3</a> device. + * + * @see TvInputManager#getDvbDeviceList() + * @see TvInputManager#openDvbDevice(DvbDeviceInfo, int) * @hide */ @SystemApi @@ -67,17 +72,19 @@ public final class DvbDeviceInfo implements Parcelable { } /** - * Returns the adapter ID of DVB device, in terms of enumerating the DVB device adapters - * installed in the system. The adapter ID counts from zero. + * Returns the adapter ID. + * + * <p>DVB Adapters contain one or more devices. */ + @IntRange(from = 0) public int getAdapterId() { return mAdapterId; } /** - * Returns the device ID of DVB device, in terms of enumerating the DVB devices attached to - * the same device adapter. The device ID counts from zero. + * Returns the device ID. */ + @IntRange(from = 0) public int getDeviceId() { return mDeviceId; } diff --git a/media/java/android/media/tv/TvContentRating.java b/media/java/android/media/tv/TvContentRating.java index 6197c707c752..5babb1698c85 100644 --- a/media/java/android/media/tv/TvContentRating.java +++ b/media/java/android/media/tv/TvContentRating.java @@ -17,7 +17,6 @@ package android.media.tv; import android.annotation.NonNull; -import android.annotation.SystemApi; import android.text.TextUtils; import com.android.internal.util.Preconditions; @@ -179,6 +178,10 @@ import java.util.Objects; * <td>TV content rating system for Canada (French)</td> * </tr> * <tr> + * <td>DTMB</td> + * <td>DTMB content rating system</td> + * </tr> + * <tr> * <td>DVB</td> * <td>DVB content rating system</td> * </tr> @@ -199,10 +202,18 @@ import java.util.Objects; * <td>TV content rating system for South Korea</td> * </tr> * <tr> + * <td>NZ_TV</td> + * <td>TV content rating system for New Zealand</td> + * </tr> + * <tr> * <td>SG_TV</td> * <td>TV content rating system for Singapore</td> * </tr> * <tr> + * <td>TH_TV</td> + * <td>TV content rating system for Thailand</td> + * </tr> + * <tr> * <td>US_MV</td> * <td>Movie content rating system for the United States</td> * </tr> @@ -356,6 +367,67 @@ import java.util.Objects; * <td>Only to be viewed by adults</td> * </tr> * <tr> + * <td valign="top" rowspan="15">DTMB</td> + * <td>DTMB_4</td> + * <td>Recommended for ages 4 and over</td> + * </tr> + * <tr> + * <td>DTMB_5</td> + * <td>Recommended for ages 5 and over</td> + * </tr> + * <tr> + * <td>DTMB_6</td> + * <td>Recommended for ages 6 and over</td> + * </tr> + * <tr> + * <td>DTMB_7</td> + * <td>Recommended for ages 7 and over</td> + * </tr> + * <tr> + * <td>DTMB_8</td> + * <td>Recommended for ages 8 and over</td> + * </tr> + * <tr> + * <td>DTMB_9</td> + * <td>Recommended for ages 9 and over</td> + * </tr> + * <tr> + * <td>DTMB_10</td> + * <td>Recommended for ages 10 and over</td> + * </tr> + * <tr> + * <td>DTMB_11</td> + * <td>Recommended for ages 11 and over</td> + * </tr> + * <tr> + * <td>DTMB_12</td> + * <td>Recommended for ages 12 and over</td> + * </tr> + * <tr> + * <td>DTMB_13</td> + * <td>Recommended for ages 13 and over</td> + * </tr> + * <tr> + * <td>DTMB_14</td> + * <td>Recommended for ages 14 and over</td> + * </tr> + * <tr> + * <td>DTMB_15</td> + * <td>Recommended for ages 15 and over</td> + * </tr> + * <tr> + * <td>DTMB_16</td> + * <td>Recommended for ages 16 and over</td> + * </tr> + * <tr> + * <td>DTMB_17</td> + * <td>Recommended for ages 17 and over</td> + * </tr> + * <tr> + * <td>DTMB_18</td> + * <td>Recommended for ages 18 and over</td> + * </tr> + * <tr> * <td valign="top" rowspan="15">DVB</td> * <td>DVB_4</td> * <td>Recommended for ages 4 and over</td> @@ -648,6 +720,22 @@ import java.util.Objects; * <td>For adults only</td> * </tr> * <tr> + * <td valign="top" rowspan="3">NZ_TV</td> + * <td>NZ_TV_G</td> + * <td>Programmes which exclude material likely to be unsuitable for children. Programmes + * may not necessarily be designed for child viewers but should not contain material likely + * to alarm or distress them</td> + * </tr> + * <tr> + * <td>NZ_TV_PGR</td> + * <td>Programmes containing material more suited for mature audiences but not necessarily + * unsuitable for child viewers when subject to the guidance of a parent or an adult</td> + * </tr> + * <tr> + * <td>NZ_TV_AO</td> + * <td>Programmes containing adult themes and directed primarily at mature audiences</td> + * </tr> + * <tr> * <td valign="top" rowspan="6">SG_TV</td> * <td>SG_TV_G</td> * <td>Suitable for all ages</td> @@ -674,6 +762,31 @@ import java.util.Objects; * <td>Suitable for adults aged 21 and above</td> * </tr> * <tr> + * <td valign="top" rowspan="6">TH_TV</td> + * <td>TH_TV_4</td> + * <td>Suitable for audiences 3 to 5 years of age</td> + * </tr> + * <tr> + * <td>TH_TV_6</td> + * <td>Suitable for audiences 6 to 12 years of age</td> + * </tr> + * <tr> + * <td>TH_TV_10</td> + * <td>Suitable for all audiences</td> + * </tr> + * <tr> + * <td>TH_TV_13</td> + * <td>Parental guidance suggested for viewers age below 13</td> + * </tr> + * <tr> + * <td>TH_TV_18</td> + * <td>Parental guidance suggested for viewers age below 18</td> + * </tr> + * <tr> + * <td>TH_TV_19</td> + * <td>Not suitable for children and teenagers</td> + * </tr> + * <tr> * <td valign="top" rowspan="5">US_MV</td> * <td>US_MV_G</td> * <td>General audiences</td> diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 854ea43f17c3..ed4fe4bc6404 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -1664,7 +1664,7 @@ public final class TvInputManager { } /** - * Returns the list of currently available DVB devices on the system. + * Returns the list of currently available DVB frontend devices on the system. * * @return the list of {@link DvbDeviceInfo} objects representing available DVB devices. * @hide @@ -1681,16 +1681,17 @@ public final class TvInputManager { } /** - * Returns a {@link ParcelFileDescriptor} of a specified DVB device for a given - * {@link DvbDeviceInfo} + * Returns a {@link ParcelFileDescriptor} of a specified DVB device of a given type for a given + * {@link DvbDeviceInfo}. * * @param info A {@link DvbDeviceInfo} to open a DVB device. - * @param deviceType A DVB device type. The type can be {@link #DVB_DEVICE_DEMUX}, - * {@link #DVB_DEVICE_DVR} or {@link #DVB_DEVICE_FRONTEND}. + * @param deviceType A DVB device type. * @return a {@link ParcelFileDescriptor} of a specified DVB device for a given - * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo} - * failed to open. + * {@link DvbDeviceInfo}, or {@code null} if the given {@link DvbDeviceInfo} + * failed to open. * @throws IllegalArgumentException if {@code deviceType} is invalid or the device is not found. + + * @see <a href="https://www.linuxtv.org/docs/dvbapi/dvbapi.html">Linux DVB API v3</a> * @hide */ @SystemApi diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java index 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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index af2569dcb1f2..14c77955939f 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -108,6 +108,7 @@ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" /> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.CREATE_USERS" /> diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java index 8631e860e0a6..d3307324b3df 100644 --- a/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java +++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextController.java @@ -19,16 +19,12 @@ package com.android.keyguard; import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; import static android.telephony.PhoneStateListener.LISTEN_NONE; -import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM; - import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Handler; -import android.os.SystemProperties; -import android.telephony.CarrierConfigManager; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; @@ -37,20 +33,18 @@ import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; -import androidx.annotation.VisibleForTesting; - import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; -import com.android.internal.telephony.TelephonyProperties; import com.android.settingslib.WirelessUtils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.keyguard.WakefulnessLifecycle; -import java.util.ArrayList; import java.util.List; import java.util.Objects; +import androidx.annotation.VisibleForTesting; + /** * Controller that generates text including the carrier names and/or the status of all the SIM * interfaces in the device. Through a callback, the updates can be retrieved either as a list or @@ -73,8 +67,6 @@ public class CarrierTextController { private Context mContext; private CharSequence mSeparator; private WakefulnessLifecycle mWakefulnessLifecycle; - @VisibleForTesting - protected boolean mDisplayOpportunisticSubscriptionCarrierText; private final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { @Override @@ -173,9 +165,6 @@ public class CarrierTextController { mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class); mSimSlotsNumber = getTelephonyManager().getSupportedModemCount(); mSimErrorState = new boolean[mSimSlotsNumber]; - updateDisplayOpportunisticSubscriptionCarrierText(SystemProperties.getBoolean( - TelephonyProperties.DISPLAY_OPPORTUNISTIC_SUBSCRIPTION_CARRIER_TEXT_PROPERTY_NAME, - false)); } private TelephonyManager getTelephonyManager() { @@ -255,63 +244,8 @@ public class CarrierTextController { } } - /** - * @param subscriptions - */ - private void filterMobileSubscriptionInSameGroup(List<SubscriptionInfo> subscriptions) { - if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) { - SubscriptionInfo info1 = subscriptions.get(0); - SubscriptionInfo info2 = subscriptions.get(1); - if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) { - // If both subscriptions are primary, show both. - if (!info1.isOpportunistic() && !info2.isOpportunistic()) return; - - // If carrier required, always show signal bar of primary subscription. - // Otherwise, show whichever subscription is currently active for Internet. - boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig() - .getBoolean(CarrierConfigManager - .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN); - if (alwaysShowPrimary) { - subscriptions.remove(info1.isOpportunistic() ? info1 : info2); - } else { - subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription - ? info2 : info1); - } - - } - } - } - - /** - * updates if opportunistic sub carrier text should be displayed or not - * - */ - @VisibleForTesting - public void updateDisplayOpportunisticSubscriptionCarrierText(boolean isEnable) { - mDisplayOpportunisticSubscriptionCarrierText = isEnable; - } - protected List<SubscriptionInfo> getSubscriptionInfo() { - List<SubscriptionInfo> subs; - if (mDisplayOpportunisticSubscriptionCarrierText) { - SubscriptionManager subscriptionManager = ((SubscriptionManager) mContext - .getSystemService( - Context.TELEPHONY_SUBSCRIPTION_SERVICE)); - subs = subscriptionManager.getActiveSubscriptionInfoList(false); - if (subs == null) { - subs = new ArrayList<>(); - } else { - filterMobileSubscriptionInSameGroup(subs); - } - } else { - subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); - if (subs == null) { - subs = new ArrayList<>(); - } else { - filterMobileSubscriptionInSameGroup(subs); - } - } - return subs; + return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); } protected void updateCarrierText() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 8d858dcb25df..0ede50de03dc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -30,6 +30,7 @@ import static android.os.BatteryManager.EXTRA_PLUGGED; import static android.os.BatteryManager.EXTRA_STATUS; import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; +import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; @@ -77,6 +78,7 @@ import android.os.UserManager; import android.provider.Settings; import android.service.dreams.DreamService; import android.service.dreams.IDreamManager; +import android.telephony.CarrierConfigManager; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; @@ -257,6 +259,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private boolean mLogoutEnabled; // If the user long pressed the lock icon, disabling face auth for the current session. private boolean mLockIconPressed; + private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; /** * Short delay before restarting biometric authentication after a successful try @@ -392,9 +395,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } }; - private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { + @VisibleForTesting + public PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @Override public void onActiveDataSubscriptionIdChanged(int subId) { + mActiveMobileDataSubscription = subId; mHandler.sendEmptyMessage(MSG_SIM_SUBSCRIPTION_INFO_CHANGED); } }; @@ -496,7 +501,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } } - /** @return List of SubscriptionInfo records, maybe empty but never null */ + /** + * @return List of SubscriptionInfo records, maybe empty but never null. + */ public List<SubscriptionInfo> getSubscriptionInfo(boolean forceReload) { List<SubscriptionInfo> sil = mSubscriptionInfo; if (sil == null || forceReload) { @@ -508,7 +515,42 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { } else { mSubscriptionInfo = sil; } - return mSubscriptionInfo; + return new ArrayList<>(mSubscriptionInfo); + } + + /** + * This method returns filtered list of SubscriptionInfo from {@link #getSubscriptionInfo}. + * above. Maybe empty but never null. + * + * In DSDS mode if both subscriptions are grouped and one is opportunistic, we filter out one + * of them based on carrier config. e.g. In this case we should only show one carrier name + * on the status bar and quick settings. + */ + public List<SubscriptionInfo> getFilteredSubscriptionInfo(boolean forceReload) { + List<SubscriptionInfo> subscriptions = getSubscriptionInfo(false); + if (subscriptions.size() == MAX_PHONE_COUNT_DUAL_SIM) { + SubscriptionInfo info1 = subscriptions.get(0); + SubscriptionInfo info2 = subscriptions.get(1); + if (info1.getGroupUuid() != null && info1.getGroupUuid().equals(info2.getGroupUuid())) { + // If both subscriptions are primary, show both. + if (!info1.isOpportunistic() && !info2.isOpportunistic()) return subscriptions; + + // If carrier required, always show signal bar of primary subscription. + // Otherwise, show whichever subscription is currently active for Internet. + boolean alwaysShowPrimary = CarrierConfigManager.getDefaultConfig() + .getBoolean(CarrierConfigManager + .KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN); + if (alwaysShowPrimary) { + subscriptions.remove(info1.isOpportunistic() ? info1 : info2); + } else { + subscriptions.remove(info1.getSubscriptionId() == mActiveMobileDataSubscription + ? info2 : info1); + } + + } + } + + return subscriptions; } @Override @@ -1061,7 +1103,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(); @@ -2637,6 +2679,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { pw.println(" " + mSubscriptionInfo.get(i)); } } + pw.println(" Current active data subId=" + mActiveMobileDataSubscription); pw.println(" Service states:"); for (int subId : mServiceStates.keySet()) { pw.println(" " + subId + "=" + mServiceStates.get(subId)); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java index d1b3c3cb12d8..2a5ccdb0c0a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/OperatorNameView.java @@ -134,7 +134,7 @@ public class OperatorNameView extends TextView implements DemoMode, DarkReceiver private void updateText() { CharSequence displayText = null; - List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); + List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); final int N = subs.size(); for (int i = 0; i < N; i++) { int subId = subs.get(i).getSubscriptionId(); 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/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java index 0d6178b1176b..f2c0434a1a95 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java @@ -97,7 +97,7 @@ public class EmergencyCryptkeeperText extends TextView { boolean allSimsMissing = true; CharSequence displayText = null; - List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getSubscriptionInfo(false); + List<SubscriptionInfo> subs = mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false); final int N = subs.size(); for (int i = 0; i < N; i++) { int subId = subs.get(i).getSubscriptionId(); 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/CarrierTextControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java index 4d9ea29e9496..5a8ff4bfbeff 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextControllerTest.java @@ -19,7 +19,7 @@ package com.android.keyguard; import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; import static android.telephony.SubscriptionManager.DATA_ROAMING_ENABLE; -import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT_SOURCE; +import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertFalse; @@ -77,21 +77,16 @@ public class CarrierTextControllerTest extends SysuiTestCase { private static final CharSequence AIRPLANE_MODE_TEXT = "Airplane mode"; private static final String TEST_CARRIER = "TEST_CARRIER"; private static final String TEST_CARRIER_2 = "TEST_CARRIER_2"; - private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24"; private static final int TEST_CARRIER_ID = 1; private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(0, "", 0, - TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", - DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID, - TEST_CARRIER_ID, 0); - private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(0, "", 0, - TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", - DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID, + TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", + DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, null, TEST_CARRIER_ID, 0); private static final SubscriptionInfo TEST_SUBSCRIPTION_NULL = new SubscriptionInfo(0, "", 0, - TEST_CARRIER, null, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", DATA_ROAMING_DISABLE, + TEST_CARRIER, null, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_DISABLE, null, null, null, null, false, null, ""); private static final SubscriptionInfo TEST_SUBSCRIPTION_ROAMING = new SubscriptionInfo(0, "", 0, - TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT_SOURCE, 0xFFFFFF, "", + TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", DATA_ROAMING_ENABLE, null, null, null, null, false, null, ""); @Mock private WifiManager mWifiManager; @@ -136,7 +131,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { mKeyguardUpdateMonitor); // This should not start listening on any of the real dependencies mCarrierTextController.setListening(mCarrierTextCallback); - mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(false); } @Test @@ -145,7 +139,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); when(mKeyguardUpdateMonitor.getSimState(0)).thenReturn(IccCardConstants.State.READY); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); @@ -165,7 +159,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { reset(mCarrierTextCallback); List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); when(mKeyguardUpdateMonitor.getSimState(0)).thenReturn(IccCardConstants.State.READY); when(mKeyguardUpdateMonitor.getSimState(1)).thenReturn( IccCardConstants.State.CARD_IO_ERROR); @@ -198,7 +192,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { @Test public void testWrongSlots() { reset(mCarrierTextCallback); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn( + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn( new ArrayList<>()); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn( IccCardConstants.State.CARD_IO_ERROR); @@ -212,7 +206,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { @Test public void testMoreSlotsThanSubs() { reset(mCarrierTextCallback); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn( + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn( new ArrayList<>()); // STOPSHIP(b/130246708) This line makes sure that SubscriptionManager provides the @@ -262,7 +256,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); @@ -286,7 +280,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { List<SubscriptionInfo> list = new ArrayList<>(); list.add(TEST_SUBSCRIPTION_ROAMING); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); @@ -366,7 +360,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { @Test public void testCreateInfo_noSubscriptions() { reset(mCarrierTextCallback); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn( + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn( new ArrayList<>()); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = @@ -390,7 +384,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { list.add(TEST_SUBSCRIPTION); list.add(TEST_SUBSCRIPTION); when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(IccCardConstants.State.READY); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); @@ -415,7 +409,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSimState(anyInt())) .thenReturn(IccCardConstants.State.READY) .thenReturn(IccCardConstants.State.NOT_READY); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); @@ -440,7 +434,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { when(mKeyguardUpdateMonitor.getSimState(anyInt())) .thenReturn(IccCardConstants.State.NOT_READY) .thenReturn(IccCardConstants.State.READY); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); @@ -467,7 +461,7 @@ public class CarrierTextControllerTest extends SysuiTestCase { .thenReturn(IccCardConstants.State.READY) .thenReturn(IccCardConstants.State.NOT_READY) .thenReturn(IccCardConstants.State.READY); - when(mKeyguardUpdateMonitor.getSubscriptionInfo(anyBoolean())).thenReturn(list); + when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(anyBoolean())).thenReturn(list); mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = @@ -482,30 +476,6 @@ public class CarrierTextControllerTest extends SysuiTestCase { captor.getValue().carrierText); } - @Test - public void testCarrierText_GroupedSubWithOpportunisticCarrierText() { - reset(mCarrierTextCallback); - List<SubscriptionInfo> list = new ArrayList<>(); - list.add(TEST_SUBSCRIPTION); - list.add(TEST_SUBSCRIPTION_2); - when(mKeyguardUpdateMonitor.getSimState(anyInt())) - .thenReturn(IccCardConstants.State.READY); - - mKeyguardUpdateMonitor.mServiceStates = new HashMap<>(); - mCarrierTextController.updateDisplayOpportunisticSubscriptionCarrierText(true); - when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); - - ArgumentCaptor<CarrierTextController.CarrierTextCallbackInfo> captor = - ArgumentCaptor.forClass( - CarrierTextController.CarrierTextCallbackInfo.class); - - mCarrierTextController.updateCarrierText(); - mTestableLooper.processAllMessages(); - verify(mCarrierTextCallback).updateCarrierInfo(captor.capture()); - - assertEquals(TEST_CARRIER_2, captor.getValue().carrierText); - } - public static class TestCarrierTextController extends CarrierTextController { private KeyguardUpdateMonitor mKUM; diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index db6177a63f96..cfffcbc00aac 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -16,15 +16,18 @@ package com.android.keyguard; +import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; +import static android.telephony.SubscriptionManager.NAME_SOURCE_DEFAULT; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -42,6 +45,7 @@ import android.hardware.fingerprint.FingerprintManager; import android.os.Bundle; import android.os.UserManager; import android.telephony.ServiceState; +import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -62,6 +66,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @SmallTest @@ -73,7 +79,18 @@ import java.util.concurrent.atomic.AtomicBoolean; // new tests. @RunWithLooper(setAsMainLooper = true) public class KeyguardUpdateMonitorTest extends SysuiTestCase { - + private static final String TEST_CARRIER = "TEST_CARRIER"; + private static final String TEST_CARRIER_2 = "TEST_CARRIER_2"; + private static final int TEST_CARRIER_ID = 1; + private static final String TEST_GROUP_UUID = "59b5c870-fc4c-47a4-a99e-9db826b48b24"; + private static final SubscriptionInfo TEST_SUBSCRIPTION = new SubscriptionInfo(1, "", 0, + TEST_CARRIER, TEST_CARRIER, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", + DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", false, TEST_GROUP_UUID, + TEST_CARRIER_ID, 0); + private static final SubscriptionInfo TEST_SUBSCRIPTION_2 = new SubscriptionInfo(2, "", 0, + TEST_CARRIER, TEST_CARRIER_2, NAME_SOURCE_DEFAULT, 0xFFFFFF, "", + DATA_ROAMING_DISABLE, null, null, null, null, false, null, "", true, TEST_GROUP_UUID, + TEST_CARRIER_ID, 0); @Mock private KeyguardUpdateMonitor.StrongAuthTracker mStrongAuthTracker; @Mock @@ -92,6 +109,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { private DevicePolicyManager mDevicePolicyManager; @Mock private KeyguardBypassController mKeyguardBypassController; + @Mock + private SubscriptionManager mSubscriptionManager; private TestableLooper mTestableLooper; private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor; @@ -119,6 +138,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { context.addMockSystemService(FaceManager.class, mFaceManager); context.addMockSystemService(UserManager.class, mUserManager); context.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager); + context.addMockSystemService(SubscriptionManager.class, mSubscriptionManager); mTestableLooper = TestableLooper.get(this); mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(context); @@ -133,7 +153,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", @@ -441,6 +461,22 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue(); } + @Test + public void testGetSubscriptionInfo_whenInGroupedSubWithOpportunistic() { + List<SubscriptionInfo> list = new ArrayList<>(); + list.add(TEST_SUBSCRIPTION); + list.add(TEST_SUBSCRIPTION_2); + when(mSubscriptionManager.getActiveSubscriptionInfoList(anyBoolean())).thenReturn(list); + mKeyguardUpdateMonitor.mPhoneStateListener.onActiveDataSubscriptionIdChanged( + TEST_SUBSCRIPTION_2.getSubscriptionId()); + mTestableLooper.processAllMessages(); + + List<SubscriptionInfo> listToVerify = mKeyguardUpdateMonitor + .getFilteredSubscriptionInfo(false); + assertThat(listToVerify.size()).isEqualTo(1); + assertThat(listToVerify.get(0)).isEqualTo(TEST_SUBSCRIPTION_2); + } + private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) { int subscription = simInited ? 1/* mock subid=1 */ : SubscriptionManager.DUMMY_SUBSCRIPTION_ID_BASE; diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp index 264ce440f59f..e0adb34dad6c 100644 --- a/packages/Tethering/common/TetheringLib/Android.bp +++ b/packages/Tethering/common/TetheringLib/Android.bp @@ -19,7 +19,15 @@ aidl_interface { local_include_dir: "src", include_dirs: ["frameworks/base/core/java"], // For framework parcelables. srcs: [ - "src/android/net/*.aidl", + // @JavaOnlyStableParcelable aidl declarations must not be listed here, as this would cause + // compilation to fail (b/148001843). + "src/android/net/IIntResultListener.aidl", + "src/android/net/ITetheringConnector.aidl", + "src/android/net/ITetheringEventCallback.aidl", + "src/android/net/TetheringCallbackStartedParcel.aidl", + "src/android/net/TetheringConfigurationParcel.aidl", + "src/android/net/TetheringRequestParcel.aidl", + "src/android/net/TetherStatesParcel.aidl", ], backend: { ndk: { @@ -35,7 +43,9 @@ java_library { name: "framework-tethering", sdk_version: "system_current", srcs: [ + "src/android/net/TetheredClient.java", "src/android/net/TetheringManager.java", + "src/android/net/TetheringConstants.java", ":framework-tethering-annotations", ], static_libs: [ @@ -62,11 +72,16 @@ java_library { filegroup { name: "framework-tethering-srcs", srcs: [ + "src/android/net/TetheredClient.aidl", + "src/android/net/TetheredClient.java", "src/android/net/TetheringManager.java", + "src/android/net/TetheringConstants.java", "src/android/net/IIntResultListener.aidl", "src/android/net/ITetheringEventCallback.aidl", "src/android/net/ITetheringConnector.aidl", + "src/android/net/TetheringCallbackStartedParcel.aidl", "src/android/net/TetheringConfigurationParcel.aidl", + "src/android/net/TetheringRequestParcel.aidl", "src/android/net/TetherStatesParcel.aidl", ], path: "src" diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl index d30c39986984..5febe73288bf 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl +++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl @@ -17,6 +17,7 @@ package android.net; import android.net.IIntResultListener; import android.net.ITetheringEventCallback; +import android.net.TetheringRequestParcel; import android.os.ResultReceiver; /** @hide */ @@ -27,8 +28,8 @@ oneway interface ITetheringConnector { void setUsbTethering(boolean enable, String callerPkg, IIntResultListener receiver); - void startTethering(int type, in ResultReceiver receiver, boolean showProvisioningUi, - String callerPkg); + void startTethering(in TetheringRequestParcel request, String callerPkg, + IIntResultListener receiver); void stopTethering(int type, String callerPkg, IIntResultListener receiver); diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl index 28361954e11e..28a810dbfac3 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl +++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl @@ -18,6 +18,7 @@ package android.net; import android.net.Network; import android.net.TetheringConfigurationParcel; +import android.net.TetheringCallbackStartedParcel; import android.net.TetherStatesParcel; /** @@ -26,8 +27,8 @@ import android.net.TetherStatesParcel; */ oneway interface ITetheringEventCallback { - void onCallbackStarted(in Network network, in TetheringConfigurationParcel config, - in TetherStatesParcel states); + /** Called immediately after the callbacks are registered */ + void onCallbackStarted(in TetheringCallbackStartedParcel parcel); void onCallbackStopped(int errorCode); void onUpstreamChanged(in Network network); void onConfigurationChanged(in TetheringConfigurationParcel config); diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl new file mode 100644 index 000000000000..0b279b882367 --- /dev/null +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.aidl @@ -0,0 +1,18 @@ +/** + * 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; + +@JavaOnlyStableParcelable parcelable TetheredClient;
\ No newline at end of file diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java new file mode 100644 index 000000000000..651468846ca8 --- /dev/null +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java @@ -0,0 +1,212 @@ +/* + * 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 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.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; + +/** + * Information on a tethered downstream client. + * @hide + */ +@SystemApi +@TestApi +public final class TetheredClient implements Parcelable { + @NonNull + private final MacAddress mMacAddress; + @NonNull + private final List<AddressInfo> mAddresses; + // TODO: use an @IntDef here + private final int mTetheringType; + + public TetheredClient(@NonNull MacAddress macAddress, + @NonNull Collection<AddressInfo> addresses, int tetheringType) { + mMacAddress = macAddress; + mAddresses = new ArrayList<>(addresses); + mTetheringType = tetheringType; + } + + private TetheredClient(@NonNull Parcel in) { + this(in.readParcelable(null), in.createTypedArrayList(AddressInfo.CREATOR), in.readInt()); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mMacAddress, flags); + dest.writeTypedList(mAddresses); + dest.writeInt(mTetheringType); + } + + @NonNull + public MacAddress getMacAddress() { + return mMacAddress; + } + + @NonNull + public List<AddressInfo> getAddresses() { + return new ArrayList<>(mAddresses); + } + + public int getTetheringType() { + return mTetheringType; + } + + /** + * Return a new {@link TetheredClient} that has all the attributes of this instance, plus the + * {@link AddressInfo} of the provided {@link TetheredClient}. + * + * <p>Duplicate addresses are removed. + * @hide + */ + public TetheredClient addAddresses(@NonNull TetheredClient other) { + final HashSet<AddressInfo> newAddresses = new HashSet<>( + mAddresses.size() + other.mAddresses.size()); + newAddresses.addAll(mAddresses); + newAddresses.addAll(other.mAddresses); + return new TetheredClient(mMacAddress, newAddresses, mTetheringType); + } + + @Override + public int hashCode() { + return Objects.hash(mMacAddress, mAddresses, mTetheringType); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof TetheredClient)) return false; + final TetheredClient other = (TetheredClient) obj; + return mMacAddress.equals(other.mMacAddress) + && mAddresses.equals(other.mAddresses) + && mTetheringType == other.mTetheringType; + } + + /** + * Information on an lease assigned to a tethered client. + */ + public static final class AddressInfo implements Parcelable { + @NonNull + private final LinkAddress mAddress; + @Nullable + private final String mHostname; + // TODO: use LinkAddress expiration time once it is supported + private final long mExpirationTime; + + /** @hide */ + public AddressInfo(@NonNull LinkAddress address, @Nullable String hostname) { + this(address, hostname, 0); + } + + /** @hide */ + public AddressInfo(@NonNull LinkAddress address, String hostname, long expirationTime) { + this.mAddress = address; + this.mHostname = hostname; + this.mExpirationTime = expirationTime; + } + + private AddressInfo(Parcel in) { + this(in.readParcelable(null), in.readString(), in.readLong()); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeParcelable(mAddress, flags); + dest.writeString(mHostname); + dest.writeLong(mExpirationTime); + } + + @NonNull + public LinkAddress getAddress() { + return mAddress; + } + + @Nullable + public String getHostname() { + return mHostname; + } + + /** @hide TODO: use expiration time in LinkAddress */ + public long getExpirationTime() { + return mExpirationTime; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public int hashCode() { + return Objects.hash(mAddress, mHostname, mExpirationTime); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof AddressInfo)) return false; + final AddressInfo other = (AddressInfo) obj; + // Use .equals() for addresses as all changes, including address expiry changes, + // should be included. + return other.mAddress.equals(mAddress) + && Objects.equals(mHostname, other.mHostname) + && mExpirationTime == other.mExpirationTime; + } + + @NonNull + public static final Creator<AddressInfo> CREATOR = new Creator<AddressInfo>() { + @NonNull + @Override + public AddressInfo createFromParcel(@NonNull Parcel in) { + return new AddressInfo(in); + } + + @NonNull + @Override + public AddressInfo[] newArray(int size) { + return new AddressInfo[size]; + } + }; + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull + public static final Creator<TetheredClient> CREATOR = new Creator<TetheredClient>() { + @NonNull + @Override + public TetheredClient createFromParcel(@NonNull Parcel in) { + return new TetheredClient(in); + } + + @NonNull + @Override + public TetheredClient[] newArray(int size) { + return new TetheredClient[size]; + } + }; +} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl new file mode 100644 index 000000000000..14ee2d3e5d38 --- /dev/null +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl @@ -0,0 +1,32 @@ +/* + * 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 android.net.Network; +import android.net.TetheringConfigurationParcel; +import android.net.TetherStatesParcel; + +/** + * Initial information reported by tethering upon callback registration. + * @hide + */ +parcelable TetheringCallbackStartedParcel { + boolean tetheringSupported; + Network upstreamNetwork; + TetheringConfigurationParcel config; + TetherStatesParcel states; +}
\ No newline at end of file diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java new file mode 100644 index 000000000000..00cf98e0fc2d --- /dev/null +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.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.net; + +import android.os.ResultReceiver; + +/** + * Collections of constants for internal tethering usage. + * + * <p>These hidden constants are not in TetheringManager as they are not part of the API stubs + * generated for TetheringManager, which prevents the tethering module from linking them at + * build time. + * TODO: investigate changing the tethering build rules so that Tethering can reference hidden + * symbols from framework-tethering even when they are in a non-hidden class. + * @hide + */ +public class TetheringConstants { + /** + * Extra used for communicating with the TetherService. Includes the type of tethering to + * enable if any. + * + * {@hide} + */ + public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; + /** + * Extra used for communicating with the TetherService. Includes the type of tethering for + * which to cancel provisioning. + * + * {@hide} + */ + public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; + /** + * Extra used for communicating with the TetherService. True to schedule a recheck of tether + * provisioning. + * + * {@hide} + */ + public static final String EXTRA_SET_ALARM = "extraSetAlarm"; + /** + * Tells the TetherService to run a provision check now. + * + * {@hide} + */ + public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; + /** + * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} + * which will receive provisioning results. Can be left empty. + * + * {@hide} + */ + public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; +} diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index 11e57186c666..37ce1d57da36 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -15,9 +15,14 @@ */ package android.net; +import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.content.Context; -import android.net.ConnectivityManager.OnTetheringEventCallback; +import android.os.Bundle; import android.os.ConditionVariable; import android.os.IBinder; import android.os.RemoteException; @@ -25,6 +30,12 @@ import android.os.ResultReceiver; import android.util.ArrayMap; import android.util.Log; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; import java.util.concurrent.Executor; /** @@ -34,7 +45,8 @@ import java.util.concurrent.Executor; * * @hide */ -// TODO: make it @SystemApi +@SystemApi +@TestApi public class TetheringManager { private static final String TAG = TetheringManager.class.getSimpleName(); private static final int DEFAULT_TIMEOUT_MS = 60_000; @@ -44,7 +56,7 @@ public class TetheringManager { private final ITetheringConnector mConnector; private final TetheringCallbackInternal mCallback; private final Context mContext; - private final ArrayMap<OnTetheringEventCallback, ITetheringEventCallback> + private final ArrayMap<TetheringEventCallback, ITetheringEventCallback> mTetheringEventCallbacks = new ArrayMap<>(); private TetheringConfigurationParcel mTetheringConfiguration; @@ -72,7 +84,7 @@ public class TetheringManager { * gives a String[] listing all the interfaces currently in local-only * mode (ie, has DHCPv4+IPv6-ULA support and no packet forwarding) */ - public static final String EXTRA_ACTIVE_LOCAL_ONLY = "localOnlyArray"; + public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; /** * gives a String[] listing all the interfaces currently tethered @@ -119,33 +131,16 @@ public class TetheringManager { public static final int TETHERING_WIFI_P2P = 3; /** - * Extra used for communicating with the TetherService. Includes the type of tethering to - * enable if any. + * Ncm local tethering type. + * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback) */ - public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType"; + public static final int TETHERING_NCM = 4; /** - * Extra used for communicating with the TetherService. Includes the type of tethering for - * which to cancel provisioning. + * Ethernet tethering type. + * @see #startTethering(TetheringRequest, Executor, StartTetheringCallback) */ - public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType"; - - /** - * Extra used for communicating with the TetherService. True to schedule a recheck of tether - * provisioning. - */ - public static final String EXTRA_SET_ALARM = "extraSetAlarm"; - - /** - * Tells the TetherService to run a provision check now. - */ - public static final String EXTRA_RUN_PROVISION = "extraRunProvision"; - - /** - * Extra used for communicating with the TetherService. Contains the {@link ResultReceiver} - * which will receive provisioning results. Can be left empty. - */ - public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback"; + public static final int TETHERING_ETHERNET = 5; public static final int TETHER_ERROR_NO_ERROR = 0; public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; @@ -160,12 +155,14 @@ public class TetheringManager { public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; public static final int TETHER_ERROR_PROVISION_FAILED = 11; public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; - public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; + public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; /** * Create a TetheringManager object for interacting with the tethering service. + * + * {@hide} */ public TetheringManager(@NonNull final Context context, @NonNull final IBinder service) { mContext = context; @@ -229,10 +226,9 @@ public class TetheringManager { private final ConditionVariable mWaitForCallback = new ConditionVariable(); @Override - public void onCallbackStarted(Network network, TetheringConfigurationParcel config, - TetherStatesParcel states) { - mTetheringConfiguration = config; - mTetherStatesParcel = states; + public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { + mTetheringConfiguration = parcel.config; + mTetherStatesParcel = parcel.states; mWaitForCallback.open(); } @@ -275,6 +271,8 @@ public class TetheringManager { * * @param iface the interface name to tether. * @return error a {@code TETHER_ERROR} value indicating success or failure type + * + * {@hide} */ @Deprecated public int tether(@NonNull final String iface) { @@ -296,6 +294,8 @@ public class TetheringManager { * * @deprecated The only usages is PanService. It uses this for legacy reasons * and will migrate away as soon as possible. + * + * {@hide} */ @Deprecated public int untether(@NonNull final String iface) { @@ -320,6 +320,8 @@ public class TetheringManager { * #startTethering or #stopTethering which encapsulate proper entitlement logic. If the API is * used and an entitlement check is needed, downstream USB tethering will be enabled but will * not have any upstream. + * + * {@hide} */ @Deprecated public int setUsbTethering(final boolean enable) { @@ -338,27 +340,171 @@ public class TetheringManager { } /** + * Use with {@link #startTethering} to specify additional parameters when starting tethering. + */ + public static class TetheringRequest { + /** A configuration set for TetheringRequest. */ + private final TetheringRequestParcel mRequestParcel; + + private TetheringRequest(final TetheringRequestParcel request) { + mRequestParcel = request; + } + + /** Builder used to create TetheringRequest. */ + public static class Builder { + private final TetheringRequestParcel mBuilderParcel; + + /** Default constructor of Builder. */ + public Builder(final int type) { + mBuilderParcel = new TetheringRequestParcel(); + mBuilderParcel.tetheringType = type; + mBuilderParcel.localIPv4Address = null; + mBuilderParcel.exemptFromEntitlementCheck = false; + mBuilderParcel.showProvisioningUi = true; + } + + /** + * Configure tethering with static IPv4 assignment (with DHCP disabled). + * + * @param localIPv4Address The preferred local IPv4 address to use. + */ + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) + @NonNull + public Builder useStaticIpv4Addresses(@NonNull final LinkAddress localIPv4Address) { + mBuilderParcel.localIPv4Address = localIPv4Address; + return this; + } + + /** Start tethering without entitlement checks. */ + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) + @NonNull + public Builder setExemptFromEntitlementCheck(boolean exempt) { + mBuilderParcel.exemptFromEntitlementCheck = exempt; + return this; + } + + /** Start tethering without showing the provisioning UI. */ + @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) + @NonNull + public Builder setSilentProvisioning(boolean silent) { + mBuilderParcel.showProvisioningUi = silent; + return this; + } + + /** Build {@link TetheringRequest] with the currently set configuration. */ + @NonNull + public TetheringRequest build() { + return new TetheringRequest(mBuilderParcel); + } + } + + /** + * Get a TetheringRequestParcel from the configuration + * @hide + */ + public TetheringRequestParcel getParcel() { + return mRequestParcel; + } + + /** String of TetheringRequest detail. */ + public String toString() { + return "TetheringRequest [ type= " + mRequestParcel.tetheringType + + ", localIPv4Address= " + mRequestParcel.localIPv4Address + + ", exemptFromEntitlementCheck= " + + mRequestParcel.exemptFromEntitlementCheck + ", showProvisioningUi= " + + mRequestParcel.showProvisioningUi + " ]"; + } + } + + /** + * Callback for use with {@link #startTethering} to find out whether tethering succeeded. + */ + public abstract static class StartTetheringCallback { + /** + * Called when tethering has been successfully started. + */ + public void onTetheringStarted() {} + + /** + * Called when starting tethering failed. + * + * @param resultCode One of the {@code TETHER_ERROR_*} constants. + */ + public void onTetheringFailed(final int resultCode) {} + } + + /** * Starts tethering and runs tether provisioning for the given type if needed. If provisioning * fails, stopTethering will be called automatically. * + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. + * + * @param request a {@link TetheringRequest} which can specify the preferred configuration. + * @param executor {@link Executor} to specify the thread upon which the callback of + * TetheringRequest will be invoked. + * @param callback A callback that will be called to indicate the success status of the + * tethering start request. */ - // TODO: improve the usage of ResultReceiver, b/145096122 - public void startTethering(final int type, @NonNull final ResultReceiver receiver, - final boolean showProvisioningUi) { + @RequiresPermission(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) + public void startTethering(@NonNull final TetheringRequest request, + @NonNull final Executor executor, @NonNull final StartTetheringCallback callback) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "startTethering caller:" + callerPkg); + final IIntResultListener listener = new IIntResultListener.Stub() { + @Override + public void onResult(final int resultCode) { + executor.execute(() -> { + if (resultCode == TETHER_ERROR_NO_ERROR) { + callback.onTetheringStarted(); + } else { + callback.onTetheringFailed(resultCode); + } + }); + } + }; try { - mConnector.startTethering(type, receiver, showProvisioningUi, callerPkg); + mConnector.startTethering(request.getParcel(), callerPkg, listener); } catch (RemoteException e) { throw new IllegalStateException(e); } } /** + * Starts tethering and runs tether provisioning for the given type if needed. If provisioning + * fails, stopTethering will be called automatically. + * + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. + * + * @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants. + * @param executor {@link Executor} to specify the thread upon which the callback of + * TetheringRequest will be invoked. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) + public void startTethering(int type, @NonNull final Executor executor, + @NonNull final StartTetheringCallback callback) { + startTethering(new TetheringRequest.Builder(type).build(), executor, callback); + } + + /** * Stops tethering for the given type. Also cancels any provisioning rechecks for that type if * applicable. + * + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. */ + @RequiresPermission(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) public void stopTethering(final int type) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "stopTethering caller:" + callerPkg); @@ -375,11 +521,69 @@ public class TetheringManager { } /** + * Callback for use with {@link #getLatestTetheringEntitlementResult} to find out whether + * entitlement succeeded. + */ + public interface OnTetheringEntitlementResultListener { + /** + * Called to notify entitlement result. + * + * @param resultCode an int value of entitlement result. It may be one of + * {@link #TETHER_ERROR_NO_ERROR}, + * {@link #TETHER_ERROR_PROVISION_FAILED}, or + * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN}. + */ + void onTetheringEntitlementResult(int resultCode); + } + + /** * Request the latest value of the tethering entitlement check. * - * Note: Allow privileged apps who have TETHER_PRIVILEGED permission to access. If it turns - * out some such apps are observed to abuse this API, change to per-UID limits on this API - * if it's really needed. + * <p>This method will only return the latest entitlement result if it is available. If no + * cached entitlement result is available, and {@code showEntitlementUi} is false, + * {@link #TETHER_ERROR_ENTITLEMENT_UNKNOWN} will be returned. If {@code showEntitlementUi} is + * true, entitlement will be run. + * + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. + * + * @param type the downstream type of tethering. Must be one of {@code #TETHERING_*} constants. + * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check. + * @param executor the executor on which callback will be invoked. + * @param listener an {@link OnTetheringEntitlementResultListener} which will be called to + * notify the caller of the result of entitlement check. The listener may be called zero + * or one time. + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) + public void requestLatestTetheringEntitlementResult(int type, boolean showEntitlementUi, + @NonNull Executor executor, + @NonNull final OnTetheringEntitlementResultListener listener) { + if (listener == null) { + throw new IllegalArgumentException( + "OnTetheringEntitlementResultListener cannot be null."); + } + + ResultReceiver wrappedListener = new ResultReceiver(null /* handler */) { + @Override + protected void onReceiveResult(int resultCode, Bundle resultData) { + executor.execute(() -> { + listener.onTetheringEntitlementResult(resultCode); + }); + } + }; + + requestLatestTetheringEntitlementResult(type, wrappedListener, + showEntitlementUi); + } + + /** + * Helper function of #requestLatestTetheringEntitlementResult to remain backwards compatible + * with ConnectivityManager#getLatestTetheringEntitlementResult + * + * {@hide} */ // TODO: improve the usage of ResultReceiver, b/145096122 public void requestLatestTetheringEntitlementResult(final int type, @@ -396,25 +600,161 @@ public class TetheringManager { } /** + * Callback for use with {@link registerTetheringEventCallback} to find out tethering + * upstream status. + */ + public abstract static class TetheringEventCallback { + /** + * Called when tethering supported status changed. + * + * <p>This will be called immediately after the callback is registered, and may be called + * multiple times later upon changes. + * + * <p>Tethering may be disabled via system properties, device configuration, or device + * policy restrictions. + * + * @param supported The new supported status + */ + public void onTetheringSupported(boolean supported) {} + + /** + * Called when tethering upstream changed. + * + * <p>This will be called immediately after the callback is registered, and may be called + * multiple times later upon changes. + * + * @param network the {@link Network} of tethering upstream. Null means tethering doesn't + * have any upstream. + */ + public void onUpstreamChanged(@Nullable Network network) {} + + /** + * Called when there was a change in tethering interface regular expressions. + * + * <p>This will be called immediately after the callback is registered, and may be called + * multiple times later upon changes. + * @param reg The new regular expressions. + * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism. + */ + @Deprecated + public void onTetherableInterfaceRegexpsChanged(@NonNull TetheringInterfaceRegexps reg) {} + + /** + * Called when there was a change in the list of tetherable interfaces. + * + * <p>This will be called immediately after the callback is registered, and may be called + * multiple times later upon changes. + * @param interfaces The list of tetherable interfaces. + */ + public void onTetherableInterfacesChanged(@NonNull List<String> interfaces) {} + + /** + * Called when there was a change in the list of tethered interfaces. + * + * <p>This will be called immediately after the callback is registered, and may be called + * multiple times later upon changes. + * @param interfaces The list of tethered interfaces. + */ + public void onTetheredInterfacesChanged(@NonNull List<String> interfaces) {} + + /** + * Called when an error occurred configuring tethering. + * + * <p>This will be called immediately after the callback is registered if the latest status + * on the interface is an error, and may be called multiple times later upon changes. + * @param ifName Name of the interface. + * @param error One of {@code TetheringManager#TETHER_ERROR_*}. + */ + public void onError(@NonNull String ifName, int error) {} + + /** + * Called when the list of tethered clients changes. + * + * <p>This callback provides best-effort information on connected clients based on state + * known to the system, however the list cannot be completely accurate (and should not be + * used for security purposes). For example, clients behind a bridge and using static IP + * assignments are not visible to the tethering device; or even when using DHCP, such + * clients may still be reported by this callback after disconnection as the system cannot + * determine if they are still connected. + * @param clients The new set of tethered clients; the collection is not ordered. + */ + public void onClientsChanged(@NonNull Collection<TetheredClient> clients) {} + } + + /** + * Regular expressions used to identify tethering interfaces. + * @deprecated Referencing interfaces by regular expressions is a deprecated mechanism. + */ + @Deprecated + public static class TetheringInterfaceRegexps { + private final String[] mTetherableBluetoothRegexs; + private final String[] mTetherableUsbRegexs; + private final String[] mTetherableWifiRegexs; + + public TetheringInterfaceRegexps(@NonNull String[] tetherableBluetoothRegexs, + @NonNull String[] tetherableUsbRegexs, @NonNull String[] tetherableWifiRegexs) { + mTetherableBluetoothRegexs = tetherableBluetoothRegexs.clone(); + mTetherableUsbRegexs = tetherableUsbRegexs.clone(); + mTetherableWifiRegexs = tetherableWifiRegexs.clone(); + } + + @NonNull + public List<String> getTetherableBluetoothRegexs() { + return Collections.unmodifiableList(Arrays.asList(mTetherableBluetoothRegexs)); + } + + @NonNull + public List<String> getTetherableUsbRegexs() { + return Collections.unmodifiableList(Arrays.asList(mTetherableUsbRegexs)); + } + + @NonNull + public List<String> getTetherableWifiRegexs() { + return Collections.unmodifiableList(Arrays.asList(mTetherableWifiRegexs)); + } + + @Override + public int hashCode() { + return Objects.hash(mTetherableBluetoothRegexs, mTetherableUsbRegexs, + mTetherableWifiRegexs); + } + + @Override + public boolean equals(@Nullable Object obj) { + if (!(obj instanceof TetheringInterfaceRegexps)) return false; + final TetheringInterfaceRegexps other = (TetheringInterfaceRegexps) obj; + return Arrays.equals(mTetherableBluetoothRegexs, other.mTetherableBluetoothRegexs) + && Arrays.equals(mTetherableUsbRegexs, other.mTetherableUsbRegexs) + && Arrays.equals(mTetherableWifiRegexs, other.mTetherableWifiRegexs); + } + } + + /** * Start listening to tethering change events. Any new added callback will receive the last * tethering status right away. If callback is registered, - * {@link OnTetheringEventCallback#onUpstreamChanged} will immediately be called. If tethering + * {@link TetheringEventCallback#onUpstreamChanged} will immediately be called. If tethering * has no upstream or disabled, the argument of callback will be null. The same callback object * cannot be registered twice. * * @param executor the executor on which callback will be invoked. * @param callback the callback to be called when tethering has change events. */ + @RequiresPermission(Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull Executor executor, - @NonNull OnTetheringEventCallback callback) { + @NonNull TetheringEventCallback callback) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "registerTetheringEventCallback caller:" + callerPkg); synchronized (mTetheringEventCallbacks) { - if (!mTetheringEventCallbacks.containsKey(callback)) { + if (mTetheringEventCallbacks.containsKey(callback)) { throw new IllegalArgumentException("callback was already registered."); } final ITetheringEventCallback remoteCallback = new ITetheringEventCallback.Stub() { + // Only accessed with a lock on this object + private final HashMap<String, Integer> mErrorStates = new HashMap<>(); + private String[] mLastTetherableInterfaces = null; + private String[] mLastTetheredInterfaces = null; + @Override public void onUpstreamChanged(Network network) throws RemoteException { executor.execute(() -> { @@ -422,11 +762,45 @@ public class TetheringManager { }); } + private synchronized void sendErrorCallbacks(final TetherStatesParcel newStates) { + for (int i = 0; i < newStates.erroredIfaceList.length; i++) { + final String iface = newStates.erroredIfaceList[i]; + final Integer lastError = mErrorStates.get(iface); + final int newError = newStates.lastErrorList[i]; + if (newError != TETHER_ERROR_NO_ERROR + && !Objects.equals(lastError, newError)) { + callback.onError(iface, newError); + } + mErrorStates.put(iface, newError); + } + } + + private synchronized void maybeSendTetherableIfacesChangedCallback( + final TetherStatesParcel newStates) { + if (Arrays.equals(mLastTetherableInterfaces, newStates.availableList)) return; + mLastTetherableInterfaces = newStates.availableList.clone(); + callback.onTetherableInterfacesChanged( + Collections.unmodifiableList(Arrays.asList(mLastTetherableInterfaces))); + } + + private synchronized void maybeSendTetheredIfacesChangedCallback( + final TetherStatesParcel newStates) { + if (Arrays.equals(mLastTetheredInterfaces, newStates.tetheredList)) return; + mLastTetheredInterfaces = newStates.tetheredList.clone(); + callback.onTetheredInterfacesChanged( + Collections.unmodifiableList(Arrays.asList(mLastTetheredInterfaces))); + } + + // Called immediately after the callbacks are registered. @Override - public void onCallbackStarted(Network network, TetheringConfigurationParcel config, - TetherStatesParcel states) { + public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { executor.execute(() -> { - callback.onUpstreamChanged(network); + callback.onTetheringSupported(parcel.tetheringSupported); + callback.onUpstreamChanged(parcel.upstreamNetwork); + sendErrorCallbacks(parcel.states); + sendRegexpsChanged(parcel.config); + maybeSendTetherableIfacesChangedCallback(parcel.states); + maybeSendTetheredIfacesChangedCallback(parcel.states); }); } @@ -437,11 +811,26 @@ public class TetheringManager { }); } + private void sendRegexpsChanged(TetheringConfigurationParcel parcel) { + callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps( + parcel.tetherableBluetoothRegexs, + parcel.tetherableUsbRegexs, + parcel.tetherableWifiRegexs)); + } + @Override - public void onConfigurationChanged(TetheringConfigurationParcel config) { } + public void onConfigurationChanged(TetheringConfigurationParcel config) { + executor.execute(() -> sendRegexpsChanged(config)); + } @Override - public void onTetherStatesChanged(TetherStatesParcel states) { } + public void onTetherStatesChanged(TetherStatesParcel states) { + executor.execute(() -> { + sendErrorCallbacks(states); + maybeSendTetherableIfacesChangedCallback(states); + maybeSendTetheredIfacesChangedCallback(states); + }); + } }; try { mConnector.registerTetheringEventCallback(remoteCallback, callerPkg); @@ -458,7 +847,11 @@ public class TetheringManager { * * @param callback previously registered callback. */ - public void unregisterTetheringEventCallback(@NonNull final OnTetheringEventCallback callback) { + @RequiresPermission(anyOf = { + Manifest.permission.TETHER_PRIVILEGED, + Manifest.permission.ACCESS_NETWORK_STATE + }) + public void unregisterTetheringEventCallback(@NonNull final TetheringEventCallback callback) { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "unregisterTetheringEventCallback caller:" + callerPkg); @@ -482,6 +875,7 @@ public class TetheringManager { * @param iface The name of the interface of interest * @return error The error code of the last error tethering or untethering the named * interface + * @hide */ public int getLastTetherError(@NonNull final String iface) { mCallback.waitForStarted(); @@ -503,6 +897,7 @@ public class TetheringManager { * * @return an array of 0 or more regular expression Strings defining * what interfaces are considered tetherable usb interfaces. + * @hide */ public @NonNull String[] getTetherableUsbRegexs() { mCallback.waitForStarted(); @@ -516,6 +911,7 @@ public class TetheringManager { * * @return an array of 0 or more regular expression Strings defining * what interfaces are considered tetherable wifi interfaces. + * @hide */ public @NonNull String[] getTetherableWifiRegexs() { mCallback.waitForStarted(); @@ -529,6 +925,7 @@ public class TetheringManager { * * @return an array of 0 or more regular expression Strings defining * what interfaces are considered tetherable bluetooth interfaces. + * @hide */ public @NonNull String[] getTetherableBluetoothRegexs() { mCallback.waitForStarted(); @@ -540,6 +937,7 @@ public class TetheringManager { * device configuration and current interface existence. * * @return an array of 0 or more Strings of tetherable interface names. + * @hide */ public @NonNull String[] getTetherableIfaces() { mCallback.waitForStarted(); @@ -552,6 +950,7 @@ public class TetheringManager { * Get the set of tethered interfaces. * * @return an array of 0 or more String of currently tethered interface names. + * @hide */ public @NonNull String[] getTetheredIfaces() { mCallback.waitForStarted(); @@ -570,6 +969,7 @@ public class TetheringManager { * * @return an array of 0 or more String indicating the interface names * which failed to tether. + * @hide */ public @NonNull String[] getTetheringErroredIfaces() { mCallback.waitForStarted(); @@ -582,6 +982,7 @@ public class TetheringManager { * Get the set of tethered dhcp ranges. * * @deprecated This API just return the default value which is not used in DhcpServer. + * @hide */ @Deprecated public @NonNull String[] getTetheredDhcpRanges() { @@ -595,6 +996,7 @@ public class TetheringManager { * due to device configuration. * * @return a boolean - {@code true} indicating Tethering is supported. + * @hide */ public boolean isTetheringSupported() { final String callerPkg = mContext.getOpPackageName(); @@ -613,7 +1015,14 @@ public class TetheringManager { /** * Stop all active tethering. + * + * <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will + * fail if a tethering entitlement check is required. */ + @RequiresPermission(anyOf = { + android.Manifest.permission.TETHER_PRIVILEGED, + android.Manifest.permission.WRITE_SETTINGS + }) public void stopAllTethering() { final String callerPkg = mContext.getOpPackageName(); Log.i(TAG, "stopAllTethering caller:" + callerPkg); diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl new file mode 100644 index 000000000000..bf19d85f6a83 --- /dev/null +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringRequestParcel.aidl @@ -0,0 +1,30 @@ +/* + * 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 android.net.LinkAddress; + +/** + * Configuration details for requesting tethering. + * @hide + */ +parcelable TetheringRequestParcel { + int tetheringType; + LinkAddress localIPv4Address; + boolean exemptFromEntitlementCheck; + boolean showProvisioningUi; +} diff --git a/packages/Tethering/res/values/config.xml b/packages/Tethering/res/values/config.xml index 6fa1f77bdd38..379cd180d483 100644 --- a/packages/Tethering/res/values/config.xml +++ b/packages/Tethering/res/values/config.xml @@ -29,6 +29,12 @@ </string-array> <!-- List of regexpressions describing the interface (if any) that represent tetherable + NCM interfaces. If the device doesn't want to support tethering over NCM this should + be empty. --> + <string-array translatable="false" name="config_tether_ncm_regexs"> + </string-array> + + <!-- List of regexpressions describing the interface (if any) that represent tetherable Wifi interfaces. If the device doesn't want to support tethering over Wifi this should be empty. An example would be "softap.*" --> <string-array translatable="false" name="config_tether_wifi_regexs"> diff --git a/packages/Tethering/res/values/overlayable.xml b/packages/Tethering/res/values/overlayable.xml index e089d9d19950..fe025c7ac993 100644 --- a/packages/Tethering/res/values/overlayable.xml +++ b/packages/Tethering/res/values/overlayable.xml @@ -17,6 +17,7 @@ <overlayable name="TetheringConfig"> <policy type="product|system|vendor"> <item type="array" name="config_tether_usb_regexs"/> + <item type="array" name="config_tether_ncm_regexs" /> <item type="array" name="config_tether_wifi_regexs"/> <item type="array" name="config_tether_wifi_p2p_regexs"/> <item type="array" name="config_tether_bluetooth_regexs"/> diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java index 0491ad7c3413..190d25098644 100644 --- a/packages/Tethering/src/android/net/ip/IpServer.java +++ b/packages/Tethering/src/android/net/ip/IpServer.java @@ -93,6 +93,8 @@ public class IpServer extends StateMachine { private static final int WIFI_HOST_IFACE_PREFIX_LENGTH = 24; private static final String WIFI_P2P_IFACE_ADDR = "192.168.49.1"; private static final int WIFI_P2P_IFACE_PREFIX_LENGTH = 24; + private static final String ETHERNET_IFACE_ADDR = "192.168.50.1"; + private static final int ETHERNET_IFACE_PREFIX_LENGTH = 24; // TODO: have PanService use some visible version of this constant private static final String BLUETOOTH_IFACE_ADDR = "192.168.44.1"; @@ -416,7 +418,8 @@ public class IpServer extends StateMachine { final Inet4Address srvAddr; int prefixLen = 0; try { - if (mInterfaceType == TetheringManager.TETHERING_USB) { + if (mInterfaceType == TetheringManager.TETHERING_USB + || mInterfaceType == TetheringManager.TETHERING_NCM) { srvAddr = (Inet4Address) parseNumericAddress(USB_NEAR_IFACE_ADDR); prefixLen = USB_PREFIX_LENGTH; } else if (mInterfaceType == TetheringManager.TETHERING_WIFI) { @@ -425,6 +428,10 @@ public class IpServer extends StateMachine { } else if (mInterfaceType == TetheringManager.TETHERING_WIFI_P2P) { srvAddr = (Inet4Address) parseNumericAddress(WIFI_P2P_IFACE_ADDR); prefixLen = WIFI_P2P_IFACE_PREFIX_LENGTH; + } else if (mInterfaceType == TetheringManager.TETHERING_ETHERNET) { + // TODO: randomize address for tethering too, similarly to wifi + srvAddr = (Inet4Address) parseNumericAddress(ETHERNET_IFACE_ADDR); + prefixLen = ETHERNET_IFACE_PREFIX_LENGTH; } else { // BT configures the interface elsewhere: only start DHCP. // TODO: make all tethering types behave the same way, and delete the bluetooth diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java index 1cabc8d0b5b7..e81d6ac7a588 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java @@ -16,14 +16,14 @@ package com.android.server.connectivity.tethering; -import static android.net.TetheringManager.EXTRA_ADD_TETHER_TYPE; -import static android.net.TetheringManager.EXTRA_PROVISION_CALLBACK; -import static android.net.TetheringManager.EXTRA_RUN_PROVISION; +import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; +import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; +import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_INVALID; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; +import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED; @@ -577,7 +577,7 @@ public class EntitlementManager { private static String errorString(int value) { switch (value) { - case TETHER_ERROR_ENTITLEMENT_UNKONWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN"; + case TETHER_ERROR_ENTITLEMENT_UNKNOWN: return "TETHER_ERROR_ENTITLEMENT_UNKONWN"; case TETHER_ERROR_NO_ERROR: return "TETHER_ERROR_NO_ERROR"; case TETHER_ERROR_PROVISION_FAILED: return "TETHER_ERROR_PROVISION_FAILED"; default: @@ -657,7 +657,7 @@ public class EntitlementManager { } final int cacheValue = mEntitlementCacheValue.get( - downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN); + downstream, TETHER_ERROR_ENTITLEMENT_UNKNOWN); if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) { receiver.send(cacheValue, null); } else { diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java index 5bf41ce25b2b..5b35bb6e713d 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java @@ -19,6 +19,7 @@ package com.android.server.connectivity.tethering; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; +import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; @@ -29,7 +30,9 @@ import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; import static android.net.TetheringManager.EXTRA_ERRORED_TETHER; import static android.net.TetheringManager.TETHERING_BLUETOOTH; +import static android.net.TetheringManager.TETHERING_ETHERNET; import static android.net.TetheringManager.TETHERING_INVALID; +import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHERING_WIFI_P2P; @@ -66,6 +69,8 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; +import android.net.EthernetManager; +import android.net.IIntResultListener; import android.net.INetd; import android.net.ITetheringEventCallback; import android.net.IpPrefix; @@ -74,7 +79,9 @@ import android.net.LinkProperties; import android.net.Network; import android.net.NetworkInfo; import android.net.TetherStatesParcel; +import android.net.TetheringCallbackStartedParcel; import android.net.TetheringConfigurationParcel; +import android.net.TetheringRequestParcel; import android.net.ip.IpServer; import android.net.shared.NetdUtils; import android.net.util.BaseNetdUnsolicitedEventListener; @@ -107,6 +114,7 @@ import android.util.SparseArray; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.MessageUtils; @@ -209,6 +217,13 @@ public class Tethering { private boolean mDataSaverEnabled = false; private String mWifiP2pTetherInterface = null; + @GuardedBy("mPublicSync") + private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest; + @GuardedBy("mPublicSync") + private String mConfiguredEthernetIface; + @GuardedBy("mPublicSync") + private EthernetCallback mEthernetCallback; + public Tethering(TetheringDependencies deps) { mLog.mark("Tethering.constructed"); mDeps = deps; @@ -405,6 +420,8 @@ public class Tethering { return TETHERING_USB; } else if (cfg.isBluetooth(iface)) { return TETHERING_BLUETOOTH; + } else if (cfg.isNcm(iface)) { + return TETHERING_NCM; } return TETHERING_INVALID; } @@ -423,9 +440,10 @@ public class Tethering { } } - void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) { - mEntitlementMgr.startProvisioningIfNeeded(type, showProvisioningUi); - enableTetheringInternal(type, true /* enabled */, receiver); + void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) { + mEntitlementMgr.startProvisioningIfNeeded(request.tetheringType, + request.showProvisioningUi); + enableTetheringInternal(request.tetheringType, true /* enabled */, listener); } void stopTethering(int type) { @@ -437,29 +455,40 @@ public class Tethering { * Enables or disables tethering for the given type. If provisioning is required, it will * schedule provisioning rechecks for the specified interface. */ - private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) { + private void enableTetheringInternal(int type, boolean enable, + final IIntResultListener listener) { int result; switch (type) { case TETHERING_WIFI: result = setWifiTethering(enable); - sendTetherResult(receiver, result); + sendTetherResult(listener, result); break; case TETHERING_USB: result = setUsbTethering(enable); - sendTetherResult(receiver, result); + sendTetherResult(listener, result); break; case TETHERING_BLUETOOTH: - setBluetoothTethering(enable, receiver); + setBluetoothTethering(enable, listener); + break; + case TETHERING_NCM: + result = setNcmTethering(enable); + sendTetherResult(listener, result); + break; + case TETHERING_ETHERNET: + result = setEthernetTethering(enable); + sendTetherResult(listener, result); break; default: Log.w(TAG, "Invalid tether type."); - sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE); + sendTetherResult(listener, TETHER_ERROR_UNKNOWN_IFACE); } } - private void sendTetherResult(ResultReceiver receiver, int result) { - if (receiver != null) { - receiver.send(result, null); + private void sendTetherResult(final IIntResultListener listener, int result) { + if (listener != null) { + try { + listener.onResult(result); + } catch (RemoteException e) { } } } @@ -485,12 +514,12 @@ public class Tethering { return TETHER_ERROR_MASTER_ERROR; } - private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) { + private void setBluetoothTethering(final boolean enable, final IIntResultListener listener) { final BluetoothAdapter adapter = mDeps.getBluetoothAdapter(); if (adapter == null || !adapter.isEnabled()) { Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: " + (adapter == null)); - sendTetherResult(receiver, TETHER_ERROR_SERVICE_UNAVAIL); + sendTetherResult(listener, TETHER_ERROR_SERVICE_UNAVAIL); return; } @@ -519,12 +548,63 @@ public class Tethering { final int result = (((BluetoothPan) proxy).isTetheringOn() == enable) ? TETHER_ERROR_NO_ERROR : TETHER_ERROR_MASTER_ERROR; - sendTetherResult(receiver, result); + sendTetherResult(listener, result); adapter.closeProfileProxy(BluetoothProfile.PAN, proxy); } }, BluetoothProfile.PAN); } + private int setEthernetTethering(final boolean enable) { + final EthernetManager em = (EthernetManager) mContext.getSystemService( + Context.ETHERNET_SERVICE); + synchronized (mPublicSync) { + if (enable) { + mEthernetCallback = new EthernetCallback(); + mEthernetIfaceRequest = em.requestTetheredInterface(mEthernetCallback); + } else { + if (mConfiguredEthernetIface != null) { + stopEthernetTetheringLocked(); + mEthernetIfaceRequest.release(); + } + mEthernetCallback = null; + } + } + return TETHER_ERROR_NO_ERROR; + } + + private void stopEthernetTetheringLocked() { + if (mConfiguredEthernetIface == null) return; + changeInterfaceState(mConfiguredEthernetIface, IpServer.STATE_AVAILABLE); + stopTrackingInterfaceLocked(mConfiguredEthernetIface); + mConfiguredEthernetIface = null; + } + + private class EthernetCallback implements EthernetManager.TetheredInterfaceCallback { + @Override + public void onAvailable(String iface) { + synchronized (mPublicSync) { + if (this != mEthernetCallback) { + // Ethernet callback arrived after Ethernet tethering stopped. Ignore. + return; + } + maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET); + changeInterfaceState(iface, IpServer.STATE_TETHERED); + mConfiguredEthernetIface = iface; + } + } + + @Override + public void onUnavailable() { + synchronized (mPublicSync) { + if (this != mEthernetCallback) { + // onAvailable called after stopping Ethernet tethering. + return; + } + stopEthernetTetheringLocked(); + } + } + } + int tether(String iface) { return tether(iface, IpServer.STATE_TETHERED); } @@ -575,6 +655,7 @@ public class Tethering { stopTethering(TETHERING_WIFI_P2P); stopTethering(TETHERING_USB); stopTethering(TETHERING_BLUETOOTH); + stopTethering(TETHERING_ETHERNET); } int getLastTetherError(String iface) { @@ -798,6 +879,7 @@ public class Tethering { final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false); final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false); final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false); + final boolean ncmEnabled = intent.getBooleanExtra(USB_FUNCTION_NCM, false); mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s", usbConnected, usbConfigured, rndisEnabled)); @@ -825,6 +907,8 @@ public class Tethering { } else if (usbConfigured && rndisEnabled) { // Tether if rndis is enabled and usb is configured. tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB); + } else if (usbConnected && ncmEnabled) { + tetherMatchingInterfaces(IpServer.STATE_LOCAL_ONLY, TETHERING_NCM); } mRndisEnabled = usbConfigured && rndisEnabled; } @@ -951,6 +1035,7 @@ public class Tethering { mWrapper.showTetheredNotification( R.drawable.stat_sys_tether_general, false); mWrapper.untetherAll(); + // TODO(b/148139325): send tetheringSupported on restriction change } } } @@ -1125,6 +1210,16 @@ public class Tethering { return TETHER_ERROR_NO_ERROR; } + private int setNcmTethering(boolean enable) { + if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")"); + UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE); + synchronized (mPublicSync) { + usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM + : UsbManager.FUNCTION_NONE); + } + return TETHER_ERROR_NO_ERROR; + } + // TODO review API - figure out how to delete these entirely. String[] getTetheredIfaces() { ArrayList<String> list = new ArrayList<String>(); @@ -1844,9 +1939,13 @@ public class Tethering { void registerTetheringEventCallback(ITetheringEventCallback callback) { mHandler.post(() -> { mTetheringEventCallbacks.register(callback); + final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel(); + parcel.tetheringSupported = mDeps.isTetheringSupported(); + parcel.upstreamNetwork = mTetherUpstream; + parcel.config = mConfig.toStableParcelable(); + parcel.states = mTetherStatesParcel; try { - callback.onCallbackStarted(mTetherUpstream, mConfig.toStableParcelable(), - mTetherStatesParcel); + callback.onCallbackStarted(parcel); } catch (RemoteException e) { // Not really very much to do here. } @@ -1881,6 +1980,7 @@ public class Tethering { for (int i = 0; i < length; i++) { try { mTetheringEventCallbacks.getBroadcastItem(i).onConfigurationChanged(config); + // TODO(b/148139325): send tetheringSupported on configuration change } catch (RemoteException e) { // Not really very much to do here. } diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java index 068c346fbfc1..7e9e26f5af40 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -83,6 +83,7 @@ public class TetheringConfiguration { public final String[] tetherableWifiRegexs; public final String[] tetherableWifiP2pRegexs; public final String[] tetherableBluetoothRegexs; + public final String[] tetherableNcmRegexs; public final boolean isDunRequired; public final boolean chooseUpstreamAutomatically; public final Collection<Integer> preferredUpstreamIfaceTypes; @@ -103,6 +104,7 @@ public class TetheringConfiguration { Resources res = getResources(ctx, activeDataSubId); tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs); + tetherableNcmRegexs = getResourceStringArray(res, R.array.config_tether_ncm_regexs); // TODO: Evaluate deleting this altogether now that Wi-Fi always passes // us an interface name. Careful consideration needs to be given to // implications for Settings and for provisioning checks. @@ -156,6 +158,11 @@ public class TetheringConfiguration { return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs); } + /** Check if interface is ncm */ + public boolean isNcm(String iface) { + return matchesDownstreamRegexs(iface, tetherableNcmRegexs); + } + /** Check whether no ui entitlement application is available.*/ public boolean hasMobileHotspotProvisionApp() { return !TextUtils.isEmpty(provisioningAppNoUi); @@ -170,6 +177,7 @@ public class TetheringConfiguration { dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs); dumpStringArray(pw, "tetherableWifiP2pRegexs", tetherableWifiP2pRegexs); dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs); + dumpStringArray(pw, "tetherableNcmRegexs", tetherableNcmRegexs); pw.print("isDunRequired: "); pw.println(isDunRequired); diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java index cb7d3920e693..7dc5c5f2db8a 100644 --- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java +++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java @@ -33,6 +33,7 @@ import android.net.ITetheringConnector; import android.net.ITetheringEventCallback; import android.net.NetworkCapabilities; import android.net.NetworkRequest; +import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.ip.IpServer; @@ -143,11 +144,11 @@ public class TetheringService extends Service { } @Override - public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi, - String callerPkg) { - if (checkAndNotifyCommonError(callerPkg, receiver)) return; + public void startTethering(TetheringRequestParcel request, String callerPkg, + IIntResultListener listener) { + if (checkAndNotifyCommonError(callerPkg, listener)) return; - mTethering.startTethering(type, receiver, showProvisioningUi); + mTethering.startTethering(request, listener); } @Override diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp index 53782fed1c50..13174c5bb57a 100644 --- a/packages/Tethering/tests/unit/Android.bp +++ b/packages/Tethering/tests/unit/Android.bp @@ -19,6 +19,7 @@ android_test { certificate: "platform", srcs: [ "src/**/*.java", + "src/**/*.kt", ], test_suites: [ "device-tests", diff --git a/packages/Tethering/tests/unit/AndroidManifest.xml b/packages/Tethering/tests/unit/AndroidManifest.xml index 0a1cdd35b10c..530bc0788a78 100644 --- a/packages/Tethering/tests/unit/AndroidManifest.xml +++ b/packages/Tethering/tests/unit/AndroidManifest.xml @@ -16,6 +16,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack.tethering.tests.unit"> + <uses-permission android:name="android.permission.TETHER_PRIVILEGED"/> + <application android:debuggable="true"> <uses-library android:name="android.test.runner" /> </application> diff --git a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt new file mode 100644 index 000000000000..83c19ec14d56 --- /dev/null +++ b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt @@ -0,0 +1,95 @@ +/* + * 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 android.net.InetAddresses.parseNumericAddress +import android.net.TetheredClient.AddressInfo +import android.net.TetheringManager.TETHERING_BLUETOOTH +import android.net.TetheringManager.TETHERING_USB +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.testutils.assertParcelSane +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +private val TEST_MACADDR = MacAddress.fromBytes(byteArrayOf(12, 23, 34, 45, 56, 67)) +private val TEST_OTHER_MACADDR = MacAddress.fromBytes(byteArrayOf(23, 34, 45, 56, 67, 78)) +private val TEST_ADDR1 = LinkAddress(parseNumericAddress("192.168.113.3"), 24) +private val TEST_ADDR2 = LinkAddress(parseNumericAddress("fe80::1:2:3"), 64) +private val TEST_ADDRINFO1 = AddressInfo(TEST_ADDR1, "test_hostname") +private val TEST_ADDRINFO2 = AddressInfo(TEST_ADDR2, null) + +@RunWith(AndroidJUnit4::class) +@SmallTest +class TetheredClientTest { + @Test + fun testParceling() { + assertParcelSane(makeTestClient(), fieldCount = 3) + } + + @Test + fun testEquals() { + assertEquals(makeTestClient(), makeTestClient()) + + // Different mac address + assertNotEquals(makeTestClient(), TetheredClient( + TEST_OTHER_MACADDR, + listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), + TETHERING_BLUETOOTH)) + + // Different hostname + assertNotEquals(makeTestClient(), TetheredClient( + TEST_MACADDR, + listOf(AddressInfo(TEST_ADDR1, "test_other_hostname"), TEST_ADDRINFO2), + TETHERING_BLUETOOTH)) + + // Null hostname + assertNotEquals(makeTestClient(), TetheredClient( + TEST_MACADDR, + listOf(AddressInfo(TEST_ADDR1, null), TEST_ADDRINFO2), + TETHERING_BLUETOOTH)) + + // Missing address + assertNotEquals(makeTestClient(), TetheredClient( + TEST_MACADDR, + listOf(TEST_ADDRINFO2), + TETHERING_BLUETOOTH)) + + // Different type + assertNotEquals(makeTestClient(), TetheredClient( + TEST_MACADDR, + listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), + TETHERING_BLUETOOTH)) + } + + @Test + fun testAddAddresses() { + val client1 = TetheredClient(TEST_MACADDR, listOf(TEST_ADDRINFO1), TETHERING_USB) + val client2 = TetheredClient(TEST_OTHER_MACADDR, listOf(TEST_ADDRINFO2), TETHERING_USB) + assertEquals(TetheredClient( + TEST_MACADDR, + listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), + TETHERING_USB), client1.addAddresses(client2)) + } + + private fun makeTestClient() = TetheredClient( + TEST_MACADDR, + listOf(TEST_ADDRINFO1, TEST_ADDRINFO2), + TETHERING_BLUETOOTH) +}
\ No newline at end of file diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java index 4f0746199786..3a1d4a61a39e 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java @@ -19,7 +19,7 @@ package com.android.server.connectivity.tethering; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; -import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKONWN; +import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED; import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; @@ -110,7 +110,7 @@ public final class EntitlementManagerTest { } public class WrappedEntitlementManager extends EntitlementManager { - public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN; + public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN; public int uiProvisionCount = 0; public int silentProvisionCount = 0; @@ -120,7 +120,7 @@ public final class EntitlementManagerTest { } public void reset() { - fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN; + fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKNOWN; uiProvisionCount = 0; silentProvisionCount = 0; } @@ -274,7 +274,7 @@ public final class EntitlementManagerTest { receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode); + assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); mCallbacklatch.countDown(); } }; @@ -343,7 +343,7 @@ public final class EntitlementManagerTest { receiver = new ResultReceiver(null) { @Override protected void onReceiveResult(int resultCode, Bundle resultData) { - assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode); + assertEquals(TETHER_ERROR_ENTITLEMENT_UNKNOWN, resultCode); mCallbacklatch.countDown(); } }; diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java index affd69154b70..e7c3e5567049 100644 --- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java @@ -18,6 +18,7 @@ package com.android.server.connectivity.tethering; import static android.hardware.usb.UsbManager.USB_CONFIGURED; import static android.hardware.usb.UsbManager.USB_CONNECTED; +import static android.hardware.usb.UsbManager.USB_FUNCTION_NCM; import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS; import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; @@ -27,6 +28,7 @@ import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED; import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY; import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER; import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER; +import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_WIFI; import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; @@ -86,7 +88,9 @@ import android.net.NetworkCapabilities; import android.net.NetworkRequest; import android.net.RouteInfo; import android.net.TetherStatesParcel; +import android.net.TetheringCallbackStartedParcel; import android.net.TetheringConfigurationParcel; +import android.net.TetheringRequestParcel; import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServer; @@ -149,6 +153,7 @@ public class TetheringTest { private static final String TEST_USB_IFNAME = "test_rndis0"; private static final String TEST_WLAN_IFNAME = "test_wlan0"; private static final String TEST_P2P_IFNAME = "test_p2p-p2p0-0"; + private static final String TEST_NCM_IFNAME = "test_ncm0"; private static final String TETHERING_NAME = "Tethering"; private static final int DHCPSERVER_START_TIMEOUT_MS = 1000; @@ -250,9 +255,11 @@ public class TetheringTest { ifName.equals(TEST_USB_IFNAME) || ifName.equals(TEST_WLAN_IFNAME) || ifName.equals(TEST_MOBILE_IFNAME) - || ifName.equals(TEST_P2P_IFNAME)); + || ifName.equals(TEST_P2P_IFNAME) + || ifName.equals(TEST_NCM_IFNAME)); final String[] ifaces = new String[] { - TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME}; + TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME, TEST_P2P_IFNAME, + TEST_NCM_IFNAME}; return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET, MacAddress.ALL_ZEROS_ADDRESS); } @@ -426,13 +433,16 @@ public class TetheringTest { .thenReturn(new String[]{ "test_p2p-p2p\\d-.*" }); when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)) .thenReturn(new String[0]); + when(mResources.getStringArray(R.array.config_tether_ncm_regexs)) + .thenReturn(new String[] { "test_ncm\\d" }); when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]); when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false); when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( false); when(mNetd.interfaceGetList()) .thenReturn(new String[] { - TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME}); + TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME, + TEST_NCM_IFNAME}); when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn(""); mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration.flags = new String[0]; @@ -467,6 +477,16 @@ public class TetheringTest { return new Tethering(mTetheringDependencies); } + private TetheringRequestParcel createTetheringRquestParcel(final int type) { + final TetheringRequestParcel request = new TetheringRequestParcel(); + request.tetheringType = type; + request.localIPv4Address = null; + request.exemptFromEntitlementCheck = false; + request.showProvisioningUi = false; + + return request; + } + @After public void tearDown() { mServiceContext.unregisterReceiver(mBroadcastReceiver); @@ -512,11 +532,16 @@ public class TetheringTest { P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST); } - private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) { + private void sendUsbBroadcast(boolean connected, boolean configured, boolean function, + int type) { final Intent intent = new Intent(UsbManager.ACTION_USB_STATE); intent.putExtra(USB_CONNECTED, connected); intent.putExtra(USB_CONFIGURED, configured); - intent.putExtra(USB_FUNCTION_RNDIS, rndisFunction); + if (type == TETHERING_USB) { + intent.putExtra(USB_FUNCTION_RNDIS, function); + } else { + intent.putExtra(USB_FUNCTION_NCM, function); + } mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } @@ -566,13 +591,22 @@ public class TetheringTest { verifyNoMoreInteractions(mWifiManager); } + private void prepareNcmTethering() { + // Emulate startTethering(TETHERING_NCM) called + mTethering.startTethering(createTetheringRquestParcel(TETHERING_NCM), null); + mLooper.dispatchAll(); + verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM); + + mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true); + } + private void prepareUsbTethering(UpstreamNetworkState upstreamState) { when(mUpstreamNetworkMonitor.getCurrentPreferredUpstream()).thenReturn(upstreamState); when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) .thenReturn(upstreamState); // Emulate pressing the USB tethering button in Settings UI. - mTethering.startTethering(TETHERING_USB, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_USB), null); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); @@ -588,7 +622,7 @@ public class TetheringTest { verifyNoMoreInteractions(mNetd); // Pretend we then receive USB configured broadcast. - sendUsbBroadcast(true, true, true); + sendUsbBroadcast(true, true, true, TETHERING_USB); mLooper.dispatchAll(); // Now we should see the start of tethering mechanics (in this case: // tetherMatchingInterfaces() which starts by fetching all interfaces). @@ -679,7 +713,7 @@ public class TetheringTest { private void runUsbTethering(UpstreamNetworkState upstreamState) { prepareUsbTethering(upstreamState); - sendUsbBroadcast(true, true, true); + sendUsbBroadcast(true, true, true, TETHERING_USB); mLooper.dispatchAll(); } @@ -802,6 +836,29 @@ public class TetheringTest { verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); } + private void runNcmTethering() { + prepareNcmTethering(); + sendUsbBroadcast(true, true, true, TETHERING_NCM); + mLooper.dispatchAll(); + } + + @Test + public void workingNcmTethering() throws Exception { + runNcmTethering(); + + verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any()); + } + + @Test + public void workingNcmTethering_LegacyDhcp() { + when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn( + true); + sendConfigurationChanged(); + runNcmTethering(); + + verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any()); + } + @Test public void workingLocalOnlyHotspotEnrichedApBroadcastWithIfaceChanged() throws Exception { workingLocalOnlyHotspotEnrichedApBroadcast(true); @@ -818,7 +875,7 @@ public class TetheringTest { when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(TETHERING_WIFI, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startSoftAp(null); verifyNoMoreInteractions(mWifiManager); @@ -845,7 +902,7 @@ public class TetheringTest { when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(TETHERING_WIFI, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startSoftAp(null); verifyNoMoreInteractions(mWifiManager); @@ -922,7 +979,7 @@ public class TetheringTest { doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME); // Emulate pressing the WiFi tethering button. - mTethering.startTethering(TETHERING_WIFI, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); mLooper.dispatchAll(); verify(mWifiManager, times(1)).startSoftAp(null); verifyNoMoreInteractions(mWifiManager); @@ -1113,11 +1170,10 @@ public class TetheringTest { } @Override - public void onCallbackStarted(Network network, TetheringConfigurationParcel config, - TetherStatesParcel states) { - mActualUpstreams.add(network); - mTetheringConfigs.add(config); - mTetherStates.add(states); + public void onCallbackStarted(TetheringCallbackStartedParcel parcel) { + mActualUpstreams.add(parcel.upstreamNetwork); + mTetheringConfigs.add(parcel.config); + mTetherStates.add(parcel.states); } @Override @@ -1188,7 +1244,7 @@ public class TetheringTest { tetherState = callback.pollTetherStatesChanged(); assertArrayEquals(tetherState.availableList, new String[] {TEST_WLAN_IFNAME}); - mTethering.startTethering(TETHERING_WIFI, null, false); + mTethering.startTethering(createTetheringRquestParcel(TETHERING_WIFI), null); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); tetherState = callback.pollTetherStatesChanged(); diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index acdc26bfcbfa..478b87c01025 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -66,6 +66,7 @@ import android.net.CaptivePortal; import android.net.ConnectionInfo; import android.net.ConnectivityManager; import android.net.ICaptivePortal; +import android.net.IConnectivityDiagnosticsCallback; import android.net.IConnectivityManager; import android.net.IDnsResolver; import android.net.IIpConnectivityMetrics; @@ -212,6 +213,7 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.HashMap; @@ -1573,48 +1575,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 @@ -1623,7 +1626,8 @@ public class ConnectivityService extends IConnectivityManager.Stub return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network)); } - private NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions( + @VisibleForTesting + NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions( NetworkCapabilities nc, int callerPid, int callerUid) { final NetworkCapabilities newNc = new NetworkCapabilities(nc); if (!checkSettingsPermission(callerPid, callerUid)) { @@ -1633,13 +1637,55 @@ public class ConnectivityService extends IConnectivityManager.Stub if (newNc.getNetworkSpecifier() != null) { newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact()); } + newNc.setAdministratorUids(Collections.EMPTY_LIST); + + maybeSanitizeLocationInfoForCaller(newNc, callerUid); + return newNc; } + private void maybeSanitizeLocationInfoForCaller( + NetworkCapabilities nc, int callerUid) { + // TODO(b/142072839): Conditionally reset the owner UID if the following + // conditions are not met: + // 1. The destination app is the network owner + // 2. The destination app has the ACCESS_COARSE_LOCATION permission granted + // if target SDK<29 or otherwise has the ACCESS_FINE_LOCATION permission granted + // 3. The user's location toggle is on + nc.setOwnerUid(INVALID_UID); + } + + private LinkProperties linkPropertiesRestrictedForCallerPermissions( + LinkProperties lp, int callerPid, int callerUid) { + if (lp == null) return new LinkProperties(); + + // Only do a permission check if sanitization is needed, to avoid unnecessary binder calls. + final boolean needsSanitization = + (lp.getCaptivePortalApiUrl() != null || lp.getCaptivePortalData() != null); + if (!needsSanitization) { + return new LinkProperties(lp); + } + + if (checkSettingsPermission(callerPid, callerUid)) { + return lp.makeSensitiveFieldsParcelingCopy(); + } + + final LinkProperties newLp = new LinkProperties(lp); + // Sensitive fields would not be parceled anyway, but sanitize for consistency before the + // object gets parceled. + newLp.setCaptivePortalApiUrl(null); + newLp.setCaptivePortalData(null); + return newLp; + } + private void restrictRequestUidsForCaller(NetworkCapabilities nc) { if (!checkSettingsPermission()) { nc.setSingleUid(Binder.getCallingUid()); } + nc.setAdministratorUids(Collections.EMPTY_LIST); + + // Clear owner UID; this can never come from an app. + nc.setOwnerUid(INVALID_UID); } private void restrictBackgroundRequestForCaller(NetworkCapabilities nc) { @@ -5767,7 +5813,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } final Set<UidRange> ranges = nai.networkCapabilities.getUids(); - final int vpnAppUid = nai.networkCapabilities.getEstablishingVpnAppUid(); + final int vpnAppUid = nai.networkCapabilities.getOwnerUid(); // TODO: this create a window of opportunity for apps to receive traffic between the time // when the old rules are removed and the time when new rules are added. To fix this, // make eBPF support two whitelisted interfaces so here new rules can be added before the @@ -5966,7 +6012,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nc == null || lp == null) return false; return nai.isVPN() && !nai.networkAgentConfig.allowBypass - && nc.getEstablishingVpnAppUid() != Process.SYSTEM_UID + && nc.getOwnerUid() != Process.SYSTEM_UID && lp.getInterfaceName() != null && (lp.hasIPv4DefaultRoute() || lp.hasIPv6DefaultRoute()); } @@ -6014,12 +6060,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // TODO Fix this window by computing an accurate diff on Set<UidRange>, so the old range // to be removed will never overlap with the new range to be added. if (wasFiltering && !prevRanges.isEmpty()) { - mPermissionMonitor.onVpnUidRangesRemoved(iface, prevRanges, - prevNc.getEstablishingVpnAppUid()); + mPermissionMonitor.onVpnUidRangesRemoved(iface, prevRanges, prevNc.getOwnerUid()); } if (shouldFilter && !newRanges.isEmpty()) { - mPermissionMonitor.onVpnUidRangesAdded(iface, newRanges, - newNc.getEstablishingVpnAppUid()); + mPermissionMonitor.onVpnUidRangesAdded(iface, newRanges, newNc.getOwnerUid()); } } catch (Exception e) { // Never crash! @@ -6138,7 +6182,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; @@ -6155,7 +6200,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: { @@ -7278,4 +7324,20 @@ public class ConnectivityService extends IConnectivityManager.Stub return mTNS; } } + + @Override + public void registerConnectivityDiagnosticsCallback( + @NonNull IConnectivityDiagnosticsCallback callback, @NonNull NetworkRequest request) { + // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality + throw new UnsupportedOperationException( + "registerConnectivityDiagnosticsCallback not yet implemented"); + } + + @Override + public void unregisterConnectivityDiagnosticsCallback( + @NonNull IConnectivityDiagnosticsCallback callback) { + // TODO(b/146444622): implement register IConnectivityDiagnosticsCallback functionality + throw new UnsupportedOperationException( + "unregisterConnectivityDiagnosticsCallback not yet implemented"); + } } diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 90b0c11cc8dd..39ad3546e452 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -2788,6 +2788,24 @@ class StorageManagerService extends IStorageManager.Stub } /* + * Clear disk encryption key bound to the associated token / secret pair. Removing the user + * binding of the Disk encryption key is done in two phases: first, this call will retrieve + * the disk encryption key using the provided token / secret pair and store it by + * encrypting it with a keymaster key not bound to the user, then fixateNewestUserKeyAuth + * is called to delete all other bindings of the disk encryption key. + */ + @Override + public void clearUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) { + enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); + + try { + mVold.clearUserKeyAuth(userId, serialNumber, encodeBytes(token), encodeBytes(secret)); + } catch (Exception e) { + Slog.wtf(TAG, e); + } + } + + /* * Delete all disk encryption token/secret pairs except the most recently added one */ @Override diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 62743aac55f6..5eacb28c76a9 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -882,6 +882,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) + != 0) { + updateReportSignalStrengthDecision(r.subId); + } if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) { try { if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = " @@ -1005,6 +1009,27 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + private void updateReportSignalStrengthDecision(int subscriptionId) { + synchronized (mRecords) { + TelephonyManager telephonyManager = (TelephonyManager) mContext + .getSystemService(Context.TELEPHONY_SERVICE); + for (Record r : mRecords) { + // If any of the system clients wants to always listen to signal strength, + // we need to set it on. + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) { + telephonyManager.createForSubscriptionId(subscriptionId) + .setAlwaysReportSignalStrength(true); + return; + } + } + // If none of the system clients wants to always listen to signal strength, + // we need to set it off. + telephonyManager.createForSubscriptionId(subscriptionId) + .setAlwaysReportSignalStrength(false); + } + } + private String getCallIncomingNumber(Record record, int phoneId) { // Only reveal the incoming number if the record has read call log permission. return record.canReadCallLog() ? mCallIncomingNumber[phoneId] : ""; @@ -1062,6 +1087,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } mRecords.remove(i); + + // Every time a client that is registrating to always receive the signal + // strength is removed from registry records, we need to check if + // the signal strength decision needs to update on its slot. + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)) { + updateReportSignalStrengthDecision(r.subId); + } return; } } @@ -2415,6 +2448,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null); } + if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null); + } + if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null); diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index f021008cef7d..5833d165f380 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -272,6 +272,7 @@ public final class ActiveServices { static final int MSG_BG_START_TIMEOUT = 1; static final int MSG_UPDATE_FOREGROUND_APPS = 2; + static final int MSG_ENSURE_NOT_START_BG = 3; ServiceMap(Looper looper, int userId) { super(looper); @@ -289,6 +290,11 @@ public final class ActiveServices { case MSG_UPDATE_FOREGROUND_APPS: { updateForegroundApps(this); } break; + case MSG_ENSURE_NOT_START_BG: { + synchronized (mAm) { + rescheduleDelayedStartsLocked(); + } + } break; } } @@ -296,7 +302,9 @@ public final class ActiveServices { if (mStartingBackground.remove(r)) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "No longer background starting: " + r); - rescheduleDelayedStartsLocked(); + removeMessages(MSG_ENSURE_NOT_START_BG); + Message msg = obtainMessage(MSG_ENSURE_NOT_START_BG); + sendMessage(msg); } if (mDelayedStartList.remove(r)) { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "No longer delaying start: " + r); diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b311233694ce..1452e25e70f9 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -1538,6 +1538,9 @@ class UserController implements Handler.Callback { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); + // Also, add the UserHandle for mainline modules which can't use the @hide + // EXTRA_USER_HANDLE. + intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId)); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, @@ -1554,6 +1557,9 @@ class UserController implements Handler.Callback { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); + // Also, add the UserHandle for mainline modules which can't use the @hide + // EXTRA_USER_HANDLE. + intent.putExtra(Intent.EXTRA_USER, UserHandle.of(profileUserId)); mInjector.broadcastIntent(intent, null, null, 0, null, null, null, AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid, @@ -1563,6 +1569,9 @@ class UserController implements Handler.Callback { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId); + // Also, add the UserHandle for mainline modules which can't use the @hide + // EXTRA_USER_HANDLE. + intent.putExtra(Intent.EXTRA_USER, UserHandle.of(newUserId)); mInjector.broadcastIntent(intent, null, null, 0, null, null, new String[] {android.Manifest.permission.MANAGE_USERS}, diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 029b7bc32ef3..e48169fc37c9 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -16,6 +16,11 @@ package com.android.server.compat; +import static android.Manifest.permission.LOG_COMPAT_CHANGE; +import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG; +import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; @@ -67,12 +72,14 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void reportChange(long changeId, ApplicationInfo appInfo) { + checkCompatChangeLogPermission(); reportChange(changeId, appInfo.uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED); } @Override public void reportChangeByPackageName(long changeId, String packageName, int userId) { + checkCompatChangeLogPermission(); ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo == null) { return; @@ -82,11 +89,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void reportChangeByUid(long changeId, int uid) { + checkCompatChangeLogPermission(); reportChange(changeId, uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED); } @Override public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) { + checkCompatChangeReadPermission(); if (mCompatConfig.isChangeEnabled(changeId, appInfo)) { reportChange(changeId, appInfo.uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED); @@ -99,6 +108,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) { + checkCompatChangeReadPermission(); ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo == null) { return true; @@ -108,6 +118,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public boolean isChangeEnabledByUid(long changeId, int uid) { + checkCompatChangeReadPermission(); String[] packages = mContext.getPackageManager().getPackagesForUid(uid); if (packages == null || packages.length == 0) { return true; @@ -140,6 +151,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void setOverrides(CompatibilityChangeConfig overrides, String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.addOverrides(overrides, packageName); killPackage(packageName); } @@ -147,11 +159,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.addOverrides(overrides, packageName); } @Override public void clearOverrides(String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); killPackage(packageName); } @@ -159,12 +173,14 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public void clearOverridesForTest(String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); mCompatConfig.removePackageOverrides(packageName); } @Override public boolean clearOverride(long changeId, String packageName) throws RemoteException, SecurityException { + checkCompatChangeOverridePermission(); boolean existed = mCompatConfig.removeOverride(changeId, packageName); killPackage(packageName); return existed; @@ -172,11 +188,13 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) { + checkCompatChangeReadPermission(); return mCompatConfig.getAppConfig(appInfo); } @Override public CompatibilityChangeInfo[] listAllChanges() { + checkCompatChangeReadPermission(); return mCompatConfig.dumpChanges(); } @@ -215,6 +233,7 @@ public class PlatformCompat extends IPlatformCompat.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + checkCompatChangeReadPermission(); if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return; mCompatConfig.dumpConfig(pw); } @@ -276,4 +295,25 @@ public class PlatformCompat extends IPlatformCompat.Stub { Binder.restoreCallingIdentity(identity); } } + + private void checkCompatChangeLogPermission() throws SecurityException { + if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot log compat change usage"); + } + } + + private void checkCompatChangeReadPermission() throws SecurityException { + if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot read compat change"); + } + } + + private void checkCompatChangeOverridePermission() throws SecurityException { + if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG) + != PERMISSION_GRANTED) { + throw new SecurityException("Cannot override compat change"); + } + } } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 69ab47a61362..2933fab465e5 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -950,7 +950,7 @@ public class Vpn { NetworkAgentConfig networkAgentConfig = new NetworkAgentConfig(); networkAgentConfig.allowBypass = mConfig.allowBypass && !mLockdown; - mNetworkCapabilities.setEstablishingVpnAppUid(Binder.getCallingUid()); + mNetworkCapabilities.setOwnerUid(Binder.getCallingUid()); mNetworkCapabilities.setUids(createUserAndRestrictedProfilesRanges(mUserHandle, mConfig.allowedApplications, mConfig.disallowedApplications)); long token = Binder.clearCallingIdentity(); 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/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 9510db09aa25..288484fdee01 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -25,7 +25,6 @@ import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback; -import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY; import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; import static com.android.internal.widget.LockPatternUtils.USER_FRP; @@ -1511,7 +1510,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (credential != null) { Slog.wtf(TAG, "CredentialType is none, but credential is non-null."); } - clearUserKeyProtection(userId); + clearUserKeyProtection(userId, null); getGateKeeperService().clearSecureUserId(userId); mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId); setKeystorePassword(null, userId); @@ -1688,9 +1687,17 @@ public class LockSettingsService extends ILockSettings.Stub { addUserKeyAuth(userId, token, secretFromCredential(credential)); } - private void clearUserKeyProtection(int userId) throws RemoteException { + private void clearUserKeyProtection(int userId, byte[] secret) { if (DEBUG) Slog.d(TAG, "clearUserKeyProtection user=" + userId); - addUserKeyAuth(userId, null, null); + final UserInfo userInfo = mUserManager.getUserInfo(userId); + final long callingId = Binder.clearCallingIdentity(); + try { + mStorageManager.clearUserKeyAuth(userId, userInfo.serialNumber, null, secret); + } catch (RemoteException e) { + throw new IllegalStateException("clearUserKeyAuth failed user=" + userId); + } finally { + Binder.restoreCallingIdentity(callingId); + } } private static byte[] secretFromCredential(byte[] credential) throws RemoteException { @@ -2512,7 +2519,7 @@ public class LockSettingsService extends ILockSettings.Stub { setAuthlessUserKeyProtection(userId, auth.deriveDiskEncryptionKey()); setKeystorePassword(auth.deriveKeyStorePassword(), userId); } else { - clearUserKeyProtection(userId); + clearUserKeyProtection(userId, null); setKeystorePassword(null, userId); getGateKeeperService().clearSecureUserId(userId); } @@ -2532,23 +2539,12 @@ public class LockSettingsService extends ILockSettings.Stub { return type == PersistentData.TYPE_SP || type == PersistentData.TYPE_SP_WEAVER; } long handle = getSyntheticPasswordHandleLocked(userId); - // This is a global setting - long enabled = getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, - SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT, UserHandle.USER_SYSTEM); - return enabled != 0 && handle != SyntheticPasswordManager.DEFAULT_HANDLE; + return handle != SyntheticPasswordManager.DEFAULT_HANDLE; } @VisibleForTesting protected boolean shouldMigrateToSyntheticPasswordLocked(int userId) { - long handle = getSyntheticPasswordHandleLocked(userId); - // This is a global setting - long enabled = getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, - SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT, UserHandle.USER_SYSTEM); - return enabled != 0 && handle == SyntheticPasswordManager.DEFAULT_HANDLE; - } - - private void enableSyntheticPasswordLocked() { - setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM); + return true; } private VerifyCredentialResponse spBasedDoVerifyCredential(byte[] userCredential, @@ -2698,7 +2694,7 @@ public class LockSettingsService extends ILockSettings.Stub { // during boot. Vold storage needs to be unlocked before manipulation of the keys can // succeed. unlockUserKey(userId, null, auth.deriveDiskEncryptionKey()); - clearUserKeyProtection(userId); + clearUserKeyProtection(userId, auth.deriveDiskEncryptionKey()); fixateNewestUserKeyAuth(userId); unlockKeystore(auth.deriveKeyStorePassword(), userId); setKeystorePassword(null, userId); @@ -2829,7 +2825,6 @@ public class LockSettingsService extends ILockSettings.Stub { throws RemoteException { if (DEBUG) Slog.d(TAG, "addEscrowToken: user=" + userId); synchronized (mSpManager) { - enableSyntheticPasswordLocked(); // Migrate to synthetic password based credentials if the user has no password, // the token can then be activated immediately. AuthenticationToken auth = null; 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/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index 61fe01f1ca6f..1e623e0eea87 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -65,6 +65,8 @@ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.SUSPEND_APPS"/> <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/> <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/> <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" /> <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> diff --git a/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java index 4d229efc8328..625766a97f13 100644 --- a/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java +++ b/services/tests/servicestests/src/com/android/server/WatchdogDiagnosticsTest.java @@ -157,6 +157,8 @@ public class WatchdogDiagnosticsTest { String expected = "TestThread2 annotated stack trace:\n" + " at java.lang.Object.wait(Native Method)\n" + + " at java.lang.Object.wait(Object.java:442)\n" + + " at java.lang.Object.wait(Object.java:568)\n" + " at com.android.server.WatchdogDiagnosticsTest$TestThread2.y(" + "WatchdogDiagnosticsTest.java:91)\n" + " - locked <HASH> (a java.lang.String)\n" + diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index 842cdd5715f9..2aaf7595dce7 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -215,6 +215,18 @@ public abstract class BaseLockSettingsServiceTests { } }).when(sm).addUserKeyAuth(anyInt(), anyInt(), any(), any()); + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Object[] args = invocation.getArguments(); + mStorageManager.clearUserKeyAuth((int) args[0] /* userId */, + (int) args[1] /* serialNumber */, + (byte[]) args[2] /* token */, + (byte[]) args[3] /* secret */); + return null; + } + }).when(sm).clearUserKeyAuth(anyInt(), anyInt(), any(), any()); + doAnswer( new Answer<Void>() { @Override diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java index 1ae1fa65ddf2..102bac111ada 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeStorageManager.java @@ -36,6 +36,15 @@ public class FakeStorageManager { getUserAuth(userId).add(new Pair<>(token, secret)); } + public void clearUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) { + ArrayList<Pair<byte[], byte[]>> auths = getUserAuth(userId); + if (token == null && secret == null) { + return; + } + auths.remove(new Pair<>(token, secret)); + auths.add(new Pair<>(null, null)); + } + public void fixateNewestUserKeyAuth(int userId) { ArrayList<Pair<byte[], byte[]>> auths = mAuth.get(userId); Pair<byte[], byte[]> latest = auths.get(auths.size() - 1); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index d9b13209d28c..33f7924b11c9 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -103,31 +103,6 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { return mService.getLong(SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId) != 0; } - @Test - public void testPasswordMigration() throws RemoteException { - final byte[] password = "testPasswordMigration-password".getBytes(); - - disableSyntheticPassword(); - mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); - long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); - final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); - enableSyntheticPassword(); - // Performs migration - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); - assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); - assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); - - // SP-based verification - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); - assertArrayNotEquals(primaryStorageKey, - mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); - } - protected void initializeCredentialUnderSP(byte[] password, int userId) throws RemoteException { enableSyntheticPassword(); int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC @@ -270,86 +245,6 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } @Test - public void testManagedProfileUnifiedChallengeMigration() throws RemoteException { - final byte[] UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd".getBytes(); - disableSyntheticPassword(); - mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); - mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); - final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); - final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); - final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); - final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID); - assertTrue(primarySid != 0); - assertTrue(profileSid != 0); - assertTrue(profileSid != primarySid); - - // do migration - enableSyntheticPassword(); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); - - // verify - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); - assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); - assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); - assertArrayNotEquals(primaryStorageKey, - mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); - assertArrayNotEquals(profileStorageKey, - mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID)); - assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); - assertTrue(hasSyntheticPassword(MANAGED_PROFILE_USER_ID)); - } - - @Test - public void testManagedProfileSeparateChallengeMigration() throws RemoteException { - final byte[] primaryPassword = - "testManagedProfileSeparateChallengeMigration-primary".getBytes(); - final byte[] profilePassword = - "testManagedProfileSeparateChallengeMigration-profile".getBytes(); - disableSyntheticPassword(); - mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); - mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID, false); - final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); - final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); - final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); - final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID); - assertTrue(primarySid != 0); - assertTrue(profileSid != 0); - assertTrue(profileSid != primarySid); - - // do migration - enableSyntheticPassword(); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - 0, MANAGED_PROFILE_USER_ID).getResponseCode()); - - // verify - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - 0, MANAGED_PROFILE_USER_ID).getResponseCode()); - assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); - assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); - assertArrayNotEquals(primaryStorageKey, - mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); - assertArrayNotEquals(profileStorageKey, - mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID)); - assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); - assertTrue(hasSyntheticPassword(MANAGED_PROFILE_USER_ID)); - } - - @Test public void testTokenBasedResetPassword() throws RemoteException { final byte[] password = "password".getBytes(); final byte[] pattern = "123654".getBytes(); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 826a89eb38bb..f54f8d1f5832 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -265,6 +265,29 @@ public final class Call { public static final String EVENT_HANDOVER_FAILED = "android.telecom.event.HANDOVER_FAILED"; + + /** + * Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this + * call because they have declined to answer it. This typically means that they are unable + * to answer the call at this time and would prefer it be sent to voicemail. + */ + public static final int REJECT_REASON_DECLINED = 1; + + /** + * Reject reason used with {@link #reject(int)} to indicate that the user is rejecting this + * call because it is an unwanted call. This allows the user to indicate that they are + * rejecting a call because it is likely a nuisance call. + */ + public static final int REJECT_REASON_UNWANTED = 2; + + /** + * @hide + */ + @IntDef(prefix = { "REJECT_REASON_" }, + value = {REJECT_REASON_DECLINED, REJECT_REASON_UNWANTED}) + @Retention(RetentionPolicy.SOURCE) + public @interface RejectReason {}; + public static class Details { /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -547,8 +570,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 +755,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(); } @@ -1511,6 +1543,16 @@ public final class Call { } /** + * Instructs the {@link ConnectionService} providing this {@link #STATE_RINGING} call that the + * user has chosen to reject the call and has indicated a reason why the call is being rejected. + * + * @param rejectReason the reason the call is being rejected. + */ + public void reject(@RejectReason int rejectReason) { + mInCallAdapter.rejectCall(mTelecomCallId, rejectReason); + } + + /** * Instructs this {@code Call} to disconnect. */ public void disconnect() { 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..72c66d20548a 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(); } @@ -3024,6 +3037,17 @@ public abstract class Connection extends Conferenceable { public void onReject() {} /** + * Notifies this Connection, which is in {@link #STATE_RINGING}, of a request to reject. + * <p> + * For managed {@link ConnectionService}s, this will be called when the user rejects a call via + * the default dialer's {@link InCallService} using {@link Call#reject(int)}. + * @param rejectReason the reason the user provided for rejecting the call. + */ + public void onReject(@android.telecom.Call.RejectReason int rejectReason) { + // to be implemented by ConnectionService. + } + + /** * Notifies this Connection, which is in {@link #STATE_RINGING}, of * a request to reject with a message. */ 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..00c2918837ac 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,10 @@ 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 final int MSG_REJECT_WITH_REASON = 38; private static Connection sNullConnection; @@ -291,6 +298,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); @@ -387,6 +451,21 @@ public abstract class ConnectionService extends Service { } @Override + public void rejectWithReason(String callId, + @android.telecom.Call.RejectReason int rejectReason, Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_REJECT); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.argi1 = rejectReason; + args.arg2 = Log.createSubsession(); + mHandler.obtainMessage(MSG_REJECT_WITH_REASON, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void rejectWithMessage(String callId, String message, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_REJECT_MESSAGE); try { @@ -802,6 +881,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 + @@ -890,6 +1069,17 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_REJECT_WITH_REASON: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_REJECT); + try { + reject((String) args.arg1, args.argi1); + } finally { + args.recycle(); + Log.endSession(); + } + break; + } case MSG_REJECT_WITH_MESSAGE: { SomeArgs args = (SomeArgs) msg.obj; Log.continueSession((Session) args.arg3, @@ -1162,6 +1352,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 +1488,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 +1737,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 +1912,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 +1948,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 +1973,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 +1996,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) { @@ -1699,6 +2008,11 @@ public abstract class ConnectionService extends Service { findConnectionForAction(callId, "reject").onReject(rejectWithMessage); } + private void reject(String callId, @android.telecom.Call.RejectReason int rejectReason) { + Log.d(this, "reject %s with reason %d", callId, rejectReason); + findConnectionForAction(callId, "reject").onReject(rejectReason); + } + private void silence(String callId) { Log.d(this, "silence %s", callId); findConnectionForAction(callId, "silence").onSilence(); @@ -2198,6 +2512,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 +2541,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 +2592,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 +2672,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/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java index 261246818f1d..594c1eb392b3 100644 --- a/telecomm/java/android/telecom/InCallAdapter.java +++ b/telecomm/java/android/telecom/InCallAdapter.java @@ -89,6 +89,19 @@ public final class InCallAdapter { } /** + * Instructs Telecom to reject the specified call. + * + * @param callId The identifier of the call to reject. + * @param rejectReason The reason the call was rejected. + */ + public void rejectCall(String callId, @Call.RejectReason int rejectReason) { + try { + mAdapter.rejectCallWithReason(callId, rejectReason); + } catch (RemoteException e) { + } + } + + /** * Instructs Telecom to disconnect the specified call. * * @param callId The identifier of the call to disconnect. 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..4249dff151c7 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); @@ -63,6 +77,8 @@ oneway interface IConnectionService { void reject(String callId, in Session.Info sessionInfo); + void rejectWithReason(String callId, int rejectReason, in Session.Info sessionInfo); + void rejectWithMessage(String callId, String message, in Session.Info sessionInfo); void disconnect(String callId, 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/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl index 60745e40aa77..eb2d714fe3f4 100644 --- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl @@ -34,6 +34,8 @@ oneway interface IInCallAdapter { void rejectCall(String callId, boolean rejectWithMessage, String textMessage); + void rejectCallWithReason(String callId, int rejectReason); + void disconnectCall(String callId); void holdCall(String callId); 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/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index d325cd84855c..b1d647fec0c5 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -67,6 +67,22 @@ public final class AccessNetworkConstants { } } + /** + * Access network type + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"RADIO_ACCESS_NETWORK_TYPE_"}, + value = { + AccessNetworkType.UNKNOWN, + AccessNetworkType.GERAN, + AccessNetworkType.UTRAN, + AccessNetworkType.EUTRAN, + AccessNetworkType.CDMA2000, + AccessNetworkType.IWLAN, + AccessNetworkType.NGRAN}) + public @interface RadioAccessNetworkType {} + public static final class AccessNetworkType { public static final int UNKNOWN = 0; public static final int GERAN = 1; @@ -115,11 +131,11 @@ public final class AccessNetworkConstants { public static final int BAND_ER900 = 14; /** @hide */ - private GeranBand() {}; + private GeranBand() {} } /** - * Frenquency bands for UTRAN. + * Frequency bands for UTRAN. * http://www.etsi.org/deliver/etsi_ts/125100_125199/125104/13.03.00_60/ts_125104v130p.pdf */ public static final class UtranBand { @@ -146,12 +162,19 @@ public final class AccessNetworkConstants { public static final int BAND_25 = 25; public static final int BAND_26 = 26; + /** Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2. */ + public static final int BAND_A = 101; + public static final int BAND_B = 102; + public static final int BAND_C = 103; + public static final int BAND_D = 104; + public static final int BAND_E = 105; + public static final int BAND_F = 106; /** @hide */ - private UtranBand() {}; + private UtranBand() {} } /** - * Frenquency bands for EUTRAN. + * Frequency bands for EUTRAN. * http://www.etsi.org/deliver/etsi_ts/136100_136199/136101/14.03.00_60/ts_136101v140p.pdf */ public static final class EutranBand { @@ -209,7 +232,7 @@ public final class AccessNetworkConstants { } /** - * Frenquency bands for CDMA2000. + * Frequency bands for CDMA2000. * http://www.3gpp2.org/Public_html/Specs/C.S0057-E_v1.0_Bandclass_Specification.pdf * @hide * @@ -240,7 +263,7 @@ public final class AccessNetworkConstants { public static final int BAND_21 = 22; /** @hide */ - private CdmaBands() {}; + private CdmaBands() {} } /** @@ -295,7 +318,7 @@ public final class AccessNetworkConstants { public static final int BAND_261 = 261; /** @hide */ - private NgranBands() {}; + private NgranBands() {} } /** @hide */ diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d863090a18d1..71411d5461a2 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}. @@ -3522,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); @@ -3749,26 +3758,26 @@ public class CarrierConfigManager { sDefaults.putIntArray(KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY, // Boundaries: [-140 dB, -44 dB] new int[] { - -125, /* SIGNAL_STRENGTH_POOR */ - -115, /* SIGNAL_STRENGTH_MODERATE */ - -105, /* SIGNAL_STRENGTH_GOOD */ - -95, /* SIGNAL_STRENGTH_GREAT */ + -110, /* SIGNAL_STRENGTH_POOR */ + -90, /* SIGNAL_STRENGTH_MODERATE */ + -80, /* SIGNAL_STRENGTH_GOOD */ + -65, /* SIGNAL_STRENGTH_GREAT */ }); sDefaults.putIntArray(KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY, // Boundaries: [-20 dB, -3 dB] new int[] { - -14, /* SIGNAL_STRENGTH_POOR */ - -12, /* SIGNAL_STRENGTH_MODERATE */ - -10, /* SIGNAL_STRENGTH_GOOD */ - -8 /* SIGNAL_STRENGTH_GREAT */ + -16, /* SIGNAL_STRENGTH_POOR */ + -11, /* SIGNAL_STRENGTH_MODERATE */ + -9, /* SIGNAL_STRENGTH_GOOD */ + -7 /* SIGNAL_STRENGTH_GREAT */ }); sDefaults.putIntArray(KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY, // Boundaries: [-23 dB, 40 dB] new int[] { - -8, /* SIGNAL_STRENGTH_POOR */ - 0, /* SIGNAL_STRENGTH_MODERATE */ - 8, /* SIGNAL_STRENGTH_GOOD */ - 16 /* SIGNAL_STRENGTH_GREAT */ + -5, /* SIGNAL_STRENGTH_POOR */ + 5, /* SIGNAL_STRENGTH_MODERATE */ + 15, /* SIGNAL_STRENGTH_GOOD */ + 30 /* SIGNAL_STRENGTH_GREAT */ }); sDefaults.putInt(KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, CellSignalStrengthNr.USE_SSRSRP); diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index 4d67bcf536cf..f4c13fff9943 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -45,28 +45,28 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa // Lifted from Default carrier configs and max range of SSRSRP // Boundaries: [-140 dB, -44 dB] private int[] mSsRsrpThresholds = new int[] { - -125, /* SIGNAL_STRENGTH_POOR */ - -115, /* SIGNAL_STRENGTH_MODERATE */ - -105, /* SIGNAL_STRENGTH_GOOD */ - -95, /* SIGNAL_STRENGTH_GREAT */ + -110, /* SIGNAL_STRENGTH_POOR */ + -90, /* SIGNAL_STRENGTH_MODERATE */ + -80, /* SIGNAL_STRENGTH_GOOD */ + -65, /* SIGNAL_STRENGTH_GREAT */ }; // Lifted from Default carrier configs and max range of SSRSRQ // Boundaries: [-20 dB, -3 dB] private int[] mSsRsrqThresholds = new int[] { - -14, /* SIGNAL_STRENGTH_POOR */ - -12, /* SIGNAL_STRENGTH_MODERATE */ - -10, /* SIGNAL_STRENGTH_GOOD */ - -8 /* SIGNAL_STRENGTH_GREAT */ + -16, /* SIGNAL_STRENGTH_POOR */ + -11, /* SIGNAL_STRENGTH_MODERATE */ + -9, /* SIGNAL_STRENGTH_GOOD */ + -7 /* SIGNAL_STRENGTH_GREAT */ }; // Lifted from Default carrier configs and max range of SSSINR // Boundaries: [-23 dB, 40 dB] private int[] mSsSinrThresholds = new int[] { - -8, /* SIGNAL_STRENGTH_POOR */ - 0, /* SIGNAL_STRENGTH_MODERATE */ - 8, /* SIGNAL_STRENGTH_GOOD */ - 16 /* SIGNAL_STRENGTH_GREAT */ + -5, /* SIGNAL_STRENGTH_POOR */ + 5, /* SIGNAL_STRENGTH_MODERATE */ + 15, /* SIGNAL_STRENGTH_GOOD */ + 30 /* SIGNAL_STRENGTH_GREAT */ }; /** diff --git a/telephony/java/android/telephony/ImsManager.java b/telephony/java/android/telephony/ImsManager.java index c706d288b7f2..704e5aa78188 100644 --- a/telephony/java/android/telephony/ImsManager.java +++ b/telephony/java/android/telephony/ImsManager.java @@ -27,11 +27,7 @@ import android.telephony.SubscriptionManager; /** * Provides access to information about Telephony IMS services on the device. - * - * @hide */ -@SystemApi -@TestApi @SystemService(Context.TELEPHONY_IMS_SERVICE) public class ImsManager { @@ -45,7 +41,10 @@ public class ImsManager { * <p class="note"> * Carrier applications may listen to this broadcast to be notified of possible IMS provisioning * issues. + * @hide */ + @SystemApi + @TestApi // Moved from TelephonyIntents, need to keep backwards compatibility with OEM apps that have // this value hard-coded in BroadcastReceiver. @SuppressLint("ActionValue") @@ -54,6 +53,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 */ @@ -67,7 +103,10 @@ public class ImsManager { * @param subscriptionId The ID of the subscription that this ImsRcsManager will use. * @throws IllegalArgumentException if the subscription is invalid. * @return a ImsRcsManager instance with the specific subscription ID. + * @hide */ + @SystemApi + @TestApi @NonNull public ImsRcsManager getImsRcsManager(int subscriptionId) { if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)) { diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java index aee7cca75252..bd2375f7d297 100644 --- a/telephony/java/android/telephony/ModemActivityInfo.java +++ b/telephony/java/android/telephony/ModemActivityInfo.java @@ -199,10 +199,10 @@ public final class ModemActivityInfo implements Parcelable { } /** + * Indicate if the ModemActivityInfo is invalid due to modem's invalid reporting. + * * @return {@code true} if this {@link ModemActivityInfo} record is valid, * {@code false} otherwise. - * - * @hide */ public boolean isValid() { for (TransmitPower powerInfo : getTransmitPowerInfo()) { diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java index 8a75831a75b8..90244b3df350 100644 --- a/telephony/java/android/telephony/PhoneCapability.java +++ b/telephony/java/android/telephony/PhoneCapability.java @@ -16,13 +16,20 @@ package android.telephony; +import android.annotation.LongDef; import android.annotation.NonNull; -import android.annotation.SystemApi; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.AccessNetworkConstants.AccessNetworkType; +import android.telephony.AccessNetworkConstants.RadioAccessNetworkType; +import android.telephony.TelephonyManager.NetworkTypeBitMask; +import com.android.internal.util.CollectionUtils; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -31,68 +38,365 @@ import java.util.Objects; * are shared between those modems defined by list of modem IDs. */ public final class PhoneCapability implements Parcelable { - // Hardcoded default DSDS capability. + /** Modem feature indicating 3GPP2 capability. */ + public static final long MODEM_FEATURE_3GPP2_REG = 1 << 0; + /** Modem feature indicating 3GPP capability. */ + public static final long MODEM_FEATURE_3GPP_REG = 1 << 1; + /** Modem feature indicating CDMA 2000 with EHRPD capability. */ + public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 1 << 2; + /** Modem feature indicating GSM capability. */ + public static final long MODEM_FEATURE_GERAN_REG = 1 << 3; + /** Modem feature indicating UMTS capability. */ + public static final long MODEM_FEATURE_UTRAN_REG = 1 << 4; + /** Modem feature indicating LTE capability. */ + public static final long MODEM_FEATURE_EUTRAN_REG = 1 << 5; + /** Modem feature indicating 5G capability.*/ + public static final long MODEM_FEATURE_NGRAN_REG = 1 << 6; + /** Modem feature indicating EN-DC capability. */ + public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 1 << 7; + /** Modem feature indicating VoLTE capability (IMS registered). */ + public static final long MODEM_FEATURE_PS_VOICE_REG = 1 << 8; + /** Modem feature indicating CS voice call capability. */ + public static final long MODEM_FEATURE_CS_VOICE_SESSION = 1 << 9; + /** Modem feature indicating Internet connection capability. */ + public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1 << 10; + /** + * Modem feature indicating dedicated bearer capability. + * For services that require a high level QoS (eg. VoLTE), the network can create + * a dedicated bearer with the required QoS on top of an established default bearer. + * This will provide a dedicated tunnel for one or more specific traffic types. + */ + public static final long MODEM_FEATURE_DEDICATED_BEARER = 1 << 11; + /** Modem feature indicating network scan capability. */ + public static final long MODEM_FEATURE_NETWORK_SCAN = 1 << 12; + /** Modem feature indicating corresponding SIM has CDMA capability. */ + public static final long MODEM_FEATURE_CSIM = 1 << 13; + /** @hide */ + @LongDef(flag = true, prefix = {"MODEM_FEATURE_" }, value = { + MODEM_FEATURE_3GPP2_REG, + MODEM_FEATURE_3GPP_REG, + MODEM_FEATURE_CDMA2000_EHRPD_REG, + MODEM_FEATURE_GERAN_REG, + MODEM_FEATURE_UTRAN_REG, + MODEM_FEATURE_EUTRAN_REG, + MODEM_FEATURE_NGRAN_REG, + MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG, + MODEM_FEATURE_PS_VOICE_REG, + MODEM_FEATURE_CS_VOICE_SESSION, + MODEM_FEATURE_INTERACTIVE_DATA_SESSION, + MODEM_FEATURE_DEDICATED_BEARER, + MODEM_FEATURE_NETWORK_SCAN, + MODEM_FEATURE_CSIM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ModemFeature { + } + + /** + * Hardcoded default DSDS capability. + * @hide + */ public static final PhoneCapability DEFAULT_DSDS_CAPABILITY; - // Hardcoded default Single SIM single standby capability. - /** @hide */ + /** + * Hardcoded default Single SIM single standby capability. + * @hide + */ public static final PhoneCapability DEFAULT_SSSS_CAPABILITY; static { - ModemInfo modemInfo1 = new ModemInfo(0, 0, true, true); - ModemInfo modemInfo2 = new ModemInfo(1, 0, true, true); + List<List<Long>> capabilities = new ArrayList<>(); + List<Long> modem1 = new ArrayList<>(); + List<Long> modem2 = new ArrayList<>(); + modem1.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG + | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_CS_VOICE_SESSION + | MODEM_FEATURE_INTERACTIVE_DATA_SESSION | MODEM_FEATURE_DEDICATED_BEARER); + modem2.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG + | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_INTERACTIVE_DATA_SESSION + | MODEM_FEATURE_DEDICATED_BEARER); + capabilities.add(modem1); + capabilities.add(modem2); + List<String> uuids = new ArrayList<>(); + uuids.add("com.xxxx.lm0"); + uuids.add("com.xxxx.lm1"); + long rats = TelephonyManager.NETWORK_TYPE_BITMASK_GSM + | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS + | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE + | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS + | TelephonyManager.NETWORK_TYPE_BITMASK_LTE; + DEFAULT_DSDS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null, + uuids, null, capabilities); + + capabilities = new ArrayList<>(); + capabilities.add(modem1); + uuids = new ArrayList<>(); + uuids.add("com.xxxx.lm0"); + DEFAULT_SSSS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null, + uuids, null, capabilities); + } - List<ModemInfo> logicalModemList = new ArrayList<>(); - logicalModemList.add(modemInfo1); - logicalModemList.add(modemInfo2); - DEFAULT_DSDS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false); + private final int mUtranUeCategoryDl; + private final int mUtranUeCategoryUl; + private final int mEutranUeCategoryDl; + private final int mEutranUeCategoryUl; + private final long mPsDataConnectionLingerTimeMillis; + private final @NetworkTypeBitMask long mSupportedRats; + private final List<Integer> mGeranBands; + private final List<Integer> mUtranBands; + private final List<Integer> mEutranBands; + private final List<Integer> mNgranBands; + private final List<String> mLogicalModemUuids; + private final List<SimSlotCapability> mSimSlotCapabilities; + private final @ModemFeature List<List<Long>> mConcurrentFeaturesSupport; - logicalModemList = new ArrayList<>(); - logicalModemList.add(modemInfo1); - DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false); + /** + * Default constructor to create a PhoneCapability object. + * @param utranUeCategoryDl 3GPP UE category for UTRAN downlink. + * @param utranUeCategoryUl 3GPP UE category for UTRAN uplink. + * @param eutranUeCategoryDl 3GPP UE category for EUTRAN downlink. + * @param eutranUeCategoryUl 3GPP UE category for EUTRAN uplink. + * @param psDataConnectionLingerTimeMillis length of the grace period to allow a smooth + * "handover" between data connections. + * @param supportedRats all radio access technologies this phone is capable of supporting. + * @param geranBands list of supported {@link AccessNetworkConstants.GeranBand}. + * @param utranBands list of supported {@link AccessNetworkConstants.UtranBand}. + * @param eutranBands list of supported {@link AccessNetworkConstants.EutranBand}. + * @param ngranBands list of supported {@link AccessNetworkConstants.NgranBands}. + * @param logicalModemUuids list of logical modem UUIDs, typically of the form + * "com.xxxx.lmX", where X is the logical modem ID. + * @param simSlotCapabilities list of {@link SimSlotCapability} for the device + * @param concurrentFeaturesSupport list of list of concurrently supportable modem feature sets. + * @hide + */ + public PhoneCapability(int utranUeCategoryDl, int utranUeCategoryUl, int eutranUeCategoryDl, + int eutranUeCategoryUl, long psDataConnectionLingerTimeMillis, + @NetworkTypeBitMask long supportedRats, @Nullable List<Integer> geranBands, + @Nullable List<Integer> utranBands, @Nullable List<Integer> eutranBands, + @Nullable List<Integer> ngranBands, @Nullable List<String> logicalModemUuids, + @Nullable List<SimSlotCapability> simSlotCapabilities, + @Nullable @ModemFeature List<List<Long>> concurrentFeaturesSupport) { + this.mUtranUeCategoryDl = utranUeCategoryDl; + this.mUtranUeCategoryUl = utranUeCategoryUl; + this.mEutranUeCategoryDl = eutranUeCategoryDl; + this.mEutranUeCategoryUl = eutranUeCategoryUl; + this.mPsDataConnectionLingerTimeMillis = psDataConnectionLingerTimeMillis; + this.mSupportedRats = supportedRats; + this.mGeranBands = CollectionUtils.emptyIfNull(geranBands); + this.mUtranBands = CollectionUtils.emptyIfNull(utranBands); + this.mEutranBands = CollectionUtils.emptyIfNull(eutranBands); + this.mNgranBands = CollectionUtils.emptyIfNull(ngranBands); + this.mLogicalModemUuids = CollectionUtils.emptyIfNull(logicalModemUuids); + this.mSimSlotCapabilities = CollectionUtils.emptyIfNull(simSlotCapabilities); + this.mConcurrentFeaturesSupport = CollectionUtils.emptyIfNull(concurrentFeaturesSupport); } - /** @hide */ - public final int maxActiveVoiceCalls; - /** @hide */ - public final int maxActiveData; - /** @hide */ - public final int max5G; - /** @hide */ - public final boolean validationBeforeSwitchSupported; - /** @hide */ - public final List<ModemInfo> logicalModemList; - /** @hide */ - public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G, - List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) { - this.maxActiveVoiceCalls = maxActiveVoiceCalls; - this.maxActiveData = maxActiveData; - this.max5G = max5G; - // Make sure it's not null. - this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList; - this.validationBeforeSwitchSupported = validationBeforeSwitchSupported; + private PhoneCapability(Parcel in) { + mUtranUeCategoryDl = in.readInt(); + mUtranUeCategoryUl = in.readInt(); + mEutranUeCategoryDl = in.readInt(); + mEutranUeCategoryUl = in.readInt(); + mPsDataConnectionLingerTimeMillis = in.readLong(); + mSupportedRats = in.readLong(); + mGeranBands = new ArrayList<>(); + in.readList(mGeranBands, Integer.class.getClassLoader()); + mUtranBands = new ArrayList<>(); + in.readList(mUtranBands, Integer.class.getClassLoader()); + mEutranBands = new ArrayList<>(); + in.readList(mEutranBands, Integer.class.getClassLoader()); + mNgranBands = new ArrayList<>(); + in.readList(mNgranBands, Integer.class.getClassLoader()); + mLogicalModemUuids = in.createStringArrayList(); + mSimSlotCapabilities = in.createTypedArrayList(SimSlotCapability.CREATOR); + int length = in.readInt(); + mConcurrentFeaturesSupport = new ArrayList<>(); + for (int i = 0; i < length; i++) { + ArrayList<Long> feature = new ArrayList<>(); + in.readList(feature, Long.class.getClassLoader()); + mConcurrentFeaturesSupport.add(feature); + } } - @Override - public String toString() { - return "maxActiveVoiceCalls=" + maxActiveVoiceCalls + " maxActiveData=" + maxActiveData - + " max5G=" + max5G + "logicalModemList:" - + Arrays.toString(logicalModemList.toArray()); + /** + * 3GPP UE category for a given Radio Access Network and direction. + * + * References are: + * TS 25.306 Table 4.1a EUTRAN downlink + * TS 25.306 Table 5.1a-2 EUTRAN uplink + * TS 25.306 Table 5.1a UTRAN downlink + * TS 25.306 Table 5.1g UTRAN uplink + * + * @param uplink true for uplink direction and false for downlink direction. + * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}. + * @return the UE category, or -1 if it is not supported. + */ + public int getUeCategory(boolean uplink, @RadioAccessNetworkType int accessNetworkType) { + if (uplink) { + switch (accessNetworkType) { + case AccessNetworkType.UTRAN: return mUtranUeCategoryUl; + case AccessNetworkType.EUTRAN: return mEutranUeCategoryUl; + default: return -1; + } + } else { + switch (accessNetworkType) { + case AccessNetworkType.UTRAN: return mUtranUeCategoryDl; + case AccessNetworkType.EUTRAN: return mEutranUeCategoryDl; + default: return -1; + } + } } - private PhoneCapability(Parcel in) { - maxActiveVoiceCalls = in.readInt(); - maxActiveData = in.readInt(); - max5G = in.readInt(); - validationBeforeSwitchSupported = in.readBoolean(); - logicalModemList = new ArrayList<>(); - in.readList(logicalModemList, ModemInfo.class.getClassLoader()); + /** + * In cellular devices that support a greater number of logical modems than + * Internet connections, some devices support a grace period to allow a smooth "handover" + * between those connections. If that feature is supported, then this API will provide + * the length of that grace period in milliseconds. If it is not supported, the default value + * for the grace period is 0. + * @return handover linger time in milliseconds, or 0 if it is not supported. + */ + public long getPsDataConnectionLingerTimeMillis() { + return mPsDataConnectionLingerTimeMillis; + } + + /** + * The radio access technologies this device is capable of supporting. + * @return a bitfield of all supported network types, defined in {@link TelephonyManager} + */ + public @NetworkTypeBitMask long getSupportedRats() { + return mSupportedRats; + } + + /** + * List of supported cellular bands for the given accessNetworkType. + * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}. + * @return a list of bands, or an empty list if the access network type is unsupported. + */ + public @NonNull List<Integer> getBands(@RadioAccessNetworkType int accessNetworkType) { + switch (accessNetworkType) { + case AccessNetworkType.GERAN: return mGeranBands; + case AccessNetworkType.UTRAN: return mUtranBands; + case AccessNetworkType.EUTRAN: return mEutranBands; + case AccessNetworkType.NGRAN: return mNgranBands; + default: return new ArrayList<>(); + } + } + + /** + * List of logical modem UUIDs, each typically "com.xxxx.lmX", where X is the logical modem ID. + * @return a list of modem UUIDs, one for every logical modem the device has. + */ + public @NonNull List<String> getLogicalModemUuids() { + return mLogicalModemUuids; + } + + /** + * List of {@link SimSlotCapability} for the device. The order of SIMs corresponds to the + * order of modems in {@link #getLogicalModemUuids}. + * @return a list of SIM slot capabilities, one for every SIM slot the device has. + */ + public @NonNull List<SimSlotCapability> getSimSlotCapabilities() { + return mSimSlotCapabilities; + } + + /** + * A List of Lists of concurrently supportable modem feature sets. + * + * Each entry in the top-level list is an independent configuration across all modems + * that describes the capabilities of the device as a whole. + * + * Each entry in the second-level list is a bitfield of ModemFeatures that describes + * the capabilities for a single modem. In the second-level list, the order of the modems + * corresponds to order of the UUIDs in {@link #getLogicalModemUuids}. + * + * For symmetric capabilities that can only be active on one modem at a time, there will be + * multiple configurations (equal to the number of modems) that shows it active on each modem. + * For asymmetric capabilities that are only available on one of the modems, all configurations + * will have that capability on just that one modem. + * + * The example below shows the concurrentFeaturesSupport for a 3-modem device with + * theoretical capabilities SYMMETRIC (available on all modems, but only one at a time) and + * ASYMMETRIC (only available on the first modem): + * { + * Configuration 1: ASYMMETRIC and SYMMETRIC on modem 1, modem 2 empty, modem 3 empty + * {(ASYMMETRIC | SYMMETRIC), (), ()}, + * + * Configuration 2: ASYMMETRIC on modem 1, SYMMETRIC on modem 2, modem 3 empty + * {(ASYMMETRIC), (SYMMETRIC), ()}, + * + * Configuration 3: ASYMMETRIC on modem 1, modem 2 empty, SYMMETRIC on modem 3 + * {(ASYMMETRIC), (), (SYMMETRIC)} + * } + * + * @return List of all concurrently supportable modem features. + */ + public @NonNull @ModemFeature List<List<Long>> getConcurrentFeaturesSupport() { + return mConcurrentFeaturesSupport; + } + + /** + * How many modems can simultaneously have PS attached. + * @return maximum number of active PS voice connections. + */ + public int getMaxActivePsVoice() { + return countFeature(MODEM_FEATURE_PS_VOICE_REG); + } + + /** + * How many modems can simultaneously support active data connections. + * For DSDS, this will be 1, and for DSDA this will be 2. + * @return maximum number of active Internet data sessions. + */ + public int getMaxActiveInternetData() { + return countFeature(MODEM_FEATURE_INTERACTIVE_DATA_SESSION); + } + + /** + * How many modems can simultaneously have dedicated bearer capability. + * @return maximum number of active dedicated bearers. + */ + public int getMaxActiveDedicatedBearers() { + return countFeature(MODEM_FEATURE_DEDICATED_BEARER); + } + + /** + * Whether the CBRS band 48 is supported or not. + * @return true if any RadioAccessNetwork supports CBRS and false if none do. + * @hide + */ + public boolean isCbrsSupported() { + return mEutranBands.contains(AccessNetworkConstants.EutranBand.BAND_48) + || mNgranBands.contains(AccessNetworkConstants.NgranBands.BAND_48); + } + + private int countFeature(@ModemFeature long feature) { + int count = 0; + for (long featureSet : mConcurrentFeaturesSupport.get(0)) { + if ((featureSet & feature) != 0) { + count++; + } + } + return count; + } + + @Override + public String toString() { + return "utranUeCategoryDl=" + mUtranUeCategoryDl + + " utranUeCategoryUl=" + mUtranUeCategoryUl + + " eutranUeCategoryDl=" + mEutranUeCategoryDl + + " eutranUeCategoryUl=" + mEutranUeCategoryUl + + " psDataConnectionLingerTimeMillis=" + mPsDataConnectionLingerTimeMillis + + " supportedRats=" + mSupportedRats + " geranBands=" + mGeranBands + + " utranBands=" + mUtranBands + " eutranBands=" + mEutranBands + + " ngranBands=" + mNgranBands + " logicalModemUuids=" + mLogicalModemUuids + + " simSlotCapabilities=" + mSimSlotCapabilities + + " concurrentFeaturesSupport=" + mConcurrentFeaturesSupport; } @Override public int hashCode() { - return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList, - validationBeforeSwitchSupported); + return Objects.hash(mUtranUeCategoryDl, mUtranUeCategoryUl, mEutranUeCategoryDl, + mEutranUeCategoryUl, mPsDataConnectionLingerTimeMillis, mSupportedRats, mGeranBands, + mUtranBands, mEutranBands, mNgranBands, mLogicalModemUuids, mSimSlotCapabilities, + mConcurrentFeaturesSupport); } @Override @@ -107,11 +411,19 @@ public final class PhoneCapability implements Parcelable { PhoneCapability s = (PhoneCapability) o; - return (maxActiveVoiceCalls == s.maxActiveVoiceCalls - && maxActiveData == s.maxActiveData - && max5G == s.max5G - && validationBeforeSwitchSupported == s.validationBeforeSwitchSupported - && logicalModemList.equals(s.logicalModemList)); + return (mUtranUeCategoryDl == s.mUtranUeCategoryDl + && mUtranUeCategoryUl == s.mUtranUeCategoryUl + && mEutranUeCategoryDl == s.mEutranUeCategoryDl + && mEutranUeCategoryUl == s.mEutranUeCategoryUl + && mPsDataConnectionLingerTimeMillis == s.mPsDataConnectionLingerTimeMillis + && mSupportedRats == s.mSupportedRats + && mGeranBands.equals(s.mGeranBands) + && mUtranBands.equals(s.mUtranBands) + && mEutranBands.equals(s.mEutranBands) + && mNgranBands.equals(s.mNgranBands) + && mLogicalModemUuids.equals(s.mLogicalModemUuids) + && mSimSlotCapabilities.equals(s.mSimSlotCapabilities) + && mConcurrentFeaturesSupport.equals(s.mConcurrentFeaturesSupport)); } /** @@ -125,20 +437,32 @@ public final class PhoneCapability implements Parcelable { * {@link Parcelable#writeToParcel} */ public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) { - dest.writeInt(maxActiveVoiceCalls); - dest.writeInt(maxActiveData); - dest.writeInt(max5G); - dest.writeBoolean(validationBeforeSwitchSupported); - dest.writeList(logicalModemList); + dest.writeInt(mUtranUeCategoryDl); + dest.writeInt(mUtranUeCategoryUl); + dest.writeInt(mEutranUeCategoryDl); + dest.writeInt(mEutranUeCategoryUl); + dest.writeLong(mPsDataConnectionLingerTimeMillis); + dest.writeLong(mSupportedRats); + dest.writeList(mGeranBands); + dest.writeList(mUtranBands); + dest.writeList(mEutranBands); + dest.writeList(mNgranBands); + dest.writeStringList(mLogicalModemUuids); + dest.writeTypedList(mSimSlotCapabilities); + dest.writeInt(mConcurrentFeaturesSupport.size()); + for (List<Long> feature : mConcurrentFeaturesSupport) { + dest.writeList(feature); + } } - public static final @android.annotation.NonNull Parcelable.Creator<PhoneCapability> CREATOR = new Parcelable.Creator() { - public PhoneCapability createFromParcel(Parcel in) { - return new PhoneCapability(in); - } + public static final @NonNull Parcelable.Creator<PhoneCapability> CREATOR = + new Parcelable.Creator() { + public PhoneCapability createFromParcel(Parcel in) { + return new PhoneCapability(in); + } - public PhoneCapability[] newArray(int size) { - return new PhoneCapability[size]; - } - }; + public PhoneCapability[] newArray(int size) { + return new PhoneCapability[size]; + } + }; } diff --git a/telephony/java/android/telephony/PreciseDataConnectionState.java b/telephony/java/android/telephony/PreciseDataConnectionState.java index 31434c1d2adf..0cfb8c56320a 100644 --- a/telephony/java/android/telephony/PreciseDataConnectionState.java +++ b/telephony/java/android/telephony/PreciseDataConnectionState.java @@ -20,6 +20,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.net.LinkProperties; import android.os.Build; @@ -31,8 +34,6 @@ import android.telephony.Annotation.DataState; import android.telephony.Annotation.NetworkType; import android.telephony.data.ApnSetting; -import dalvik.system.VMRuntime; - import java.util.Objects; @@ -134,6 +135,13 @@ public final class PreciseDataConnectionState implements Parcelable { } /** + * To check the SDK version for {@link PreciseDataConnectionState#getDataConnectionState}. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long GET_DATA_CONNECTION_STATE_CODE_CHANGE = 147600208L; + + /** * Returns the state of data connection that supported the apn types returned by * {@link #getDataConnectionApnTypeBitMask()} * @@ -144,7 +152,7 @@ public final class PreciseDataConnectionState implements Parcelable { @SystemApi public @DataState int getDataConnectionState() { if (mState == TelephonyManager.DATA_DISCONNECTING - && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) { + && !Compatibility.isChangeEnabled(GET_DATA_CONNECTION_STATE_CODE_CHANGE)) { return TelephonyManager.DATA_CONNECTED; } diff --git a/telephony/java/android/telephony/SimSlotCapability.java b/telephony/java/android/telephony/SimSlotCapability.java new file mode 100644 index 000000000000..3d38d0429908 --- /dev/null +++ b/telephony/java/android/telephony/SimSlotCapability.java @@ -0,0 +1,130 @@ +/* + * 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.telephony; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; + +/** + * Capabilities for a SIM Slot. + */ +public final class SimSlotCapability implements Parcelable { + /** Slot type for UICC (removable SIM). */ + public static final int SLOT_TYPE_UICC = 1; + /** Slot type for iUICC/iSIM (integrated SIM). */ + public static final int SLOT_TYPE_IUICC = 2; + /** Slot type for eUICC/eSIM (embedded SIM). */ + public static final int SLOT_TYPE_EUICC = 3; + /** Slot type for soft SIM (no physical SIM). */ + public static final int SLOT_TYPE_SOFT_SIM = 4; + + /** @hide */ + @IntDef(prefix = {"SLOT_TYPE_" }, value = { + SLOT_TYPE_UICC, + SLOT_TYPE_IUICC, + SLOT_TYPE_EUICC, + SLOT_TYPE_SOFT_SIM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SlotType { + } + + private final int mPhysicalSlotIndex; + private final int mSlotType; + + /** @hide */ + public SimSlotCapability(int physicalSlotId, int slotType) { + this.mPhysicalSlotIndex = physicalSlotId; + this.mSlotType = slotType; + } + + private SimSlotCapability(Parcel in) { + mPhysicalSlotIndex = in.readInt(); + mSlotType = in.readInt(); + } + + /** + * @return physical SIM slot index + */ + public int getPhysicalSlotIndex() { + return mPhysicalSlotIndex; + } + + /** + * @return type of SIM {@link SlotType} + */ + public @SlotType int getSlotType() { + return mSlotType; + } + + @Override + public String toString() { + return "mPhysicalSlotIndex=" + mPhysicalSlotIndex + " slotType=" + mSlotType; + } + + @Override + public int hashCode() { + return Objects.hash(mPhysicalSlotIndex, mSlotType); + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof SimSlotCapability) || hashCode() != o.hashCode()) { + return false; + } + + if (this == o) { + return true; + } + + SimSlotCapability s = (SimSlotCapability) o; + + return (mPhysicalSlotIndex == s.mPhysicalSlotIndex && mSlotType == s.mSlotType); + } + + /** + * {@link Parcelable#describeContents} + */ + public @ContentsFlags int describeContents() { + return 0; + } + + /** + * {@link Parcelable#writeToParcel} + */ + public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) { + dest.writeInt(mPhysicalSlotIndex); + dest.writeInt(mSlotType); + } + + public static final @NonNull Parcelable.Creator<SimSlotCapability> CREATOR = + new Parcelable.Creator() { + public SimSlotCapability createFromParcel(Parcel in) { + return new SimSlotCapability(in); + } + + public SimSlotCapability[] newArray(int size) { + return new SimSlotCapability[size]; + } + }; +} diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index b5dea7c02148..e7b2613d7bc4 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -25,11 +25,12 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; import android.annotation.TestApi; -import android.app.ActivityThread; import android.app.PendingIntent; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; -import android.content.pm.PackageManager; import android.database.CursorWindow; import android.net.Uri; import android.os.Build; @@ -388,7 +389,7 @@ public final class SmsManager { String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - true /* persistMessage*/, ActivityThread.currentPackageName()); + true /* persistMessage*/, null); } /** @@ -598,7 +599,7 @@ public final class SmsManager { String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent) { sendTextMessageInternal(destinationAddress, scAddress, text, sentIntent, deliveryIntent, - false /* persistMessage */, ActivityThread.currentPackageName()); + false /* persistMessage */, null); } private void sendTextMessageInternal( @@ -641,7 +642,7 @@ public final class SmsManager { ISms iSms = getISmsServiceOrThrow(); if (iSms != null) { iSms.sendTextForSubscriberWithOptions(subId, - ActivityThread.currentPackageName(), destinationAddress, + null, destinationAddress, scAddress, text, sentIntent, deliveryIntent, persistMessage, finalPriority, expectMore, finalValidity); @@ -663,7 +664,7 @@ public final class SmsManager { ISms iSms = getISmsServiceOrThrow(); if (iSms != null) { iSms.sendTextForSubscriberWithOptions(getSubscriptionId(), - ActivityThread.currentPackageName(), destinationAddress, + null, destinationAddress, scAddress, text, sentIntent, deliveryIntent, persistMessage, finalPriority, expectMore, finalValidity); @@ -881,7 +882,7 @@ public final class SmsManager { String destinationAddress, String scAddress, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, true /* persistMessage*/, ActivityThread.currentPackageName()); + deliveryIntents, true /* persistMessage*/, null); } /** @@ -898,8 +899,9 @@ public final class SmsManager { * subscription. * </p> * - * @param packageName serves as the default package name if - * {@link ActivityThread#currentPackageName()} is null. + * @param packageName serves as the default package name if the package name that is + * associated with the user id is null. + * * @hide */ @SystemApi @@ -909,9 +911,7 @@ public final class SmsManager { @NonNull List<String> parts, @Nullable List<PendingIntent> sentIntents, @Nullable List<PendingIntent> deliveryIntents, @NonNull String packageName) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, true /* persistMessage*/, - ActivityThread.currentPackageName() == null - ? packageName : ActivityThread.currentPackageName()); + deliveryIntents, true /* persistMessage*/, packageName); } private void sendMultipartTextMessageInternal( @@ -1012,7 +1012,7 @@ public final class SmsManager { String destinationAddress, String scAddress, List<String> parts, List<PendingIntent> sentIntents, List<PendingIntent> deliveryIntents) { sendMultipartTextMessageInternal(destinationAddress, scAddress, parts, sentIntents, - deliveryIntents, false /* persistMessage*/, ActivityThread.currentPackageName()); + deliveryIntents, false /* persistMessage*/, null); } /** @@ -1174,7 +1174,7 @@ public final class SmsManager { ISms iSms = getISmsServiceOrThrow(); if (iSms != null) { iSms.sendMultipartTextForSubscriberWithOptions(subId, - ActivityThread.currentPackageName(), destinationAddress, + null, destinationAddress, scAddress, parts, sentIntents, deliveryIntents, persistMessage, finalPriority, expectMore, finalValidity); } @@ -1196,7 +1196,7 @@ public final class SmsManager { ISms iSms = getISmsServiceOrThrow(); if (iSms != null) { iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(), - ActivityThread.currentPackageName(), destinationAddress, + null, destinationAddress, scAddress, parts, sentIntents, deliveryIntents, persistMessage, finalPriority, expectMore, finalValidity); } @@ -1327,7 +1327,7 @@ public final class SmsManager { public void onSuccess(int subId) { try { ISms iSms = getISmsServiceOrThrow(); - iSms.sendDataForSubscriber(subId, ActivityThread.currentPackageName(), + iSms.sendDataForSubscriber(subId, null, destinationAddress, scAddress, destinationPort & 0xFFFF, data, sentIntent, deliveryIntent); } catch (RemoteException e) { @@ -1453,7 +1453,6 @@ public final class SmsManager { private void resolveSubscriptionForOperation(SubscriptionResolverResult resolverResult) { int subId = getSubscriptionId(); boolean isSmsSimPickActivityNeeded = false; - final Context context = ActivityThread.currentApplication().getApplicationContext(); try { ISms iSms = getISmsService(); if (iSms != null) { @@ -1475,14 +1474,14 @@ public final class SmsManager { return; } // We need to ask the user pick an appropriate subid for the operation. - Log.d(TAG, "resolveSubscriptionForOperation isSmsSimPickActivityNeeded is true for package " - + context.getPackageName()); + Log.d(TAG, "resolveSubscriptionForOperation isSmsSimPickActivityNeeded is true for calling" + + " package. "); try { // Create the SMS pick activity and call back once the activity is complete. Can't do // it here because we do not have access to the activity context that is performing this // operation. // Requires that the calling process has the SEND_SMS permission. - getITelephony().enqueueSmsPickResult(context.getOpPackageName(), + getITelephony().enqueueSmsPickResult(null, new IIntegerConsumer.Stub() { @Override public void accept(int subId) { @@ -1500,6 +1499,13 @@ public final class SmsManager { } } + /** + * To check the SDK version for SmsManager.sendResolverResult method. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P) + private static final long GET_TARGET_SDK_VERSION_CODE_CHANGE = 145147528L; + private void sendResolverResult(SubscriptionResolverResult resolverResult, int subId, boolean pickActivityShown) { if (SubscriptionManager.isValidSubscriptionId(subId)) { @@ -1507,7 +1513,8 @@ public final class SmsManager { return; } - if (getTargetSdkVersion() <= Build.VERSION_CODES.P && !pickActivityShown) { + if (!Compatibility.isChangeEnabled(GET_TARGET_SDK_VERSION_CODE_CHANGE) + && !pickActivityShown) { // Do not fail, return a success with an INVALID subid for apps targeting P or below // that tried to perform an operation and the SMS disambiguation dialog was never shown, // as these applications may not have been written to handle the failure case properly. @@ -1520,19 +1527,6 @@ public final class SmsManager { } } - private static int getTargetSdkVersion() { - final Context context = ActivityThread.currentApplication().getApplicationContext(); - int targetSdk; - try { - targetSdk = context.getPackageManager().getApplicationInfo( - context.getOpPackageName(), 0).targetSdkVersion; - } catch (PackageManager.NameNotFoundException e) { - // Default to old behavior if we can not find this. - targetSdk = -1; - } - return targetSdk; - } - private static ITelephony getITelephony() { ITelephony binder = ITelephony.Stub.asInterface( ServiceManager.getService(Context.TELEPHONY_SERVICE)); @@ -1577,7 +1571,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,27 +1585,32 @@ 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(); if (iSms != null) { success = iSms.copyMessageToIccEfForSubscriber(getSubscriptionId(), - ActivityThread.currentPackageName(), + null, status, pdu, smsc); } } catch (RemoteException ex) { @@ -1622,7 +1621,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. * @@ -1652,7 +1651,7 @@ public final class SmsManager { ISms iSms = getISmsService(); if (iSms != null) { success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(), - ActivityThread.currentPackageName(), + null, messageIndex, STATUS_ON_ICC_FREE, null /* pdu */); } } catch (RemoteException ex) { @@ -1695,7 +1694,7 @@ public final class SmsManager { ISms iSms = getISmsService(); if (iSms != null) { success = iSms.updateMessageOnIccEfForSubscriber(getSubscriptionId(), - ActivityThread.currentPackageName(), + null, messageIndex, newStatus, pdu); } } catch (RemoteException ex) { @@ -1706,7 +1705,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. * @@ -1747,7 +1746,7 @@ public final class SmsManager { if (iSms != null) { records = iSms.getAllMessagesFromIccEfForSubscriber( getSubscriptionId(), - ActivityThread.currentPackageName()); + null); } } catch (RemoteException ex) { // ignore it @@ -1872,8 +1871,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 +1883,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 +1894,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 +2034,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). */ @@ -2481,7 +2489,7 @@ public final class SmsManager { try { ISms iccSms = getISmsServiceOrThrow(); return iccSms.createAppSpecificSmsToken(getSubscriptionId(), - ActivityThread.currentPackageName(), intent); + null, intent); } catch (RemoteException ex) { ex.rethrowFromSystemServer(); @@ -2601,7 +2609,7 @@ public final class SmsManager { try { ISms iccSms = getISmsServiceOrThrow(); return iccSms.createAppSpecificSmsTokenWithPackageInfo(getSubscriptionId(), - ActivityThread.currentPackageName(), prefixes, intent); + null, prefixes, intent); } catch (RemoteException ex) { ex.rethrowFromSystemServer(); @@ -2692,7 +2700,7 @@ public final class SmsManager { ISms iccISms = getISmsServiceOrThrow(); if (iccISms != null) { return iccISms.checkSmsShortCodeDestination(getSubscriptionId(), - ActivityThread.currentPackageName(), null, destAddress, countryIso); + null, null, destAddress, countryIso); } } catch (RemoteException e) { Log.e(TAG, "checkSmsShortCodeDestination() RemoteException", e); @@ -2728,7 +2736,7 @@ public final class SmsManager { ISms iSms = getISmsService(); if (iSms != null) { smsc = iSms.getSmscAddressFromIccEfForSubscriber( - getSubscriptionId(), ActivityThread.currentPackageName()); + getSubscriptionId(), null); } } catch (RemoteException ex) { // ignore it @@ -2762,7 +2770,7 @@ public final class SmsManager { ISms iSms = getISmsService(); if (iSms != null) { return iSms.setSmscAddressOnIccEfForSubscriber( - smsc, getSubscriptionId(), ActivityThread.currentPackageName()); + smsc, getSubscriptionId(), null); } } catch (RemoteException ex) { // ignore it 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/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index d4ab04cc2170..ea95aacc3184 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -266,7 +266,7 @@ public class SubscriptionManager { * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String UNIQUE_KEY_SUBSCRIPTION_ID = "_id"; + public static final String UNIQUE_KEY_SUBSCRIPTION_ID = SimInfo.UNIQUE_KEY_SUBSCRIPTION_ID; /** * TelephonyProvider column name for a unique identifier for the subscription within the @@ -275,18 +275,18 @@ public class SubscriptionManager { * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String ICC_ID = "icc_id"; + public static final String ICC_ID = SimInfo.ICC_ID; /** * TelephonyProvider column name for user SIM_SlOT_INDEX * <P>Type: INTEGER (int)</P> */ /** @hide */ - public static final String SIM_SLOT_INDEX = "sim_id"; + public static final String SIM_SLOT_INDEX = SimInfo.SIM_SLOT_INDEX; /** SIM is not inserted */ /** @hide */ - public static final int SIM_NOT_INSERTED = -1; + public static final int SIM_NOT_INSERTED = SimInfo.SIM_NOT_INSERTED; /** * The slot-index for Bluetooth Remote-SIM subscriptions @@ -301,23 +301,7 @@ public class SubscriptionManager { * Default value is 0. */ /** @hide */ - public static final String SUBSCRIPTION_TYPE = "subscription_type"; - - /** - * TelephonyProvider column name white_listed_apn_data. - * It's a bitmask of APN types that will be allowed on this subscription even if it's metered - * and mobile data is turned off by the user. - * <P>Type: INTEGER (int)</P> For example, if TYPE_MMS is is true, Telephony will allow MMS - * data connection to setup even if MMS is metered and mobile_data is turned off on that - * subscription. - * - * Default value is 0. - * - * @deprecated Replaced by {@link #DATA_ENABLED_OVERRIDE_RULES} - * @hide - */ - @Deprecated - public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data"; + public static final String SUBSCRIPTION_TYPE = SimInfo.SUBSCRIPTION_TYPE; /** * TelephonyProvider column name data_enabled_override_rules. @@ -330,7 +314,15 @@ public class SubscriptionManager { * * @hide */ - public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules"; + public static final String DATA_ENABLED_OVERRIDE_RULES = SimInfo.DATA_ENABLED_OVERRIDE_RULES; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"SUBSCRIPTION_TYPE_"}, + value = { + SUBSCRIPTION_TYPE_LOCAL_SIM, + SUBSCRIPTION_TYPE_REMOTE_SIM}) + public @interface SubscriptionType {} /** * This constant is to designate a subscription as a Local-SIM Subscription. @@ -338,7 +330,7 @@ public class SubscriptionManager { * device. * </p> */ - public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = 0; + public static final int SUBSCRIPTION_TYPE_LOCAL_SIM = SimInfo.SUBSCRIPTION_TYPE_LOCAL_SIM; /** * This constant is to designate a subscription as a Remote-SIM Subscription. @@ -364,29 +356,21 @@ public class SubscriptionManager { * was never seen before. * </p> */ - public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = 1; - - /** @hide */ - @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = {"SUBSCRIPTION_TYPE_"}, - value = { - SUBSCRIPTION_TYPE_LOCAL_SIM, - SUBSCRIPTION_TYPE_REMOTE_SIM}) - public @interface SubscriptionType {} + public static final int SUBSCRIPTION_TYPE_REMOTE_SIM = SimInfo.SUBSCRIPTION_TYPE_REMOTE_SIM; /** * TelephonyProvider column name for user displayed name. * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String DISPLAY_NAME = "display_name"; + public static final String DISPLAY_NAME = SimInfo.DISPLAY_NAME; /** * TelephonyProvider column name for the service provider name for the SIM. * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String CARRIER_NAME = "carrier_name"; + public static final String CARRIER_NAME = SimInfo.CARRIER_NAME; /** * Default name resource @@ -400,44 +384,44 @@ public class SubscriptionManager { * * @hide */ - public static final String NAME_SOURCE = "name_source"; + public static final String NAME_SOURCE = SimInfo.NAME_SOURCE; /** * The name_source is the default, which is from the carrier id. * @hide */ - public static final int NAME_SOURCE_DEFAULT_SOURCE = 0; + public static final int NAME_SOURCE_DEFAULT = SimInfo.NAME_SOURCE_DEFAULT; /** * The name_source is from SIM EF_SPN. * @hide */ - public static final int NAME_SOURCE_SIM_SPN = 1; + public static final int NAME_SOURCE_SIM_SPN = SimInfo.NAME_SOURCE_SIM_SPN; /** * The name_source is from user input * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - public static final int NAME_SOURCE_USER_INPUT = 2; + public static final int NAME_SOURCE_USER_INPUT = SimInfo.NAME_SOURCE_USER_INPUT; /** * The name_source is carrier (carrier app, carrier config, etc.) * @hide */ - public static final int NAME_SOURCE_CARRIER = 3; + public static final int NAME_SOURCE_CARRIER = SimInfo.NAME_SOURCE_CARRIER; /** * The name_source is from SIM EF_PNN. * @hide */ - public static final int NAME_SOURCE_SIM_PNN = 4; + public static final int NAME_SOURCE_SIM_PNN = SimInfo.NAME_SOURCE_SIM_PNN; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"NAME_SOURCE_"}, value = { - NAME_SOURCE_DEFAULT_SOURCE, + NAME_SOURCE_DEFAULT, NAME_SOURCE_SIM_SPN, NAME_SOURCE_USER_INPUT, NAME_SOURCE_CARRIER, @@ -450,67 +434,30 @@ public class SubscriptionManager { * <P>Type: INTEGER (int)</P> */ /** @hide */ - public static final String COLOR = "color"; - - /** @hide */ - public static final int COLOR_1 = 0; - - /** @hide */ - public static final int COLOR_2 = 1; - - /** @hide */ - public static final int COLOR_3 = 2; - - /** @hide */ - public static final int COLOR_4 = 3; - - /** @hide */ - public static final int COLOR_DEFAULT = COLOR_1; + public static final String COLOR = SimInfo.COLOR; /** * TelephonyProvider column name for the phone number of a SIM. * <P>Type: TEXT (String)</P> */ /** @hide */ - public static final String NUMBER = "number"; - - /** - * TelephonyProvider column name for the number display format of a SIM. - * <P>Type: INTEGER (int)</P> - */ - /** @hide */ - public static final String DISPLAY_NUMBER_FORMAT = "display_number_format"; - - /** @hide */ - public static final int DISPLAY_NUMBER_NONE = 0; - - /** @hide */ - public static final int DISPLAY_NUMBER_FIRST = 1; - - /** @hide */ - public static final int DISPLAY_NUMBER_LAST = 2; - - /** @hide */ - public static final int DISPLAY_NUMBER_DEFAULT = DISPLAY_NUMBER_FIRST; + public static final String NUMBER = SimInfo.NUMBER; /** - * TelephonyProvider column name for permission for data roaming of a SIM. + * TelephonyProvider column name for whether data roaming is enabled. * <P>Type: INTEGER (int)</P> */ /** @hide */ - public static final String DATA_ROAMING = "data_roaming"; + public static final String DATA_ROAMING = SimInfo.DATA_ROAMING; /** Indicates that data roaming is enabled for a subscription */ - public static final int DATA_ROAMING_ENABLE = 1; + public static final int DATA_ROAMING_ENABLE = SimInfo.DATA_ROAMING_ENABLE; /** Indicates that data roaming is disabled for a subscription */ - public static final int DATA_ROAMING_DISABLE = 0; - - /** @hide */ - public static final int DATA_ROAMING_DEFAULT = DATA_ROAMING_DISABLE; + public static final int DATA_ROAMING_DISABLE = SimInfo.DATA_ROAMING_DISABLE; /** @hide */ - public static final int SIM_PROVISIONED = 0; + public static final int DATA_ROAMING_DEFAULT = SimInfo.DATA_ROAMING_DEFAULT; /** * TelephonyProvider column name for subscription carrier id. @@ -518,61 +465,61 @@ public class SubscriptionManager { * <p>Type: INTEGER (int) </p> * @hide */ - public static final String CARRIER_ID = "carrier_id"; + public static final String CARRIER_ID = SimInfo.CARRIER_ID; /** * @hide A comma-separated list of EHPLMNs associated with the subscription * <P>Type: TEXT (String)</P> */ - public static final String EHPLMNS = "ehplmns"; + public static final String EHPLMNS = SimInfo.EHPLMNS; /** * @hide A comma-separated list of HPLMNs associated with the subscription * <P>Type: TEXT (String)</P> */ - public static final String HPLMNS = "hplmns"; + public static final String HPLMNS = SimInfo.HPLMNS; /** * TelephonyProvider column name for the MCC associated with a SIM, stored as a string. * <P>Type: TEXT (String)</P> * @hide */ - public static final String MCC_STRING = "mcc_string"; + public static final String MCC_STRING = SimInfo.MCC_STRING; /** * TelephonyProvider column name for the MNC associated with a SIM, stored as a string. * <P>Type: TEXT (String)</P> * @hide */ - public static final String MNC_STRING = "mnc_string"; + public static final String MNC_STRING = SimInfo.MNC_STRING; /** * TelephonyProvider column name for the MCC associated with a SIM. * <P>Type: INTEGER (int)</P> * @hide */ - public static final String MCC = "mcc"; + public static final String MCC = SimInfo.MCC; /** * TelephonyProvider column name for the MNC associated with a SIM. * <P>Type: INTEGER (int)</P> * @hide */ - public static final String MNC = "mnc"; + public static final String MNC = SimInfo.MNC; /** * TelephonyProvider column name for the iso country code associated with a SIM. * <P>Type: TEXT (String)</P> * @hide */ - public static final String ISO_COUNTRY_CODE = "iso_country_code"; + public static final String ISO_COUNTRY_CODE = SimInfo.ISO_COUNTRY_CODE; /** * TelephonyProvider column name for the sim provisioning status associated with a SIM. * <P>Type: INTEGER (int)</P> * @hide */ - public static final String SIM_PROVISIONING_STATUS = "sim_provisioning_status"; + public static final String SIM_PROVISIONING_STATUS = SimInfo.SIM_PROVISIONING_STATUS; /** * TelephonyProvider column name for whether a subscription is embedded (that is, present on an @@ -580,7 +527,7 @@ public class SubscriptionManager { * <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded. * @hide */ - public static final String IS_EMBEDDED = "is_embedded"; + public static final String IS_EMBEDDED = SimInfo.IS_EMBEDDED; /** * TelephonyProvider column name for SIM card identifier. For UICC card it is the ICCID of the @@ -588,7 +535,7 @@ public class SubscriptionManager { * <P>Type: TEXT (String)</P> * @hide */ - public static final String CARD_ID = "card_id"; + public static final String CARD_ID = SimInfo.CARD_ID; /** * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from @@ -596,7 +543,7 @@ public class SubscriptionManager { * <p>TYPE: BLOB * @hide */ - public static final String ACCESS_RULES = "access_rules"; + public static final String ACCESS_RULES = SimInfo.ACCESS_RULES; /** * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from @@ -606,7 +553,7 @@ public class SubscriptionManager { * @hide */ public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS = - "access_rules_from_carrier_configs"; + SimInfo.ACCESS_RULES_FROM_CARRIER_CONFIGS; /** * TelephonyProvider column name identifying whether an embedded subscription is on a removable @@ -616,79 +563,79 @@ public class SubscriptionManager { * <p>TYPE: INTEGER (int), 1 for removable or 0 for non-removable. * @hide */ - public static final String IS_REMOVABLE = "is_removable"; + public static final String IS_REMOVABLE = SimInfo.IS_REMOVABLE; /** * TelephonyProvider column name for extreme threat in CB settings * @hide */ - public static final String CB_EXTREME_THREAT_ALERT = "enable_cmas_extreme_threat_alerts"; + public static final String CB_EXTREME_THREAT_ALERT = SimInfo.CB_EXTREME_THREAT_ALERT; /** * TelephonyProvider column name for severe threat in CB settings *@hide */ - public static final String CB_SEVERE_THREAT_ALERT = "enable_cmas_severe_threat_alerts"; + public static final String CB_SEVERE_THREAT_ALERT = SimInfo.CB_SEVERE_THREAT_ALERT; /** * TelephonyProvider column name for amber alert in CB settings *@hide */ - public static final String CB_AMBER_ALERT = "enable_cmas_amber_alerts"; + public static final String CB_AMBER_ALERT = SimInfo.CB_AMBER_ALERT; /** * TelephonyProvider column name for emergency alert in CB settings *@hide */ - public static final String CB_EMERGENCY_ALERT = "enable_emergency_alerts"; + public static final String CB_EMERGENCY_ALERT = SimInfo.CB_EMERGENCY_ALERT; /** * TelephonyProvider column name for alert sound duration in CB settings *@hide */ - public static final String CB_ALERT_SOUND_DURATION = "alert_sound_duration"; + public static final String CB_ALERT_SOUND_DURATION = SimInfo.CB_ALERT_SOUND_DURATION; /** * TelephonyProvider column name for alert reminder interval in CB settings *@hide */ - public static final String CB_ALERT_REMINDER_INTERVAL = "alert_reminder_interval"; + public static final String CB_ALERT_REMINDER_INTERVAL = SimInfo.CB_ALERT_REMINDER_INTERVAL; /** * TelephonyProvider column name for enabling vibrate in CB settings *@hide */ - public static final String CB_ALERT_VIBRATE = "enable_alert_vibrate"; + public static final String CB_ALERT_VIBRATE = SimInfo.CB_ALERT_VIBRATE; /** * TelephonyProvider column name for enabling alert speech in CB settings *@hide */ - public static final String CB_ALERT_SPEECH = "enable_alert_speech"; + public static final String CB_ALERT_SPEECH = SimInfo.CB_ALERT_SPEECH; /** * TelephonyProvider column name for ETWS test alert in CB settings *@hide */ - public static final String CB_ETWS_TEST_ALERT = "enable_etws_test_alerts"; + public static final String CB_ETWS_TEST_ALERT = SimInfo.CB_ETWS_TEST_ALERT; /** * TelephonyProvider column name for enable channel50 alert in CB settings *@hide */ - public static final String CB_CHANNEL_50_ALERT = "enable_channel_50_alerts"; + public static final String CB_CHANNEL_50_ALERT = SimInfo.CB_CHANNEL_50_ALERT; /** * TelephonyProvider column name for CMAS test alert in CB settings *@hide */ - public static final String CB_CMAS_TEST_ALERT= "enable_cmas_test_alerts"; + public static final String CB_CMAS_TEST_ALERT = SimInfo.CB_CMAS_TEST_ALERT; /** * TelephonyProvider column name for Opt out dialog in CB settings *@hide */ - public static final String CB_OPT_OUT_DIALOG = "show_cmas_opt_out_dialog"; + public static final String CB_OPT_OUT_DIALOG = SimInfo.CB_OPT_OUT_DIALOG; /** * TelephonyProvider column name for enable Volte. @@ -697,37 +644,44 @@ public class SubscriptionManager { * {@link CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL}. *@hide */ - public static final String ENHANCED_4G_MODE_ENABLED = "volte_vt_enabled"; + public static final String ENHANCED_4G_MODE_ENABLED = SimInfo.ENHANCED_4G_MODE_ENABLED; /** * TelephonyProvider column name for enable VT (Video Telephony over IMS) *@hide */ - public static final String VT_IMS_ENABLED = "vt_ims_enabled"; + public static final String VT_IMS_ENABLED = SimInfo.VT_IMS_ENABLED; /** * TelephonyProvider column name for enable Wifi calling *@hide */ - public static final String WFC_IMS_ENABLED = "wfc_ims_enabled"; + public static final String WFC_IMS_ENABLED = SimInfo.WFC_IMS_ENABLED; /** * TelephonyProvider column name for Wifi calling mode *@hide */ - public static final String WFC_IMS_MODE = "wfc_ims_mode"; + public static final String WFC_IMS_MODE = SimInfo.WFC_IMS_MODE; /** * TelephonyProvider column name for Wifi calling mode in roaming *@hide */ - public static final String WFC_IMS_ROAMING_MODE = "wfc_ims_roaming_mode"; + public static final String WFC_IMS_ROAMING_MODE = SimInfo.WFC_IMS_ROAMING_MODE; /** * TelephonyProvider column name for enable Wifi calling in roaming *@hide */ - public static final String WFC_IMS_ROAMING_ENABLED = "wfc_ims_roaming_enabled"; + public static final String WFC_IMS_ROAMING_ENABLED = SimInfo.WFC_IMS_ROAMING_ENABLED; + + /** + * Determines if the user has enabled IMS RCS User Capability Exchange (UCE) for this + * subscription. + * @hide + */ + public static final String IMS_RCS_UCE_ENABLED = SimInfo.IMS_RCS_UCE_ENABLED; /** * TelephonyProvider column name for whether a subscription is opportunistic, that is, @@ -736,7 +690,7 @@ public class SubscriptionManager { * <p>Type: INTEGER (int), 1 for opportunistic or 0 for non-opportunistic. * @hide */ - public static final String IS_OPPORTUNISTIC = "is_opportunistic"; + public static final String IS_OPPORTUNISTIC = SimInfo.IS_OPPORTUNISTIC; /** * TelephonyProvider column name for group ID. Subscriptions with same group ID @@ -745,7 +699,7 @@ public class SubscriptionManager { * * @hide */ - public static final String GROUP_UUID = "group_uuid"; + public static final String GROUP_UUID = SimInfo.GROUP_UUID; /** * TelephonyProvider column name for group owner. It's the package name who created @@ -753,14 +707,7 @@ public class SubscriptionManager { * * @hide */ - public static final String GROUP_OWNER = "group_owner"; - - /** - * TelephonyProvider column name for whether a subscription is metered or not, that is, whether - * the network it connects to charges for subscription or not. For example, paid CBRS or unpaid. - * @hide - */ - public static final String IS_METERED = "is_metered"; + public static final String GROUP_OWNER = SimInfo.GROUP_OWNER; /** * TelephonyProvider column name for the profile class of a subscription @@ -768,7 +715,7 @@ public class SubscriptionManager { * <P>Type: INTEGER (int)</P> * @hide */ - public static final String PROFILE_CLASS = "profile_class"; + public static final String PROFILE_CLASS = SimInfo.PROFILE_CLASS; /** * Profile class of the subscription @@ -776,11 +723,11 @@ public class SubscriptionManager { */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "PROFILE_CLASS_" }, value = { - PROFILE_CLASS_TESTING, - PROFILE_CLASS_PROVISIONING, - PROFILE_CLASS_OPERATIONAL, - PROFILE_CLASS_UNSET, - PROFILE_CLASS_DEFAULT + SimInfo.PROFILE_CLASS_TESTING, + SimInfo.PROFILE_CLASS_PROVISIONING, + SimInfo.PROFILE_CLASS_OPERATIONAL, + SimInfo.PROFILE_CLASS_UNSET, + SimInfo.PROFILE_CLASS_DEFAULT }) public @interface ProfileClass {} @@ -792,7 +739,7 @@ public class SubscriptionManager { * @hide */ @SystemApi - public static final int PROFILE_CLASS_TESTING = 0; + public static final int PROFILE_CLASS_TESTING = SimInfo.PROFILE_CLASS_TESTING; /** * A provisioning profile is pre-loaded onto the eUICC and @@ -801,7 +748,7 @@ public class SubscriptionManager { * @hide */ @SystemApi - public static final int PROFILE_CLASS_PROVISIONING = 1; + public static final int PROFILE_CLASS_PROVISIONING = SimInfo.PROFILE_CLASS_PROVISIONING; /** * An operational profile can be pre-loaded or downloaded @@ -810,7 +757,7 @@ public class SubscriptionManager { * @hide */ @SystemApi - public static final int PROFILE_CLASS_OPERATIONAL = 2; + public static final int PROFILE_CLASS_OPERATIONAL = SimInfo.PROFILE_CLASS_OPERATIONAL; /** * The profile class is unset. This occurs when profile class @@ -819,14 +766,14 @@ public class SubscriptionManager { * @hide */ @SystemApi - public static final int PROFILE_CLASS_UNSET = -1; + public static final int PROFILE_CLASS_UNSET = SimInfo.PROFILE_CLASS_UNSET; /** * Default profile class * @hide */ @SystemApi - public static final int PROFILE_CLASS_DEFAULT = PROFILE_CLASS_UNSET; + public static final int PROFILE_CLASS_DEFAULT = SimInfo.PROFILE_CLASS_DEFAULT; /** * IMSI (International Mobile Subscriber Identity). @@ -834,13 +781,19 @@ public class SubscriptionManager { * @hide */ //TODO: add @SystemApi - public static final String IMSI = "imsi"; + public static final String IMSI = SimInfo.IMSI; /** * Whether uicc applications is set to be enabled or disabled. By default it's enabled. * @hide */ - public static final String UICC_APPLICATIONS_ENABLED = "uicc_applications_enabled"; + public static final String UICC_APPLICATIONS_ENABLED = SimInfo.UICC_APPLICATIONS_ENABLED; + + /** + * Indicate which network type is allowed. By default it's enabled. + * @hide + */ + public static final String ALLOWED_NETWORK_TYPES = SimInfo.ALLOWED_NETWORK_TYPES; /** * Broadcast Action: The user has changed one of the default subs related to @@ -2353,7 +2306,28 @@ public class SubscriptionManager { try { return Integer.parseInt(result); } catch (NumberFormatException err) { - logd("getBooleanSubscriptionProperty NumberFormat exception"); + logd("getIntegerSubscriptionProperty NumberFormat exception"); + } + } + return defValue; + } + + /** + * Returns long value corresponding to query result. + * @param subId Subscription Id of Subscription + * @param propKey Column name in SubscriptionInfo database + * @param defValue Default long value to be returned + * @return long result value to be returned + * @hide + */ + public static long getLongSubscriptionProperty(int subId, String propKey, long defValue, + Context context) { + String result = getSubscriptionProperty(subId, propKey, context); + if (result != null) { + try { + return Long.parseLong(result); + } catch (NumberFormatException err) { + logd("getLongSubscriptionProperty NumberFormat exception"); } } return defValue; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index ef631b8a05dc..3ac357a30aac 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -37,7 +37,6 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.WorkerThread; -import android.app.ActivityThread; import android.app.PendingIntent; import android.compat.Compatibility; import android.compat.annotation.ChangeId; @@ -109,8 +108,6 @@ import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.SmsApplication; import com.android.telephony.Rlog; -import dalvik.system.VMRuntime; - import java.io.FileInputStream; import java.io.IOException; import java.lang.annotation.Retention; @@ -374,8 +371,17 @@ public class TelephonyManager { // effort and get the context from the current activity thread. if (mContext != null) { return mContext.getOpPackageName(); + } else { + ITelephony telephony = getITelephony(); + if (telephony == null) return null; + try { + return telephony.getCurrentPackageName(); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } } - return ActivityThread.currentOpPackageName(); } private String getFeatureId() { @@ -1222,6 +1228,80 @@ public class TelephonyManager { public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID"; /** + * Broadcast Action: The Service Provider string(s) have been updated. Activities or + * services that use these strings should update their display. + * + * <p>The intent will have the following extra values: + * <dl> + * <dt>{@link #EXTRA_SHOW_PLMN}</dt> + * <dd>Boolean that indicates whether the PLMN should be shown.</dd> + * <dt>{@link #EXTRA_PLMN}</dt> + * <dd>The operator name of the registered network, as a string.</dd> + * <dt>{@link #EXTRA_SHOW_SPN}</dt> + * <dd>Boolean that indicates whether the SPN should be shown.</dd> + * <dt>{@link #EXTRA_SPN}</dt> + * <dd>The service provider name, as a string.</dd> + * <dt>{@link #EXTRA_DATA_SPN}</dt> + * <dd>The service provider name for data service, as a string.</dd> + * </dl> + * + * Note that {@link #EXTRA_SHOW_PLMN} may indicate that {@link #EXTRA_PLMN} should be displayed, + * even though the value for {@link #EXTRA_PLMN} is null. This can happen, for example, if the + * phone has not registered to a network yet. In this case the receiver may substitute an + * appropriate placeholder string (eg, "No service"). + * + * It is recommended to display {@link #EXTRA_PLMN} before / above {@link #EXTRA_SPN} if + * both are displayed. + * + * <p>Note: this is a protected intent that can only be sent by the system. + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SERVICE_PROVIDERS_UPDATED = + "android.telephony.action.SERVICE_PROVIDERS_UPDATED"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * whether the PLMN should be shown. + * @hide + */ + @SystemApi + public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * the operator name of the registered network. + * @hide + */ + @SystemApi + public static final String EXTRA_PLMN = "android.telephony.extra.PLMN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * whether the PLMN should be shown. + * @hide + */ + @SystemApi + public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * the service provider name. + * @hide + */ + @SystemApi + public static final String EXTRA_SPN = "android.telephony.extra.SPN"; + + /** + * String intent extra to be used with {@link ACTION_SERVICE_PROVIDERS_UPDATED} to indicate + * the service provider name for data service. + * @hide + */ + @SystemApi + public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN"; + + /** * Broadcast intent action indicating that when data stall recovery is attempted by Telephony, * intended for report every data stall recovery step attempted. * @@ -1440,6 +1520,24 @@ public class TelephonyManager { // /** + * Returns the {@link PhoneCapability} for the device or null if it is not available. + * <p> + * Requires Permission: READ_PHONE_STATE or that the calling app has + * carrier privileges (see {@link #hasCarrierPrivileges}). + */ + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + @Nullable + public PhoneCapability getPhoneCapability() { + try { + ITelephony telephony = getITelephony(); + return telephony == null ? null : + telephony.getPhoneCapability(getSubId(), getOpPackageName(), getFeatureId()); + } catch (RemoteException ex) { + return null; + } + } + + /** * Returns the software version number for the device, for example, * the IMEI/SV for GSM phones. Return null if the software version is * not available. @@ -2346,7 +2444,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); } /** @@ -5109,6 +5207,13 @@ public class TelephonyManager { public static final int DATA_DISCONNECTING = 4; /** + * To check the SDK version for {@link TelephonyManager#getDataState}. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long GET_DATA_STATE_CODE_CHANGE = 147600208L; + + /** * Returns a constant indicating the current data connection state * (cellular). * @@ -5126,7 +5231,7 @@ public class TelephonyManager { int state = telephony.getDataStateForSubId( getSubId(SubscriptionManager.getActiveDataSubscriptionId())); if (state == TelephonyManager.DATA_DISCONNECTING - && VMRuntime.getRuntime().getTargetSdkVersion() < Build.VERSION_CODES.R) { + && !Compatibility.isChangeEnabled(GET_DATA_STATE_CODE_CHANGE)) { return TelephonyManager.DATA_CONNECTED; } @@ -5179,6 +5284,13 @@ public class TelephonyManager { // /** + * To check the SDK version for {@link TelephonyManager#listen}. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.P) + private static final long LISTEN_CODE_CHANGE = 147600208L; + + /** * Registers a listener object to receive notification of changes * in specified telephony states. * <p> @@ -5217,7 +5329,7 @@ public class TelephonyManager { // subId from PhoneStateListener is deprecated Q on forward, use the subId from // TelephonyManager instance. keep using subId from PhoneStateListener for pre-Q. int subId = mSubId; - if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) { + if (Compatibility.isChangeEnabled(LISTEN_CODE_CHANGE)) { // since mSubId in PhoneStateListener is deprecated from Q on forward, this is // the only place to set mSubId and its for "informational" only. // TODO: remove this once we completely get rid of mSubId in PhoneStateListener @@ -6878,6 +6990,30 @@ public class TelephonyManager { } } + + /** + * Resets the {@link android.telephony.ims.ImsService} associated with the specified sim slot. + * Used by diagnostic apps to force the IMS stack to be disabled and re-enabled in an effort to + * recover from scenarios where the {@link android.telephony.ims.ImsService} gets in to a bad + * state. + * + * @param slotIndex the sim slot to reset the IMS stack on. + * @hide */ + @SystemApi + @WorkerThread + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void resetIms(int slotIndex) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.resetIms(slotIndex); + } + } catch (RemoteException e) { + Rlog.e(TAG, "toggleImsOnOff, RemoteException: " + + e.getMessage()); + } + } + /** * Enables IMS for the framework. This will trigger IMS registration and ImsFeature capability * status updates, if not already enabled. @@ -7286,7 +7422,9 @@ public class TelephonyManager { * * @return the preferred network type. * @hide + * @deprecated Use {@link #getPreferredNetworkTypeBitmask} instead. */ + @Deprecated @RequiresPermission((android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)) @UnsupportedAppUsage public @PrefNetworkMode int getPreferredNetworkType(int subId) { @@ -7331,6 +7469,30 @@ public class TelephonyManager { } /** + * Get the allowed network types. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). + * + * @return the allowed network type bitmask + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @SystemApi + public @NetworkTypeBitMask long getAllowedNetworkTypes() { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getAllowedNetworkTypes(getSubId()); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "getAllowedNetworkTypes RemoteException", ex); + } + return -1; + } + + /** * Sets the network selection mode to automatic. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the @@ -7585,7 +7747,9 @@ public class TelephonyManager { * @param networkType the preferred network type * @return true on success; false on any failure. * @hide + * @deprecated Use {@link #setPreferredNetworkTypeBitmask} instead. */ + @Deprecated @UnsupportedAppUsage public boolean setPreferredNetworkType(int subId, @PrefNetworkMode int networkType) { try { @@ -7600,7 +7764,8 @@ public class TelephonyManager { } /** - * Set the preferred network type bitmask. + * Set the preferred network type bitmask but if {@link #setAllowedNetworkTypes} has been set, + * only the allowed network type will set to the modem. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} @@ -7630,6 +7795,29 @@ public class TelephonyManager { } /** + * Set the allowed network types of the device. This is for carrier or privileged apps to + * enable/disable certain network types on the device. The user preferred network types should + * be set through {@link #setPreferredNetworkTypeBitmask}. + * + * @param allowedNetworkTypes The bitmask of allowed network types. + * @return true on success; false on any failure. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @SystemApi + public boolean setAllowedNetworkTypes(@NetworkTypeBitMask long allowedNetworkTypes) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.setAllowedNetworkTypes(getSubId(), allowedNetworkTypes); + } + } catch (RemoteException ex) { + Rlog.e(TAG, "setAllowedNetworkTypes RemoteException", ex); + } + return false; + } + + /** * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA. * * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). @@ -10501,6 +10689,27 @@ public class TelephonyManager { } /** + * Enable or disable signal strength changes from radio will always be reported in any + * condition (e.g. screen is off). This is only allowed for System caller. + * + * @param isEnabled {@code true} for enabling; {@code false} for disabling. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public void setAlwaysReportSignalStrength(boolean isEnabled) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setAlwaysReportSignalStrength(getSubId(), isEnabled); + } + } catch (RemoteException ex) { + Log.e(TAG, "setAlwaysReportSignalStrength RemoteException", ex); + ex.rethrowAsRuntimeException(); + } + } + + /** * Get the most recently available signal strength information. * * Get the most recent SignalStrength information reported by the modem. Due diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java index cb3f0f92625e..643f452d2e75 100644 --- a/telephony/java/android/telephony/ims/ImsException.java +++ b/telephony/java/android/telephony/ims/ImsException.java @@ -61,7 +61,6 @@ public final class ImsException extends Exception { * This is a configuration error and there should be no retry. The subscription used for this * operation is either invalid or has become inactive. The active subscriptions can be queried * with {@link SubscriptionManager#getActiveSubscriptionInfoList()}. - * @hide */ public static final int CODE_ERROR_INVALID_SUBSCRIPTION = 3; diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index c66672f13129..ba8e90ff539b 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -57,7 +57,8 @@ import java.util.function.Consumer; * registration and MmTel capability status callbacks, as well as query/modify user settings for the * associated subscription. * - * @see #createForSubscriptionId(int) + * Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an instance of this + * manager. */ public class ImsMmTelManager implements RegistrationManager { @@ -225,8 +226,13 @@ public class ImsMmTelManager implements RegistrationManager { * (see {@link android.telephony.TelephonyManager#hasCarrierPrivileges}). * * @throws IllegalArgumentException if the subscription is invalid. - * + * @deprecated Use {@link android.telephony.ims.ImsManager#getImsMmTelManager(int)} to get an + * instance of this class. + * @hide */ + @SystemApi + @TestApi + @Deprecated @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, @@ -242,7 +248,7 @@ public class ImsMmTelManager implements RegistrationManager { } /** - * Only visible for testing, use {@link #createForSubscriptionId(int)} instead. + * Only visible for testing, use {@link ImsManager#getImsMmTelManager(int)} instead. * @hide */ @VisibleForTesting @@ -252,7 +258,7 @@ public class ImsMmTelManager implements RegistrationManager { /** * Registers a {@link RegistrationCallback} with the system, which will provide registration - * updates for the subscription specified in {@link #createForSubscriptionId(int)}. Use + * updates for the subscription specified in {@link ImsManager#getImsMmTelManager(int)}. Use * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up. * @@ -450,7 +456,7 @@ 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 {@see #isAvailable(int, int)} + * {@link ImsManager#getImsMmTelManager(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 diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index 62bc2ae44573..2b3072eefe2e 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -60,9 +60,10 @@ import com.android.internal.annotations.VisibleForTesting; * The telephony framework will then bind to the ImsService you have defined in your manifest * if you are either: * 1) Defined as the default ImsService for the device in the device overlay using - * "config_ims_package". + * "config_ims_mmtel_package" or "config_ims_rcs_package". * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using - * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}. + * {@link CarrierConfigManager#KEY_CONFIG_IMS_MMTEL_PACKAGE_OVERRIDE_STRING} or + * {@link CarrierConfigManager#KEY_CONFIG_IMS_RCS_PACKAGE_OVERRIDE_STRING}. * * There are two ways to define to the platform which {@link ImsFeature}s this {@link ImsService} * supports, dynamic or static definitions. diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index 3e2903fa6f47..57b9b7a30f8c 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -16,10 +16,11 @@ package android.telephony.ims; -import android.annotation.IntDef; +import android.annotation.LongDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -37,6 +38,7 @@ import java.util.Map; * @hide */ @SystemApi +@TestApi public final class RcsContactUceCapability implements Parcelable { /** Supports 1-to-1 chat */ @@ -99,11 +101,16 @@ public final class RcsContactUceCapability implements Parcelable { public static final int CAPABILITY_CHAT_BOT_ROLE = (1 << 27); /** Supports the unidirectional plug-ins framework. */ public static final int CAPABILITY_PLUG_IN = (1 << 28); + /** Supports standalone Chatbot communication. */ + public static final int CAPABILITY_STANDALONE_CHAT_BOT = (1 << 29); + /** Supports MMTEL based call composer. */ + public static final int CAPABILITY_MMTEL_CALL_COMPOSER = (1 << 30); + /** @hide*/ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "CAPABILITY_", flag = true, value = { + @LongDef(prefix = "CAPABILITY_", flag = true, value = { CAPABILITY_CHAT_STANDALONE, CAPABILITY_CHAT_SESSION, CAPABILITY_CHAT_SESSION_STORE_FORWARD, @@ -132,7 +139,9 @@ public final class RcsContactUceCapability implements Parcelable { CAPABILITY_SHARED_SKETCH, CAPABILITY_CHAT_BOT, CAPABILITY_CHAT_BOT_ROLE, - CAPABILITY_PLUG_IN + CAPABILITY_PLUG_IN, + CAPABILITY_STANDALONE_CHAT_BOT, + CAPABILITY_MMTEL_CALL_COMPOSER }) public @interface CapabilityFlag {} @@ -159,11 +168,11 @@ public final class RcsContactUceCapability implements Parcelable { * @param type The capability to map to a service URI that is different from the contact's * URI. */ - public @NonNull Builder add(@CapabilityFlag int type, @NonNull Uri serviceUri) { + public @NonNull Builder add(@CapabilityFlag long type, @NonNull Uri serviceUri) { mCapabilities.mCapabilities |= type; // Put each of these capabilities into the map separately. - for (int shift = 0; shift < Integer.SIZE; shift++) { - int cap = type & (1 << shift); + for (long shift = 0; shift < Integer.SIZE; shift++) { + long cap = type & (1 << shift); if (cap != 0) { mCapabilities.mServiceMap.put(cap, serviceUri); // remove that capability from the field. @@ -181,7 +190,7 @@ public final class RcsContactUceCapability implements Parcelable { * Add a UCE capability flag that this contact supports. * @param type the capability that the contact supports. */ - public @NonNull Builder add(@CapabilityFlag int type) { + public @NonNull Builder add(@CapabilityFlag long type) { mCapabilities.mCapabilities |= type; return this; } @@ -207,7 +216,7 @@ public final class RcsContactUceCapability implements Parcelable { private final Uri mContactUri; private long mCapabilities; private List<String> mExtensionTags = new ArrayList<>(); - private Map<Integer, Uri> mServiceMap = new HashMap<>(); + private Map<Long, Uri> mServiceMap = new HashMap<>(); /** * Use {@link Builder} to build an instance of this interface. @@ -225,7 +234,7 @@ public final class RcsContactUceCapability implements Parcelable { // read mServiceMap as key,value pair int mapSize = in.readInt(); for (int i = 0; i < mapSize; i++) { - mServiceMap.put(in.readInt(), in.readParcelable(Uri.class.getClassLoader())); + mServiceMap.put(in.readLong(), in.readParcelable(Uri.class.getClassLoader())); } } @@ -250,8 +259,8 @@ public final class RcsContactUceCapability implements Parcelable { // write mServiceMap as key,value pairs int mapSize = mServiceMap.keySet().size(); out.writeInt(mapSize); - for (int key : mServiceMap.keySet()) { - out.writeInt(key); + for (long key : mServiceMap.keySet()) { + out.writeLong(key); out.writeParcelable(mServiceMap.get(key), 0); } } @@ -266,7 +275,7 @@ public final class RcsContactUceCapability implements Parcelable { * @param type The capability flag to query. * @return true if the capability flag specified is set, false otherwise. */ - public boolean isCapable(@CapabilityFlag int type) { + public boolean isCapable(@CapabilityFlag long type) { return (mCapabilities & type) > 0; } @@ -290,13 +299,13 @@ public final class RcsContactUceCapability implements Parcelable { * <p> * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless * a different service {@link Uri} was associated with this capability using - * {@link Builder#add(int, Uri)}. + * {@link Builder#add(long, Uri)}. * * @return a String containing the {@link Uri} associated with the service tag or * {@code null} if this capability is not set as capable. - * @see #isCapable(int) + * @see #isCapable(long) */ - public @Nullable Uri getServiceUri(@CapabilityFlag int type) { + public @Nullable Uri getServiceUri(@CapabilityFlag long type) { Uri result = mServiceMap.getOrDefault(type, null); // If the capability is capable, but does not have a service URI associated, use the default // contact URI. diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index b47bcb9b119b..d3f393ae11a2 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -21,6 +21,8 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.content.Context; import android.net.Uri; import android.os.Binder; @@ -29,6 +31,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.aidl.IRcsUceControllerCallback; +import android.telephony.ims.feature.RcsFeature; import android.util.Log; import java.lang.annotation.Retention; @@ -42,6 +45,8 @@ import java.util.concurrent.Executor; * @see ImsRcsManager#getUceAdapter() for information on creating an instance of this class. * @hide */ +@SystemApi +@TestApi public class RcsUceAdapter { private static final String TAG = "RcsUceAdapter"; @@ -197,6 +202,7 @@ public class RcsUceAdapter { /** * Not to be instantiated directly, use * {@link ImsRcsManager#getUceAdapter()} to instantiate this manager class. + * @hide */ RcsUceAdapter(int subId) { mSubId = subId; @@ -222,7 +228,7 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public void requestCapabilities(@CallbackExecutor Executor executor, + public void requestCapabilities(@NonNull @CallbackExecutor Executor executor, @NonNull List<Uri> contactNumbers, @NonNull CapabilitiesCallback c) throws ImsException { if (c == null) { @@ -299,7 +305,7 @@ public class RcsUceAdapter { * for the associated subscription. * * @return true if the user’s setting for UCE is enabled, false otherwise. If false, - * {@link ImsRcsManager#isCapable(int)} will return false for + * {@link ImsRcsManager#isCapable(int, int)} will return false for * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} and * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} * @see #setUceSettingEnabled(boolean) diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index 0d5a979e5894..a3ce1b585f76 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -325,7 +325,6 @@ public class MmTelFeature extends ImsFeature { */ @NonNull @Override - @SystemApi @TestApi public String toString() { StringBuilder builder = new StringBuilder("MmTel Capabilities - ["); builder.append("Voice: "); @@ -514,7 +513,7 @@ 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 + * @hide */ @SystemApi @TestApi public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile, diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 501e0e8fa22c..e4efc20437bb 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -349,9 +349,8 @@ public class RcsFeature extends ImsFeature { * * @return An instance of {@link RcsSipOptionsImplBase} that implements SIP options exchange if * it is supported by the device. - * @hide */ - public RcsSipOptionsImplBase getOptionsExchangeImpl() { + public @NonNull RcsSipOptionsImplBase getOptionsExchangeImpl() { // Base Implementation, override to implement functionality return new RcsSipOptionsImplBase(); } @@ -365,9 +364,8 @@ public class RcsFeature extends ImsFeature { * * @return An instance of {@link RcsPresenceExchangeImplBase} that implements presence * exchange if it is supported by the device. - * @hide */ - public RcsPresenceExchangeImplBase getPresenceExchangeImpl() { + public @NonNull RcsPresenceExchangeImplBase getPresenceExchangeImpl() { // Base Implementation, override to implement functionality. return new RcsPresenceExchangeImplBase(); } diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java index f4367da4a4dc..e8f69ea64a22 100644 --- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java @@ -410,6 +410,13 @@ public class ImsCallSessionImplBase implements AutoCloseable { * Rejects an incoming call or session update. * * @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}. + * The {@link android.telecom.InCallService} (dialer app) can use the + * {@link android.telecom.Call#reject(int)} API to reject a call while specifying + * a user-indicated reason for rejecting the call. + * Normal call declines ({@link android.telecom.Call#REJECT_REASON_DECLINED}) will + * map to {@link ImsReasonInfo#CODE_USER_DECLINE}. + * Unwanted calls ({@link android.telecom.Call#REJECT_REASON_UNWANTED}) will map + * to {@link ImsReasonInfo#CODE_SIP_USER_MARKED_UNWANTED}. * {@link ImsCallSession.Listener#callSessionStartFailed} */ public void reject(int reason) { 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/android/telephony/ims/stub/RcsCapabilityExchange.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java index fda295a27111..a24af2f74e27 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchange.java @@ -17,6 +17,8 @@ package android.telephony.ims.stub; import android.annotation.IntDef; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.RemoteException; import android.telephony.ims.ImsException; import android.telephony.ims.aidl.IRcsFeatureListener; @@ -32,6 +34,8 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemApi +@TestApi public class RcsCapabilityExchange { /** Service is unknown. */ diff --git a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java index bb034489a296..f200ea2af2bc 100644 --- a/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsPresenceExchangeImplBase.java @@ -18,6 +18,8 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.ImsException; @@ -37,6 +39,8 @@ import java.util.List; * * @hide */ +@SystemApi +@TestApi public class RcsPresenceExchangeImplBase extends RcsCapabilityExchange { private static final String LOG_TAG = "RcsPresenceExchangeIB"; diff --git a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java index 1c68fc08529e..355c4dde75d8 100644 --- a/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsSipOptionsImplBase.java @@ -19,6 +19,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.net.Uri; import android.os.RemoteException; import android.telephony.ims.ImsException; @@ -35,6 +37,8 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ +@SystemApi +@TestApi public class RcsSipOptionsImplBase extends RcsCapabilityExchange { private static final String LOG_TAG = "RcsSipOptionsImplBase"; @@ -69,6 +73,11 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange { */ public static final int RESPONSE_DOES_NOT_EXIST_ANYWHERE = 4; + /** + * Indicates that the remote user responded with a 400 BAD REQUEST response. + */ + public static final int RESPONSE_BAD_REQUEST = 5; + /** @hide*/ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "RESPONSE_", value = { @@ -77,7 +86,8 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange { RESPONSE_TEMPORARILY_UNAVAILABLE, RESPONSE_REQUEST_TIMEOUT, RESPONSE_NOT_FOUND, - RESPONSE_DOES_NOT_EXIST_ANYWHERE + RESPONSE_DOES_NOT_EXIST_ANYWHERE, + RESPONSE_BAD_REQUEST }) public @interface SipResponseCode {} @@ -188,7 +198,6 @@ public class RcsSipOptionsImplBase extends RcsCapabilityExchange { * @param reason A non-null String containing the reason associated with the SIP code. * @param operationToken The token provided by the framework when * {@link #onRemoteCapabilityRequest(Uri, RcsContactUceCapability, int)} was called. - * */ public void respondToCapabilityRequestWithError(@NonNull Uri contactUri, @SipResponseCode int code, @NonNull String reason, int operationToken) { 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/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 1f84451a35e4..1d794cde7d9c 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -38,6 +38,7 @@ import android.telephony.ICellInfoCallback; import android.telephony.ModemActivityInfo; import android.telephony.NeighboringCellInfo; import android.telephony.NetworkScanRequest; +import android.telephony.PhoneCapability; import android.telephony.PhoneNumberRange; import android.telephony.RadioAccessFamily; import android.telephony.ServiceState; @@ -828,6 +829,11 @@ interface ITelephony { void disableIms(int slotId); /** + * Toggle framework IMS disables and enables. + */ + void resetIms(int slotIndex); + + /** * Get IImsMmTelFeature binder from ImsResolver that corresponds to the subId and MMTel feature * as well as registering the MmTelFeature for callbacks using the IImsServiceFeatureCallback * interface. @@ -922,6 +928,23 @@ interface ITelephony { int subId, in OperatorInfo operatorInfo, boolean persisSelection); /** + * Get the allowed network types that store in the telephony provider. + * + * @param subId the id of the subscription. + * @return allowedNetworkTypes the allowed network types. + */ + long getAllowedNetworkTypes(int subId); + + /** + * Set the allowed network types. + * + * @param subId the id of the subscription. + * @param allowedNetworkTypes the allowed network types. + * @return true on success; false on any failure. + */ + boolean setAllowedNetworkTypes(int subId, long allowedNetworkTypes); + + /** * Set the preferred network type. * Used for device configuration by some CDMA operators. * @@ -973,6 +996,11 @@ interface ITelephony { boolean isManualNetworkSelectionAllowed(int subId); /** + * Enable or disable always reporting signal strength changes from radio. + */ + void setAlwaysReportSignalStrength(int subId, boolean isEnable); + + /** * Get P-CSCF address from PCO after data connection is established or modified. * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN * @param callingPackage The package making the call. @@ -1803,12 +1831,17 @@ interface ITelephony { /** * Return the network selection mode on the subscription with id {@code subId}. */ - int getNetworkSelectionMode(int subId); + int getNetworkSelectionMode(int subId); - /** + /** + * Return the PhoneCapability for the device. + */ + PhoneCapability getPhoneCapability(int subId, String callingPackage, String callingFeatureId); + + /** * Return true if the device is in emergency sms mode, false otherwise. */ - boolean isInEmergencySmsMode(); + boolean isInEmergencySmsMode(); /** * Return the modem radio power state for slot index. @@ -2087,6 +2120,11 @@ interface ITelephony { int getRadioHalVersion(); /** + * Get the current calling package name. + */ + String getCurrentPackageName(); + + /** * Returns true if the specified type of application (e.g. {@link #APPTYPE_CSIM} is present * on the UICC card. * @hide diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 284544b7f6f2..0e0a22eecd28 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -477,6 +477,7 @@ public interface RILConstants { int RIL_REQUEST_STOP_KEEPALIVE = 145; int RIL_REQUEST_ENABLE_MODEM = 146; int RIL_REQUEST_GET_MODEM_STATUS = 147; + int RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE = 148; /* The following requests are not defined in RIL.h */ int RIL_REQUEST_HAL_NON_RIL_BASE = 200; 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/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java index 42cafd43f8bd..5a66e805c575 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java @@ -66,7 +66,7 @@ public class ChangeAppRotationTest extends FlickerTestBase { @Parameters(name = "{0}-{1}") public static Collection<Object[]> getParams() { int[] supportedRotations = - {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270}; + {Surface.ROTATION_0, Surface.ROTATION_90}; Collection<Object[]> params = new ArrayList<>(); for (int begin : supportedRotations) { for (int end : supportedRotations) { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java index fc6719e2f9d9..f740af9b89bf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CloseImeWindowToHomeTest.java @@ -25,6 +25,7 @@ import com.android.server.wm.flicker.helpers.ImeAppHelper; import org.junit.Before; import org.junit.FixMethodOrder; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; @@ -62,6 +63,7 @@ public class CloseImeWindowToHomeTest extends NonRotationTestBase { .forAllEntries()); } + @Ignore("Flaky") @Test public void checkVisibility_imeLayerBecomesInvisible() { checkResults(result -> LayersTraceSubject.assertThat(result) @@ -71,6 +73,7 @@ public class CloseImeWindowToHomeTest extends NonRotationTestBase { .forAllEntries()); } + @Ignore("Flaky") @Test public void checkVisibility_imeAppLayerBecomesInvisible() { checkResults(result -> LayersTraceSubject.assertThat(result) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java index ae55a75d7e67..37d7c4ca2b46 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/SeamlessAppRotationTest.java @@ -30,10 +30,12 @@ import android.graphics.Rect; import android.view.Surface; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.FlakyTest; import androidx.test.filters.LargeTest; import org.junit.Before; import org.junit.FixMethodOrder; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.MethodSorters; @@ -50,6 +52,8 @@ import java.util.Collection; @LargeTest @RunWith(Parameterized.class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) +@FlakyTest(bugId = 147659548) +@Ignore("Waiting bug feedback") public class SeamlessAppRotationTest extends FlickerTestBase { private int mBeginRotation; private int mEndRotation; @@ -65,7 +69,7 @@ public class SeamlessAppRotationTest extends FlickerTestBase { @Parameters(name = "{0}") public static Collection<Object[]> getParams() { int[] supportedRotations = - {Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270}; + {Surface.ROTATION_0, Surface.ROTATION_90}; Collection<Object[]> params = new ArrayList<>(); ArrayList<Intent> testIntents = new ArrayList<>(); @@ -108,7 +112,7 @@ public class SeamlessAppRotationTest extends FlickerTestBase { super.runTransition( changeAppRotation(mIntent, intentId, InstrumentationRegistry.getContext(), - mUiDevice, mBeginRotation, mEndRotation).repeat(5).build()); + mUiDevice, mBeginRotation, mEndRotation).build()); } @Test diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp index 5e9ef8efc402..609896ea9e95 100644 --- a/tests/PlatformCompatGating/Android.bp +++ b/tests/PlatformCompatGating/Android.bp @@ -18,7 +18,6 @@ android_test { name: "PlatformCompatGating", // Only compile source java files in this apk. srcs: ["src/**/*.java"], - certificate: "platform", libs: [ "android.test.runner", "android.test.base", diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java index 932ec643d478..c00aa2ac25b3 100644 --- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java +++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java @@ -16,7 +16,9 @@ package android.compat.testing; +import android.Manifest; import android.app.Instrumentation; +import android.app.UiAutomation; import android.compat.Compatibility; import android.compat.Compatibility.ChangeConfig; import android.content.Context; @@ -83,12 +85,16 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule { @Override public void evaluate() throws Throwable { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + UiAutomation uiAutomation = instrumentation.getUiAutomation(); String packageName = instrumentation.getTargetContext().getPackageName(); IPlatformCompat platformCompat = IPlatformCompat.Stub .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); if (platformCompat == null) { throw new IllegalStateException("Could not get IPlatformCompat service!"); } + uiAutomation.adoptShellPermissionIdentity( + Manifest.permission.READ_COMPAT_CHANGE_CONFIG, + Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG); Compatibility.setOverrides(mConfig); try { platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig), @@ -101,6 +107,7 @@ public class PlatformCompatChangeRule extends CoreCompatChangeRule { } catch (RemoteException e) { throw new RuntimeException("Could not call IPlatformCompat binder method!", e); } finally { + uiAutomation.dropShellPermissionIdentity(); Compatibility.clearOverrides(); } } diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java index b2e573b6c74b..06c6301d66f3 100644 --- a/tests/net/common/java/android/net/LinkAddressTest.java +++ b/tests/net/common/java/android/net/LinkAddressTest.java @@ -38,6 +38,8 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.os.SystemClock; + import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -316,9 +318,76 @@ public class LinkAddressTest { l = new LinkAddress(V6_ADDRESS, 64, 123, 456); assertParcelingIsLossless(l); + l = new LinkAddress(V6_ADDRESS, 64, 123, 456, + 1L, 3600000L); + assertParcelingIsLossless(l); l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK); - assertParcelSane(l, 4); + assertParcelSane(l, 6); + } + + @Test + public void testDeprecationTime() { + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + LinkAddress.LIFETIME_UNKNOWN, 100000L); + fail("Only one time provided should cause exception"); + } catch (IllegalArgumentException expected) { } + + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + 200000L, 100000L); + fail("deprecation time later than expiration time should cause exception"); + } catch (IllegalArgumentException expected) { } + + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + -2, 100000L); + fail("negative deprecation time should cause exception"); + } catch (IllegalArgumentException expected) { } + } + + @Test + public void testExpirationTime() { + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + 200000L, LinkAddress.LIFETIME_UNKNOWN); + fail("Only one time provided should cause exception"); + } catch (IllegalArgumentException expected) { } + + try { + new LinkAddress(V6_ADDRESS, 64, 0, 456, + 100000L, -2); + fail("negative expiration time should cause exception"); + } catch (IllegalArgumentException expected) { } + } + + @Test + public void testGetFlags() { + LinkAddress l = new LinkAddress(V6_ADDRESS, 64, 123, RT_SCOPE_HOST); + assertEquals(123, l.getFlags()); + + // Test if deprecated bit was added/remove automatically based on the provided deprecation + // time + l = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_HOST, + 1L, LinkAddress.LIFETIME_PERMANENT); + // Check if the flag is added automatically. + assertTrue((l.getFlags() & IFA_F_DEPRECATED) != 0); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST, + SystemClock.elapsedRealtime() + 100000L, LinkAddress.LIFETIME_PERMANENT); + // Check if the flag is removed automatically. + assertTrue((l.getFlags() & IFA_F_DEPRECATED) == 0); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_HOST, + LinkAddress.LIFETIME_PERMANENT, LinkAddress.LIFETIME_PERMANENT); + // Check if the permanent flag is added. + assertTrue((l.getFlags() & IFA_F_PERMANENT) != 0); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_PERMANENT, RT_SCOPE_HOST, + 1000L, SystemClock.elapsedRealtime() + 100000L); + // Check if the permanent flag is removed + assertTrue((l.getFlags() & IFA_F_PERMANENT) == 0); } private void assertGlobalPreferred(LinkAddress l, String msg) { @@ -389,5 +458,12 @@ public class LinkAddressTest { (IFA_F_TEMPORARY|IFA_F_TENTATIVE|IFA_F_OPTIMISTIC), RT_SCOPE_UNIVERSE); assertGlobalPreferred(l, "v6,global,tempaddr+optimistic"); + + l = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, + RT_SCOPE_UNIVERSE, SystemClock.elapsedRealtime() + 100000, + SystemClock.elapsedRealtime() + 200000); + // Although the deprecated bit is set, but the deprecation time is in the future, test + // if the flag is removed automatically. + assertGlobalPreferred(l, "v6,global,tempaddr+deprecated in the future"); } } 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/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 15691127cab7..3e4f3d818840 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -269,9 +269,10 @@ public class NetworkCapabilitiesTest { .setUids(uids) .addCapability(NET_CAPABILITY_EIMS) .addCapability(NET_CAPABILITY_NOT_METERED); + netCap.setOwnerUid(123); assertParcelingIsLossless(netCap); netCap.setSSID(TEST_SSID); - assertParcelSane(netCap, 12); + assertParcelSane(netCap, 13); } @Test 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..7ab4b56fae80 --- /dev/null +++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java @@ -0,0 +1,248 @@ +/* + * 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.ConnectivityDiagnosticsBinder; +import static android.net.ConnectivityDiagnosticsManager.ConnectivityDiagnosticsCallback; +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 static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.os.PersistableBundle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; + +import java.util.concurrent.Executor; + +@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 static final Executor INLINE_EXECUTOR = x -> x.run(); + + @Mock private ConnectivityDiagnosticsCallback mCb; + + private ConnectivityDiagnosticsBinder mBinder; + + @Before + public void setUp() { + mCb = mock(ConnectivityDiagnosticsCallback.class); + + mBinder = new ConnectivityDiagnosticsBinder(mCb, INLINE_EXECUTOR); + } + + 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); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnConnectivityReport() { + mBinder.onConnectivityReport(createSampleConnectivityReport()); + + // The callback will be invoked synchronously by inline executor. Immediately check the + // latch without waiting. + verify(mCb).onConnectivityReport(eq(createSampleConnectivityReport())); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() { + mBinder.onDataStallSuspected(createSampleDataStallReport()); + + // The callback will be invoked synchronously by inline executor. Immediately check the + // latch without waiting. + verify(mCb).onDataStallSuspected(eq(createSampleDataStallReport())); + } + + @Test + public void testConnectivityDiagnosticsCallbackOnNetworkConnectivityReported() { + final Network n = new Network(NET_ID); + final boolean connectivity = true; + + mBinder.onNetworkConnectivityReported(n, connectivity); + + // The callback will be invoked synchronously by inline executor. Immediately check the + // latch without waiting. + verify(mCb).onNetworkConnectivityReported(eq(n), eq(connectivity)); + } +} diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java new file mode 100644 index 000000000000..d6a2176d7e81 --- /dev/null +++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; + +import android.test.mock.MockContext; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.net.VpnProfile; +import com.android.org.bouncycastle.x509.X509V1CertificateGenerator; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +import javax.security.auth.x500.X500Principal; + +/** Unit tests for {@link Ikev2VpnProfile.Builder}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class Ikev2VpnProfileTest { + private static final String SERVER_ADDR_STRING = "1.2.3.4"; + private static final String IDENTITY_STRING = "Identity"; + private static final String USERNAME_STRING = "username"; + private static final String PASSWORD_STRING = "pa55w0rd"; + private static final String EXCL_LIST = "exclList"; + private static final byte[] PSK_BYTES = "preSharedKey".getBytes(); + private static final int TEST_MTU = 1300; + + private final MockContext mMockContext = + new MockContext() { + @Override + public String getOpPackageName() { + return "fooPackage"; + } + }; + private final ProxyInfo mProxy = new ProxyInfo(SERVER_ADDR_STRING, -1, EXCL_LIST); + + private X509Certificate mUserCert; + private X509Certificate mServerRootCa; + private PrivateKey mPrivateKey; + + @Before + public void setUp() throws Exception { + mServerRootCa = generateRandomCertAndKeyPair().cert; + + final CertificateAndKey userCertKey = generateRandomCertAndKeyPair(); + mUserCert = userCertKey.cert; + mPrivateKey = userCertKey.key; + } + + private Ikev2VpnProfile.Builder getBuilderWithDefaultOptions() { + final Ikev2VpnProfile.Builder builder = + new Ikev2VpnProfile.Builder(SERVER_ADDR_STRING, IDENTITY_STRING); + + builder.setBypassable(true); + builder.setProxy(mProxy); + builder.setMaxMtu(TEST_MTU); + builder.setMetered(true); + + return builder; + } + + @Test + public void testBuildValidProfileWithOptions() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + // Check non-auth parameters correctly stored + assertEquals(SERVER_ADDR_STRING, profile.getServerAddr()); + assertEquals(IDENTITY_STRING, profile.getUserIdentity()); + assertEquals(mProxy, profile.getProxyInfo()); + assertTrue(profile.isBypassable()); + assertTrue(profile.isMetered()); + assertEquals(TEST_MTU, profile.getMaxMtu()); + } + + @Test + public void testBuildUsernamePasswordProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + assertEquals(USERNAME_STRING, profile.getUsername()); + assertEquals(PASSWORD_STRING, profile.getPassword()); + assertEquals(mServerRootCa, profile.getServerRootCaCert()); + + assertNull(profile.getPresharedKey()); + assertNull(profile.getRsaPrivateKey()); + assertNull(profile.getUserCert()); + } + + @Test + public void testBuildDigitalSignatureProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + assertEquals(profile.getUserCert(), mUserCert); + assertEquals(mPrivateKey, profile.getRsaPrivateKey()); + assertEquals(profile.getServerRootCaCert(), mServerRootCa); + + assertNull(profile.getPresharedKey()); + assertNull(profile.getUsername()); + assertNull(profile.getPassword()); + } + + @Test + public void testBuildPresharedKeyProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final Ikev2VpnProfile profile = builder.build(); + assertNotNull(profile); + + assertArrayEquals(PSK_BYTES, profile.getPresharedKey()); + + assertNull(profile.getServerRootCaCert()); + assertNull(profile.getUsername()); + assertNull(profile.getPassword()); + assertNull(profile.getRsaPrivateKey()); + assertNull(profile.getUserCert()); + } + + @Test + public void testBuildNoAuthMethodSet() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + try { + builder.build(); + fail("Expected exception due to lack of auth method"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testBuildInvalidMtu() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + try { + builder.setMaxMtu(500); + fail("Expected exception due to too-small MTU"); + } catch (IllegalArgumentException expected) { + } + } + + private void verifyVpnProfileCommon(VpnProfile profile) { + assertEquals(SERVER_ADDR_STRING, profile.server); + assertEquals(IDENTITY_STRING, profile.ipsecIdentifier); + assertEquals(mProxy, profile.proxy); + assertTrue(profile.isBypassable); + assertTrue(profile.isMetered); + assertEquals(TEST_MTU, profile.maxMtu); + } + + @Test + public void testPskConvertToVpnProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final VpnProfile profile = builder.build().toVpnProfile(); + + verifyVpnProfileCommon(profile); + assertEquals(Ikev2VpnProfile.encodeForIpsecSecret(PSK_BYTES), profile.ipsecSecret); + + // Check nothing else is set + assertEquals("", profile.username); + assertEquals("", profile.password); + assertEquals("", profile.ipsecUserCert); + assertEquals("", profile.ipsecCaCert); + } + + @Test + public void testUsernamePasswordConvertToVpnProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + + verifyVpnProfileCommon(profile); + assertEquals(USERNAME_STRING, profile.username); + assertEquals(PASSWORD_STRING, profile.password); + assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert); + + // Check nothing else is set + assertEquals("", profile.ipsecUserCert); + assertEquals("", profile.ipsecSecret); + } + + @Test + public void testRsaConvertToVpnProfile() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + + verifyVpnProfileCommon(profile); + assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert); + assertEquals( + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()), + profile.ipsecSecret); + assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert); + + // Check nothing else is set + assertEquals("", profile.username); + assertEquals("", profile.password); + } + + @Test + public void testPskFromVpnProfileDiscardsIrrelevantValues() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final VpnProfile profile = builder.build().toVpnProfile(); + profile.username = USERNAME_STRING; + profile.password = PASSWORD_STRING; + profile.ipsecCaCert = Ikev2VpnProfile.certificateToPemString(mServerRootCa); + profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert); + + final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); + assertNull(result.getUsername()); + assertNull(result.getPassword()); + assertNull(result.getUserCert()); + assertNull(result.getRsaPrivateKey()); + assertNull(result.getServerRootCaCert()); + } + + @Test + public void testUsernamePasswordFromVpnProfileDiscardsIrrelevantValues() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + profile.ipsecSecret = new String(PSK_BYTES); + profile.ipsecUserCert = Ikev2VpnProfile.certificateToPemString(mUserCert); + + final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); + assertNull(result.getPresharedKey()); + assertNull(result.getUserCert()); + assertNull(result.getRsaPrivateKey()); + } + + @Test + public void testRsaFromVpnProfileDiscardsIrrelevantValues() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final VpnProfile profile = builder.build().toVpnProfile(); + profile.username = USERNAME_STRING; + profile.password = PASSWORD_STRING; + + final Ikev2VpnProfile result = Ikev2VpnProfile.fromVpnProfile(profile); + assertNull(result.getUsername()); + assertNull(result.getPassword()); + assertNull(result.getPresharedKey()); + } + + @Test + public void testPskConversionIsLossless() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthPsk(PSK_BYTES); + final Ikev2VpnProfile ikeProfile = builder.build(); + + assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); + } + + @Test + public void testUsernamePasswordConversionIsLossless() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthUsernamePassword(USERNAME_STRING, PASSWORD_STRING, mServerRootCa); + final Ikev2VpnProfile ikeProfile = builder.build(); + + assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); + } + + @Test + public void testRsaConversionIsLossless() throws Exception { + final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions(); + + builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa); + final Ikev2VpnProfile ikeProfile = builder.build(); + + assertEquals(ikeProfile, Ikev2VpnProfile.fromVpnProfile(ikeProfile.toVpnProfile())); + } + + private static class CertificateAndKey { + public final X509Certificate cert; + public final PrivateKey key; + + CertificateAndKey(X509Certificate cert, PrivateKey key) { + this.cert = cert; + this.key = key; + } + } + + private static CertificateAndKey generateRandomCertAndKeyPair() throws Exception { + final Date validityBeginDate = + new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1L)); + final Date validityEndDate = + new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1L)); + + // Generate a keypair + final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(512); + final KeyPair keyPair = keyPairGenerator.generateKeyPair(); + + final X500Principal dnName = new X500Principal("CN=test.android.com"); + final X509V1CertificateGenerator certGen = new X509V1CertificateGenerator(); + certGen.setSerialNumber(BigInteger.valueOf(System.currentTimeMillis())); + certGen.setSubjectDN(dnName); + certGen.setIssuerDN(dnName); + certGen.setNotBefore(validityBeginDate); + certGen.setNotAfter(validityEndDate); + certGen.setPublicKey(keyPair.getPublic()); + certGen.setSignatureAlgorithm("SHA256WithRSAEncryption"); + + final X509Certificate cert = certGen.generate(keyPair.getPrivate(), "AndroidOpenSSL"); + return new CertificateAndKey(cert, keyPair.getPrivate()); + } +} diff --git a/tests/net/java/android/net/VpnManagerTest.java b/tests/net/java/android/net/VpnManagerTest.java new file mode 100644 index 000000000000..655c4d118592 --- /dev/null +++ b/tests/net/java/android/net/VpnManagerTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static org.mockito.Mockito.mock; + +import android.test.mock.MockContext; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** Unit tests for {@link VpnManager}. */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class VpnManagerTest { + private static final String VPN_PROFILE_KEY = "KEY"; + + private IConnectivityManager mMockCs; + private VpnManager mVpnManager; + private final MockContext mMockContext = + new MockContext() { + @Override + public String getOpPackageName() { + return "fooPackage"; + } + }; + + @Before + public void setUp() throws Exception { + mMockCs = mock(IConnectivityManager.class); + mVpnManager = new VpnManager(mMockContext, mMockCs); + } + + @Test + public void testProvisionVpnProfile() throws Exception { + try { + mVpnManager.provisionVpnProfile(mock(PlatformVpnProfile.class)); + } catch (UnsupportedOperationException expected) { + } + } + + @Test + public void testDeleteProvisionedVpnProfile() throws Exception { + try { + mVpnManager.deleteProvisionedVpnProfile(); + } catch (UnsupportedOperationException expected) { + } + } + + @Test + public void testStartProvisionedVpnProfile() throws Exception { + try { + mVpnManager.startProvisionedVpnProfile(); + } catch (UnsupportedOperationException expected) { + } + } + + @Test + public void testStopProvisionedVpnProfile() throws Exception { + try { + mVpnManager.stopProvisionedVpnProfile(); + } catch (UnsupportedOperationException expected) { + } + } +} diff --git a/tests/net/java/com/android/internal/net/VpnProfileTest.java b/tests/net/java/com/android/internal/net/VpnProfileTest.java new file mode 100644 index 000000000000..8a4b53343c26 --- /dev/null +++ b/tests/net/java/com/android/internal/net/VpnProfileTest.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.net; + +import static com.android.testutils.ParcelUtilsKt.assertParcelSane; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.net.IpSecAlgorithm; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.Arrays; + +/** Unit tests for {@link VpnProfile}. */ +@SmallTest +@RunWith(JUnit4.class) +public class VpnProfileTest { + private static final String DUMMY_PROFILE_KEY = "Test"; + + @Test + public void testDefaults() throws Exception { + final VpnProfile p = new VpnProfile(DUMMY_PROFILE_KEY); + + assertEquals(DUMMY_PROFILE_KEY, p.key); + assertEquals("", p.name); + assertEquals(VpnProfile.TYPE_PPTP, p.type); + assertEquals("", p.server); + assertEquals("", p.username); + assertEquals("", p.password); + assertEquals("", p.dnsServers); + assertEquals("", p.searchDomains); + assertEquals("", p.routes); + assertTrue(p.mppe); + assertEquals("", p.l2tpSecret); + assertEquals("", p.ipsecIdentifier); + assertEquals("", p.ipsecSecret); + assertEquals("", p.ipsecUserCert); + assertEquals("", p.ipsecCaCert); + assertEquals("", p.ipsecServerCert); + assertEquals(null, p.proxy); + assertTrue(p.getAllowedAlgorithms() != null && p.getAllowedAlgorithms().isEmpty()); + assertFalse(p.isBypassable); + assertFalse(p.isMetered); + assertEquals(1400, p.maxMtu); + assertFalse(p.areAuthParamsInline); + } + + private VpnProfile getSampleIkev2Profile(String key) { + final VpnProfile p = new VpnProfile(key); + + p.name = "foo"; + p.type = VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS; + p.server = "bar"; + p.username = "baz"; + p.password = "qux"; + p.dnsServers = "8.8.8.8"; + p.searchDomains = ""; + p.routes = "0.0.0.0/0"; + p.mppe = false; + p.l2tpSecret = ""; + p.ipsecIdentifier = "quux"; + p.ipsecSecret = "quuz"; + p.ipsecUserCert = "corge"; + p.ipsecCaCert = "grault"; + p.ipsecServerCert = "garply"; + p.proxy = null; + p.setAllowedAlgorithms( + Arrays.asList( + IpSecAlgorithm.AUTH_CRYPT_AES_GCM, + IpSecAlgorithm.AUTH_HMAC_SHA512, + IpSecAlgorithm.CRYPT_AES_CBC)); + p.isBypassable = true; + p.isMetered = true; + p.maxMtu = 1350; + p.areAuthParamsInline = true; + + // Not saved, but also not compared. + p.saveLogin = true; + + return p; + } + + @Test + public void testEquals() { + assertEquals( + getSampleIkev2Profile(DUMMY_PROFILE_KEY), getSampleIkev2Profile(DUMMY_PROFILE_KEY)); + + final VpnProfile modified = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + modified.maxMtu--; + assertNotEquals(getSampleIkev2Profile(DUMMY_PROFILE_KEY), modified); + } + + @Test + public void testParcelUnparcel() { + assertParcelSane(getSampleIkev2Profile(DUMMY_PROFILE_KEY), 22); + } + + @Test + public void testSetInvalidAlgorithmValueDelimiter() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + + try { + profile.setAllowedAlgorithms( + Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test")); + fail("Expected failure due to value separator in algorithm name"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testSetInvalidAlgorithmListDelimiter() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + + try { + profile.setAllowedAlgorithms( + Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test")); + fail("Expected failure due to value separator in algorithm name"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testEncodeDecode() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode()); + assertEquals(profile, decoded); + } + + @Test + public void testEncodeDecodeTooManyValues() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + final byte[] tooManyValues = + (new String(profile.encode()) + VpnProfile.VALUE_DELIMITER + "invalid").getBytes(); + + assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooManyValues)); + } + + @Test + public void testEncodeDecodeInvalidNumberOfValues() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + final String encoded = new String(profile.encode()); + final byte[] tooFewValues = + encoded.substring(0, encoded.lastIndexOf(VpnProfile.VALUE_DELIMITER)).getBytes(); + + assertNull(VpnProfile.decode(DUMMY_PROFILE_KEY, tooFewValues)); + } + + @Test + public void testEncodeDecodeLoginsNotSaved() { + final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY); + profile.saveLogin = false; + + final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode()); + assertNotEquals(profile, decoded); + + // Add the username/password back, everything else must be equal. + decoded.username = profile.username; + decoded.password = profile.password; + assertEquals(profile, decoded); + } +} diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 1901a1db633b..e80b7c9d0156 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 @@ -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()); @@ -6216,12 +6313,24 @@ public class ConnectivityServiceTest { assertEquals(wifiLp, mService.getActiveLinkProperties()); } + @Test + public void testNetworkCapabilitiesRestrictedForCallerPermissions() { + int callerUid = Process.myUid(); + final NetworkCapabilities originalNc = new NetworkCapabilities(); + originalNc.setOwnerUid(callerUid); + + final NetworkCapabilities newNc = + mService.networkCapabilitiesRestrictedForCallerPermissions( + originalNc, Process.myPid(), callerUid); + + assertEquals(Process.INVALID_UID, newNc.getOwnerUid()); + } - private TestNetworkAgentWrapper establishVpn(LinkProperties lp, int establishingUid, - Set<UidRange> vpnRange) throws Exception { + private TestNetworkAgentWrapper establishVpn( + LinkProperties lp, int ownerUid, Set<UidRange> vpnRange) throws Exception { final TestNetworkAgentWrapper vpnNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp); - vpnNetworkAgent.getNetworkCapabilities().setEstablishingVpnAppUid(establishingUid); + vpnNetworkAgent.getNetworkCapabilities().setOwnerUid(ownerUid); mMockVpn.setNetworkAgent(vpnNetworkAgent); mMockVpn.connect(); mMockVpn.setUids(vpnRange); diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index f85bb5f8ad59..b9eb3f2b9d04 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -3194,27 +3194,27 @@ public class WifiManager { * soft AP state and number of connected devices immediately after a successful call to this API * via callback. Note that receiving an immediate WIFI_AP_STATE_FAILED value for soft AP state * indicates that the latest attempt to start soft AP has failed. Caller can unregister a - * previously registered callback using {@link unregisterSoftApCallback} + * previously registered callback using {@link #unregisterSoftApCallback} * <p> * Applications should have the * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers * without the permission will trigger a {@link java.lang.SecurityException}. * <p> * - * @param executor The executor to execute the callbacks of the {@code executor} - * object. If null, then the application's main executor will be used. + * @param executor The Executor on whose thread to execute the callbacks of the {@code callback} + * object. * @param callback Callback for soft AP events * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) - public void registerSoftApCallback(@Nullable @CallbackExecutor Executor executor, + public void registerSoftApCallback(@NonNull @CallbackExecutor Executor executor, @NonNull SoftApCallback callback) { + if (executor == null) throw new IllegalArgumentException("executor cannot be null"); if (callback == null) throw new IllegalArgumentException("callback cannot be null"); Log.v(TAG, "registerSoftApCallback: callback=" + callback + ", executor=" + executor); - executor = (executor == null) ? mContext.getMainExecutor() : executor; Binder binder = new Binder(); try { mService.registerSoftApCallback(binder, new SoftApCallbackProxy(executor, callback), diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java index 4260c20255ac..b130c1737c62 100644 --- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java +++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java @@ -693,25 +693,27 @@ public class WifiManagerTest { } /** - * Verify an IllegalArgumentException is thrown if callback is not provided. + * Verify an IllegalArgumentException is thrown if executor is null. */ @Test - public void unregisterSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForCallback() { + public void registerSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForExecutor() { try { - mWifiManager.unregisterSoftApCallback(null); + mWifiManager.registerSoftApCallback(null, mSoftApCallback); fail("expected IllegalArgumentException"); } catch (IllegalArgumentException expected) { } } /** - * Verify main looper is used when handler is not provided. + * Verify an IllegalArgumentException is thrown if callback is not provided. */ @Test - public void registerSoftApCallbackUsesMainLooperOnNullArgumentForHandler() { - when(mContext.getMainLooper()).thenReturn(mLooper.getLooper()); - mWifiManager.registerSoftApCallback(null, mSoftApCallback); - verify(mContext).getMainExecutor(); + public void unregisterSoftApCallbackThrowsIllegalArgumentExceptionOnNullArgumentForCallback() { + try { + mWifiManager.unregisterSoftApCallback(null); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } } /** |