diff options
439 files changed, 5115 insertions, 34500 deletions
diff --git a/Android.bp b/Android.bp index a04455509944..0d2caff8b42f 100644 --- a/Android.bp +++ b/Android.bp @@ -549,6 +549,7 @@ java_defaults { "telephony/java/com/android/ims/ImsConfigListener.aidl", "telephony/java/com/android/internal/telephony/IApnSourceService.aidl", "telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl", + "telephony/java/com/android/internal/telephony/IIntegerConsumer.aidl", "telephony/java/com/android/internal/telephony/IMms.aidl", "telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl", "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl", @@ -669,7 +670,7 @@ java_defaults { "system/security/keystore/binder", ], - generate_get_transaction_name: true + generate_get_transaction_name: true, }, exclude_srcs: [ @@ -677,7 +678,7 @@ java_defaults { "core/java/android/content/pm/AndroidTestBaseUpdater.java", ], - no_framework_libs: true, + sdk_version: "core_platform", libs: [ "ext", ], @@ -733,6 +734,7 @@ filegroup { "core/java/android/os/IIncidentManager.aidl", "core/java/android/os/IIncidentReportStatusListener.aidl", ], + path: "core/java", } filegroup { @@ -741,6 +743,7 @@ filegroup { "core/java/android/os/IStatsCompanionService.aidl", "core/java/android/os/IStatsManager.aidl", ], + path: "core/java", } java_library { @@ -756,15 +759,22 @@ java_library { plugins: ["unsupportedappusage-annotation-processor"], } -// A host library including just UnsupportedAppUsage.java so that the annotation -// processor can also use this annotation. -java_library_host { +// A library including just UnsupportedAppUsage.java classes. +// +// Provided for target so that libraries can use it without depending on +// the whole of framework or the core platform API. +// +// Built for host so that the annotation processor can also use this annotation. +java_library { name: "unsupportedappusage-annotation", + host_supported: true, srcs: [ "core/java/android/annotation/IntDef.java", "core/java/android/annotation/UnsupportedAppUsage.java", ":unsupportedappusage_annotation_files", ], + + sdk_version: "core_current", } // A temporary build target that is conditionally included on the bootclasspath if @@ -791,7 +801,7 @@ gensrcs { name: "framework-javastream-protos", depfile: true, - tool_files: [ "tools/genprotos.sh", ], + tool_files: ["tools/genprotos.sh"], tools: [ "aprotoc", "protoc-gen-javastream", @@ -802,13 +812,13 @@ gensrcs { // end up with a command that is extremely long, potentially going passed MAX_ARG_STRLEN due to // the way sbox rewrites the command. See b/70221552. cmd: "$(location tools/genprotos.sh) " + - " $(location aprotoc) " + - " $(location protoc-gen-javastream) " + - " $(location soong_zip) " + - " $(genDir) " + - " $(depfile) " + - " $(in) " + - " $(out)", + " $(location aprotoc) " + + " $(location protoc-gen-javastream) " + + " $(location soong_zip) " + + " $(genDir) " + + " $(depfile) " + + " $(in) " + + " $(out)", srcs: [ "core/proto/**/*.proto", "libs/incident/**/*.proto", @@ -826,7 +836,7 @@ filegroup { "core/java/android/annotation/UnsupportedAppUsage.java", "core/java/com/android/internal/annotations/GuardedBy.java", "core/java/com/android/internal/annotations/VisibleForTesting.java", - ] + ], } filegroup { @@ -847,7 +857,7 @@ filegroup { "core/java/com/android/internal/util/TrafficStatsConstants.java", "core/java/com/android/internal/util/WakeupMessage.java", "core/java/android/net/shared/*.java", - ] + ], } // Build ext.jar @@ -855,7 +865,7 @@ filegroup { java_library { name: "ext", installable: true, - no_framework_libs: true, + sdk_version: "core_platform", static_libs: [ "libphonenumber-platform", "nist-sip", @@ -883,7 +893,7 @@ java_library_host { type: "full", }, errorprone: { - javacflags: ["-Xep:MissingOverride:OFF"], // b/72714520 + javacflags: ["-Xep:MissingOverride:OFF"], // b/72714520 }, } @@ -1014,7 +1024,7 @@ optional_subdirs = [ // updated to use hwbinder.stubs. java_library { name: "hwbinder", - no_framework_libs: true, + sdk_version: "core_platform", srcs: [ "core/java/android/os/HidlSupport.java", @@ -1102,12 +1112,30 @@ filegroup { ], } +// Make the api/system-current.txt file available for use by modules in other +// directories. +filegroup { + name: "frameworks-base-api-system-current.txt", + srcs: [ + "api/system-current.txt", + ], +} + +// Make the api/system-removed.txt file available for use by modules in other +// directories. +filegroup { + name: "frameworks-base-api-system-removed.txt", + srcs: [ + "api/system-removed.txt", + ], +} + framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " + - "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + - "-overview $(location core/java/overview.html) " + - // Federate Support Library references against local API file. - "-federate SupportLib https://developer.android.com " + - "-federationapi SupportLib $(location :current-support-api) " + "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + + "-overview $(location core/java/overview.html) " + + // Federate Support Library references against local API file. + "-federate SupportLib https://developer.android.com " + + "-federationapi SupportLib $(location :current-support-api) " framework_docs_only_libs = [ "voip-common", @@ -1155,7 +1183,22 @@ metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.x "--hide RequiresPermission " + "--hide MissingPermission --hide BroadcastBehavior " + "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " + - "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo" + "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo " + +// http://b/129765390 Rewrite links to "platform" or "technotes" folders +// which are siblings (and thus outside of) {@docRoot}. +// +// We have to escape \ as \\ and $ as $$ here because they get resolved by +// different layers of the build tooling. The arguments are wrapped in '' so +// that the shell doesn't add yet another level of escaping. +metalava_framework_docs_args += " --replace-documentation " + + // packages whose descendants to apply replacement to (all packages from + // libcore/ojluni/src/main/java that contribute to documentation). + "com.sun:java:javax:jdk.net:sun " + + // regex of the string to replace + "'(<a\\s+href\\s?=[\\*\\s]*\")(?:(?:\\{@docRoot\\}/\\.\\./)|(?:(?:\\.\\./)+))((?:platform|technotes).+)\">' " + + // replacement (with $1, $2 backreferences to the regex groups) + "'$$1https://docs.oracle.com/javase/8/docs/$$2\">' " stubs_defaults { name: "framework-doc-stubs-default", @@ -1189,7 +1232,7 @@ stubs_defaults { doc_defaults { name: "framework-docs-default", libs: framework_docs_only_libs + - ["stub-annotations"], + ["stub-annotations"], html_dirs: [ "docs/html", ], @@ -1321,8 +1364,8 @@ droiddoc { "android.whichdoc offline", ], proofread_file: "offline-system-sdk-referenceonly-docs-proofrerad.txt", - args: framework_docs_only_args + " -hide 101 -hide 104 -hide 108" + - " -offlinemode -title \"Android System SDK\" -referenceonly", + args: framework_docs_only_args + " -hide 101 -hide 104 -hide 108" + + " -offlinemode -title \"Android System SDK\" -referenceonly", write_sdk_values: true, static_doc_index_redirect: "docs/docs-documentation-redirect.html", static_doc_properties: "docs/source.properties", @@ -1339,7 +1382,7 @@ droiddoc { "android.hasSamples true", ], proofread_file: "online-sdk-docs-proofrerad.txt", - args: framework_docs_only_args + + args: framework_docs_only_args + " -toroot / -samplegroup Admin " + " -samplegroup Background " + " -samplegroup Connectivity " + @@ -1432,10 +1475,10 @@ droiddoc { ], proofread_file: "ds-static-docs-proofrerad.txt", args: framework_docs_only_args + - " -staticonly " + - " -toroot / " + - " -devsite " + - " -ignoreJdLinks ", + " -staticonly " + + " -toroot / " + + " -devsite " + + " -ignoreJdLinks ", } droiddoc { @@ -1449,9 +1492,9 @@ droiddoc { ], proofread_file: "ds-ref-navtree-docs-proofrerad.txt", args: framework_docs_only_args + - " -toroot / " + - " -atLinksNavtree " + - " -navtreeonly ", + " -toroot / " + + " -atLinksNavtree " + + " -navtreeonly ", } droiddoc { @@ -1491,8 +1534,8 @@ droiddoc { ], proofread_file: "hidden-docs-proofrerad.txt", args: framework_docs_only_args + - " -referenceonly " + - " -title \"Android SDK - Including hidden APIs.\"", + " -referenceonly " + + " -title \"Android SDK - Including hidden APIs.\"", } droidstubs { @@ -1514,7 +1557,7 @@ droidstubs { "core/java/android/util/AndroidException.java", ], installable: false, - no_framework_libs: true, + sdk_version: "core_platform", annotations_enabled: true, previous_api: ":last-released-public-api", merge_annotations_dirs: [ @@ -1544,10 +1587,9 @@ droidstubs { args: metalava_framework_docs_args + " --show-unannotated " + " --show-annotation android.annotation.SystemApi " + - " --show-annotation android.annotation.TestApi " + " --show-annotation android.annotation.TestApi ", } - droidstubs { name: "hiddenapi-mappings", defaults: ["metalava-api-stubs-default"], @@ -1566,7 +1608,7 @@ droidstubs { " --hide UnhiddenSystemApi " + " --show-unannotated " + " --show-annotation android.annotation.SystemApi " + - " --show-annotation android.annotation.TestApi " + " --show-annotation android.annotation.TestApi ", } filegroup { @@ -1670,5 +1712,5 @@ filegroup { aidl_mapping { name: "framework-aidl-mappings", srcs: [":framework-defaults"], - output: "framework-aidl-mappings.txt" + output: "framework-aidl-mappings.txt", } diff --git a/api/current.txt b/api/current.txt index 97c10497cc58..015874d6e30c 100755 --- a/api/current.txt +++ b/api/current.txt @@ -29110,7 +29110,6 @@ package android.nfc { } public final class NfcAdapter { - method public boolean deviceSupportsNfcSecure(); method public void disableForegroundDispatch(android.app.Activity); method @Deprecated public void disableForegroundNdefPush(android.app.Activity); method public void disableReaderMode(android.app.Activity); @@ -29122,7 +29121,8 @@ package android.nfc { method @Deprecated public boolean invokeBeam(android.app.Activity); method public boolean isEnabled(); method @Deprecated public boolean isNdefPushEnabled(); - method public boolean isNfcSecureEnabled(); + method public boolean isSecureNfcEnabled(); + method public boolean isSecureNfcSupported(); method @Deprecated public void setBeamPushUris(android.net.Uri[], android.app.Activity); method @Deprecated public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity); method @Deprecated public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...); @@ -41143,6 +41143,7 @@ package android.telecom { method public void unregisterCallback(android.telecom.Call.Callback); field @Deprecated public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts"; 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 STATE_ACTIVE = 4; // 0x4 field public static final int STATE_CONNECTING = 9; // 0x9 @@ -41291,6 +41292,7 @@ package android.telecom { public static class CallScreeningService.CallResponse { method public boolean getDisallowCall(); method public boolean getRejectCall(); + method public boolean getSilenceCall(); method public boolean getSkipCallLog(); method public boolean getSkipNotification(); } @@ -41300,6 +41302,7 @@ package android.telecom { method public android.telecom.CallScreeningService.CallResponse build(); method public android.telecom.CallScreeningService.CallResponse.Builder setDisallowCall(boolean); method public android.telecom.CallScreeningService.CallResponse.Builder setRejectCall(boolean); + method @NonNull public android.telecom.CallScreeningService.CallResponse.Builder setSilenceCall(boolean); method public android.telecom.CallScreeningService.CallResponse.Builder setSkipCallLog(boolean); method public android.telecom.CallScreeningService.CallResponse.Builder setSkipNotification(boolean); } @@ -43064,7 +43067,7 @@ package android.telephony { method public String getNetworkOperator(); method public String getNetworkOperatorName(); method public String getNetworkSpecifier(); - method public int getNetworkType(); + method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType(); method 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(); diff --git a/api/system-current.txt b/api/system-current.txt index 3170a0bf4dfb..7f29b1d05fc4 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3958,9 +3958,9 @@ package android.nfc { method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean disableNdefPush(); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enable(); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableNdefPush(); + method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean enableSecureNfc(boolean); method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean removeNfcUnlockHandler(android.nfc.NfcAdapter.NfcUnlockHandler); method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, int); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public boolean setNfcSecure(boolean); field public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 1; // 0x1 } diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp index d3875315b84e..f92502370566 100644 --- a/cmds/app_process/Android.bp +++ b/cmds/app_process/Android.bp @@ -6,11 +6,11 @@ cc_binary { multilib: { lib32: { version_script: ":art_sigchain_version_script32.txt", - stem: "app_process32", + suffix: "32", }, lib64: { version_script: ":art_sigchain_version_script64.txt", - stem: "app_process64", + suffix: "64", }, }, @@ -21,6 +21,7 @@ cc_binary { "libbinder", "libcutils", "libdl", + "libhidlbase", "libhwbinder", "liblog", "libnativeloader", diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index f178fa254e69..20493e73cf06 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -301,7 +301,7 @@ cc_benchmark { // ==== java proto device library (for test only) ============================== java_library { name: "statsdprotolite", - no_framework_libs: true, + sdk_version: "core_platform", proto: { type: "lite", include_dirs: ["external/protobuf/src"], diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index f04f017d8911..6506df2ade75 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -2818,7 +2818,7 @@ message NetworkDnsEventReported { // Only valid for event_type = EVENT_RESNSEND. optional int32 res_nsend_flags = 5; - optional android.stats.dnsresolver.Transport network_type = 6; + optional android.stats.dnsresolver.NetworkType network_type = 6; // The DNS over TLS mode on a specific netId. optional android.stats.dnsresolver.PrivateDnsModes private_dns_modes = 7; diff --git a/core/java/android/app/backup/OWNERS b/core/java/android/app/backup/OWNERS index 1c9a43acfa65..9c21e8fe5e45 100644 --- a/core/java/android/app/backup/OWNERS +++ b/core/java/android/app/backup/OWNERS @@ -1,7 +1,9 @@ -artikz@google.com +alsutton@google.com +anniemeng@google.com brufino@google.com bryanmawhinney@google.com ctate@google.com jorlow@google.com -mkarpinski@google.com +nathch@google.com +rthakohov@google.com diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 31bbd16497cb..39d63de87da3 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1028,7 +1028,8 @@ public final class BluetoothAdapter { */ @RequiresPermission(Manifest.permission.BLUETOOTH) @AdapterState - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getState()} instead to determine " + + "whether you can use BLE & BT classic.") public int getLeState() { int state = BluetoothAdapter.STATE_OFF; @@ -1484,7 +1485,8 @@ public final class BluetoothAdapter { * @return true if the scan mode was set, false otherwise * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link #ACTION_REQUEST_DISCOVERABLE}, which " + + "shows UI that confirms the user wants to go into discoverable mode.") public boolean setScanMode(@ScanMode int mode, int duration) { if (getState() != STATE_ON) { return false; diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 34c7372202ee..ee33103ebb2f 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1051,7 +1051,7 @@ public final class BluetoothDevice implements Parcelable { * @return the Bluetooth alias, or null if no alias or there was a problem * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") public String getAlias() { final IBluetooth service = sService; if (service == null) { @@ -1100,7 +1100,7 @@ public final class BluetoothDevice implements Parcelable { * @see #getAlias() * @see #getName() */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link #getName()} instead.") public String getAliasName() { String name = getAlias(); if (name == null) { @@ -1975,7 +1975,8 @@ public final class BluetoothDevice implements Parcelable { * permissions. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use " + + "{@link #createInsecureRfcommSocketToServiceRecord} instead.") public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException { if (!isBluetoothEnabled()) { Log.e(TAG, "Bluetooth is not enabled"); diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 9862a63ef238..672174f8f76c 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -280,6 +280,7 @@ public final class BluetoothHeadset implements BluetoothProfile { * {@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} of * {@link #ACTION_AUDIO_STATE_CHANGED} intent. */ + public static final int STATE_AUDIO_CONNECTED = 12; /** * Intent used to broadcast the headset's indicator status @@ -322,8 +323,6 @@ public final class BluetoothHeadset implements BluetoothProfile { public static final String EXTRA_HF_INDICATORS_IND_VALUE = "android.bluetooth.headset.extra.HF_INDICATORS_IND_VALUE"; - public static final int STATE_AUDIO_CONNECTED = 12; - private static final int MESSAGE_HEADSET_SERVICE_CONNECTED = 100; private static final int MESSAGE_HEADSET_SERVICE_DISCONNECTED = 101; diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java index ec0180c5adde..69682c6ab5d0 100644 --- a/core/java/android/bluetooth/BluetoothMapClient.java +++ b/core/java/android/bluetooth/BluetoothMapClient.java @@ -53,6 +53,10 @@ public final class BluetoothMapClient implements BluetoothProfile { * NOTE: HANDLE is only valid for a single session with the device. */ public static final String EXTRA_MESSAGE_HANDLE = "android.bluetooth.mapmce.profile.extra.MESSAGE_HANDLE"; + public static final String EXTRA_MESSAGE_TIMESTAMP = + "android.bluetooth.mapmce.profile.extra.MESSAGE_TIMESTAMP"; + public static final String EXTRA_MESSAGE_READ_STATUS = + "android.bluetooth.mapmce.profile.extra.MESSAGE_READ_STATUS"; public static final String EXTRA_SENDER_CONTACT_URI = "android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_URI"; public static final String EXTRA_SENDER_CONTACT_NAME = diff --git a/core/java/android/bluetooth/BluetoothProfileConnector.java b/core/java/android/bluetooth/BluetoothProfileConnector.java index d9987249a6e2..863fd3698cbd 100644 --- a/core/java/android/bluetooth/BluetoothProfileConnector.java +++ b/core/java/android/bluetooth/BluetoothProfileConnector.java @@ -32,12 +32,12 @@ import android.util.Log; * @hide */ public abstract class BluetoothProfileConnector<T> { - private int mProfileId; + private final int mProfileId; private BluetoothProfile.ServiceListener mServiceListener; - private BluetoothProfile mProfileProxy; + private final BluetoothProfile mProfileProxy; private Context mContext; - private String mProfileName; - private String mServiceName; + private final String mProfileName; + private final String mServiceName; private volatile T mService; private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback = @@ -65,7 +65,7 @@ public abstract class BluetoothProfileConnector<T> { logDebug("Proxy object disconnected"); doUnbind(); if (mServiceListener != null) { - mServiceListener.onServiceDisconnected(BluetoothProfile.A2DP); + mServiceListener.onServiceDisconnected(mProfileId); } } }; diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java index c06b837a9ee3..3a23808f3617 100644 --- a/core/java/android/bluetooth/BluetoothServerSocket.java +++ b/core/java/android/bluetooth/BluetoothServerSocket.java @@ -77,7 +77,8 @@ public final class BluetoothServerSocket implements Closeable { private static final String TAG = "BluetoothServerSocket"; private static final boolean DBG = false; - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use public {@link BluetoothServerSocket} API " + + "instead.") /*package*/ final BluetoothSocket mSocket; private Handler mHandler; private int mMessage; diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index 3a1e2f58c99d..a6e3153d6af7 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -131,7 +131,7 @@ public final class BluetoothSocket implements Closeable { private boolean mExcludeSdp = false; /* when true no SPP SDP record will be created */ private boolean mAuthMitm = false; /* when true Man-in-the-middle protection will be enabled*/ private boolean mMin16DigitPin = false; /* Minimum 16 digit pin for sec mode 2 connections */ - @UnsupportedAppUsage + @UnsupportedAppUsage(publicAlternatives = "Use {@link BluetoothSocket} public API instead.") private ParcelFileDescriptor mPfd; @UnsupportedAppUsage private LocalSocket mSocket; diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index b879047f05dc..a9ba6ca1ea99 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2999,7 +2999,10 @@ public class Intent implements Parcelable, Cloneable { * * @deprecated Apps that redirect outgoing calls should use the * {@link android.telecom.CallRedirectionService} API. Apps that perform call screening - * should use the {@link android.telecom.CallScreeningService} API. + * should use the {@link android.telecom.CallScreeningService} API. Apps which need to be + * notified of basic call state should use + * {@link android.telephony.PhoneStateListener#onCallStateChanged(int, String)} to determine + * when a new outgoing call is placed. */ @Deprecated @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index fbfbfc04d81f..111a8c48a46c 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -655,7 +655,7 @@ public class ConnectivityManager { * {@hide} */ @Deprecated - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 130143562) + @UnsupportedAppUsage public static final int TYPE_WIFI_P2P = 13; /** diff --git a/core/java/android/net/DnsResolver.java b/core/java/android/net/DnsResolver.java index 4b2b4c35b292..0b1a84534e38 100644 --- a/core/java/android/net/DnsResolver.java +++ b/core/java/android/net/DnsResolver.java @@ -16,7 +16,7 @@ package android.net; -import static android.net.NetworkUtils.getDnsNetId; +import static android.net.NetworkUtils.getDnsNetwork; import static android.net.NetworkUtils.resNetworkCancel; import static android.net.NetworkUtils.resNetworkQuery; import static android.net.NetworkUtils.resNetworkResult; @@ -34,6 +34,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.os.CancellationSignal; import android.os.Looper; +import android.os.MessageQueue; import android.system.ErrnoException; import android.util.Log; @@ -333,7 +334,7 @@ public final class DnsResolver { final Object lock = new Object(); final Network queryNetwork; try { - queryNetwork = (network != null) ? network : new Network(getDnsNetId()); + queryNetwork = (network != null) ? network : getDnsNetwork(); } catch (ErrnoException e) { executor.execute(() -> callback.onError(new DnsException(ERROR_SYSTEM, e))); return; @@ -433,7 +434,7 @@ public final class DnsResolver { final FileDescriptor queryfd; final Network queryNetwork; try { - queryNetwork = (network != null) ? network : new Network(getDnsNetId()); + queryNetwork = (network != null) ? network : getDnsNetwork(); queryfd = resNetworkQuery(queryNetwork.getNetIdForResolv(), domain, CLASS_IN, nsType, flags); } catch (ErrnoException e) { @@ -466,10 +467,20 @@ public final class DnsResolver { private void registerFDListener(@NonNull Executor executor, @NonNull FileDescriptor queryfd, @NonNull Callback<? super byte[]> answerCallback, @Nullable CancellationSignal cancellationSignal, @NonNull Object lock) { - Looper.getMainLooper().getQueue().addOnFileDescriptorEventListener( + final MessageQueue mainThreadMessageQueue = Looper.getMainLooper().getQueue(); + mainThreadMessageQueue.addOnFileDescriptorEventListener( queryfd, FD_EVENTS, (fd, events) -> { + // b/134310704 + // Unregister fd event listener before resNetworkResult is called to prevent + // race condition caused by fd reused. + // For example when querying v4 and v6, it's possible that the first query ends + // and the fd is closed before the second request starts, which might return + // the same fd for the second request. By that time, the looper must have + // unregistered the fd, otherwise another event listener can't be registered. + mainThreadMessageQueue.removeOnFileDescriptorEventListener(fd); + executor.execute(() -> { DnsResponse resp = null; ErrnoException exception = null; @@ -490,7 +501,11 @@ public final class DnsResolver { } answerCallback.onAnswer(resp.answerbuf, resp.rcode); }); - // Unregister this fd listener + + // The file descriptor has already been unregistered, so it does not really + // matter what is returned here. In spirit 0 (meaning "unregister this FD") + // is still the closest to what the looper needs to do. When returning 0, + // Looper knows to ignore the fd if it has already been unregistered. return 0; }); } diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 41efc5040885..e5f3d26667a0 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -66,7 +66,6 @@ interface INetworkStatsService { /** Force update of ifaces. */ void forceUpdateIfaces( in Network[] defaultNetworks, - in VpnInfo[] vpnArray, in NetworkState[] networkStates, in String activeIface); /** Force update of statistics. */ diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java index 355265598365..43c8ff2335ca 100644 --- a/core/java/android/net/IpSecConfig.java +++ b/core/java/android/net/IpSecConfig.java @@ -15,6 +15,7 @@ */ package android.net; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -333,25 +334,25 @@ public final class IpSecConfig implements Parcelable { } }; - @VisibleForTesting - /** Equals method used for testing */ - public static boolean equals(IpSecConfig lhs, IpSecConfig rhs) { - if (lhs == null || rhs == null) return (lhs == rhs); - return (lhs.mMode == rhs.mMode - && lhs.mSourceAddress.equals(rhs.mSourceAddress) - && lhs.mDestinationAddress.equals(rhs.mDestinationAddress) - && ((lhs.mNetwork != null && lhs.mNetwork.equals(rhs.mNetwork)) - || (lhs.mNetwork == rhs.mNetwork)) - && lhs.mEncapType == rhs.mEncapType - && lhs.mEncapSocketResourceId == rhs.mEncapSocketResourceId - && lhs.mEncapRemotePort == rhs.mEncapRemotePort - && lhs.mNattKeepaliveInterval == rhs.mNattKeepaliveInterval - && lhs.mSpiResourceId == rhs.mSpiResourceId - && IpSecAlgorithm.equals(lhs.mEncryption, rhs.mEncryption) - && IpSecAlgorithm.equals(lhs.mAuthenticatedEncryption, rhs.mAuthenticatedEncryption) - && IpSecAlgorithm.equals(lhs.mAuthentication, rhs.mAuthentication) - && lhs.mMarkValue == rhs.mMarkValue - && lhs.mMarkMask == rhs.mMarkMask - && lhs.mXfrmInterfaceId == rhs.mXfrmInterfaceId); + @Override + public boolean equals(@Nullable Object other) { + if (!(other instanceof IpSecConfig)) return false; + final IpSecConfig rhs = (IpSecConfig) other; + return (mMode == rhs.mMode + && mSourceAddress.equals(rhs.mSourceAddress) + && mDestinationAddress.equals(rhs.mDestinationAddress) + && ((mNetwork != null && mNetwork.equals(rhs.mNetwork)) + || (mNetwork == rhs.mNetwork)) + && mEncapType == rhs.mEncapType + && mEncapSocketResourceId == rhs.mEncapSocketResourceId + && mEncapRemotePort == rhs.mEncapRemotePort + && mNattKeepaliveInterval == rhs.mNattKeepaliveInterval + && mSpiResourceId == rhs.mSpiResourceId + && IpSecAlgorithm.equals(mEncryption, rhs.mEncryption) + && IpSecAlgorithm.equals(mAuthenticatedEncryption, rhs.mAuthenticatedEncryption) + && IpSecAlgorithm.equals(mAuthentication, rhs.mAuthentication) + && mMarkValue == rhs.mMarkValue + && mMarkMask == rhs.mMarkMask + && mXfrmInterfaceId == rhs.mXfrmInterfaceId); } } diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index a12df28eac6b..93ae4f11888e 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -148,15 +148,13 @@ public final class IpSecTransform implements AutoCloseable { } /** - * Equals method used for testing - * - * @hide + * Standard equals. */ - @VisibleForTesting - public static boolean equals(IpSecTransform lhs, IpSecTransform rhs) { - if (lhs == null || rhs == null) return (lhs == rhs); - return IpSecConfig.equals(lhs.getConfig(), rhs.getConfig()) - && lhs.mResourceId == rhs.mResourceId; + public boolean equals(Object other) { + if (this == other) return true; + if (!(other instanceof IpSecTransform)) return false; + final IpSecTransform rhs = (IpSecTransform) other; + return getConfig().equals(rhs.getConfig()) && mResourceId == rhs.mResourceId; } /** diff --git a/services/net/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java index 27ed11e119b7..a77c244d6b40 100644 --- a/services/net/java/android/net/NattKeepalivePacketData.java +++ b/core/java/android/net/NattKeepalivePacketData.java @@ -19,9 +19,9 @@ package android.net; import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; import static android.net.SocketKeepalive.ERROR_INVALID_PORT; -import android.annotation.NonNull; import android.net.SocketKeepalive.InvalidPacketException; import android.net.util.IpUtils; +import android.os.Parcel; import android.os.Parcelable; import android.system.OsConstants; @@ -79,17 +79,40 @@ public final class NattKeepalivePacketData extends KeepalivePacketData implement return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array()); } - /** - * Convert this NattKeepalivePacketData to a NattKeepalivePacketDataParcelable. - */ - @NonNull - public NattKeepalivePacketDataParcelable toStableParcelable() { - final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable(); + /** Parcelable Implementation */ + public int describeContents() { + return 0; + } - parcel.srcAddress = srcAddress.getAddress(); - parcel.srcPort = srcPort; - parcel.dstAddress = dstAddress.getAddress(); - parcel.dstPort = dstPort; - return parcel; + /** Write to parcel */ + public void writeToParcel(Parcel out, int flags) { + out.writeString(srcAddress.getHostAddress()); + out.writeString(dstAddress.getHostAddress()); + out.writeInt(srcPort); + out.writeInt(dstPort); } + + /** Parcelable Creator */ + public static final Parcelable.Creator<NattKeepalivePacketData> CREATOR = + new Parcelable.Creator<NattKeepalivePacketData>() { + public NattKeepalivePacketData createFromParcel(Parcel in) { + final InetAddress srcAddress = + InetAddresses.parseNumericAddress(in.readString()); + final InetAddress dstAddress = + InetAddresses.parseNumericAddress(in.readString()); + final int srcPort = in.readInt(); + final int dstPort = in.readInt(); + try { + return NattKeepalivePacketData.nattKeepalivePacket(srcAddress, srcPort, + dstAddress, dstPort); + } catch (InvalidPacketException e) { + throw new IllegalArgumentException( + "Invalid NAT-T keepalive data: " + e.error); + } + } + + public NattKeepalivePacketData[] newArray(int size) { + return new NattKeepalivePacketData[size]; + } + }; } diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 2ff6043576bd..ed088d0a6756 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -433,7 +433,24 @@ public abstract class NetworkAgent extends Handler { * {@link #saveAcceptUnvalidated} to respect the user's choice. */ public void explicitlySelected(boolean acceptUnvalidated) { - queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, acceptUnvalidated ? 1 : 0, 0); + explicitlySelected(true /* explicitlySelected */, acceptUnvalidated); + } + + /** + * Called by the bearer to indicate this network was manually selected by the user. + * This should be called before the NetworkInfo is marked CONNECTED so that this + * Network can be given special treatment at that time. If {@code acceptUnvalidated} is + * {@code true}, then the system will switch to this network. If it is {@code false} and the + * network cannot be validated, the system will ask the user whether to switch to this network. + * If the user confirms and selects "don't ask again", then the system will call + * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever + * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement + * {@link #saveAcceptUnvalidated} to respect the user's choice. + */ + public void explicitlySelected(boolean explicitlySelected, boolean acceptUnvalidated) { + queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, + explicitlySelected ? 1 : 0, + acceptUnvalidated ? 1 : 0); } /** @@ -510,7 +527,6 @@ public abstract class NetworkAgent extends Handler { * override this method. */ protected void addKeepalivePacketFilter(Message msg) { - onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED); } /** @@ -519,7 +535,6 @@ public abstract class NetworkAgent extends Handler { * must override this method. */ protected void removeKeepalivePacketFilter(Message msg) { - onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_UNSUPPORTED); } /** diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 27e041496173..14a0cbf611b5 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -23,7 +23,6 @@ import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; -import android.util.Slog; import android.util.SparseBooleanArray; import com.android.internal.annotations.VisibleForTesting; @@ -34,10 +33,10 @@ import libcore.util.EmptyArray; import java.io.CharArrayWriter; import java.io.PrintWriter; import java.util.Arrays; -import java.util.function.Predicate; import java.util.HashSet; import java.util.Map; import java.util.Objects; +import java.util.function.Predicate; /** * Collection of active network statistics. Can contain summary details across @@ -996,8 +995,8 @@ public class NetworkStats implements Parcelable { return; } filter(e -> (limitUid == UID_ALL || limitUid == e.uid) - && (limitTag == TAG_ALL || limitTag == e.tag) - && (limitIfaces == INTERFACES_ALL + && (limitTag == TAG_ALL || limitTag == e.tag) + && (limitIfaces == INTERFACES_ALL || ArrayUtils.contains(limitIfaces, e.iface))); } @@ -1300,7 +1299,8 @@ public class NetworkStats implements Parcelable { } final Entry tmpEntry = new Entry(); - for (int i = 0; i < size; i++) { + final int origSize = size; + for (int i = 0; i < origSize; i++) { if (!Objects.equals(iface[i], tunIface)) { // Consider only entries that go onto the VPN interface. continue; @@ -1316,8 +1316,9 @@ public class NetworkStats implements Parcelable { tmpEntry.roaming = roaming[i]; tmpEntry.defaultNetwork = defaultNetwork[i]; - // In a first pass, compute each UID's total share of data across all underlyingIfaces. - // This is computed on the basis of the share of each UID's usage over tunIface. + // In a first pass, compute this entry's total share of data across all + // underlyingIfaces. This is computed on the basis of the share of this entry's usage + // over tunIface. // TODO: Consider refactoring first pass into a separate helper method. long totalRxBytes = 0; if (tunIfaceTotal.rxBytes > 0) { @@ -1394,9 +1395,11 @@ public class NetworkStats implements Parcelable { * perInterfaceTotal[j].operations / underlyingIfacesTotal.operations; } - + // tmpEntry now contains the migrated data of the i-th entry for the j-th underlying + // interface. Add that data usage to this object. combineValues(tmpEntry); if (tag[i] == TAG_NONE) { + // Add the migrated data to moved so it is deducted from the VPN app later. moved[j].add(tmpEntry); // Add debug info tmpEntry.set = SET_DBG_VPN_IN; @@ -1412,8 +1415,8 @@ public class NetworkStats implements Parcelable { @NonNull String[] underlyingIfaces, @NonNull Entry[] moved) { for (int i = 0; i < underlyingIfaces.length; i++) { - // Add debug info moved[i].uid = tunUid; + // Add debug info moved[i].set = SET_DBG_VPN_OUT; moved[i].tag = TAG_NONE; moved[i].iface = underlyingIfaces[i]; diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 1728d9640226..228e62daf2bc 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -153,10 +153,9 @@ public class NetworkUtils { /** * DNS resolver series jni method. - * Attempts to get netid of network which resolver will - * use if no network is explicitly selected. + * Attempts to get network which resolver will use if no network is explicitly selected. */ - public static native int getDnsNetId() throws ErrnoException; + public static native Network getDnsNetwork() throws ErrnoException; /** * Get the tcp repair window associated with the {@code fd}. diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java index 46eddde968a0..ec73866a647d 100644 --- a/core/java/android/net/SocketKeepalive.java +++ b/core/java/android/net/SocketKeepalive.java @@ -44,9 +44,11 @@ import java.util.concurrent.Executor; * {@link SocketKeepalive.Callback#onStopped} if the operation was successful or * {@link SocketKeepalive.Callback#onError} if an error occurred. * - * The device SHOULD support keepalive offload. If it does not, it MUST reply with + * For cellular, the device MUST support at least 1 keepalive slot. + * + * For WiFi, the device SHOULD support keepalive offload. If it does not, it MUST reply with * {@link SocketKeepalive.Callback#onError} with {@code ERROR_UNSUPPORTED} to any keepalive offload - * request. If it does, it MUST support at least 3 concurrent keepalive slots per transport. + * request. If it does, it MUST support at least 3 concurrent keepalive slots. */ public abstract class SocketKeepalive implements AutoCloseable { static final String TAG = "SocketKeepalive"; diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java index baf7ae007343..0600036848d6 100644 --- a/core/java/android/net/StaticIpConfiguration.java +++ b/core/java/android/net/StaticIpConfiguration.java @@ -22,7 +22,6 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.net.shared.InetAddressUtils; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -54,7 +53,7 @@ import java.util.Objects; @TestApi public final class StaticIpConfiguration implements Parcelable { /** @hide */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) + @UnsupportedAppUsage @Nullable public LinkAddress ipAddress; /** @hide */ diff --git a/core/java/android/net/util/DnsUtils.java b/core/java/android/net/util/DnsUtils.java index e6abd5059027..7908353eeda2 100644 --- a/core/java/android/net/util/DnsUtils.java +++ b/core/java/android/net/util/DnsUtils.java @@ -141,14 +141,17 @@ public class DnsUtils { */ public static @NonNull List<InetAddress> rfc6724Sort(@Nullable Network network, @NonNull List<InetAddress> answers) { - List<SortableAddress> sortableAnswerList = new ArrayList<>(); - answers.forEach(addr -> sortableAnswerList.add( - new SortableAddress(addr, findSrcAddress(network, addr)))); + final ArrayList<SortableAddress> sortableAnswerList = new ArrayList<>(); + for (InetAddress addr : answers) { + sortableAnswerList.add(new SortableAddress(addr, findSrcAddress(network, addr))); + } Collections.sort(sortableAnswerList, sRfc6724Comparator); final List<InetAddress> sortedAnswers = new ArrayList<>(); - sortableAnswerList.forEach(ans -> sortedAnswers.add(ans.address)); + for (SortableAddress ans : sortableAnswerList) { + sortedAnswers.add(ans.address); + } return sortedAnswers; } diff --git a/core/java/android/net/util/KeepaliveUtils.java b/core/java/android/net/util/KeepaliveUtils.java index 569fed1fc994..bfc4563fbf8f 100644 --- a/core/java/android/net/util/KeepaliveUtils.java +++ b/core/java/android/net/util/KeepaliveUtils.java @@ -34,9 +34,6 @@ public final class KeepaliveUtils { public static final String TAG = "KeepaliveUtils"; - // Minimum supported keepalive count per transport if the network supports keepalive. - public static final int MIN_SUPPORTED_KEEPALIVE_COUNT = 3; - public static class KeepaliveDeviceConfigurationException extends AndroidRuntimeException { public KeepaliveDeviceConfigurationException(final String msg) { super(msg); @@ -84,10 +81,7 @@ public final class KeepaliveUtils { throw new KeepaliveDeviceConfigurationException("Invalid transport " + transport); } - // Customized values should be either 0 to indicate the network doesn't support - // keepalive offload, or a positive value that is at least - // MIN_SUPPORTED_KEEPALIVE_COUNT if supported. - if (supported != 0 && supported < MIN_SUPPORTED_KEEPALIVE_COUNT) { + if (supported < 0) { throw new KeepaliveDeviceConfigurationException( "Invalid supported count " + supported + " for " + NetworkCapabilities.transportNameOf(transport)); diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java index 1364d8c17e1b..d9bcb3ce0d8f 100644 --- a/core/java/android/net/util/SocketUtils.java +++ b/core/java/android/net/util/SocketUtils.java @@ -69,7 +69,10 @@ public final class SocketUtils { */ @NonNull public static SocketAddress makePacketSocketAddress(int protocol, int ifIndex) { - return new PacketSocketAddress((short) protocol, ifIndex); + return new PacketSocketAddress( + (short) protocol /* sll_protocol */, + ifIndex /* sll_ifindex */, + null /* sll_addr */); } /** @@ -77,7 +80,10 @@ public final class SocketUtils { */ @NonNull public static SocketAddress makePacketSocketAddress(int ifIndex, @NonNull byte[] hwAddr) { - return new PacketSocketAddress(ifIndex, hwAddr); + return new PacketSocketAddress( + (short) 0 /* sll_protocol */, + ifIndex /* sll_ifindex */, + hwAddr /* sll_addr */); } /** diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index eb347e7e253e..bc698f97738a 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -1714,11 +1714,12 @@ public final class NfcAdapter { /** * Sets Secure NFC feature. * <p>This API is for the Settings application. + * @return True if successful * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) - public boolean setNfcSecure(boolean enable) { + public boolean enableSecureNfc(boolean enable) { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1736,7 +1737,7 @@ public final class NfcAdapter { * @return True if device supports Secure NFC, false otherwise * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. */ - public boolean deviceSupportsNfcSecure() { + public boolean isSecureNfcSupported() { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } @@ -1751,12 +1752,12 @@ public final class NfcAdapter { /** * Checks Secure NFC feature is enabled. * - * @return True if device supports Secure NFC is enabled, false otherwise + * @return True if Secure NFC is enabled, false otherwise * @throws UnsupportedOperationException if FEATURE_NFC is unavailable. * @throws UnsupportedOperationException if device doesn't support - * Secure NFC functionality. {@link #deviceSupportsNfcSecure} + * Secure NFC functionality. {@link #isSecureNfcSupported} */ - public boolean isNfcSecureEnabled() { + public boolean isSecureNfcEnabled() { if (!sHasNfcFeature) { throw new UnsupportedOperationException(); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index ae743fbda482..fe2fbb1f4d47 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -2797,65 +2797,67 @@ public final class Parcel { return null; } Parcelable.Creator<?> creator; + HashMap<String, Parcelable.Creator<?>> map; synchronized (mCreators) { - HashMap<String,Parcelable.Creator<?>> map = mCreators.get(loader); + map = mCreators.get(loader); if (map == null) { map = new HashMap<>(); mCreators.put(loader, map); } creator = map.get(name); - if (creator == null) { - try { - // If loader == null, explicitly emulate Class.forName(String) "caller - // classloader" behavior. - ClassLoader parcelableClassLoader = - (loader == null ? getClass().getClassLoader() : loader); - // Avoid initializing the Parcelable class until we know it implements - // Parcelable and has the necessary CREATOR field. http://b/1171613. - Class<?> parcelableClass = Class.forName(name, false /* initialize */, - parcelableClassLoader); - if (!Parcelable.class.isAssignableFrom(parcelableClass)) { - throw new BadParcelableException("Parcelable protocol requires subclassing " - + "from Parcelable on class " + name); - } - Field f = parcelableClass.getField("CREATOR"); - if ((f.getModifiers() & Modifier.STATIC) == 0) { - throw new BadParcelableException("Parcelable protocol requires " - + "the CREATOR object to be static on class " + name); - } - Class<?> creatorType = f.getType(); - if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) { - // Fail before calling Field.get(), not after, to avoid initializing - // parcelableClass unnecessarily. - throw new BadParcelableException("Parcelable protocol requires a " - + "Parcelable.Creator object called " - + "CREATOR on class " + name); - } - creator = (Parcelable.Creator<?>) f.get(null); - } - catch (IllegalAccessException e) { - Log.e(TAG, "Illegal access when unmarshalling: " + name, e); - throw new BadParcelableException( - "IllegalAccessException when unmarshalling: " + name); - } - catch (ClassNotFoundException e) { - Log.e(TAG, "Class not found when unmarshalling: " + name, e); - throw new BadParcelableException( - "ClassNotFoundException when unmarshalling: " + name); - } - catch (NoSuchFieldException e) { - throw new BadParcelableException("Parcelable protocol requires a " - + "Parcelable.Creator object called " - + "CREATOR on class " + name); - } - if (creator == null) { - throw new BadParcelableException("Parcelable protocol requires a " - + "non-null Parcelable.Creator object called " - + "CREATOR on class " + name); - } + } + if (creator != null) { + return creator; + } - map.put(name, creator); + try { + // If loader == null, explicitly emulate Class.forName(String) "caller + // classloader" behavior. + ClassLoader parcelableClassLoader = + (loader == null ? getClass().getClassLoader() : loader); + // Avoid initializing the Parcelable class until we know it implements + // Parcelable and has the necessary CREATOR field. http://b/1171613. + Class<?> parcelableClass = Class.forName(name, false /* initialize */, + parcelableClassLoader); + if (!Parcelable.class.isAssignableFrom(parcelableClass)) { + throw new BadParcelableException("Parcelable protocol requires subclassing " + + "from Parcelable on class " + name); + } + Field f = parcelableClass.getField("CREATOR"); + if ((f.getModifiers() & Modifier.STATIC) == 0) { + throw new BadParcelableException("Parcelable protocol requires " + + "the CREATOR object to be static on class " + name); } + Class<?> creatorType = f.getType(); + if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) { + // Fail before calling Field.get(), not after, to avoid initializing + // parcelableClass unnecessarily. + throw new BadParcelableException("Parcelable protocol requires a " + + "Parcelable.Creator object called " + + "CREATOR on class " + name); + } + creator = (Parcelable.Creator<?>) f.get(null); + } catch (IllegalAccessException e) { + Log.e(TAG, "Illegal access when unmarshalling: " + name, e); + throw new BadParcelableException( + "IllegalAccessException when unmarshalling: " + name); + } catch (ClassNotFoundException e) { + Log.e(TAG, "Class not found when unmarshalling: " + name, e); + throw new BadParcelableException( + "ClassNotFoundException when unmarshalling: " + name); + } catch (NoSuchFieldException e) { + throw new BadParcelableException("Parcelable protocol requires a " + + "Parcelable.Creator object called " + + "CREATOR on class " + name); + } + if (creator == null) { + throw new BadParcelableException("Parcelable protocol requires a " + + "non-null Parcelable.Creator object called " + + "CREATOR on class " + name); + } + + synchronized (mCreators) { + map.put(name, creator); } return creator; diff --git a/core/java/android/provider/OWNERS b/core/java/android/provider/OWNERS new file mode 100644 index 000000000000..8b7d6ad851f9 --- /dev/null +++ b/core/java/android/provider/OWNERS @@ -0,0 +1,4 @@ +per-file DeviceConfig.java = svetoslavganov@google.com +per-file DeviceConfig.java = hackbod@google.com + + diff --git a/core/java/android/util/ByteStringUtils.java b/core/java/android/util/ByteStringUtils.java deleted file mode 100644 index e4798f09517d..000000000000 --- a/core/java/android/util/ByteStringUtils.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2017 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.util; - -import libcore.util.HexEncoding; - -/** - * A utility class for common byte array to hex string operations and vise versa. - * - * @hide - */ -public final class ByteStringUtils { - - private ByteStringUtils() { - /* hide constructor */ - } - - /** - * Returns the hex encoded string representation of bytes. - * @param bytes Byte array to encode. - * @return Hex encoded string representation of bytes. - */ - public static String toHexString(byte[] bytes) { - if (bytes == null || bytes.length == 0 || bytes.length % 2 != 0) { - return null; - } - - return HexEncoding.encodeToString(bytes, true /* upperCase */); - } - - /** - * Returns the decoded byte array representation of str. - * @param str Hex encoded string to decode. - * @return Decoded byte array representation of str. - */ - public static byte[] fromHexToByteArray(String str) { - if (str == null || str.length() == 0 || str.length() % 2 != 0) { - return null; - } - - return HexEncoding.decode(str, false /* allowSingleChar */); - } -} diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 929496f2d237..350094edad54 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -106,6 +106,8 @@ public final class Magnifier { // Lock to synchronize between the UI thread and the thread that handles pixel copy results. // Only sync mWindow writes from UI thread with mWindow reads from sPixelCopyHandlerThread. private final Object mLock = new Object(); + // The lock used to synchronize the UI and render threads when a #dismiss is performed. + private final Object mDestroyLock = new Object(); /** * Initializes a magnifier. @@ -173,7 +175,7 @@ public final class Magnifier { mParentSurface.mSurface, mWindowWidth, mWindowHeight, mWindowElevation, mWindowCornerRadius, Handler.getMain() /* draw the magnifier on the UI thread */, mLock, - mCallback); + mDestroyLock, mCallback); } } performPixelCopy(startX, startY, true /* update window position */); @@ -187,9 +189,11 @@ public final class Magnifier { */ public void dismiss() { if (mWindow != null) { - synchronized (mLock) { - mWindow.destroy(); - mWindow = null; + synchronized (mDestroyLock) { + synchronized (mLock) { + mWindow.destroy(); + mWindow = null; + } } mPrevPosInView.x = NONEXISTENT_PREVIOUS_CONFIG_VALUE; mPrevPosInView.y = NONEXISTENT_PREVIOUS_CONFIG_VALUE; @@ -478,14 +482,16 @@ public final class Magnifier { // is performed on the UI thread and a frame callback on the render thread. // When both mLock and mDestroyLock need to be held at the same time, // mDestroyLock should be acquired before mLock in order to avoid deadlocks. - private final Object mDestroyLock = new Object(); + private final Object mDestroyLock; InternalPopupWindow(final Context context, final Display display, final Surface parentSurface, final int width, final int height, final float elevation, final float cornerRadius, - final Handler handler, final Object lock, final Callback callback) { + final Handler handler, final Object lock, final Object destroyLock, + final Callback callback) { mDisplay = display; mLock = lock; + mDestroyLock = destroyLock; mCallback = callback; mContentWidth = width; diff --git a/core/java/com/android/internal/http/HttpDateTime.java b/core/java/com/android/internal/http/HttpDateTime.java deleted file mode 100644 index f7706e310952..000000000000 --- a/core/java/com/android/internal/http/HttpDateTime.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2007 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.http; - -import android.annotation.UnsupportedAppUsage; -import android.text.format.Time; - -import java.util.Calendar; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Helper for parsing an HTTP date. - */ -public final class HttpDateTime { - - /* - * Regular expression for parsing HTTP-date. - * - * Wdy, DD Mon YYYY HH:MM:SS GMT - * RFC 822, updated by RFC 1123 - * - * Weekday, DD-Mon-YY HH:MM:SS GMT - * RFC 850, obsoleted by RFC 1036 - * - * Wdy Mon DD HH:MM:SS YYYY - * ANSI C's asctime() format - * - * with following variations - * - * Wdy, DD-Mon-YYYY HH:MM:SS GMT - * Wdy, (SP)D Mon YYYY HH:MM:SS GMT - * Wdy,DD Mon YYYY HH:MM:SS GMT - * Wdy, DD-Mon-YY HH:MM:SS GMT - * Wdy, DD Mon YYYY HH:MM:SS -HHMM - * Wdy, DD Mon YYYY HH:MM:SS - * Wdy Mon (SP)D HH:MM:SS YYYY - * Wdy Mon DD HH:MM:SS YYYY GMT - * - * HH can be H if the first digit is zero. - * - * Mon can be the full name of the month. - */ - private static final String HTTP_DATE_RFC_REGEXP = - "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]" - + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])"; - - private static final String HTTP_DATE_ANSIC_REGEXP = - "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]" - + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})"; - - /** - * The compiled version of the HTTP-date regular expressions. - */ - private static final Pattern HTTP_DATE_RFC_PATTERN = - Pattern.compile(HTTP_DATE_RFC_REGEXP); - private static final Pattern HTTP_DATE_ANSIC_PATTERN = - Pattern.compile(HTTP_DATE_ANSIC_REGEXP); - - private static class TimeOfDay { - TimeOfDay(int h, int m, int s) { - this.hour = h; - this.minute = m; - this.second = s; - } - - int hour; - int minute; - int second; - } - - @UnsupportedAppUsage - public static long parse(String timeString) - throws IllegalArgumentException { - - int date = 1; - int month = Calendar.JANUARY; - int year = 1970; - TimeOfDay timeOfDay; - - Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString); - if (rfcMatcher.find()) { - date = getDate(rfcMatcher.group(1)); - month = getMonth(rfcMatcher.group(2)); - year = getYear(rfcMatcher.group(3)); - timeOfDay = getTime(rfcMatcher.group(4)); - } else { - Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString); - if (ansicMatcher.find()) { - month = getMonth(ansicMatcher.group(1)); - date = getDate(ansicMatcher.group(2)); - timeOfDay = getTime(ansicMatcher.group(3)); - year = getYear(ansicMatcher.group(4)); - } else { - throw new IllegalArgumentException(); - } - } - - // FIXME: Y2038 BUG! - if (year >= 2038) { - year = 2038; - month = Calendar.JANUARY; - date = 1; - } - - Time time = new Time(Time.TIMEZONE_UTC); - time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date, - month, year); - return time.toMillis(false /* use isDst */); - } - - private static int getDate(String dateString) { - if (dateString.length() == 2) { - return (dateString.charAt(0) - '0') * 10 - + (dateString.charAt(1) - '0'); - } else { - return (dateString.charAt(0) - '0'); - } - } - - /* - * jan = 9 + 0 + 13 = 22 - * feb = 5 + 4 + 1 = 10 - * mar = 12 + 0 + 17 = 29 - * apr = 0 + 15 + 17 = 32 - * may = 12 + 0 + 24 = 36 - * jun = 9 + 20 + 13 = 42 - * jul = 9 + 20 + 11 = 40 - * aug = 0 + 20 + 6 = 26 - * sep = 18 + 4 + 15 = 37 - * oct = 14 + 2 + 19 = 35 - * nov = 13 + 14 + 21 = 48 - * dec = 3 + 4 + 2 = 9 - */ - private static int getMonth(String monthString) { - int hash = Character.toLowerCase(monthString.charAt(0)) + - Character.toLowerCase(monthString.charAt(1)) + - Character.toLowerCase(monthString.charAt(2)) - 3 * 'a'; - switch (hash) { - case 22: - return Calendar.JANUARY; - case 10: - return Calendar.FEBRUARY; - case 29: - return Calendar.MARCH; - case 32: - return Calendar.APRIL; - case 36: - return Calendar.MAY; - case 42: - return Calendar.JUNE; - case 40: - return Calendar.JULY; - case 26: - return Calendar.AUGUST; - case 37: - return Calendar.SEPTEMBER; - case 35: - return Calendar.OCTOBER; - case 48: - return Calendar.NOVEMBER; - case 9: - return Calendar.DECEMBER; - default: - throw new IllegalArgumentException(); - } - } - - private static int getYear(String yearString) { - if (yearString.length() == 2) { - int year = (yearString.charAt(0) - '0') * 10 - + (yearString.charAt(1) - '0'); - if (year >= 70) { - return year + 1900; - } else { - return year + 2000; - } - } else if (yearString.length() == 3) { - // According to RFC 2822, three digit years should be added to 1900. - int year = (yearString.charAt(0) - '0') * 100 - + (yearString.charAt(1) - '0') * 10 - + (yearString.charAt(2) - '0'); - return year + 1900; - } else if (yearString.length() == 4) { - return (yearString.charAt(0) - '0') * 1000 - + (yearString.charAt(1) - '0') * 100 - + (yearString.charAt(2) - '0') * 10 - + (yearString.charAt(3) - '0'); - } else { - return 1970; - } - } - - private static TimeOfDay getTime(String timeString) { - // HH might be H - int i = 0; - int hour = timeString.charAt(i++) - '0'; - if (timeString.charAt(i) != ':') - hour = hour * 10 + (timeString.charAt(i++) - '0'); - // Skip ':' - i++; - - int minute = (timeString.charAt(i++) - '0') * 10 - + (timeString.charAt(i++) - '0'); - // Skip ':' - i++; - - int second = (timeString.charAt(i++) - '0') * 10 - + (timeString.charAt(i++) - '0'); - - return new TimeOfDay(hour, minute, second); - } -} diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 1854ea940379..69cdf799dd71 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -237,6 +237,11 @@ static const char* ENABLE_APEX_IMAGE = "enable_apex_image"; // Flag to pass to the runtime when using the apex image. static const char* kApexImageOption = "-Ximage:/system/framework/apex.art"; +// Feature flag name for disabling lock profiling. +static const char* DISABLE_LOCK_PROFILING = "disable_lock_profiling"; +// Runtime option disabling lock profiling. +static const char* kLockProfThresholdRuntimeOption = "-Xlockprofthreshold:0"; + static AndroidRuntime* gCurRuntime = NULL; /* @@ -613,7 +618,7 @@ bool AndroidRuntime::parseCompilerRuntimeOption(const char* property, * * Returns 0 on success. */ -int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) +int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote) { JavaVMInitArgs initArgs; char propBuf[PROPERTY_VALUE_MAX]; @@ -685,6 +690,17 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) ALOGI("Using default boot image"); } + std::string disable_lock_profiling = + server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, + DISABLE_LOCK_PROFILING, + /*default_value=*/ ""); + if (disable_lock_profiling == "true") { + addOption(kLockProfThresholdRuntimeOption); + ALOGI("Disabling lock profiling: '%s'\n", kLockProfThresholdRuntimeOption); + } else { + ALOGI("Leaving lock profiling enabled"); + } + bool checkJni = false; property_get("dalvik.vm.checkjni", propBuf, ""); if (strcmp(propBuf, "true") == 0) { @@ -733,6 +749,10 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) addOption("-verbose:gc"); //addOption("-verbose:class"); + if (primary_zygote) { + addOption("-Xprimaryzygote"); + } + /* * The default starting and maximum size of the heap. Larger * values should be specified in a product property override. @@ -1094,6 +1114,8 @@ void AndroidRuntime::start(const char* className, const Vector<String8>& options className != NULL ? className : "(unknown)", getuid()); static const String8 startSystemServer("start-system-server"); + // Whether this is the primary zygote, meaning the zygote which will fork system server. + bool primary_zygote = false; /* * 'startSystemServer == true' means runtime is obsolete and not run from @@ -1101,6 +1123,7 @@ void AndroidRuntime::start(const char* className, const Vector<String8>& options */ for (size_t i = 0; i < options.size(); ++i) { if (options[i] == startSystemServer) { + primary_zygote = true; /* track our progress through the boot sequence */ const int LOG_BOOT_PROGRESS_START = 3000; LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); @@ -1136,7 +1159,7 @@ void AndroidRuntime::start(const char* className, const Vector<String8>& options JniInvocation jni_invocation; jni_invocation.Init(NULL); JNIEnv* env; - if (startVm(&mJavaVM, &env, zygote) != 0) { + if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { return; } onVmCreated(env); diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp index 00e0e3a74d70..08aa1d97fa1c 100644 --- a/core/jni/android_net_NetUtils.cpp +++ b/core/jni/android_net_NetUtils.cpp @@ -304,13 +304,19 @@ static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobjec jniSetFileDescriptorOfFD(env, javaFd, -1); } -static jint android_net_utils_getDnsNetId(JNIEnv *env, jobject thiz) { - int dnsNetId = getNetworkForDns(); - if (dnsNetId < 0) { - throwErrnoException(env, "getDnsNetId", -dnsNetId); +static jobject android_net_utils_getDnsNetwork(JNIEnv *env, jobject thiz) { + unsigned dnsNetId = 0; + if (int res = getNetworkForDns(&dnsNetId) < 0) { + throwErrnoException(env, "getDnsNetId", -res); + return nullptr; } + bool privateDnsBypass = dnsNetId & NETID_USE_LOCAL_NAMESERVERS; - return dnsNetId; + static jclass class_Network = MakeGlobalRefOrDie( + env, FindClassOrDie(env, "android/net/Network")); + static jmethodID ctor = env->GetMethodID(class_Network, "<init>", "(IZ)V"); + return env->NewObject( + class_Network, ctor, dnsNetId & ~NETID_USE_LOCAL_NAMESERVERS, privateDnsBypass); } static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) { @@ -369,7 +375,7 @@ static const JNINativeMethod gNetworkUtilMethods[] = { { "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery }, { "resNetworkResult", "(Ljava/io/FileDescriptor;)Landroid/net/DnsResolver$DnsResponse;", (void*) android_net_utils_resNetworkResult }, { "resNetworkCancel", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_resNetworkCancel }, - { "getDnsNetId", "()I", (void*) android_net_utils_getDnsNetId }, + { "getDnsNetwork", "()Landroid/net/Network;", (void*) android_net_utils_getDnsNetwork }, }; int register_android_net_NetworkUtils(JNIEnv* env) diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp index e33da9167e05..57688c456738 100644 --- a/core/jni/android_util_jar_StrictJarFile.cpp +++ b/core/jni/android_util_jar_StrictJarFile.cpp @@ -112,7 +112,7 @@ jlong StrictJarFile_nativeStartIteration(JNIEnv* env, jobject, jlong nativeHandl jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandle) { ZipEntry data; - ZipString entryName; + std::string entryName; IterationHandle* handle = reinterpret_cast<IterationHandle*>(iterationHandle); const int32_t error = Next(*handle->CookieAddress(), &data, &entryName); @@ -121,10 +121,7 @@ jobject StrictJarFile_nativeNextEntry(JNIEnv* env, jobject, jlong iterationHandl return NULL; } - std::unique_ptr<char[]> entryNameCString(new char[entryName.name_length + 1]); - memcpy(entryNameCString.get(), entryName.name, entryName.name_length); - entryNameCString[entryName.name_length] = '\0'; - ScopedLocalRef<jstring> entryNameString(env, env->NewStringUTF(entryNameCString.get())); + ScopedLocalRef<jstring> entryNameString(env, env->NewStringUTF(entryName.c_str())); return newZipEntry(env, data, entryNameString.get()); } diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 2071b989aef7..6d10fc2b37db 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -238,7 +238,7 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, fail_fn_t fail_fn) } if (!whitelist->IsAllowed(file_path)) { - fail_fn(std::string("Not whitelisted : ").append(file_path)); + fail_fn(android::base::StringPrintf("Not whitelisted (%d): %s", fd, file_path.c_str())); } // File descriptor flags : currently on FD_CLOEXEC. We can set these diff --git a/core/jni/include/android_runtime/AndroidRuntime.h b/core/jni/include/android_runtime/AndroidRuntime.h index 3ec8b1fe903f..9d803f614b38 100644 --- a/core/jni/include/android_runtime/AndroidRuntime.h +++ b/core/jni/include/android_runtime/AndroidRuntime.h @@ -131,7 +131,7 @@ private: const char* runtimeArg, const char* quotingArg); void parseExtraOpts(char* extraOptsBuf, const char* quotingArg); - int startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote); + int startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote); Vector<JavaVMOption> mOptions; bool mExitWithoutCleanup; diff --git a/core/proto/android/server/connectivity/Android.bp b/core/proto/android/server/connectivity/Android.bp index c0ac2cb8f800..413623963851 100644 --- a/core/proto/android/server/connectivity/Android.bp +++ b/core/proto/android/server/connectivity/Android.bp @@ -21,5 +21,4 @@ java_library_static { "data_stall_event.proto", ], sdk_version: "system_current", - no_framework_libs: true, -}
\ No newline at end of file +} diff --git a/core/proto/android/stats/connectivity/Android.bp b/core/proto/android/stats/connectivity/Android.bp index 5aa4ddbdf7f9..5d642d3845fe 100644 --- a/core/proto/android/stats/connectivity/Android.bp +++ b/core/proto/android/stats/connectivity/Android.bp @@ -21,5 +21,4 @@ java_library_static { "network_stack.proto", ], sdk_version: "system_current", - no_framework_libs: true, -}
\ No newline at end of file +} diff --git a/core/proto/android/stats/dnsresolver/Android.bp b/core/proto/android/stats/dnsresolver/Android.bp index 0b5aa8677a6e..1e8c76314448 100644 --- a/core/proto/android/stats/dnsresolver/Android.bp +++ b/core/proto/android/stats/dnsresolver/Android.bp @@ -21,5 +21,4 @@ java_library_static { "dns_resolver.proto", ], sdk_version: "system_current", - no_framework_libs: true, } diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto index af6fea017bef..b7bf3848d921 100644 --- a/core/proto/android/stats/dnsresolver/dns_resolver.proto +++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto @@ -1,214 +1,216 @@ -/*
- * 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.
- */
-syntax = "proto2";
-package android.stats.dnsresolver;
-
-enum EventType {
- EVENT_UNKNOWN = 0;
- EVENT_GETADDRINFO = 1;
- EVENT_GETHOSTBYNAME = 2;
- EVENT_GETHOSTBYADDR = 3;
- EVENT_RES_NSEND = 4;
-}
-
-// The return value of the DNS resolver for each DNS lookups.
-// bionic/libc/include/netdb.h
-// system/netd/resolv/include/netd_resolv/resolv.h
-enum ReturnCode {
- RC_EAI_NO_ERROR = 0;
- RC_EAI_ADDRFAMILY = 1;
- RC_EAI_AGAIN = 2;
- RC_EAI_BADFLAGS = 3;
- RC_EAI_FAIL = 4;
- RC_EAI_FAMILY = 5;
- RC_EAI_MEMORY = 6;
- RC_EAI_NODATA = 7;
- RC_EAI_NONAME = 8;
- RC_EAI_SERVICE = 9;
- RC_EAI_SOCKTYPE = 10;
- RC_EAI_SYSTEM = 11;
- RC_EAI_BADHINTS = 12;
- RC_EAI_PROTOCOL = 13;
- RC_EAI_OVERFLOW = 14;
- RC_RESOLV_TIMEOUT = 255;
- RC_EAI_MAX = 256;
-}
-
-enum NsRcode {
- NS_R_NO_ERROR = 0; // No error occurred.
- NS_R_FORMERR = 1; // Format error.
- NS_R_SERVFAIL = 2; // Server failure.
- NS_R_NXDOMAIN = 3; // Name error.
- NS_R_NOTIMPL = 4; // Unimplemented.
- NS_R_REFUSED = 5; // Operation refused.
- // these are for BIND_UPDATE
- NS_R_YXDOMAIN = 6; // Name exists
- NS_R_YXRRSET = 7; // RRset exists
- NS_R_NXRRSET = 8; // RRset does not exist
- NS_R_NOTAUTH = 9; // Not authoritative for zone
- NS_R_NOTZONE = 10; // Zone of record different from zone section
- NS_R_MAX = 11;
- // The following are EDNS extended rcodes
- NS_R_BADVERS = 16;
- // The following are TSIG errors
- // NS_R_BADSIG = 16,
- NS_R_BADKEY = 17;
- NS_R_BADTIME = 18;
-}
-
-// Currently defined type values for resources and queries.
-enum NsType {
- NS_T_INVALID = 0; // Cookie.
- NS_T_A = 1; // Host address.
- NS_T_NS = 2; // Authoritative server.
- NS_T_MD = 3; // Mail destination.
- NS_T_MF = 4; // Mail forwarder.
- NS_T_CNAME = 5; // Canonical name.
- NS_T_SOA = 6; // Start of authority zone.
- NS_T_MB = 7; // Mailbox domain name.
- NS_T_MG = 8; // Mail group member.
- NS_T_MR = 9; // Mail rename name.
- NS_T_NULL = 10; // Null resource record.
- NS_T_WKS = 11; // Well known service.
- NS_T_PTR = 12; // Domain name pointer.
- NS_T_HINFO = 13; // Host information.
- NS_T_MINFO = 14; // Mailbox information.
- NS_T_MX = 15; // Mail routing information.
- NS_T_TXT = 16; // Text strings.
- NS_T_RP = 17; // Responsible person.
- NS_T_AFSDB = 18; // AFS cell database.
- NS_T_X25 = 19; // X_25 calling address.
- NS_T_ISDN = 20; // ISDN calling address.
- NS_T_RT = 21; // Router.
- NS_T_NSAP = 22; // NSAP address.
- NS_T_NSAP_PTR = 23; // Reverse NSAP lookup (deprecated).
- NS_T_SIG = 24; // Security signature.
- NS_T_KEY = 25; // Security key.
- NS_T_PX = 26; // X.400 mail mapping.
- NS_T_GPOS = 27; // Geographical position (withdrawn).
- NS_T_AAAA = 28; // IPv6 Address.
- NS_T_LOC = 29; // Location Information.
- NS_T_NXT = 30; // Next domain (security).
- NS_T_EID = 31; // Endpoint identifier.
- NS_T_NIMLOC = 32; // Nimrod Locator.
- NS_T_SRV = 33; // Server Selection.
- NS_T_ATMA = 34; // ATM Address
- NS_T_NAPTR = 35; // Naming Authority PoinTeR
- NS_T_KX = 36; // Key Exchange
- NS_T_CERT = 37; // Certification record
- NS_T_A6 = 38; // IPv6 address (experimental)
- NS_T_DNAME = 39; // Non-terminal DNAME
- NS_T_SINK = 40; // Kitchen sink (experimentatl)
- NS_T_OPT = 41; // EDNS0 option (meta-RR)
- NS_T_APL = 42; // Address prefix list (RFC 3123)
- NS_T_DS = 43; // Delegation Signer
- NS_T_SSHFP = 44; // SSH Fingerprint
- NS_T_IPSECKEY = 45; // IPSEC Key
- NS_T_RRSIG = 46; // RRset Signature
- NS_T_NSEC = 47; // Negative security
- NS_T_DNSKEY = 48; // DNS Key
- NS_T_DHCID = 49; // Dynamic host configuratin identifier
- NS_T_NSEC3 = 50; // Negative security type 3
- NS_T_NSEC3PARAM = 51; // Negative security type 3 parameters
- NS_T_HIP = 55; // Host Identity Protocol
- NS_T_SPF = 99; // Sender Policy Framework
- NS_T_TKEY = 249; // Transaction key
- NS_T_TSIG = 250; // Transaction signature.
- NS_T_IXFR = 251; // Incremental zone transfer.
- NS_T_AXFR = 252; // Transfer zone of authority.
- NS_T_MAILB = 253; // Transfer mailbox records.
- NS_T_MAILA = 254; // Transfer mail agent records.
- NS_T_ANY = 255; // Wildcard match.
- NS_T_ZXFR = 256; // BIND-specific, nonstandard.
- NS_T_DLV = 32769; // DNSSEC look-aside validatation.
- NS_T_MAX = 65536;
-}
-
-enum IpVersion {
- IV_UNKNOWN = 0;
- IV_IPV4 = 1;
- IV_IPV6 = 2;
-}
-
-enum TransportType {
- TT_UNKNOWN = 0;
- TT_UDP = 1;
- TT_TCP = 2;
- TT_DOT = 3;
-}
-
-enum PrivateDnsModes {
- PDM_UNKNOWN = 0;
- PDM_OFF = 1;
- PDM_OPPORTUNISTIC = 2;
- PDM_STRICT = 3;
-}
-
-enum Transport {
- // Indicates this network uses a Cellular transport.
- TRANSPORT_DEFAULT = 0; // TRANSPORT_CELLULAR
- // Indicates this network uses a Wi-Fi transport.
- TRANSPORT_WIFI = 1;
- // Indicates this network uses a Bluetooth transport.
- TRANSPORT_BLUETOOTH = 2;
- // Indicates this network uses an Ethernet transport.
- TRANSPORT_ETHERNET = 3;
- // Indicates this network uses a VPN transport.
- TRANSPORT_VPN = 4;
- // Indicates this network uses a Wi-Fi Aware transport.
- TRANSPORT_WIFI_AWARE = 5;
- // Indicates this network uses a LoWPAN transport.
- TRANSPORT_LOWPAN = 6;
-}
-
-enum CacheStatus{
- // the cache can't handle that kind of queries.
- // or the answer buffer is too small.
- CS_UNSUPPORTED = 0;
- // the cache doesn't know about this query.
- CS_NOTFOUND = 1;
- // the cache found the answer.
- CS_FOUND = 2;
- // Don't do anything on cache.
- CS_SKIP = 3;
-}
-
-message DnsQueryEvent {
- optional android.stats.dnsresolver.NsRcode rcode = 1;
-
- optional android.stats.dnsresolver.NsType type = 2;
-
- optional android.stats.dnsresolver.CacheStatus cache_hit = 3;
-
- optional android.stats.dnsresolver.IpVersion ip_version = 4;
-
- optional android.stats.dnsresolver.TransportType transport = 5;
-
- // Number of DNS query retry times
- optional int32 retry_times = 6;
-
- // Ordinal number of name server.
- optional int32 dns_server_count = 7;
-
- // Used only by TCP and DOT. True for new connections.
- optional bool connected = 8;
-
- optional int32 latency_micros = 9;
-}
-
-message DnsQueryEvents {
- repeated DnsQueryEvent dns_query_event = 1;
-}
+/* + * 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. + */ +syntax = "proto2"; +package android.stats.dnsresolver; + +enum EventType { + EVENT_UNKNOWN = 0; + EVENT_GETADDRINFO = 1; + EVENT_GETHOSTBYNAME = 2; + EVENT_GETHOSTBYADDR = 3; + EVENT_RES_NSEND = 4; +} + +// The return value of the DNS resolver for each DNS lookups. +// bionic/libc/include/netdb.h +// system/netd/resolv/include/netd_resolv/resolv.h +enum ReturnCode { + RC_EAI_NO_ERROR = 0; + RC_EAI_ADDRFAMILY = 1; + RC_EAI_AGAIN = 2; + RC_EAI_BADFLAGS = 3; + RC_EAI_FAIL = 4; + RC_EAI_FAMILY = 5; + RC_EAI_MEMORY = 6; + RC_EAI_NODATA = 7; + RC_EAI_NONAME = 8; + RC_EAI_SERVICE = 9; + RC_EAI_SOCKTYPE = 10; + RC_EAI_SYSTEM = 11; + RC_EAI_BADHINTS = 12; + RC_EAI_PROTOCOL = 13; + RC_EAI_OVERFLOW = 14; + RC_RESOLV_TIMEOUT = 255; + RC_EAI_MAX = 256; +} + +enum NsRcode { + NS_R_NO_ERROR = 0; // No error occurred. + NS_R_FORMERR = 1; // Format error. + NS_R_SERVFAIL = 2; // Server failure. + NS_R_NXDOMAIN = 3; // Name error. + NS_R_NOTIMPL = 4; // Unimplemented. + NS_R_REFUSED = 5; // Operation refused. + // these are for BIND_UPDATE + NS_R_YXDOMAIN = 6; // Name exists + NS_R_YXRRSET = 7; // RRset exists + NS_R_NXRRSET = 8; // RRset does not exist + NS_R_NOTAUTH = 9; // Not authoritative for zone + NS_R_NOTZONE = 10; // Zone of record different from zone section + NS_R_MAX = 11; + // The following are EDNS extended rcodes + NS_R_BADVERS = 16; + // The following are TSIG errors + // NS_R_BADSIG = 16, + NS_R_BADKEY = 17; + NS_R_BADTIME = 18; + NS_R_TIMEOUT = 255; +} + +// Currently defined type values for resources and queries. +enum NsType { + NS_T_INVALID = 0; // Cookie. + NS_T_A = 1; // Host address. + NS_T_NS = 2; // Authoritative server. + NS_T_MD = 3; // Mail destination. + NS_T_MF = 4; // Mail forwarder. + NS_T_CNAME = 5; // Canonical name. + NS_T_SOA = 6; // Start of authority zone. + NS_T_MB = 7; // Mailbox domain name. + NS_T_MG = 8; // Mail group member. + NS_T_MR = 9; // Mail rename name. + NS_T_NULL = 10; // Null resource record. + NS_T_WKS = 11; // Well known service. + NS_T_PTR = 12; // Domain name pointer. + NS_T_HINFO = 13; // Host information. + NS_T_MINFO = 14; // Mailbox information. + NS_T_MX = 15; // Mail routing information. + NS_T_TXT = 16; // Text strings. + NS_T_RP = 17; // Responsible person. + NS_T_AFSDB = 18; // AFS cell database. + NS_T_X25 = 19; // X_25 calling address. + NS_T_ISDN = 20; // ISDN calling address. + NS_T_RT = 21; // Router. + NS_T_NSAP = 22; // NSAP address. + NS_T_NSAP_PTR = 23; // Reverse NSAP lookup (deprecated). + NS_T_SIG = 24; // Security signature. + NS_T_KEY = 25; // Security key. + NS_T_PX = 26; // X.400 mail mapping. + NS_T_GPOS = 27; // Geographical position (withdrawn). + NS_T_AAAA = 28; // IPv6 Address. + NS_T_LOC = 29; // Location Information. + NS_T_NXT = 30; // Next domain (security). + NS_T_EID = 31; // Endpoint identifier. + NS_T_NIMLOC = 32; // Nimrod Locator. + NS_T_SRV = 33; // Server Selection. + NS_T_ATMA = 34; // ATM Address + NS_T_NAPTR = 35; // Naming Authority PoinTeR + NS_T_KX = 36; // Key Exchange + NS_T_CERT = 37; // Certification record + NS_T_A6 = 38; // IPv6 address (experimental) + NS_T_DNAME = 39; // Non-terminal DNAME + NS_T_SINK = 40; // Kitchen sink (experimentatl) + NS_T_OPT = 41; // EDNS0 option (meta-RR) + NS_T_APL = 42; // Address prefix list (RFC 3123) + NS_T_DS = 43; // Delegation Signer + NS_T_SSHFP = 44; // SSH Fingerprint + NS_T_IPSECKEY = 45; // IPSEC Key + NS_T_RRSIG = 46; // RRset Signature + NS_T_NSEC = 47; // Negative security + NS_T_DNSKEY = 48; // DNS Key + NS_T_DHCID = 49; // Dynamic host configuratin identifier + NS_T_NSEC3 = 50; // Negative security type 3 + NS_T_NSEC3PARAM = 51; // Negative security type 3 parameters + NS_T_HIP = 55; // Host Identity Protocol + NS_T_SPF = 99; // Sender Policy Framework + NS_T_TKEY = 249; // Transaction key + NS_T_TSIG = 250; // Transaction signature. + NS_T_IXFR = 251; // Incremental zone transfer. + NS_T_AXFR = 252; // Transfer zone of authority. + NS_T_MAILB = 253; // Transfer mailbox records. + NS_T_MAILA = 254; // Transfer mail agent records. + NS_T_ANY = 255; // Wildcard match. + NS_T_ZXFR = 256; // BIND-specific, nonstandard. + NS_T_DLV = 32769; // DNSSEC look-aside validatation. + NS_T_MAX = 65536; +} + +enum IpVersion { + IV_UNKNOWN = 0; + IV_IPV4 = 1; + IV_IPV6 = 2; +} + +enum Protocol { + PROTO_UNKNOWN = 0; + PROTO_UDP = 1; + PROTO_TCP = 2; + PROTO_DOT = 3; +} + +enum PrivateDnsModes { + PDM_UNKNOWN = 0; + PDM_OFF = 1; + PDM_OPPORTUNISTIC = 2; + PDM_STRICT = 3; +} + +enum NetworkType { + NT_UNKNOWN = 0; + // Indicates this network uses a Cellular transport. + NT_CELLULAR = 1; + // Indicates this network uses a Wi-Fi transport. + NT_WIFI = 2; + // Indicates this network uses a Bluetooth transport. + NT_BLUETOOTH = 3; + // Indicates this network uses an Ethernet transport. + NT_ETHERNET = 4; + // Indicates this network uses a VPN transport. + NT_VPN = 5; + // Indicates this network uses a Wi-Fi Aware transport. + NT_WIFI_AWARE = 6; + // Indicates this network uses a LoWPAN transport. + NT_LOWPAN = 7; +} + +enum CacheStatus{ + // the cache can't handle that kind of queries. + // or the answer buffer is too small. + CS_UNSUPPORTED = 0; + // the cache doesn't know about this query. + CS_NOTFOUND = 1; + // the cache found the answer. + CS_FOUND = 2; + // Don't do anything on cache. + CS_SKIP = 3; +} + +message DnsQueryEvent { + optional android.stats.dnsresolver.NsRcode rcode = 1; + + optional android.stats.dnsresolver.NsType type = 2; + + optional android.stats.dnsresolver.CacheStatus cache_hit = 3; + + optional android.stats.dnsresolver.IpVersion ip_version = 4; + + optional android.stats.dnsresolver.Protocol protocol = 5; + + // Number of DNS query retry times + optional int32 retry_times = 6; + + // Ordinal number of name server. + optional int32 dns_server_index = 7; + + // Used only by TCP and DOT. True for new connections. + optional bool connected = 8; + + optional int32 latency_micros = 9; +} + +message DnsQueryEvents { + repeated DnsQueryEvent dns_query_event = 1; +} diff --git a/core/res/Android.bp b/core/res/Android.bp index 4e60f8ca025c..3402033b04f1 100644 --- a/core/res/Android.bp +++ b/core/res/Android.bp @@ -16,7 +16,7 @@ android_app { name: "framework-res", - no_framework_libs: true, + sdk_version: "core_platform", certificate: "platform", // Soong special-cases framework-res to install this alongside diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ccf5199d3d2b..990deaaf8184 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1579,6 +1579,7 @@ @hide --> <permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" android:protectionLevel="signature|privileged" /> + <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/> <!-- @SystemApi Allows an internal user to set signal strength in NetworkRequest. This kind of request will wake up device when signal strength meets the given value. diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 8a2648cd6692..1fce5be4def0 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -353,7 +353,7 @@ overridden by the device to present the capability of creating socket keepalives. --> <!-- An Array of "[NetworkCapabilities.TRANSPORT_*],[supported keepalives] --> <string-array translatable="false" name="config_networkSupportedKeepaliveCount"> - <item>0,3</item> + <item>0,1</item> <item>1,3</item> </string-array> @@ -2806,6 +2806,14 @@ empty string is passed in --> <string name="config_wlan_data_service_package" translatable="false"></string> + <!-- Cellular data service class name to bind to by default. If none is specified in an overlay, an + empty string is passed in --> + <string name="config_wwan_data_service_class" translatable="false"></string> + + <!-- IWLAN data service class name to bind to by default. If none is specified in an overlay, an + empty string is passed in --> + <string name="config_wlan_data_service_class" translatable="false"></string> + <bool name="config_networkSamplingWakesDevice">true</bool> <!--From SmsMessage--> @@ -3543,13 +3551,21 @@ <!-- Cellular network service package name to bind to by default. --> <string name="config_wwan_network_service_package" translatable="false">com.android.phone</string> + <!-- Cellular network service class name to bind to by default.--> + <string name="config_wwan_network_service_class" translatable="false"></string> + <!-- IWLAN network service package name to bind to by default. If none is specified in an overlay, an empty string is passed in --> <string name="config_wlan_network_service_package" translatable="false"></string> + <!-- IWLAN network service class name to bind to by default. If none is specified in an overlay, an + empty string is passed in --> + <string name="config_wlan_network_service_class" translatable="false"></string> <!-- Telephony qualified networks service package name to bind to by default. --> <string name="config_qualified_networks_service_package" translatable="false"></string> + <!-- Telephony qualified networks service class name to bind to by default. --> + <string name="config_qualified_networks_service_class" translatable="false"></string> <!-- Wear devices: Controls the radios affected by Activity Mode. --> <string-array name="config_wearActivityModeRadios"> <item>"wifi"</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f8fc95306f4c..e8b2b1f3cecd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -271,9 +271,14 @@ <java-symbol type="bool" name="config_dynamic_bind_ims" /> <java-symbol type="string" name="config_wwan_network_service_package" /> <java-symbol type="string" name="config_wlan_network_service_package" /> + <java-symbol type="string" name="config_wwan_network_service_class" /> + <java-symbol type="string" name="config_wlan_network_service_class" /> <java-symbol type="string" name="config_wwan_data_service_package" /> <java-symbol type="string" name="config_wlan_data_service_package" /> + <java-symbol type="string" name="config_wwan_data_service_class" /> + <java-symbol type="string" name="config_wlan_data_service_class" /> <java-symbol type="string" name="config_qualified_networks_service_package" /> + <java-symbol type="string" name="config_qualified_networks_service_class" /> <java-symbol type="bool" name="config_networkSamplingWakesDevice" /> <java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" /> <java-symbol type="bool" name="config_sip_wifi_only" /> diff --git a/core/tests/benchmarks/src/android/text/format/AndroidTimeVsOthersBenchmark.java b/core/tests/benchmarks/src/android/text/format/AndroidTimeVsOthersBenchmark.java new file mode 100644 index 000000000000..ea244001aae4 --- /dev/null +++ b/core/tests/benchmarks/src/android/text/format/AndroidTimeVsOthersBenchmark.java @@ -0,0 +1,85 @@ +/* + * 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.text.format; + +import com.google.caliper.Benchmark; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; + +public class AndroidTimeVsOthersBenchmark { + + private static final String[] TIMEZONE_IDS = { + "Europe/London", + "America/Los_Angeles", + "Asia/Shanghai", + }; + + @Benchmark + public void toMillis_androidTime(int reps) { + long answer = 0; + for (int i = 0; i < reps; i++) { + String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; + Time time = new Time(timezoneId); + time.set(1, 2, 3, 4, 5, 2010); + answer = time.toMillis(false); + } + // System.out.println(answer); + } + + @Benchmark + public void toMillis_javaTime(int reps) { + long answer = 0; + for (int i = 0; i < reps; i++) { + String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; + LocalDateTime time = LocalDateTime.of(2010, 5 + 1, 4, 3, 2, 1); + ZoneOffset offset = ZoneId.of(timezoneId).getRules().getOffset(time); + answer = time.toInstant(offset).toEpochMilli(); + } + // System.out.println(answer); + } + + @Benchmark + public void toMillis_javaUtil(int reps) { + long answer = 0; + for (int i = 0; i < reps; i++) { + String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; + java.util.TimeZone timeZone = java.util.TimeZone.getTimeZone(timezoneId); + java.util.Calendar calendar = new java.util.GregorianCalendar(timeZone); + calendar.set(2010, 5, 4, 3, 2, 1); + calendar.set(java.util.Calendar.MILLISECOND, 0); + answer = calendar.getTimeInMillis(); + } + // System.out.println(answer); + } + + @Benchmark + public void toMillis_androidIucUtil(int reps) { + long answer = 0; + for (int i = 0; i < reps; i++) { + String timezoneId = TIMEZONE_IDS[i % TIMEZONE_IDS.length]; + android.icu.util.TimeZone timeZone = + android.icu.util.TimeZone.getTimeZone(timezoneId); + android.icu.util.Calendar calendar = new android.icu.util.GregorianCalendar(timeZone); + calendar.set(2010, 5, 4, 3, 2, 1); + calendar.set(android.icu.util.Calendar.MILLISECOND, 0); + answer = calendar.getTimeInMillis(); + } + // System.out.println(answer); + } +} diff --git a/graphics/proto/Android.bp b/graphics/proto/Android.bp index 1d06348fb02f..ddced597759f 100644 --- a/graphics/proto/Android.bp +++ b/graphics/proto/Android.bp @@ -5,7 +5,6 @@ java_library_static { type: "lite", }, srcs: ["game_driver.proto"], - no_framework_libs: true, jarjar_rules: "jarjar-rules.txt", sdk_version: "28", } diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 7ba7828d71c4..5a8579a275df 100644 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -211,7 +211,7 @@ bool ApkAssets::ForEachFile(const std::string& root_path, return false; } - ::ZipString name; + std::string name; ::ZipEntry entry; // We need to hold back directories because many paths will contain them and we want to only @@ -220,7 +220,7 @@ bool ApkAssets::ForEachFile(const std::string& root_path, int32_t result; while ((result = ::Next(cookie, &entry, &name)) == 0) { - StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length); + StringPiece full_file_path(name); StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); if (!leaf_file_path.empty()) { diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index ee5f7783635d..e77ac3df474c 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -39,7 +39,7 @@ using namespace android; class _ZipEntryRO { public: ZipEntry entry; - ZipString name; + std::string_view name; void *cookie; _ZipEntryRO() : cookie(NULL) {} @@ -96,7 +96,7 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const { _ZipEntryRO* data = new _ZipEntryRO; - data->name = ZipString(entryName); + data->name = entryName; const int32_t error = FindEntry(mHandle, entryName, &(data->entry)); if (error) { @@ -194,14 +194,14 @@ int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, size_t bufLen) const { const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); - const uint16_t requiredSize = zipEntry->name.name_length + 1; + const uint16_t requiredSize = zipEntry->name.length() + 1; if (bufLen < requiredSize) { ALOGW("Buffer too short, requires %d bytes for entry name", requiredSize); return requiredSize; } - memcpy(buffer, zipEntry->name.name, requiredSize - 1); + memcpy(buffer, zipEntry->name.data(), requiredSize - 1); buffer[requiredSize - 1] = '\0'; return 0; diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 39ed9a0478b6..2016b5e914cd 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -138,7 +138,7 @@ void JankTracker::finishFrame(const FrameInfo& frame) { (*mGlobalData)->reportJank(); } - bool isTripleBuffered = mSwapDeadline > frame[FrameInfoIndex::IntendedVsync]; + bool isTripleBuffered = (mSwapDeadline - frame[FrameInfoIndex::IntendedVsync]) > (mFrameInterval * 0.1); mSwapDeadline = std::max(mSwapDeadline + mFrameInterval, frame[FrameInfoIndex::IntendedVsync] + mFrameInterval); diff --git a/libs/incident/include/android/os/IncidentReportArgs.h b/libs/incident/include/android/os/IncidentReportArgs.h index ee1e33c43b89..52b555e17ad5 100644 --- a/libs/incident/include/android/os/IncidentReportArgs.h +++ b/libs/incident/include/android/os/IncidentReportArgs.h @@ -36,6 +36,12 @@ const uint8_t DEST_LOCAL = 0; const uint8_t DEST_EXPLICIT = 100; const uint8_t DEST_AUTOMATIC = 200; +// Aliases for the above. +const uint8_t PRIVACY_POLICY_LOCAL = 0; +const uint8_t PRIVACY_POLICY_EXPLICIT = 100; +const uint8_t PRIVACY_POLICY_AUTOMATIC = 200; +const uint8_t PRIVACY_POLICY_UNSET = 255; + class IncidentReportArgs : public Parcelable { public: @@ -48,7 +54,10 @@ public: void setAll(bool all); void setDest(int dest); + void setPrivacyPolicy(int); void addSection(int section); + void setReceiverPkg(const string&); + void setReceiverCls(const string&); void addHeader(const IncidentHeaderProto& headerProto); inline bool all() const { return mAll; } diff --git a/libs/incident/src/IncidentReportArgs.cpp b/libs/incident/src/IncidentReportArgs.cpp index fbc21e558806..fd0d5cc52db3 100644 --- a/libs/incident/src/IncidentReportArgs.cpp +++ b/libs/incident/src/IncidentReportArgs.cpp @@ -194,5 +194,23 @@ IncidentReportArgs::merge(const IncidentReportArgs& that) } } +// stub +void +IncidentReportArgs::setPrivacyPolicy(int) +{ +} + +// stub +void +IncidentReportArgs::setReceiverPkg(const string&) +{ +} + +// stub +void +IncidentReportArgs::setReceiverCls(const string&) +{ +} + } } diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp deleted file mode 100644 index c9183f6c1dd7..000000000000 --- a/packages/CaptivePortalLogin/Android.bp +++ /dev/null @@ -1,43 +0,0 @@ -// -// 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. -// - -java_defaults { - name: "CaptivePortalLoginDefaults", - srcs: ["src/**/*.java"], - sdk_version: "system_current", - min_sdk_version: "28", - static_libs: [ - "android-support-v4", - "metrics-constants-protos", - "captiveportal-lib", - ], - manifest: "AndroidManifest.xml", -} - -android_app { - name: "CaptivePortalLogin", - defaults: ["CaptivePortalLoginDefaults"], - certificate: "networkstack", -} - -// Alternative CaptivePortalLogin signed with the platform cert, to use -// with InProcessNetworkStack. -android_app { - name: "PlatformCaptivePortalLogin", - defaults: ["CaptivePortalLoginDefaults"], - certificate: "platform", - overrides: ["CaptivePortalLogin"], -} diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml deleted file mode 100644 index 0a03425f4699..000000000000 --- a/packages/CaptivePortalLogin/AndroidManifest.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.captiveportallogin" - android:versionCode="11" - android:versionName="Q-initial"> - - <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" /> - <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> - <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> - <uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" /> - <uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" /> - <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" /> - - <application android:label="@string/app_name" - android:icon="@drawable/app_icon" - android:usesCleartextTraffic="true" - android:supportsRtl="true" > - <activity - android:name="com.android.captiveportallogin.CaptivePortalLoginActivity" - android:label="@string/action_bar_label" - android:theme="@style/AppTheme" - android:configChanges="keyboardHidden|orientation|screenSize" > - <intent-filter> - <action android:name="android.net.conn.CAPTIVE_PORTAL"/> - <category android:name="android.intent.category.DEFAULT"/> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/packages/CaptivePortalLogin/OWNERS b/packages/CaptivePortalLogin/OWNERS deleted file mode 100644 index d3836d4c6c57..000000000000 --- a/packages/CaptivePortalLogin/OWNERS +++ /dev/null @@ -1,8 +0,0 @@ -set noparent - -codewiz@google.com -jchalard@google.com -junyulai@google.com -lorenzo@google.com -reminv@google.com -satk@google.com diff --git a/packages/CaptivePortalLogin/assets/quantum_ic_warning_amber_96.png b/packages/CaptivePortalLogin/assets/quantum_ic_warning_amber_96.png Binary files differdeleted file mode 100644 index 08294cee4587..000000000000 --- a/packages/CaptivePortalLogin/assets/quantum_ic_warning_amber_96.png +++ /dev/null diff --git a/packages/CaptivePortalLogin/res/drawable/app_icon.xml b/packages/CaptivePortalLogin/res/drawable/app_icon.xml deleted file mode 100644 index 456ca83f5227..000000000000 --- a/packages/CaptivePortalLogin/res/drawable/app_icon.xml +++ /dev/null @@ -1,26 +0,0 @@ -<!-- - 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. ---> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background> - <color android:color="@*android:color/accent_device_default_light" /> - </background> - <foreground> - <inset - android:drawable="@drawable/maybe_wifi" - android:inset="25%"> - </inset> - </foreground> -</adaptive-icon> diff --git a/packages/CaptivePortalLogin/res/drawable/maybe_wifi.xml b/packages/CaptivePortalLogin/res/drawable/maybe_wifi.xml deleted file mode 100644 index 207aade406ef..000000000000 --- a/packages/CaptivePortalLogin/res/drawable/maybe_wifi.xml +++ /dev/null @@ -1,27 +0,0 @@ -<!-- -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. ---> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="26.0dp" - android:height="24.0dp" - android:viewportWidth="26.0" - android:viewportHeight="24.0"> - <path - android:fillColor="#4DFFFFFF" - android:pathData="M19.1,14l-3.4,0l0,-1.5c0,-1.8 0.8,-2.8 1.5,-3.4C18.1,8.3 19.200001,8 20.6,8c1.2,0 2.3,0.3 3.1,0.8l1.9,-2.3C25.1,6.1 20.299999,2.1 13,2.1S0.9,6.1 0.4,6.5L13,22l0,0l0,0l0,0l0,0l6.5,-8.1L19.1,14z"/> - <path - android:fillColor="#FFFFFFFF" - android:pathData="M19.5,17.799999c0,-0.8 0.1,-1.3 0.2,-1.6c0.2,-0.3 0.5,-0.7 1.1,-1.2c0.4,-0.4 0.7,-0.8 1,-1.1s0.4,-0.8 0.4,-1.2c0,-0.5 -0.1,-0.9 -0.4,-1.2c-0.3,-0.3 -0.7,-0.4 -1.2,-0.4c-0.4,0 -0.8,0.1 -1.1,0.3c-0.3,0.2 -0.4,0.6 -0.4,1.1l-1.9,0c0,-1 0.3,-1.7 1,-2.2c0.6,-0.5 1.5,-0.8 2.5,-0.8c1.1,0 2,0.3 2.6,0.8c0.6,0.5 0.9,1.3 0.9,2.3c0,0.7 -0.2,1.3 -0.6,1.8c-0.4,0.6 -0.9,1.1 -1.5,1.6c-0.3,0.3 -0.5,0.5 -0.6,0.7c-0.1,0.2 -0.1,0.6 -0.1,1L19.5,17.700001zM21.4,21l-1.9,0l0,-1.8l1.9,0L21.4,21z"/> -</vector> diff --git a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml b/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml deleted file mode 100644 index c292323b60c4..000000000000 --- a/packages/CaptivePortalLogin/res/layout/activity_captive_portal_login.xml +++ /dev/null @@ -1,43 +0,0 @@ -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/container" - android:layout_width="match_parent" - android:layout_height="match_parent" - tools:context="com.android.captiveportallogin.CaptivePortalLoginActivity" - tools:ignore="MergeRootFrame" > - - <LinearLayout - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" > - - <FrameLayout - android:layout_width="match_parent" - android:layout_height="4dp" > - - <!-- Eliminates ProgressBar padding by boxing it into a 4dp high container --> - <ProgressBar - android:id="@+id/progress_bar" - style="@android:style/Widget.Material.Light.ProgressBar.Horizontal" - android:indeterminate="false" - android:max="100" - android:progress="0" - android:layout_gravity="center" - android:layout_width="match_parent" - android:layout_height="wrap_content" /> - </FrameLayout> - - <android.support.v4.widget.SwipeRefreshLayout - android:id="@+id/swipe_refresh" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <WebView - android:id="@+id/webview" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_alignParentBottom="false" - android:layout_alignParentRight="false" /> - </android.support.v4.widget.SwipeRefreshLayout> - - </LinearLayout> -</FrameLayout> diff --git a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml deleted file mode 100644 index ce05e78757a1..000000000000 --- a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml +++ /dev/null @@ -1,97 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" > - - <!-- ssl error type --> - <TextView - android:id="@+id/ssl_error_type" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="start" - android:text="SSL_UNKNOWN" - android:layout_marginStart="24dip" - android:layout_marginEnd="24dip" - android:layout_marginBottom="0dip" - android:layout_marginTop="24dip" /> - - <!-- Page info: --> - <TextView - android:id="@+id/page_info" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/page_info" - android:textStyle="bold" - android:layout_marginStart="24dip" - android:layout_marginEnd="24dip" /> - - <!-- Title: --> - <TextView - android:id="@+id/title" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:textStyle="bold" - android:layout_marginStart="24dip" - android:layout_marginEnd="24dip" /> - - <!-- Address: --> - <TextView - android:id="@+id/address_header" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/page_info_address" - android:layout_marginStart="24dip" - android:layout_marginEnd="24dip" /> - - <TextView - android:id="@+id/address" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginStart="24dip" - android:layout_marginEnd="24dip" /> - - <ScrollView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingStart="4dip" - android:paddingEnd="4dip" > - - <!-- certificate view: --> - <LinearLayout - android:id="@+id/certificate_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_marginBottom="16dip" > - <TextView - android:id="@+id/ssl_error_msg" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?android:attr/textAppearanceSmall" - android:layout_marginStart="20dip" - android:layout_marginEnd="20dip" - android:gravity="center_vertical" - android:layout_marginBottom="4dip" - android:layout_marginTop="16dip" /> - </LinearLayout> - - </ScrollView> - -</LinearLayout> diff --git a/packages/CaptivePortalLogin/res/menu/captive_portal_login.xml b/packages/CaptivePortalLogin/res/menu/captive_portal_login.xml deleted file mode 100644 index 1a88c5c846cc..000000000000 --- a/packages/CaptivePortalLogin/res/menu/captive_portal_login.xml +++ /dev/null @@ -1,15 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - tools:context="com.android.captiveportallogin.CaptivePortalLoginActivity" > - <item - android:id="@+id/action_do_not_use_network" - android:orderInCategory="100" - android:showAsAction="never" - android:title="@string/action_do_not_use_network"/> - <item - android:id="@+id/action_use_network" - android:orderInCategory="200" - android:showAsAction="never" - android:title="@string/action_use_network"/> - -</menu> diff --git a/packages/CaptivePortalLogin/res/values-af/strings.xml b/packages/CaptivePortalLogin/res/values-af/strings.xml deleted file mode 100644 index cf4dc824f597..000000000000 --- a/packages/CaptivePortalLogin/res/values-af/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortal-aanmelding"</string> - <string name="action_use_network" msgid="6076184727448466030">"Gebruik hierdie netwerk nes dit is"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Moenie hierdie netwerk gebruik nie"</string> - <string name="action_bar_label" msgid="917235635415966620">"Meld by netwerk aan"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Meld aan by %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Die netwerk waarby jy probeer aansluit, het sekuriteitkwessies."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Byvoorbeeld, die aanmeldbladsy behoort dalk nie aan die organisasie wat gewys word nie."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Gaan in elk geval deur blaaier voort"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Bladsy-inligting"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sekuriteitswaarskuwing"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Bekyk sertifikaat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Hierdie sertifikaat is nie van \'n betroubare owerheid nie."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Die naam van die werf kom nie ooreen met die naam op die sertifikaat nie."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Hierdie sertifikaat het verval."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Hierdie sertifikaat is nog nie geldig nie."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Hierdie sertifikaat het \'n ongeldige datum."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Hierdie sertifikaat is ongeldig."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Onbekende sertifikaatfout."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-am/strings.xml b/packages/CaptivePortalLogin/res/values-am/strings.xml deleted file mode 100644 index cdcb5a54daed..000000000000 --- a/packages/CaptivePortalLogin/res/values-am/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ይህን አውታረ መረብ እንዳለ ተጠቀምበት"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ይህን አውታረ መረብ አትጠቀምበት"</string> - <string name="action_bar_label" msgid="917235635415966620">"ወደ አውታረ መረብ በመለያ ይግቡ"</string> - <string name="action_bar_title" msgid="5645564790486983117">"ወደ %1$s ይግቡ"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"ለመቀላቀል እየሞከሩ ያሉት አውታረ መረብ የደህንነት ችግሮች አሉበት።"</string> - <string name="ssl_error_example" msgid="647898534624078900">"ለምሳሌ፣ የመግቢያ ገጹ የሚታየው ድርጅት ላይሆን ይችላል።"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ለማንኛውም በአሳሽ በኩል ይቀጥሉ"</string> - <string name="ok" msgid="1509280796718850364">"እሺ"</string> - <string name="page_info" msgid="4048529256302257195">"የገፅ መረጃ"</string> - <string name="page_info_address" msgid="2222306609532903254">"አድራሻ:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"የደህንነት ቅንብሮች"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"ምስክሮች ይመልከቱ"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"ይህ ምስክር ከታማኝ ቦታ አይደለም።"</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"የጣቢያው ስም ከምስክር ወረቀቱ ስም ጋር አይዛመድም።"</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"ይህ ምስክር ጊዜው አልፏል"</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"ይህ ምስክር ገና ትክክል አይደለም።"</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"ይህ ምስክር ትክክለኛ ቀን አለው።"</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"ይህ ምስክር ትክክል ያልሆነ ነው።"</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"ያልታወቀ የምስክር ስህተት።"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ar/strings.xml b/packages/CaptivePortalLogin/res/values-ar/strings.xml deleted file mode 100644 index 799b8aa1f3a8..000000000000 --- a/packages/CaptivePortalLogin/res/values-ar/strings.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"استخدام هذه الشبكة كما هي"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"عدم استخدام هذه الشبكة"</string> - <string name="action_bar_label" msgid="917235635415966620">"تسجيل الدخول إلى الشبكة"</string> - <!-- String.format failed for translation --> - <!-- no translation found for action_bar_title (5645564790486983117) --> - <skip /> - <string name="ssl_error_warning" msgid="6653188881418638872">"الشبكة التي تحاول الانضمام إليها بها مشكلات أمنية."</string> - <string name="ssl_error_example" msgid="647898534624078900">"على سبيل المثال، قد لا تنتمي صفحة تسجيل الدخول إلى المنظمة المعروضة."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"المتابعة على أي حال عبر المتصفح"</string> - <string name="ok" msgid="1509280796718850364">"موافق"</string> - <string name="page_info" msgid="4048529256302257195">"معلومات الصفحة"</string> - <string name="page_info_address" msgid="2222306609532903254">"العنوان:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"تحذير أمان"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"عرض الشهادة"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"هذه الشهادة ليست من جهة موثوق بها."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"لا يتطابق اسم الموقع مع الاسم على الشهادة."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"انتهت صلاحية هذه الشهادة."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"هذه الشهادة ليست صالحة بعد."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"تشتمل هذه الشهادة على تاريخ غير صالح."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"هذه الشهادة غير صالحة."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"حدث خطأ غير معروف بالشهادة."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-as/strings.xml b/packages/CaptivePortalLogin/res/values-as/strings.xml deleted file mode 100644 index 94c314772483..000000000000 --- a/packages/CaptivePortalLogin/res/values-as/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"এই নেটৱৰ্কটো এইদৰে ব্যৱহাৰ কৰক"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"এই নেটৱৰ্কটো ব্যৱহাৰ নকৰিব"</string> - <string name="action_bar_label" msgid="917235635415966620">"নেটৱৰ্কত ছাইন ইন কৰক"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$st ছাইন ইন কৰক"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"আপুনি সংযোগ কৰিবলৈ চেষ্টা কৰি থকা নেটৱৰ্কটোত সুৰক্ষাজনিত সমস্যা আছে।"</string> - <string name="ssl_error_example" msgid="647898534624078900">"উদাহৰণস্বৰূপে, আপোনাক দেখুওৱা লগ ইনৰ পৃষ্ঠাটো প্ৰতিষ্ঠানটোৰ নিজা নহ\'বও পাৰে।"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"তথাপি ব্ৰাউজাৰৰ জৰিয়তে অব্যাহত ৰাখক"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-az/strings.xml b/packages/CaptivePortalLogin/res/values-az/strings.xml deleted file mode 100644 index 44b406dfd19b..000000000000 --- a/packages/CaptivePortalLogin/res/values-az/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Bu şəbəkəni olduğu kimi istifadə edin"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Bu şəbəkəni istifadə etməyin"</string> - <string name="action_bar_label" msgid="917235635415966620">"Şəbəkəyə daxil olun"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Daxil olun: %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Qoşulmaq istədiyiniz şəbəkənin təhlükəsizlik problemləri var."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Məsələn, giriş səhifəsi göstərilən təşkilata aid olmaya bilər."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Hər bir halda brazuer ilə davam edin"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml b/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml deleted file mode 100644 index f2a6e076ce7b..000000000000 --- a/packages/CaptivePortalLogin/res/values-b+sr+Latn/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Koristi ovu mrežu takvu kakva je"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne koristi ovu mrežu"</string> - <string name="action_bar_label" msgid="917235635415966620">"Prijavi me na mrežu"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Prijavite se u: %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj pokušavate da se pridružite ima bezbednosnih problema."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Na primer, stranica za prijavljivanje možda ne pripada prikazanoj organizaciji."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi preko pregledača"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-be/strings.xml b/packages/CaptivePortalLogin/res/values-be/strings.xml deleted file mode 100644 index 09ed1dec88ab..000000000000 --- a/packages/CaptivePortalLogin/res/values-be/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Выкарыстоўваць гэтую сетку як ёсць"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Не выкарыстоўваць гэту сетку"</string> - <string name="action_bar_label" msgid="917235635415966620">"Увайсці ў сетку"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Увайсці ў %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"У сеткі, да якой вы спрабуеце далучыцца, ёсць праблемы з бяспекай."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Напрыклад, старонка ўваходу можа не належаць указанай арганізацыі."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Усё роўна працягнуць праз браўзер"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-bg/strings.xml b/packages/CaptivePortalLogin/res/values-bg/strings.xml deleted file mode 100644 index 4dd8aa0c536c..000000000000 --- a/packages/CaptivePortalLogin/res/values-bg/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Директно използване на тази мрежа"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Без използване на тази мрежа"</string> - <string name="action_bar_label" msgid="917235635415966620">"Вход в мрежата"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Влезте в/ъв %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Мрежата, към която опитвате да се присъедините, има проблеми със сигурността."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Например страницата за вход може да не принадлежи на показаната организация."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Продължаване през браузър въпреки това"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Данни за страницата"</string> - <string name="page_info_address" msgid="2222306609532903254">"Адрес:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Предупреждение относно защитата"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Преглед на сертификата"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Сертификатът не е от надежден орган."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Името на сайта не съответства на името в сертификата."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Сертификатът е изтекъл."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Сертификатът още не е валиден."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Този сертификат е с невалидна дата."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Този сертификат е невалиден."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Неизвестна грешка в сертификата."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-bn/strings.xml b/packages/CaptivePortalLogin/res/values-bn/strings.xml deleted file mode 100644 index fb703cfaadc9..000000000000 --- a/packages/CaptivePortalLogin/res/values-bn/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"যেভাবে আছে সেভাবেই এই নেটওয়ার্ক ব্যবহার করুন"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"এই নেটওয়ার্ক ব্যবহার করবেন না"</string> - <string name="action_bar_label" msgid="917235635415966620">"নেটওয়ার্কে সাইন-ইন করুন"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s তে সাইন-ইন করুন"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"আপনি যে নেটওয়ার্কে যোগ দেওয়ার চেষ্টা করছেন তাতে নিরাপত্তার সমস্যা আছে।"</string> - <string name="ssl_error_example" msgid="647898534624078900">"উদাহরণস্বরূপ, লগ-ইন পৃষ্ঠাটি প্রদর্শিত প্রতিষ্ঠানের অন্তর্গত নাও হতে পারে৷"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"যাই হোক না কেন ব্রাউজারের মাধ্যমে অবিরত রাখুন"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Sideinfo"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhetsadvarsel"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis sertifikat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikatet er ikke fra en pålitelig myndighet."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikatet er utløpt."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikatet er ikke gyldig ennå."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette sertifikatet har en ugyldig dato."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette sertifikatet er ugyldig."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukjent sertifikatfeil."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-bs/strings.xml b/packages/CaptivePortalLogin/res/values-bs/strings.xml deleted file mode 100644 index 10be0e529449..000000000000 --- a/packages/CaptivePortalLogin/res/values-bs/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"Prijava na zaštitnom portalu"</string> - <string name="action_use_network" msgid="6076184727448466030">"Koristi ovu mrežu kakva jeste"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne koristi ovu mrežu"</string> - <string name="action_bar_label" msgid="917235635415966620">"Prijava na mrežu"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Prijava na %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj pokušavate pristupiti ima sigurnosnih problema."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Naprimjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi preko preglednika"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ca/strings.xml b/packages/CaptivePortalLogin/res/values-ca/strings.xml deleted file mode 100644 index a2c9ed809ba3..000000000000 --- a/packages/CaptivePortalLogin/res/values-ca/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Fes servir aquesta xarxa tal com està."</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"No facis servir aquesta xarxa."</string> - <string name="action_bar_label" msgid="917235635415966620">"Inicia la sessió a la xarxa"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Inicia la sessió a %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"La xarxa a què et vols connectar té problemes de seguretat."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Per exemple, la pàgina d\'inici de sessió podria no pertànyer a l\'organització que es mostra."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continua igualment mitjançant el navegador"</string> - <string name="ok" msgid="1509280796718850364">"D\'acord"</string> - <string name="page_info" msgid="4048529256302257195">"Informació de la pàgina"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adreça:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertiment de seguretat"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualitza el certificat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Aquest certificat no és d\'una autoritat de confiança."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nom del lloc no coincideix amb el del certificat."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Aquest certificat ha caducat."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Aquest certificat encara no és vàlid."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Aquest certificat té una data no vàlida."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Aquest certificat no és vàlid."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificat desconegut."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-cs/strings.xml b/packages/CaptivePortalLogin/res/values-cs/strings.xml deleted file mode 100644 index be649a50f26c..000000000000 --- a/packages/CaptivePortalLogin/res/values-cs/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Použít tuto síť tak, jak je"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Tuto síť nepoužívat"</string> - <string name="action_bar_label" msgid="917235635415966620">"Přihlásit se k síti"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Přihlaste se k síti %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Síť, ke které se pokoušíte připojit, má bezpečnostní problémy."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Například přihlašovací stránka nemusí patřit do zobrazované organizace."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Přesto pokračovat prostřednictvím prohlížeče"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Informace o stránce"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozornění zabezpečení"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zobrazit certifikát"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Tento certifikát nepochází od důvěryhodné autority."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Název webu se neshoduje s názvem uvedeným v certifikátu."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Platnost certifikátu vypršela."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Tento certifikát ještě není platný."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Datum tohoto certifikátu není platné."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Tento certifikát je neplatný."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznámá chyba certifikátu."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-da/strings.xml b/packages/CaptivePortalLogin/res/values-da/strings.xml deleted file mode 100644 index 8183105a1aff..000000000000 --- a/packages/CaptivePortalLogin/res/values-da/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"Login til captive portal"</string> - <string name="action_use_network" msgid="6076184727448466030">"Brug dette netværk, som det er"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Brug ikke dette netværk"</string> - <string name="action_bar_label" msgid="917235635415966620">"Log ind på netværk"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Log ind på %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Der er sikkerhedsproblemer på det netværk, du forsøger at logge ind på."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Det er f.eks. ikke sikkert, at loginsiden tilhører den anførte organisation."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Fortsæt alligevel via browseren"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Sideoplysninger"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhedsadvarsel"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis certifikat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dette certifikat stammer ikke fra en troværdig autoritet."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på websitet stemmer ikke overens med navnet på certifikatet."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Dette certifikat er udløbet."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dette certifikat er endnu ikke gyldigt."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette certifikat har en ugyldig dato."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette certifikat er ugyldigt."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukendt fejl i certifikatet."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-de/strings.xml b/packages/CaptivePortalLogin/res/values-de/strings.xml deleted file mode 100644 index 68862bf3e2e1..000000000000 --- a/packages/CaptivePortalLogin/res/values-de/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Dieses Netzwerk im Istzustand verwenden"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Dieses Netzwerk nicht verwenden"</string> - <string name="action_bar_label" msgid="917235635415966620">"Im Netzwerk anmelden"</string> - <string name="action_bar_title" msgid="5645564790486983117">"In %1$s anmelden"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Im Netzwerk, zu dem du eine Verbindung herstellen möchtest, liegen Sicherheitsprobleme vor."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Beispiel: Die Log-in-Seite gehört möglicherweise nicht zur angezeigten Organisation."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Trotzdem in einem Browser fortfahren"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Seiteninfo"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sicherheitswarnung"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zertifikat ansehen"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dieses Zertifikat wurde nicht von einer vertrauenswürdigen Stelle ausgegeben."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Name der Website stimmt nicht mit dem Namen auf dem Zertifikat überein."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Dieses Zertifikat ist abgelaufen."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dieses Zertifikat ist noch nicht gültig."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dieses Zertifikat weist ein ungültiges Datum auf."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Dieses Zertifikat ist ungültig."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Unbekannter Zertifikatfehler"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-el/strings.xml b/packages/CaptivePortalLogin/res/values-el/strings.xml deleted file mode 100644 index 16bf6e22761d..000000000000 --- a/packages/CaptivePortalLogin/res/values-el/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Χρήση αυτού του δικτύου ως έχει"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Να μη χρησιμοποιείται αυτό το δίκτυο"</string> - <string name="action_bar_label" msgid="917235635415966620">"Σύνδεση στο δίκτυο"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Συνδεθείτε στο %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Παρουσιάζονται προβλήματα ασφάλειας στο δίκτυο στο οποίο προσπαθείτε να συνδεθείτε."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Για παράδειγμα, η σελίδα σύνδεσης ενδέχεται να μην ανήκει στον οργανισμό που εμφανίζεται."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Συνέχεια ούτως ή άλλως μέσω του προγράμματος περιήγησης"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Πληροφορίες σελίδας"</string> - <string name="page_info_address" msgid="2222306609532903254">"Διεύθυνση:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Προειδοποίηση ασφαλείας"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Προβολή πιστοποιητικού"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Αυτό το πιστοποιητικό δεν προέρχεται από αξιόπιστη αρχή."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Το όνομα του ιστότοπου δεν αντιστοιχεί με το όνομα στο πιστοποιητικό."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Αυτό το πιστοποιητικό έχει λήξει."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Αυτό το πιστοποιητικό δεν είναι έγκυρο ακόμα."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Αυτό το πιστοποιητικό δεν έχει έγκυρη ημερομηνία."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Αυτό το πιστοποιητικό δεν είναι έγκυρο."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Άγνωστο σφάλμα πιστοποιητικού."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml b/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml deleted file mode 100644 index 2e8d1f082d1f..000000000000 --- a/packages/CaptivePortalLogin/res/values-en-rAU/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string> - <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string> - <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml b/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml deleted file mode 100644 index 2e8d1f082d1f..000000000000 --- a/packages/CaptivePortalLogin/res/values-en-rCA/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string> - <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string> - <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml deleted file mode 100644 index f940299af6a8..000000000000 --- a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string> - <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string> - <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Page info"</string> - <string name="page_info_address" msgid="2222306609532903254">"Address:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Security warning"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"View certificate"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"This certificate isn\'t from a trusted authority."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"The name of the site doesn\'t match the name on the certificate."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"This certificate has expired."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"This certificate isn\'t valid yet."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"This certificate has an invalid date."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"This certificate is invalid."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Unknown certificate error."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml deleted file mode 100644 index f940299af6a8..000000000000 --- a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string> - <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"The network that you’re trying to join has security issues."</string> - <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page might not belong to the organisation shown."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Page info"</string> - <string name="page_info_address" msgid="2222306609532903254">"Address:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Security warning"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"View certificate"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"This certificate isn\'t from a trusted authority."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"The name of the site doesn\'t match the name on the certificate."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"This certificate has expired."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"This certificate isn\'t valid yet."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"This certificate has an invalid date."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"This certificate is invalid."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Unknown certificate error."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml b/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml deleted file mode 100644 index 6d29fd97c34a..000000000000 --- a/packages/CaptivePortalLogin/res/values-en-rXC/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string> - <string name="action_bar_label" msgid="917235635415966620">"Sign in to network"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Sign in to %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"The network you’re trying to join has security issues."</string> - <string name="ssl_error_example" msgid="647898534624078900">"For example, the login page may not belong to the organization shown."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continue anyway via browser"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml b/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml deleted file mode 100644 index c01166474074..000000000000 --- a/packages/CaptivePortalLogin/res/values-es-rUS/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Usar esta red como está"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"No usar esta red"</string> - <string name="action_bar_label" msgid="917235635415966620">"Acceder a la red"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Acceder a %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas conectarte tiene problemas de seguridad."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de acceso no pertenezca a la organización que aparece."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos desde el navegador"</string> - <string name="ok" msgid="1509280796718850364">"Aceptar"</string> - <string name="page_info" msgid="4048529256302257195">"Información de la página"</string> - <string name="page_info_address" msgid="2222306609532903254">"Dirección:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertencia de seguridad"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado no proviene de una autoridad confiable."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nombre del sitio no coincide con el nombre del certificado."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado ha expirado."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado aún no es válido."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La fecha de este certificado no es válida."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado no es válido."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificado desconocido"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-es/strings.xml b/packages/CaptivePortalLogin/res/values-es/strings.xml deleted file mode 100644 index 65244e7e9156..000000000000 --- a/packages/CaptivePortalLogin/res/values-es/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Utilizar esta red tal cual"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"No utilizar esta red"</string> - <string name="action_bar_label" msgid="917235635415966620">"Iniciar sesión en la red"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Inicia sesión en %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"La red a la que intentas unirte tiene problemas de seguridad."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Por ejemplo, es posible que la página de inicio de sesión no pertenezca a la organización mostrada."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar de todos modos a través del navegador"</string> - <string name="ok" msgid="1509280796718850364">"Aceptar"</string> - <string name="page_info" msgid="4048529256302257195">"Información de la página"</string> - <string name="page_info_address" msgid="2222306609532903254">"Dirección:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Advertencia de seguridad"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado no procede de una entidad de certificación de confianza."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"El nombre del sitio no coincide con el del certificado."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado ha caducado."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado aún no es válido."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La fecha de este certificado no es válida."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado no es válido."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Error de certificado desconocido"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-et/strings.xml b/packages/CaptivePortalLogin/res/values-et/strings.xml deleted file mode 100644 index e4c4c9801d5c..000000000000 --- a/packages/CaptivePortalLogin/res/values-et/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Kasuta seda võrku olemasoleval kujul"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ära kasuta seda võrku"</string> - <string name="action_bar_label" msgid="917235635415966620">"Logi võrku sisse"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Logige sisse: %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Võrgul, millega üritate ühenduse luua, on turvaprobleeme."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Näiteks ei pruugi sisselogimisleht kuuluda kuvatavale organisatsioonile."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Jätka siiski brauseris"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Lehe teave"</string> - <string name="page_info_address" msgid="2222306609532903254">"Aadress:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Turvahoiatus"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Kuva sertifikaat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"See sertifikaat ei pärine usaldusväärselt asutuselt."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Saidi nimi ei vasta sertifikaadil olevale nimele."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"See sertifikaat on aegunud."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"See sertifikaat pole veel kehtiv."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Sellel sertifikaadil on kehtetu kuupäev."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"See sertifikaat on kehtetu."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Tundmatu sertifikaadiviga."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-eu/strings.xml b/packages/CaptivePortalLogin/res/values-eu/strings.xml deleted file mode 100644 index 8925aac3cb9e..000000000000 --- a/packages/CaptivePortalLogin/res/values-eu/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Erabili sare hau bere horretan"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ez erabili sare hau"</string> - <string name="action_bar_label" msgid="917235635415966620">"Hasi saioa sarean"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Hasi saioa %1$s sarean"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Erabili nahi duzun sareak segurtasun-arazoak ditu."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Adibidez, baliteke saioa hasteko orria adierazitako erakundearena ez izatea."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Jarraitu arakatzailearen bidez, halere"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-fa/strings.xml b/packages/CaptivePortalLogin/res/values-fa/strings.xml deleted file mode 100644 index 27b9b7f15fab..000000000000 --- a/packages/CaptivePortalLogin/res/values-fa/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"از این شبکه همانطور که هست استفاده شود"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"از این شبکه استفاده نشود"</string> - <string name="action_bar_label" msgid="917235635415966620">"ورود به سیستم شبکه"</string> - <string name="action_bar_title" msgid="5645564790486983117">"ورود به سیستم %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"شبکهای که میخواهید به آن بپیوندید مشکلات امنیتی دارد."</string> - <string name="ssl_error_example" msgid="647898534624078900">"به عنوان مثال، صفحه ورود به سیستم ممکن است متعلق به سازمان نشان داده شده نباشد."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"در هر صورت از طریق مرورگر ادامه یابد"</string> - <string name="ok" msgid="1509280796718850364">"تأیید"</string> - <string name="page_info" msgid="4048529256302257195">"اطلاعات صفحه"</string> - <string name="page_info_address" msgid="2222306609532903254">"آدرس:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"اخطار امنیتی"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"مشاهده گواهی"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"این گواهی از یک منبع مورد اطمینان صادر نشده است."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"نام سایت با نام موجود در گواهی مطابقت ندارد."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"این گواهی منقضی شده است."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"این گواهی هنوز معتبر نیست."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"تاریخ این گواهی نامعتبر است."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"این گواهی نامعتبر است."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"خطای ناشناخته در گواهی."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-fi/strings.xml b/packages/CaptivePortalLogin/res/values-fi/strings.xml deleted file mode 100644 index 8086fbf96088..000000000000 --- a/packages/CaptivePortalLogin/res/values-fi/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Käytä tätä verkkoa sellaisenaan"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Älä käytä tätä verkkoa"</string> - <string name="action_bar_label" msgid="917235635415966620">"Kirjaudu verkkoon"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Kirjaudu sisään kohteeseen %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Verkossa, johon yrität muodostaa yhteyttä, on turvallisuusongelmia."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Kirjautumissivu ei välttämättä kuulu näytetylle organisaatiolle."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Jatka silti selaimen kautta."</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Sivun tiedot"</string> - <string name="page_info_address" msgid="2222306609532903254">"Osoite:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Suojausvaroitus"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Näytä varmenne"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Varmenteen myöntäjä ei ole luotettava taho."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Sivuston nimi ei vastaa varmenteessa olevaa nimeä."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Varmenne ei ole enää voimassa."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Varmenne ei ole vielä voimassa."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Varmenteen päiväys ei kelpaa."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Varmenne on virheellinen."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Tuntematon varmennevirhe."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml b/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml deleted file mode 100644 index a7525a542825..000000000000 --- a/packages/CaptivePortalLogin/res/values-fr-rCA/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Utiliser ce réseau tel quel"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne pas utiliser ce réseau"</string> - <string name="action_bar_label" msgid="917235635415966620">"Connectez-vous au réseau"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Connexion à %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Par exemple, la page de connexion pourrait ne pas appartenir à l\'organisation représentée."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continuer quand même dans un navigateur"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-fr/strings.xml b/packages/CaptivePortalLogin/res/values-fr/strings.xml deleted file mode 100644 index 39fc5692bc21..000000000000 --- a/packages/CaptivePortalLogin/res/values-fr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Utiliser ce réseau tel quel"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne pas utiliser ce réseau"</string> - <string name="action_bar_label" msgid="917235635415966620">"Se connecter au réseau"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Se connecter à %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Le réseau que vous essayez de rejoindre présente des problèmes de sécurité."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Par exemple, la page de connexion peut ne pas appartenir à l\'organisation représentée."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continuer quand même dans le navigateur"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Infos sur la page"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresse :"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avertissement de sécurité"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Afficher le certificat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ce certificat provient d\'une autorité non approuvée."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Le nom du site ne correspond pas au nom indiqué dans le certificat."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Le certificat a expiré."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Ce certificat n\'est pas encore valide."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"La date de ce certificat n\'est pas valide."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Ce certificat n\'est pas valide."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Erreur : Certificat inconnu."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-gl/strings.xml b/packages/CaptivePortalLogin/res/values-gl/strings.xml deleted file mode 100644 index 657828504287..000000000000 --- a/packages/CaptivePortalLogin/res/values-gl/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Utilizar esta rede tal como está"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Non utilizar esta rede"</string> - <string name="action_bar_label" msgid="917235635415966620">"Inicia sesión na rede"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Iniciar sesión en %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"A rede á que tentas unirte ten problemas de seguranza."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, é posible que a páxina de inicio de sesión non pertenza á organización que se mostra."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar igualmente co navegador"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-gu/strings.xml b/packages/CaptivePortalLogin/res/values-gu/strings.xml deleted file mode 100644 index c15eca46e547..000000000000 --- a/packages/CaptivePortalLogin/res/values-gu/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"આ નેટવર્કનો જેમનો તેમ ઉપયોગ કરો"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"આ નેટવર્કનો ઉપયોગ કરશો નહીં"</string> - <string name="action_bar_label" msgid="917235635415966620">"નેટવર્ક પર સાઇન ઇન કરો"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$sમાં સઇન ઇન કરો"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"તમે જોડાવાનો પ્રયાસ કરી રહ્યાં છો તે નેટવર્કમાં સુરક્ષા સમસ્યાઓ છે."</string> - <string name="ssl_error_example" msgid="647898534624078900">"ઉદાહરણ તરીકે, લોગિન પૃષ્ઠ દર્શાવેલ સંસ્થાનું હોઈ શકતું નથી."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"બ્રાઉઝર મારફતે કોઈપણ રીતે ચાલુ રાખો"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-hi/strings.xml b/packages/CaptivePortalLogin/res/values-hi/strings.xml deleted file mode 100644 index d924fffb8c1a..000000000000 --- a/packages/CaptivePortalLogin/res/values-hi/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"इस नेटवर्क का उपयोग जैसा है वैसा ही करें"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"इस नेटवर्क का उपयोग न करें"</string> - <string name="action_bar_label" msgid="917235635415966620">"नेटवर्क में साइन इन करें"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s में साइन इन करें"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"आप जिस नेटवर्क में शामिल होने का प्रयास कर रहे हैं उसमें सुरक्षा समस्याएं हैं."</string> - <string name="ssl_error_example" msgid="647898534624078900">"उदाहरण के लिए, हो सकता है कि लॉगिन पृष्ठ दिखाए गए संगठन से संबद्ध ना हो."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ब्राउज़र के द्वारा फिर जारी रखें"</string> - <string name="ok" msgid="1509280796718850364">"ठीक"</string> - <string name="page_info" msgid="4048529256302257195">"पृष्ठ जानकारी"</string> - <string name="page_info_address" msgid="2222306609532903254">"पता:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"सुरक्षा चेतावनी"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"प्रमाणपत्र देखें"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"यह प्रमाणपत्र किसी विश्वस्त प्राधिकारी का नहीं है."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"साइट का नाम, प्रमाणपत्र के नाम से मिलान नहीं करता."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"इस प्रमाणपत्र की समय सीमा समाप्त हो गई है."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"यह प्रमाणपत्र अभी तक मान्य नहीं है."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"इस प्रमाणपत्र में एक अमान्य दिनांक है."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"यह प्रमाणपत्र अमान्य है."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"अज्ञात प्रमाणपत्र त्रुटि."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-hr/strings.xml b/packages/CaptivePortalLogin/res/values-hr/strings.xml deleted file mode 100644 index 11b1dd3f50e9..000000000000 --- a/packages/CaptivePortalLogin/res/values-hr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Upotrebljavaj ovu mrežu u zatečenom stanju"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne upotrebljavaj ovu mrežu"</string> - <string name="action_bar_label" msgid="917235635415966620">"Prijava na mrežu"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Prijavite se na %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Mreža kojoj se pokušavate pridružiti ima sigurnosne poteškoće."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Na primjer, stranica za prijavu možda ne pripada prikazanoj organizaciji."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Ipak nastavi putem preglednika"</string> - <string name="ok" msgid="1509280796718850364">"U redu"</string> - <string name="page_info" msgid="4048529256302257195">"Informacije o stranici"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozorenje o sigurnosti"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Prikaži certifikat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ovaj certifikat ne potječe iz pouzdanog izvora."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Naziv web-lokacije ne podudara se s nazivom na certifikatu."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Ovaj je certifikat istekao."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Ovaj certifikat još nije važeći."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Ovaj certifikat ima nevažeći datum."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Ovaj certifikat nije valjan."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Nepoznata pogreška certifikata."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-hu/strings.xml b/packages/CaptivePortalLogin/res/values-hu/strings.xml deleted file mode 100644 index 145e2abd0906..000000000000 --- a/packages/CaptivePortalLogin/res/values-hu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Hálózat használata jelen állapotában"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne használja ezt a hálózatot"</string> - <string name="action_bar_label" msgid="917235635415966620">"Bejelentkezés a hálózatba"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Bejelentkezés a következőbe: %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Biztonsági problémák vannak azzal a hálózattal, amelyhez csatlakozni szeretne."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Például lehet, hogy a bejelentkezési oldal nem a megjelenített szervezethez tartozik."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Folytatás ennek ellenére böngészőn keresztül"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Oldaladatok"</string> - <string name="page_info_address" msgid="2222306609532903254">"Cím:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Biztonsági figyelmeztetés"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tanúsítvány megtekintése"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ez a tanúsítvány nem hiteles tanúsítványkibocsátótól származik."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"A webhely neve nem egyezik a tanúsítványon lévő névvel."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"A tanúsítvány lejárt."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"A tanúsítvány még nem érvényes."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"A tanúsítvány dátuma érvénytelen."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Ez a tanúsítvány érvénytelen."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Ismeretlen tanúsítványhiba."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-hy/strings.xml b/packages/CaptivePortalLogin/res/values-hy/strings.xml deleted file mode 100644 index a0ee8626475d..000000000000 --- a/packages/CaptivePortalLogin/res/values-hy/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Օգտագործել այս ցանցն ինչպես կա"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Չօգտագործել այս ցանցը"</string> - <string name="action_bar_label" msgid="917235635415966620">"Մուտք գործել ցանց"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Մուտք գործել %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Ցանցը, որին փորձում եք միանալ, անվտանգության խնդիրներ ունի:"</string> - <string name="ssl_error_example" msgid="647898534624078900">"Օրինակ՝ մուտքի էջը կարող է ցուցադրված կազմակերպության էջը չլինել:"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Շարունակել այնուամենայնիվ դիտարկիչի միջոցով"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-in/strings.xml b/packages/CaptivePortalLogin/res/values-in/strings.xml deleted file mode 100644 index e5b4eb43517b..000000000000 --- a/packages/CaptivePortalLogin/res/values-in/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Gunakan jaringan ini sebagaimana adanya"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Jangan gunakan jaringan ini"</string> - <string name="action_bar_label" msgid="917235635415966620">"Masuk ke jaringan"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Login ke %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Jaringan yang ingin Anda masuki mengalami masalah keamanan."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Misalnya, halaman masuk mungkin bukan milik organisasi yang ditampilkan."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Tetap lanjutkan melalui browser"</string> - <string name="ok" msgid="1509280796718850364">"Oke"</string> - <string name="page_info" msgid="4048529256302257195">"Info laman"</string> - <string name="page_info_address" msgid="2222306609532903254">"Alamat:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Peringatan sertifikat"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Lihat sertifikat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikat ini tidak berasal dari otoritas tepercaya."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nama situs tidak cocok dengan nama pada sertifikat."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikat ini telah kedaluwarsa."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikat ini belum valid."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Tanggal sertifikat ini tidak valid."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Sertifikat ini tidak valid."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Kesalahan sertifikat tak dikenal."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-is/strings.xml b/packages/CaptivePortalLogin/res/values-is/strings.xml deleted file mode 100644 index 8fde24b665c3..000000000000 --- a/packages/CaptivePortalLogin/res/values-is/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Nota þetta net óbreytt"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ekki nota þetta net"</string> - <string name="action_bar_label" msgid="917235635415966620">"Skrá inn á net"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Skrá inn á %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Öryggisvandamál eru á netinu sem þú ert að reyna að tengjast."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Til dæmis getur verið að innskráningarsíðan tilheyri ekki fyrirtækinu sem birtist."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Halda samt áfram í vafra"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-it/strings.xml b/packages/CaptivePortalLogin/res/values-it/strings.xml deleted file mode 100644 index 2cc4038fbe63..000000000000 --- a/packages/CaptivePortalLogin/res/values-it/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Utilizza questa rete così com\'è"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Non utilizzare questa rete"</string> - <string name="action_bar_label" msgid="917235635415966620">"Accedi alla rete"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Accedi a %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"La rete a cui stai tentando di accedere presenta problemi di sicurezza."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Ad esempio, la pagina di accesso potrebbe non appartenere all\'organizzazione indicata."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continua comunque dal browser"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Info pagina"</string> - <string name="page_info_address" msgid="2222306609532903254">"Indirizzo:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avviso di sicurezza"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualizza certificato"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Questo certificato non proviene da un\'autorità attendibile."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Il nome del sito non corrisponde al nome nel certificato."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Il certificato è scaduto."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Questo certificato non è ancora valido."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Questo certificato presenta una data non valida."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Questo certificato non è valido."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Errore certificato sconosciuto."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-iw/strings.xml b/packages/CaptivePortalLogin/res/values-iw/strings.xml deleted file mode 100644 index 527e69247104..000000000000 --- a/packages/CaptivePortalLogin/res/values-iw/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"השתמש ברשת זו כפי שהיא"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"אל תשתמש ברשת זו"</string> - <string name="action_bar_label" msgid="917235635415966620">"היכנס לרשת"</string> - <string name="action_bar_title" msgid="5645564790486983117">"כניסה אל %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"יש בעיות אבטחה ברשת שאליה אתה מנסה להתחבר."</string> - <string name="ssl_error_example" msgid="647898534624078900">"לדוגמה, ייתכן שדף ההתחברות אינו שייך לארגון המוצג."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"המשך בכל זאת באמצעות דפדפן"</string> - <string name="ok" msgid="1509280796718850364">"אישור"</string> - <string name="page_info" msgid="4048529256302257195">"פרטי דף"</string> - <string name="page_info_address" msgid="2222306609532903254">"כתובת:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"אזהרת אבטחה"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"הצג אישור"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"אישור זה אינו מגיע מרשות אמינה."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"שם האתר לא תואם לשם באישור."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"פג תוקפו של אישור זה."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"אישור זה אינו חוקי עדיין."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"לאישור זה יש תאריך בלתי חוקי."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"אישור זה אינו חוקי."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"שגיאת אישור לא ידועה."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ja/strings.xml b/packages/CaptivePortalLogin/res/values-ja/strings.xml deleted file mode 100644 index bcc8686f8c65..000000000000 --- a/packages/CaptivePortalLogin/res/values-ja/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"このネットワークをそのまま使用する"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"このネットワークを使用しない"</string> - <string name="action_bar_label" msgid="917235635415966620">"ネットワークにログイン"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s にログイン"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"接続しようとしているネットワークにセキュリティの問題があります。"</string> - <string name="ssl_error_example" msgid="647898534624078900">"たとえば、ログインページが表示されている組織に属していない可能性があります。"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ブラウザから続行"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"ページ情報"</string> - <string name="page_info_address" msgid="2222306609532903254">"アドレス:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"セキュリティ警告"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"証明書を表示"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"この証明書は信頼できる認証機関のものではありません。"</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"サイト名と証明書上の名前が一致しません。"</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"この証明書は有効期限切れです。"</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"この証明書はまだ有効ではありません。"</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"この証明書の日付は無効です。"</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"この証明書は無効です。"</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"不明な証明書エラーです。"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ka/strings.xml b/packages/CaptivePortalLogin/res/values-ka/strings.xml deleted file mode 100644 index 1ccff12e11e1..000000000000 --- a/packages/CaptivePortalLogin/res/values-ka/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ამ ქსელის გამოყენება, როგორც არის"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ეს ქსელი არ გამოიყენო"</string> - <string name="action_bar_label" msgid="917235635415966620">"ქსელში შესვლა"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s-ში შესვლა"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"ქსელს, რომელზედაც მიერთებას ცდილობთ, უსაფრთხოების პრობლემები აქვს."</string> - <string name="ssl_error_example" msgid="647898534624078900">"მაგალითად, სისტემაში შესვლის გვერდი შეიძლება არ ეკუთვნოდეს ნაჩვენებ ორგანიზაციას."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ბრაუზერში გაგრძელება"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-kk/strings.xml b/packages/CaptivePortalLogin/res/values-kk/strings.xml deleted file mode 100644 index a904dea47797..000000000000 --- a/packages/CaptivePortalLogin/res/values-kk/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Осы желіні бар күйінде пайдалану"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Осы желіні пайдаланбау"</string> - <string name="action_bar_label" msgid="917235635415966620">"Желіге кіру"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s жүйесіне кіру"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Қосылайын деп жатқан желіңіз қауіпсіз болуы мүмкін."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Мысалы, кіру беті көрсетілген ұйымға тиесілі болмауы мүмкін."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Бәрібір браузер арқылы жалғастыру"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-km/strings.xml b/packages/CaptivePortalLogin/res/values-km/strings.xml deleted file mode 100644 index a0497f8460ee..000000000000 --- a/packages/CaptivePortalLogin/res/values-km/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ប្រើបណ្ដាញនេះជា"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"កុំប្រើបណ្ដាញនេះ"</string> - <string name="action_bar_label" msgid="917235635415966620">"ចូលទៅបណ្ដាញ"</string> - <string name="action_bar_title" msgid="5645564790486983117">"ចូលទៅ %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"បណ្តាញដែលអ្នកកំពុងព្យាយាមចូលមានបញ្ហាសុវត្ថិភាព។"</string> - <string name="ssl_error_example" msgid="647898534624078900">"ឧបករណ៍៖ ទំព័រចូលនេះអាចនឹងមិនមែនជាកម្មសិទ្ធិរបស់ស្ថាប័នដែលបានបង្ហាញនេះទេ។"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"យ៉ាងណាក៏ដោយនៅតែបន្តតាមរយៈកម្មវិធីរុករក"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-kn/strings.xml b/packages/CaptivePortalLogin/res/values-kn/strings.xml deleted file mode 100644 index 3084504af3ed..000000000000 --- a/packages/CaptivePortalLogin/res/values-kn/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ಈ ನೆಟ್ವರ್ಕ್ ಅನ್ನು ಹೀಗೆ ಬಳಸಿ"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ಈ ನೆಟ್ವರ್ಕ್ ಬಳಸಬೇಡಿ"</string> - <string name="action_bar_label" msgid="917235635415966620">"ನೆಟ್ವರ್ಕ್ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಿ"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"ನೀವು ಸೇರಬೇಕೆಂದಿರುವ ನೆಟ್ವರ್ಕ್ ಭದ್ರತೆ ಸಮಸ್ಯೆಗಳನ್ನು ಹೊಂದಿದೆ."</string> - <string name="ssl_error_example" msgid="647898534624078900">"ಉದಾಹರಣೆಗೆ, ಲಾಗಿನ್ ಪುಟವು ತೋರಿಸಲಾಗಿರುವ ಸಂಸ್ಥೆಗೆ ಸಂಬಂಧಿಸಿರುವುದಿಲ್ಲ."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ಹೇಗಾದರೂ ಬ್ರೌಸರ್ ಮೂಲಕ ಮುಂದುವರಿಸಿ"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ko/strings.xml b/packages/CaptivePortalLogin/res/values-ko/strings.xml deleted file mode 100644 index 7a7f7e075b30..000000000000 --- a/packages/CaptivePortalLogin/res/values-ko/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"현재 상태로 이 네트워크 사용"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"이 네트워크 사용 안함"</string> - <string name="action_bar_label" msgid="917235635415966620">"네트워크에 로그인"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s에 로그인"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"가입하려는 네트워크에 보안 문제가 있습니다."</string> - <string name="ssl_error_example" msgid="647898534624078900">"예를 들어 로그인 페이지가 표시된 조직에 속하지 않을 수 있습니다."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"브라우저를 통해 계속하기"</string> - <string name="ok" msgid="1509280796718850364">"확인"</string> - <string name="page_info" msgid="4048529256302257195">"페이지 정보"</string> - <string name="page_info_address" msgid="2222306609532903254">"주소:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"보안 경고"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"인증서 보기"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"신뢰할 수 있는 인증 기관에서 발급한 인증서가 아닙니다."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"사이트 이름이 인증서에 있는 것과 일치하지 않습니다."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"인증서가 만료되었습니다."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"인증서가 아직 유효하지 않습니다."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"인증서 날짜가 유효하지 않습니다."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"인증서가 잘못되었습니다."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"알 수 없는 인증서 오류입니다."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ky/strings.xml b/packages/CaptivePortalLogin/res/values-ky/strings.xml deleted file mode 100644 index af81ce3ac808..000000000000 --- a/packages/CaptivePortalLogin/res/values-ky/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Бул тармак кандай болсо, ошондой колдонулсун"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Бул тармак колдонулбасын"</string> - <string name="action_bar_label" msgid="917235635415966620">"Тармакка кирүү"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s каттоо эсебине кириңиз"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Кошулайын деген тармагыңызда коопсуздук көйгөйлөрү бар."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Мисалы, каттоо эсебине кирүү баракчасы көрсөтүлгөн уюмга таандык эмес болушу мүмкүн."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Баары бир серепчи аркылуу улантуу"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-lo/strings.xml b/packages/CaptivePortalLogin/res/values-lo/strings.xml deleted file mode 100644 index ee2b26354931..000000000000 --- a/packages/CaptivePortalLogin/res/values-lo/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ໃຊ້ເຄືອຂ່າຍນີ້ຕາມທີ່ມັນເປັນ"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ບໍ່ໃຊ້ເຄືອຂ່າຍນີ້"</string> - <string name="action_bar_label" msgid="917235635415966620">"ລົງຊື່ເຂົ້າເຄືອຂ່າຍ"</string> - <string name="action_bar_title" msgid="5645564790486983117">"ເຂົ້າສູ່ລະບົບ %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"ເຄືອຂ່າຍທີ່ທ່ານກຳລັງເຂົ້າຮ່ວມມີບັນຫາຄວາມປອດໄພ."</string> - <string name="ssl_error_example" msgid="647898534624078900">"ຕົວຢ່າງ, ໜ້າລົງຊື່ເຂົ້າໃຊ້ອາດຈະບໍ່ເປັນຂອງອົງການທີ່ສະແດງຂຶ້ນ."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ແນວໃດກໍ່ສືບຕໍ່ຜ່ານບຣາວເຊີ"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-lt/strings.xml b/packages/CaptivePortalLogin/res/values-lt/strings.xml deleted file mode 100644 index 158f7cea00d8..000000000000 --- a/packages/CaptivePortalLogin/res/values-lt/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Naudoti šį tinklą tokį, koks yra"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Nenaudoti šio tinklo"</string> - <string name="action_bar_label" msgid="917235635415966620">"Prisijungti prie tinklo"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Prisijungimas prie „%1$s“"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Kilo tinklo, prie kurio bandote prisijungti, problemų."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Pavyzdžiui, prisijungimo puslapis gali nepriklausyti rodomai organizacijai."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Vis tiek tęsti naudojant naršyklę"</string> - <string name="ok" msgid="1509280796718850364">"Gerai"</string> - <string name="page_info" msgid="4048529256302257195">"Puslapio informacija"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresas:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Saugos įspėjimas"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Žiūrėti sertifikatą"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Šį sertifikatą išdavė nepatikima įstaiga."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Svetainės pavadinimas neatitinka sertifikate nurodyto pavadinimo."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Šio sertifikato galiojimo laikas baigėsi."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Šis sertifikatas dar negalioja."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Šio sertifikato data netinkama."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Šis sertifikatas netinkamas."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Nežinoma sertifikato klaida."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-lv/strings.xml b/packages/CaptivePortalLogin/res/values-lv/strings.xml deleted file mode 100644 index a42cb220a0d1..000000000000 --- a/packages/CaptivePortalLogin/res/values-lv/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Izmantot tīklu ar pašreizējiem iestatījumiem"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Neizmantot šo tīklu"</string> - <string name="action_bar_label" msgid="917235635415966620">"Pierakstīties tīklā"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Pierakstieties produktā %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Tīklam, kuram mēģināt pievienoties, ir drošības problēmas."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Piemēram, pieteikšanās lapa, iespējams, nepieder norādītajai organizācijai."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Tik un tā turpināt, izmantojot pārlūkprogrammu"</string> - <string name="ok" msgid="1509280796718850364">"Labi"</string> - <string name="page_info" msgid="4048529256302257195">"Lapas informācija"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adrese:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Drošības brīdinājums"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Skatīt sertifikātu"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Šo sertifikātu nav izsniegusi uzticama iestāde."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Vietnes nosaukums neatbilst nosaukumam sertifikātā."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Šī sertifikāta derīguma termiņš ir beidzies."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Šis sertifikāts vēl nav derīgs."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Šī sertifikāta datums nav derīgs."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Šis sertifikāts nav derīgs."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Nezināma sertifikāta kļūda."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-mk/strings.xml b/packages/CaptivePortalLogin/res/values-mk/strings.xml deleted file mode 100644 index 2ae32c8fc8d1..000000000000 --- a/packages/CaptivePortalLogin/res/values-mk/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Користи ја мрежата во оваа состојба"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Не ја користи мрежата"</string> - <string name="action_bar_label" msgid="917235635415966620">"Најавете се на мрежа"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Најавете се на %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Мрежата на која се обидувате да се придружите има проблеми со безбедноста."</string> - <string name="ssl_error_example" msgid="647898534624078900">"На пример, страницата за најавување може да не припаѓа на организацијата што е прикажана."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Сепак продолжи преку прелистувач"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ml/strings.xml b/packages/CaptivePortalLogin/res/values-ml/strings.xml deleted file mode 100644 index 79551f88b887..000000000000 --- a/packages/CaptivePortalLogin/res/values-ml/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ഈ നെറ്റ്വർക്ക് മാറ്റമൊന്നും വരുത്താതെ ഉപയോഗിക്കുക"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ഈ നെറ്റ്വർക്ക് ഉപയോഗിക്കരുത്"</string> - <string name="action_bar_label" msgid="917235635415966620">"നെറ്റ്വർക്കിൽ സൈൻ ഇൻ ചെയ്യുക"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s എന്നതിലേക്ക് സൈൻ ഇൻ ചെയ്യുക"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"നിങ്ങൾ ചേരാൻ ശ്രമിക്കുന്ന നെറ്റ്വർക്കിൽ സുരക്ഷാ പ്രശ്നങ്ങളുണ്ടായിരിക്കാം."</string> - <string name="ssl_error_example" msgid="647898534624078900">"ഉദാഹരണത്തിന്, കാണിച്ചിരിക്കുന്ന ഓർഗനൈസേഷന്റേതായിരിക്കില്ല ലോഗിൻ പേജ്."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"എന്തായാലും ബ്രൗസർ വഴി തുടരുക"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-mn/strings.xml b/packages/CaptivePortalLogin/res/values-mn/strings.xml deleted file mode 100644 index 67670915f299..000000000000 --- a/packages/CaptivePortalLogin/res/values-mn/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Энэ сүлжээг ашиглана уу"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Энэ сүлжээг бүү ашиглана уу"</string> - <string name="action_bar_label" msgid="917235635415966620">"Сүлжээнд нэвтэрнэ үү"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s-д нэвтрэх"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Таны нэгдэх гэж буй сүлжээ аюулгүй байдлын асуудалтай байна."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Жишээлбэл нэвтрэх хуудас нь харагдах байгууллагынх биш байж болзошгүй."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Ямартаа ч хөтчөөр үргэлжлүүлэх"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-mr/strings.xml b/packages/CaptivePortalLogin/res/values-mr/strings.xml deleted file mode 100644 index fac0a082d86c..000000000000 --- a/packages/CaptivePortalLogin/res/values-mr/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"हे नेटवर्क जसेच्या तसे वापरा"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"हे नेटवर्क वापरू नका"</string> - <string name="action_bar_label" msgid="917235635415966620">"नेटवर्क मध्ये साइन इन करा"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$sमध्ये साइन इन करा"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"ज्या नेटवर्कमध्ये आपण सामील होण्याचा प्रयत्न करीत आहात त्यात सुरक्षितता समस्या आहेत."</string> - <string name="ssl_error_example" msgid="647898534624078900">"उदाहरणार्थ, लॉगिन पृष्ठ कदाचित दर्शविलेल्या संस्थेच्या मालकीचे नसावे."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ब्राउझरद्वारे तरीही सुरु ठेवा"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ms/strings.xml b/packages/CaptivePortalLogin/res/values-ms/strings.xml deleted file mode 100644 index aaa51c8fbe3f..000000000000 --- a/packages/CaptivePortalLogin/res/values-ms/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Gunakan rangkaian ini"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Jangan gunakan rangkaian ini"</string> - <string name="action_bar_label" msgid="917235635415966620">"Log masuk ke rangkaian"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Log masuk ke %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Rangkaian yang anda cuba sertai mempunyai isu keselamatan."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Contohnya, halaman log masuk mungkin bukan milik organisasi yang ditunjukkan."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Teruskan juga melalui penyemak imbas"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Maklumat halaman"</string> - <string name="page_info_address" msgid="2222306609532903254">"Alamat:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Amaran keselamatan"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Lihat sijil"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sijil ini bukan daripada pihak berkuasa yang dipercayai."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nama tapak tidak sepadan dengan nama pada sijil."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Sijil ini telah tamat tempoh."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sijil ini belum lagi sah."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Sijil ini mempunyai tarikh yang tidak sah."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Sijil ini tidak sah."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Ralat sijil tidak diketahui."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-my/strings.xml b/packages/CaptivePortalLogin/res/values-my/strings.xml deleted file mode 100644 index 902834b96523..000000000000 --- a/packages/CaptivePortalLogin/res/values-my/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ဒီကွန်ရက်ကို လက်ရှိအတိုင်း သုံးရန်"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ဒီကွန်ရက်ကို မသုံးပါနှင့်"</string> - <string name="action_bar_label" msgid="917235635415966620">"ကွန်ယက်သို့ လက်မှတ်ထိုးဝင်ရန်"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s သို့ လက်မှတ်ထိုးဝင်ပါ"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"သင်ချိတ်ဆက်ရန် ကြိုးစားနေသည့် ကွန်ရက်သည် လုံခြုံရေးပြဿနာ ရှိနေသည်။"</string> - <string name="ssl_error_example" msgid="647898534624078900">"ဥပမာ၊ ဝင်ရောက်ရန် စာမျက်နှာသည် ပြသထားသည့် အဖွဲ့အစည်းနှင့် သက်ဆိုင်မှု မရှိနိုင်ပါ။"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ဘရောက်ဇာမှတစ်ဆင့် ဆက်လုပ်ရန်"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-nb/strings.xml b/packages/CaptivePortalLogin/res/values-nb/strings.xml deleted file mode 100644 index 29c23ed2ee61..000000000000 --- a/packages/CaptivePortalLogin/res/values-nb/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Bruk dette nettverket som det er"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ikke bruk dette nettverket"</string> - <string name="action_bar_label" msgid="917235635415966620">"Logg på nettverk"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Logg på %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Nettverket du prøver å logge på, har sikkerhetsproblemer."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Det er for eksempel mulig at påloggingssiden kanskje ikke tilhører organisasjonen som vises."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Fortsett likevel via nettleseren"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Sideinfo"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresse:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Sikkerhetsadvarsel"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vis sertifikat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Sertifikatet er ikke fra en pålitelig myndighet."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Navnet på nettstedet samsvarer ikke med navnet på sertifikatet."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Sertifikatet er utløpt."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Sertifikatet er ikke gyldig ennå."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dette sertifikatet har en ugyldig dato."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Dette sertifikatet er ugyldig."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Ukjent sertifikatfeil."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ne/strings.xml b/packages/CaptivePortalLogin/res/values-ne/strings.xml deleted file mode 100644 index 87a30c0eff7c..000000000000 --- a/packages/CaptivePortalLogin/res/values-ne/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"यो सञ्जाल जस्तो छ प्रयोग गर्नुहोस्"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"यो सञ्जाल प्रयोग नगर्नुहोस्"</string> - <string name="action_bar_label" msgid="917235635415966620">"सञ्जालमा साइन इन गर्नुहोस्"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s मा साइन इन गर्नुहोस्"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"तपाईँले सामेल हुन प्रयास गरिरहनु भएको नेटवर्कमा सुरक्षा मुद्दाहरू छन्।"</string> - <string name="ssl_error_example" msgid="647898534624078900">"उदाहरणका लागि, लग इन पृष्ठ देखाइएको संस्थाको नहुन सक्छ।"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"जे भए पनि ब्राउजर मार्फत जारी राख्नुहोस्"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-nl/strings.xml b/packages/CaptivePortalLogin/res/values-nl/strings.xml deleted file mode 100644 index 2cbca06c13dd..000000000000 --- a/packages/CaptivePortalLogin/res/values-nl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Dit netwerk in de huidige staat gebruiken"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Dit netwerk niet gebruiken"</string> - <string name="action_bar_label" msgid="917235635415966620">"Inloggen bij netwerk"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Inloggen bij %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Het netwerk waarmee u verbinding probeert te maken, heeft beveiligingsproblemen."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Zo hoort de weergegeven inlogpagina misschien niet bij de weergegeven organisatie."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Toch doorgaan via browser"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Pagina-informatie"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Beveiligingsmelding"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Certificaat weergeven"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Dit is geen certificaat van een vertrouwde autoriteit."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"De naam van deze site komt niet overeen met de naam op het certificaat."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Dit certificaat is verlopen."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Dit certificaat is nog niet geldig."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Dit certificaat heeft een ongeldige datum."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Dit certificaat is ongeldig."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Onbekende certificaatfout."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-or/strings.xml b/packages/CaptivePortalLogin/res/values-or/strings.xml deleted file mode 100644 index 80074c394b83..000000000000 --- a/packages/CaptivePortalLogin/res/values-or/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ଏହି ନେଟ୍ୱର୍କ ଯେପରି ଅଛି, ସେହିପରି ବ୍ୟବହାର କରନ୍ତୁ"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ଏହି ନେଟ୍ୱର୍କକୁ ବ୍ୟବହାର କରନ୍ତୁ ନାହିଁ"</string> - <string name="action_bar_label" msgid="917235635415966620">"ନେଟ୍ୱର୍କରେ ସାଇନ୍ ଇନ୍ କରନ୍ତୁ"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$sରେ ସାଇନ୍-ଇନ୍ କରନ୍ତୁ"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"ଆପଣ ଯୋଗ ଦେବାକୁ ଚେଷ୍ଟା କରୁଥିବା ନେଟ୍ୱର୍କର ସୁରକ୍ଷା ସମସ୍ୟା ଅଛି।"</string> - <string name="ssl_error_example" msgid="647898534624078900">"ଉଦାହରଣସ୍ୱରୂପ, ଲଗଇନ୍ ପୃଷ୍ଠା ଦେଖାଯାଇଥିବା ସଂସ୍ଥାର ନହୋଇଥାଇପାରେ।"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ବ୍ରାଉଜର୍ ଜରିଆରେ ଯେମିତିବି ହେଉ ଜାରି ରଖନ୍ତୁ"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-pa/strings.xml b/packages/CaptivePortalLogin/res/values-pa/strings.xml deleted file mode 100644 index 03e252f8b31a..000000000000 --- a/packages/CaptivePortalLogin/res/values-pa/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ਇਸ ਨੈੱਟਵਰਕ ਨੂੰ ਉਵੇਂ ਵਰਤੋ ਜਿਵੇਂ ਇਹ ਹੈ"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ਇਹ ਨੈੱਟਵਰਕ ਨਾ ਵਰਤੋ"</string> - <string name="action_bar_label" msgid="917235635415966620">"ਨੈੱਟਵਰਕ \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s \'ਤੇ ਸਾਈਨ-ਇਨ ਕਰੋ"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"ਤੁਹਾਡੇ ਦੁਆਰਾ ਸ਼ਾਮਿਲ ਹੋਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕੀਤੇ ਜਾ ਰਹੇ ਨੈੱਟਵਰਕ ਵਿੱਚ ਸੁਰੱਖਿਆ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ।"</string> - <string name="ssl_error_example" msgid="647898534624078900">"ਉਦਾਹਰਣ ਵੱਜੋਂ, ਲੌਗ-ਇਨ ਪੰਨਾ ਦਿਖਾਈ ਗਈ ਸੰਸਥਾ ਨਾਲ ਸੰਬੰਧਿਤ ਨਹੀਂ ਹੋ ਸਕਦਾ ਹੈ।"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ਬ੍ਰਾਊਜ਼ਰ ਰਾਹੀਂ ਫਿਰ ਵੀ ਜਾਰੀ ਰੱਖੋ"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-pl/strings.xml b/packages/CaptivePortalLogin/res/values-pl/strings.xml deleted file mode 100644 index 9ba066ebb12d..000000000000 --- a/packages/CaptivePortalLogin/res/values-pl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Używaj tej sieci tak jak jest"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Nie używaj tej sieci"</string> - <string name="action_bar_label" msgid="917235635415966620">"Zaloguj się do sieci"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Zaloguj się w aplikacji %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"W sieci, z którą próbujesz się połączyć, występują problemy z zabezpieczeniami."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Na przykład strona logowania może nie należeć do wyświetlanej organizacji."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Kontynuuj mimo to w przeglądarce"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Informacje o stronie"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Ostrzeżenie zabezpieczeń"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Wyświetl certyfikat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Certyfikat nie pochodzi od zaufanego urzędu."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Nazwa witryny nie pasuje do nazwy na certyfikacie."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Ten certyfikat wygasł."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Certyfikat nie jest jeszcze ważny."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Certyfikat ma nieprawidłową datę."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Certyfikat jest nieprawidłowy."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Nieznany błąd certyfikatu"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml deleted file mode 100644 index 3d1064cf98d9..000000000000 --- a/packages/CaptivePortalLogin/res/values-pt-rBR/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Usar esta rede como está"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Não usar esta rede"</string> - <string name="action_bar_label" msgid="917235635415966620">"Fazer login na rede"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Fazer login em %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual você está tentando se conectar tem problemas de segurança."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim pelo navegador"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml b/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml deleted file mode 100644 index 5bef235af136..000000000000 --- a/packages/CaptivePortalLogin/res/values-pt-rPT/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Utilizar esta rede como está"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Não utilizar esta rede"</string> - <string name="action_bar_label" msgid="917235635415966620">"Início de sessão na rede"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Iniciar sessão em %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual está a tentar aceder tem problemas de segurança."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de início de sessão pode não pertencer à entidade apresentada."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim através do navegador"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Informações da página"</string> - <string name="page_info_address" msgid="2222306609532903254">"Endereço:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Aviso de segurança"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Ver certificado"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado não pertence a uma autoridade fidedigna."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"O nome do Web site não corresponde ao nome constante no certificado."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado expirou."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado ainda não é válido."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Este certificado tem uma data inválida."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado é inválido."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Erro: certificado desconhecido."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-pt/strings.xml b/packages/CaptivePortalLogin/res/values-pt/strings.xml deleted file mode 100644 index ebe4148fcca9..000000000000 --- a/packages/CaptivePortalLogin/res/values-pt/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Usar esta rede como está"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Não usar esta rede"</string> - <string name="action_bar_label" msgid="917235635415966620">"Fazer login na rede"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Fazer login em %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"A rede à qual você está tentando se conectar tem problemas de segurança."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Por exemplo, a página de login pode não pertencer à organização mostrada."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continuar mesmo assim pelo navegador"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Informações da página"</string> - <string name="page_info_address" msgid="2222306609532903254">"Endereço:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Aviso de segurança"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visualizar certificado"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Este certificado não é de uma autoridade confiável."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"O nome do site não corresponde ao nome no certificado."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Este certificado expirou."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Este certificado ainda não é válido."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Este certificado tem uma data inválida."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Este certificado é inválido."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Erro de certificado desconhecido."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ro/strings.xml b/packages/CaptivePortalLogin/res/values-ro/strings.xml deleted file mode 100644 index e2e4eac97876..000000000000 --- a/packages/CaptivePortalLogin/res/values-ro/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Utilizați această rețea în starea actuală"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Nu utilizați această rețea"</string> - <string name="action_bar_label" msgid="917235635415966620">"Conectați-vă la rețea"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Conectați-vă la %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Rețeaua la care încercați să vă conectați are probleme de securitate."</string> - <string name="ssl_error_example" msgid="647898534624078900">"De exemplu, este posibil ca pagina de conectare să nu aparțină organizației afișate."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Continuați oricum prin browser"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Informaţii pagină"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresă:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Avertisment de securitate"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Vizualizaţi certificatul"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Acest certificat nu provine de la o autoritate de încredere."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Numele acestui site nu se potriveşte cu numele de pe certificat."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Acest certificat a expirat."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Acest certificat nu este încă valid."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Acest certificat are o dată nevalidă."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Acest certificat este nevalid."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Eroare de certificat necunoscută."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ru/strings.xml b/packages/CaptivePortalLogin/res/values-ru/strings.xml deleted file mode 100644 index c0153e6d7611..000000000000 --- a/packages/CaptivePortalLogin/res/values-ru/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Использовать эту сеть"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Не использовать эту сеть"</string> - <string name="action_bar_label" msgid="917235635415966620">"Регистрация в сети"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Войти: %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Сеть, к которой вы хотите подключиться, небезопасна."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Например, страница входа в аккаунт может быть фиктивной."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Игнорировать и открыть браузер"</string> - <string name="ok" msgid="1509280796718850364">"ОК"</string> - <string name="page_info" msgid="4048529256302257195">"Информация о странице"</string> - <string name="page_info_address" msgid="2222306609532903254">"Адрес:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Угроза безопасности"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Просмотреть сертификат"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Этот сертификат получен из ненадежных источников."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Название сайта не соответствует названию в сертификате."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Срок действия сертификата истек."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Сертификат еще не действителен."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Дата этого сертификата недействительна."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Этот сертификат недействителен."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Неизвестная ошибка сертификата."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-si/strings.xml b/packages/CaptivePortalLogin/res/values-si/strings.xml deleted file mode 100644 index a307913025ff..000000000000 --- a/packages/CaptivePortalLogin/res/values-si/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"මෙම ජාලය ලෙසම භාවිතා කරන්න"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"මෙම ජාලය භාවිතා කරන්න එපා"</string> - <string name="action_bar_label" msgid="917235635415966620">"ජාලයට පුරනය වන්න"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s වෙත පුරන්න"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"ඔබ සම්බන්ධ වීමට උත්සහ කරන ජාලයේ ආරක්ෂක ගැටළු ඇත."</string> - <string name="ssl_error_example" msgid="647898534624078900">"උදාහරණයක් ලෙස, පුරනය වන පිටුව පෙන්වා ඇති සංවිධානයට අයිති නැති විය හැක."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"කෙසේ වුවත් බ්රවුසරය හරහා ඉදිරියට යන්න"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-sk/strings.xml b/packages/CaptivePortalLogin/res/values-sk/strings.xml deleted file mode 100644 index 8ba24b1c4ed9..000000000000 --- a/packages/CaptivePortalLogin/res/values-sk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Použiť túto sieť tak, ako je"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Túto sieť nepoužívať"</string> - <string name="action_bar_label" msgid="917235635415966620">"Prihlásiť sa do siete"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Prihláste sa do služby %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Sieť, ku ktorej sa pokúšate pripojiť, má problémy so zabezpečením"</string> - <string name="ssl_error_example" msgid="647898534624078900">"Napríklad prihlasovacia stránka nemusí patriť uvedenej organizácii."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Pokračovať pomocou prehliadača"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Informácie o stránke"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adresa:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Upozornenie zabezpečenia"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Zobraziť certifikát"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Tento certifikát nepochádza od dôveryhodnej autority."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Názov stránky sa nezhoduje s názvom uvedeným v certifikáte."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Platnosť certifikátu skončila."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Tento certifikát zatiaľ nie je platný."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Tento certifikát má neplatný dátum."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Tento certifikát je neplatný."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznáma chyba certifikátu."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-sl/strings.xml b/packages/CaptivePortalLogin/res/values-sl/strings.xml deleted file mode 100644 index b7d9a8a81b8b..000000000000 --- a/packages/CaptivePortalLogin/res/values-sl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Uporabljajte to omrežje, »kakršno je«"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ne uporabljajte tega omrežja"</string> - <string name="action_bar_label" msgid="917235635415966620">"Prijavite se v omrežje"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Prijava v %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Omrežje, ki se mu poskušate pridružiti, ima varnostne težave."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Stran za prijavo na primer morda ne pripada prikazani organizaciji."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Vseeno nadaljuj v brskalniku"</string> - <string name="ok" msgid="1509280796718850364">"V redu"</string> - <string name="page_info" msgid="4048529256302257195">"Podatki o strani"</string> - <string name="page_info_address" msgid="2222306609532903254">"Naslov:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Varnostno opozorilo"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Prikaži potrdilo"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Potrdila ni izdal zaupanja vreden overitelj."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Ime spletnega mesta se ne ujema z imenom na potrdilu."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Potrdilo je poteklo."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"To potrdilo še ni veljavno."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Potrdilo ima neveljaven datum."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"To potrdilo ni veljavno."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Neznana napaka potrdila."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-sq/strings.xml b/packages/CaptivePortalLogin/res/values-sq/strings.xml deleted file mode 100644 index b06da6dbf3cf..000000000000 --- a/packages/CaptivePortalLogin/res/values-sq/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Përdore këtë rrjet siç është"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Mos e përdor këtë rrjet"</string> - <string name="action_bar_label" msgid="917235635415966620">"Identifikohu në rrjet"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Identifikohu në %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Rrjeti në të cilin po përpiqesh të bashkohesh ka probleme sigurie."</string> - <string name="ssl_error_example" msgid="647898534624078900">"për shembull, faqja e identifikimit mund të mos i përkasë organizatës së shfaqur."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Vazhdo gjithsesi nëpërmjet shfletuesit"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-sr/strings.xml b/packages/CaptivePortalLogin/res/values-sr/strings.xml deleted file mode 100644 index 967c8ba87ac1..000000000000 --- a/packages/CaptivePortalLogin/res/values-sr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Користи ову мрежу такву каква је"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Не користи ову мрежу"</string> - <string name="action_bar_label" msgid="917235635415966620">"Пријави ме на мрежу"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Пријавите се у: %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Мрежа којој покушавате да се придружите има безбедносних проблема."</string> - <string name="ssl_error_example" msgid="647898534624078900">"На пример, страница за пријављивање можда не припада приказаној организацији."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Ипак настави преко прегледача"</string> - <string name="ok" msgid="1509280796718850364">"Потврди"</string> - <string name="page_info" msgid="4048529256302257195">"Информације о страници"</string> - <string name="page_info_address" msgid="2222306609532903254">"Адреса:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Безбедносно упозорење"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Прикажи сертификат"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Овај сертификат не потиче од поузданог ауторитета."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Назив сајта се не подудара са називом на сертификату."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Овај сертификат је истекао."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Овај сертификат још увек није важећи."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Датум овог сертификата је неважећи."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Овај сертификат је неважећи."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Непозната грешка сертификата."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-sv/strings.xml b/packages/CaptivePortalLogin/res/values-sv/strings.xml deleted file mode 100644 index 75356f01d81f..000000000000 --- a/packages/CaptivePortalLogin/res/values-sv/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Använd det här nätverket som det är"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Använd inte det här nätverket"</string> - <string name="action_bar_label" msgid="917235635415966620">"Logga in på nätverket"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Logga in på %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Nätverket du försöker ansluta till har säkerhetsproblem."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Det kan t.ex. hända att inloggningssidan inte tillhör den organisation som visas."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Fortsätt ändå via webbläsaren"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Sidinformation"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adress:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Säkerhetsvarning"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Visa certifikat"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Certifikatet kommer inte från en betrodd utfärdare."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Webbplatsens namn stämmer inte med namnet på certifikatet."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Certifikatet har upphört att gälla."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Certifikatet är inte giltigt än."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Det här certifikatet har ett ogiltigt datum."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Certifikatet är ogiltigt."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Okänt certifikatfel."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-sw/strings.xml b/packages/CaptivePortalLogin/res/values-sw/strings.xml deleted file mode 100644 index feb2ddeba9c1..000000000000 --- a/packages/CaptivePortalLogin/res/values-sw/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Tumia mtandao huu jinsi ulivyo"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Usitumie mtandao huu"</string> - <string name="action_bar_label" msgid="917235635415966620">"Ingia katika mtandao"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Ingia katika akaunti ya %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Mtandao unaojaribu kujiunga nao una matatizo ya usalama."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Kwa mfano, ukurasa wa kuingia katika akaunti unaweza usiwe unamilikiwa na shirika lililoonyeshwa."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Endelea hata hivyo kupitia kivinjari"</string> - <string name="ok" msgid="1509280796718850364">"Sawa"</string> - <string name="page_info" msgid="4048529256302257195">"Maelezo ya ukurasa"</string> - <string name="page_info_address" msgid="2222306609532903254">"Anwani:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Ilani ya usalama"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tazama cheti"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Cheti hiki hakijatoka kwa mamlaka inayoaminika."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Jina la tovuti halilingani na jina lililo katika cheti."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Cheti hiki kimepitwa na muda"</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Cheti bado si halali."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Cheti hiki kina tarehe batili."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Hati hii ni batili."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Hitilafu isiyojulikana ya cheti."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ta/strings.xml b/packages/CaptivePortalLogin/res/values-ta/strings.xml deleted file mode 100644 index 6a60ed765904..000000000000 --- a/packages/CaptivePortalLogin/res/values-ta/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"இந்த நெட்வொர்க்கைப் பயன்படுத்து"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"இந்த நெட்வொர்க்கைப் பயன்படுத்த வேண்டாம்"</string> - <string name="action_bar_label" msgid="917235635415966620">"நெட்வொர்க்கில் உள்நுழையவும்"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s இல் உள்நுழைக"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"நீங்கள் சேர முயற்சிக்கும் நெட்வொர்க்கில் பாதுகாப்புச் சிக்கல்கள் உள்ளன."</string> - <string name="ssl_error_example" msgid="647898534624078900">"எடுத்துக்காட்டாக, உள்நுழைவுப் பக்கமானது காட்டப்படும் அமைப்பிற்குச் சொந்தமானதாக இல்லாமல் இருக்கலாம்."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"பரவாயில்லை, உலாவி வழியாகத் தொடரவும்"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-te/strings.xml b/packages/CaptivePortalLogin/res/values-te/strings.xml deleted file mode 100644 index c209d344bee2..000000000000 --- a/packages/CaptivePortalLogin/res/values-te/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ఈ నెట్వర్క్ని యథావిధిగా ఉపయోగించు"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ఈ నెట్వర్క్ని ఉపయోగించవద్దు"</string> - <string name="action_bar_label" msgid="917235635415966620">"నెట్వర్క్కి సైన్ ఇన్ చేయండి"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$sకి సైన్ ఇన్ చేయండి"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"మీరు చేరడానికి ప్రయత్నిస్తున్న నెట్వర్క్ భద్రతా సమస్యలను కలిగి ఉంది."</string> - <string name="ssl_error_example" msgid="647898534624078900">"ఉదాహరణకు, లాగిన్ పేజీ చూపిన సంస్థకు చెందినది కాకపోవచ్చు."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ఏదేమైనా బ్రౌజర్ ద్వారా కొనసాగించు"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-th/strings.xml b/packages/CaptivePortalLogin/res/values-th/strings.xml deleted file mode 100644 index 11a2131dc64c..000000000000 --- a/packages/CaptivePortalLogin/res/values-th/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"ใช้เครือข่ายนี้ตามที่เป็นอยู่"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"ไม่ใช้เครือข่ายนี้"</string> - <string name="action_bar_label" msgid="917235635415966620">"ลงชื่อเข้าใช้เครือข่าย"</string> - <string name="action_bar_title" msgid="5645564790486983117">"ลงชื่อเข้าใช้ %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"เครือข่ายที่คุณพยายามเข้าร่วมมีปัญหาด้านความปลอดภัย"</string> - <string name="ssl_error_example" msgid="647898534624078900">"ตัวอย่างเช่น หน้าเข้าสู่ระบบอาจไม่ใช่ขององค์กรที่แสดงไว้"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"ดำเนินการต่อผ่านเบราว์เซอร์"</string> - <string name="ok" msgid="1509280796718850364">"ตกลง"</string> - <string name="page_info" msgid="4048529256302257195">"ข้อมูลหน้าเว็บ"</string> - <string name="page_info_address" msgid="2222306609532903254">"ที่อยู่:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"คำเตือนเกี่ยวกับความปลอดภัย"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"ดูใบรับรอง"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"ใบรับรองนี้ไม่ได้มาจากผู้ออกที่เชื่อถือได้"</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"ชื่อไซต์ไม่ตรงกับในใบรับรอง"</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"ใบรับรองนี้หมดอายุแล้ว"</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"ใบรับรองนี้ยังใช้งานไม่ได้"</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"ใบรับรองนี้มีวันที่ไม่ถูกต้อง"</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"ใบรับรองนี้ไม่ถูกต้อง"</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"ข้อผิดพลาดใบรับรองที่ไม่รู้จัก"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-tl/strings.xml b/packages/CaptivePortalLogin/res/values-tl/strings.xml deleted file mode 100644 index 07a247992197..000000000000 --- a/packages/CaptivePortalLogin/res/values-tl/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Gamitin ang network na ito nang walang pagbabago"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Huwag gamitin ang network na ito"</string> - <string name="action_bar_label" msgid="917235635415966620">"Mag-sign in sa network"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Mag-sign in sa %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"May mga isyu sa seguridad ang network kung saan mo sinusubukang sumali."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Halimbawa, maaaring hindi sa organisasyong ipinapakita ang page sa pag-log in."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Magpatuloy pa rin sa pamamagitan ng browser"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Impormasyon ng pahina"</string> - <string name="page_info_address" msgid="2222306609532903254">"Address:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Babala sa seguridad"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Tingnan ang certificate"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Ang certificate ay hindi mula sa isang pinagkakatiwalaang kinauukulan."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Ang pangalan ng site ay hindi tumutugma sa pangalan sa certificate."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Nag-expire na ang certificate na ito."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Wala pang bisa ang certificate na ito."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Ang certificate ay mayroong di-wastong petsa."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Di-wasto ang certificate na ito."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Hindi kilalang error ng certificate."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-tr/strings.xml b/packages/CaptivePortalLogin/res/values-tr/strings.xml deleted file mode 100644 index cdedd3306a01..000000000000 --- a/packages/CaptivePortalLogin/res/values-tr/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Bu ağı olduğu gibi kullan"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Bu ağı kullanma"</string> - <string name="action_bar_label" msgid="917235635415966620">"Ağda oturum açın"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s üzerinde oturum açın"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Katılmaya çalıştığınız ağda güvenlik sorunları var."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Örneğin, giriş sayfası, gösterilen kuruluşa ait olmayabilir."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Yine de tarayıcıyla devam et"</string> - <string name="ok" msgid="1509280796718850364">"Tamam"</string> - <string name="page_info" msgid="4048529256302257195">"Sayfa bilgileri"</string> - <string name="page_info_address" msgid="2222306609532903254">"Adres:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Güvenlik uyarısı"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Sertifikayı görüntüle"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Bu sertifika güvenilir bir yetkiliden değil."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Sitenin adı sertifika üzerindeki adla eşleşmiyor."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Bu sertifikanın süresi dolmuş."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Bu sertifika henüz geçerli değil."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Bu sertifikanın tarihi geçersiz."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Bu sertifika geçersiz."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Bilinmeyen sertifika hatası."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-uk/strings.xml b/packages/CaptivePortalLogin/res/values-uk/strings.xml deleted file mode 100644 index 0f4cd1672427..000000000000 --- a/packages/CaptivePortalLogin/res/values-uk/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Використовувати цю мережу як є"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Не використовувати цю мережу"</string> - <string name="action_bar_label" msgid="917235635415966620">"Увійти в мережу"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Увійти в обліковий запис %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"У мережі, до якої ви намагаєтеся під’єднатись, є проблеми з безпекою."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Наприклад, сторінка входу може не належати вказаній організації."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Усе одно продовжити у веб-переглядачі"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Інфо про стор."</string> - <string name="page_info_address" msgid="2222306609532903254">"Адреса:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Застереж. про небезп."</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Переглянути сертиф."</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Сертифікат видано ненадійним центром сертифікації."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Назва сайту не збігається з назвою в сертифікаті."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Термін дії сертиф. завершився."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Цей сертифікат ще не дійсний."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Цей сертифікат має недійсну дату."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Цей сертифікат недійсний."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Помилка невідомого сертифіката."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-ur/strings.xml b/packages/CaptivePortalLogin/res/values-ur/strings.xml deleted file mode 100644 index 05d8fb9468d7..000000000000 --- a/packages/CaptivePortalLogin/res/values-ur/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"جوں کا توں اس نیٹ ورک کا استعمال کریں"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"اس نیٹ ورک کا استعمال نہ کریں"</string> - <string name="action_bar_label" msgid="917235635415966620">"نیٹ ورک میں سائن ان کریں"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s میں سائن ان کریں"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"جس نیٹ ورک میں آپ شامل ہونے کی کوشش کر رہے ہیں اس میں سیکیورٹی کے مسائل ہیں۔"</string> - <string name="ssl_error_example" msgid="647898534624078900">"مثال کے طور پر ہو سکتا ہے کہ لاگ ان صفحہ دکھائی گئی تنظیم سے تعلق نہ رکھتا ہو۔"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"براؤزر کے ذریعے بہرحال جاری رکھیں"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-uz/strings.xml b/packages/CaptivePortalLogin/res/values-uz/strings.xml deleted file mode 100644 index cac96eab2c21..000000000000 --- a/packages/CaptivePortalLogin/res/values-uz/strings.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Ushbu tarmoqdan o‘z holicha foydalanilsin"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ushbu tarmoqdan foydalanilmasin"</string> - <string name="action_bar_label" msgid="917235635415966620">"Tarmoqqa kirish"</string> - <string name="action_bar_title" msgid="5645564790486983117">"%1$s hisobiga kirish"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Siz ulanmoqchi bo‘lgan tarmoqda xavfsizlik bilan bog‘liq muammolar mavjud."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Masalan, tizimga kirish sahifasi ko‘rsatilgan tashkilotga tegishli bo‘lmasligi mumkin."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"E’tiborsiz qoldirilsin va brauzer ochilsin"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-vi/strings.xml b/packages/CaptivePortalLogin/res/values-vi/strings.xml deleted file mode 100644 index 9c702b953fd5..000000000000 --- a/packages/CaptivePortalLogin/res/values-vi/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Sử dụng mạng này"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Không sử dụng mạng này"</string> - <string name="action_bar_label" msgid="917235635415966620">"Đăng nhập vào mạng"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Đăng nhập vào %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Mạng mà bạn đang cố gắng tham gia có vấn đề về bảo mật."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Ví dụ, trang đăng nhập có thể không thuộc về tổ chức được hiển thị."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Vẫn tiếp tục qua trình duyệt"</string> - <string name="ok" msgid="1509280796718850364">"OK"</string> - <string name="page_info" msgid="4048529256302257195">"Thông tin trang"</string> - <string name="page_info_address" msgid="2222306609532903254">"Địa chỉ:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Cảnh báo bảo mật"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Xem chứng chỉ"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Chứng chỉ này không xuất phát từ tổ chức phát hành đáng tin cậy."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Tên của trang web không khớp với tên trên chứng chỉ."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Chứng chỉ này đã hết hạn."</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Chứng chỉ này chưa hợp lệ."</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Chứng chỉ này có ngày không hợp lệ."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Chứng chỉ này không hợp lệ."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Lỗi chứng chỉ không xác định."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml deleted file mode 100644 index 70c2a08682af..000000000000 --- a/packages/CaptivePortalLogin/res/values-zh-rCN/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"直接使用此网络"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"不要使用此网络"</string> - <string name="action_bar_label" msgid="917235635415966620">"登录到网络"</string> - <string name="action_bar_title" msgid="5645564790486983117">"登录%1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"您尝试加入的网络存在安全问题。"</string> - <string name="ssl_error_example" msgid="647898534624078900">"例如,登录页面可能并不属于页面上显示的单位。"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"仍然通过浏览器继续操作"</string> - <string name="ok" msgid="1509280796718850364">"确定"</string> - <string name="page_info" msgid="4048529256302257195">"网页信息"</string> - <string name="page_info_address" msgid="2222306609532903254">"网址:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全警告"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"查看证书"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"该证书并非来自可信的授权中心。"</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"网站的名称与证书上的名称不一致。"</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"该证书已过期。"</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"该证书尚未生效。"</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"该证书的日期无效。"</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"该证书无效。"</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"未知证书错误。"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml deleted file mode 100644 index df1c700582ff..000000000000 --- a/packages/CaptivePortalLogin/res/values-zh-rHK/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"依照現況使用這個網絡"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"不要使用這個網絡"</string> - <string name="action_bar_label" msgid="917235635415966620">"登入網絡"</string> - <string name="action_bar_title" msgid="5645564790486983117">"登入「%1$s」"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"您正在嘗試加入的網絡有安全性問題。"</string> - <string name="ssl_error_example" msgid="647898534624078900">"例如,登入頁面並不屬於所顯示的機構。"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string> - <string name="ok" msgid="1509280796718850364">"確定"</string> - <string name="page_info" msgid="4048529256302257195">"網頁資訊"</string> - <string name="page_info_address" msgid="2222306609532903254">"地址:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全性警告"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"查看憑證"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"這個憑證並非由受信任的權威機構發出。"</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"網站名稱與憑證上的名稱不相符。"</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"這個憑證已過期。"</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"這個憑證尚未生效。"</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"此憑證的日期無效。"</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"此憑證是無效的。"</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"不明的憑證錯誤。"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml b/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml deleted file mode 100644 index 2a2e39729f2e..000000000000 --- a/packages/CaptivePortalLogin/res/values-zh-rTW/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"依現況使用這個網路"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"不使用這個網路"</string> - <string name="action_bar_label" msgid="917235635415966620">"登入網路"</string> - <string name="action_bar_title" msgid="5645564790486983117">"登入 %1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"你嘗試加入的網路有安全問題。"</string> - <string name="ssl_error_example" msgid="647898534624078900">"例如,登入網頁中顯示的機構可能並非該網頁實際隸屬的機構。"</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"透過瀏覽器繼續"</string> - <string name="ok" msgid="1509280796718850364">"確定"</string> - <string name="page_info" msgid="4048529256302257195">"頁面資訊"</string> - <string name="page_info_address" msgid="2222306609532903254">"位址:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"安全性警告"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"檢視憑證"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"這個憑證並非來自信任的授權單位。"</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"網站名稱與憑證上的名稱不相符。"</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"此憑證已過期"</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"這個憑證尚未生效。"</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"這個憑證的日期無效。"</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"這個憑證無效。"</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"不明的憑證錯誤。"</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values-zu/strings.xml b/packages/CaptivePortalLogin/res/values-zu/strings.xml deleted file mode 100644 index 794364588f94..000000000000 --- a/packages/CaptivePortalLogin/res/values-zu/strings.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<resources xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name" msgid="5934709770924185752">"I-CaptivePortalLogin"</string> - <string name="action_use_network" msgid="6076184727448466030">"Sebenzisa le nethiwekhi njengoba injalo"</string> - <string name="action_do_not_use_network" msgid="4577366536956516683">"Ungasebenzisi le nethiwekhi"</string> - <string name="action_bar_label" msgid="917235635415966620">"Ngena ngemvume kunethiwekhi"</string> - <string name="action_bar_title" msgid="5645564790486983117">"Ngena ngemvume ku-%1$s"</string> - <string name="ssl_error_warning" msgid="6653188881418638872">"Inethiwekhi ozama ukuyijoyina inezinkinga zokuvikela."</string> - <string name="ssl_error_example" msgid="647898534624078900">"Isibonelo, ikhasi lokungena ngemvume kungenzeka lingelenhlangano ebonisiwe."</string> - <string name="ssl_error_continue" msgid="6492718244923937110">"Qhubeka noma kunjalo ngesiphequluli"</string> - <string name="ok" msgid="1509280796718850364">"KULUNGILE"</string> - <string name="page_info" msgid="4048529256302257195">"Ulwazi lekhasi"</string> - <string name="page_info_address" msgid="2222306609532903254">"Ikheli:"</string> - <string name="ssl_security_warning_title" msgid="6607795404322797541">"Isexwayiso sokuvikeleka"</string> - <string name="ssl_error_view_certificate" msgid="1472768887529093862">"Buka isitifiketi"</string> - <string name="ssl_error_untrusted" msgid="7754507359360636447">"Lesi sitifiketi asiphumi embusweni othembekile."</string> - <string name="ssl_error_mismatch" msgid="3809794439740523641">"Igama lale ngosi alifani negama elikusitifiketi."</string> - <string name="ssl_error_expired" msgid="5739349389499575559">"Lesi sitifiketi siphelelwe yisikhathi"</string> - <string name="ssl_error_not_yet_valid" msgid="8193083327719048247">"Lesi sitifiketi asilungile okwamanje"</string> - <string name="ssl_error_date_invalid" msgid="3705563379257285534">"Lesi sitifiketi sinosuku olungalungile."</string> - <string name="ssl_error_invalid" msgid="9041704741505449967">"Lesi sitifiketi asilungile."</string> - <string name="ssl_error_unknown" msgid="5679243486524754571">"Iphutha lesitifiketi elingaziwa."</string> -</resources> diff --git a/packages/CaptivePortalLogin/res/values/dimens.xml b/packages/CaptivePortalLogin/res/values/dimens.xml deleted file mode 100644 index 55c1e5908c7e..000000000000 --- a/packages/CaptivePortalLogin/res/values/dimens.xml +++ /dev/null @@ -1,7 +0,0 @@ -<resources> - - <!-- Default screen margins, per the Android Design guidelines. --> - <dimen name="activity_horizontal_margin">16dp</dimen> - <dimen name="activity_vertical_margin">16dp</dimen> - -</resources> diff --git a/packages/CaptivePortalLogin/res/values/strings.xml b/packages/CaptivePortalLogin/res/values/strings.xml deleted file mode 100644 index e9698dbbd784..000000000000 --- a/packages/CaptivePortalLogin/res/values/strings.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - - <string name="app_name">CaptivePortalLogin</string> - <string name="action_use_network">Use this network as is</string> - <string name="action_do_not_use_network">Do not use this network</string> - <string name="action_bar_label">Sign in to network</string> - <string name="action_bar_title">Sign in to %1$s</string> - <string name="ssl_error_warning">The network you’re trying to join has security issues.</string> - <string name="ssl_error_example">For example, the login page may not belong to the organization shown.</string> - <string name="ssl_error_continue">Continue anyway via browser</string> - <string name="ssl_error_untrusted">This certificate isn\'t from a trusted authority.</string> - <string name="ssl_error_mismatch">The name of the site doesn\'t match the name on the certificate.</string> - <string name="ssl_error_expired">This certificate has expired.</string> - <string name="ssl_error_not_yet_valid">This certificate isn\'t valid yet.</string> - <string name="ssl_error_date_invalid">This certificate has an invalid date.</string> - <string name="ssl_error_invalid">This certificate is invalid.</string> - <string name="ssl_error_unknown">Unknown certificate error.</string> - <string name="ssl_security_warning_title">Security warning</string> - <string name="ssl_error_view_certificate">View certificate</string> - <string name="ok">OK</string> - <string name="page_info_address">Address:</string> - <string name="page_info">Page info</string> - -</resources> diff --git a/packages/CaptivePortalLogin/res/values/styles.xml b/packages/CaptivePortalLogin/res/values/styles.xml deleted file mode 100644 index f6c233954b52..000000000000 --- a/packages/CaptivePortalLogin/res/values/styles.xml +++ /dev/null @@ -1,19 +0,0 @@ -<resources> - - <!-- - Base application theme, dependent on API level. This theme is replaced - by AppBaseTheme from res/values-vXX/styles.xml on newer devices. - --> - <style name="AppBaseTheme" parent="@android:style/Theme.DeviceDefault.Settings"> - <!-- - Theme customizations available in newer API levels can go in - res/values-vXX/styles.xml, while customizations related to - backward-compatibility can go here. - --> - </style> - - <!-- Application theme. --> - <style name="AppTheme" parent="AppBaseTheme"> - <!-- All customizations that are NOT specific to a particular API-level can go here. --> - </style> -</resources> diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java deleted file mode 100644 index 9488afb446c4..000000000000 --- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java +++ /dev/null @@ -1,706 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.captiveportallogin; - -import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Application; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.Bitmap; -import android.net.CaptivePortal; -import android.net.ConnectivityManager; -import android.net.ConnectivityManager.NetworkCallback; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkRequest; -import android.net.Proxy; -import android.net.Uri; -import android.net.captiveportal.CaptivePortalProbeSpec; -import android.net.http.SslCertificate; -import android.net.http.SslError; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Bundle; -import android.os.SystemProperties; -import android.support.v4.widget.SwipeRefreshLayout; -import android.text.TextUtils; -import android.util.ArrayMap; -import android.util.Log; -import android.util.SparseArray; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.webkit.CookieManager; -import android.webkit.SslErrorHandler; -import android.webkit.WebChromeClient; -import android.webkit.WebResourceRequest; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.LinearLayout; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; - -import java.io.IOException; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.net.HttpURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Objects; -import java.util.Random; -import java.util.concurrent.atomic.AtomicBoolean; - -public class CaptivePortalLoginActivity extends Activity { - private static final String TAG = CaptivePortalLoginActivity.class.getSimpleName(); - private static final boolean DBG = true; - private static final boolean VDBG = false; - - private static final int SOCKET_TIMEOUT_MS = 10000; - public static final String HTTP_LOCATION_HEADER_NAME = "Location"; - - private enum Result { - DISMISSED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED), - UNWANTED(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED), - WANTED_AS_IS(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS); - - final int metricsEvent; - Result(int metricsEvent) { this.metricsEvent = metricsEvent; } - }; - - private URL mUrl; - private CaptivePortalProbeSpec mProbeSpec; - private String mUserAgent; - private Network mNetwork; - private CaptivePortal mCaptivePortal; - private NetworkCallback mNetworkCallback; - private ConnectivityManager mCm; - private WifiManager mWifiManager; - private boolean mLaunchBrowser = false; - private MyWebViewClient mWebViewClient; - private SwipeRefreshLayout mSwipeRefreshLayout; - // Ensures that done() happens once exactly, handling concurrent callers with atomic operations. - private final AtomicBoolean isDone = new AtomicBoolean(false); - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL); - logMetricsEvent(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY); - - mCm = getSystemService(ConnectivityManager.class); - mWifiManager = getSystemService(WifiManager.class); - mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK); - mUserAgent = - getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT); - mUrl = getUrl(); - if (mUrl == null) { - // getUrl() failed to parse the url provided in the intent: bail out in a way that - // at least provides network access. - done(Result.WANTED_AS_IS); - return; - } - if (DBG) { - Log.d(TAG, String.format("onCreate for %s", mUrl.toString())); - } - - final String spec = getIntent().getStringExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC); - try { - mProbeSpec = CaptivePortalProbeSpec.parseSpecOrNull(spec); - } catch (Exception e) { - // Make extra sure that invalid configurations do not cause crashes - mProbeSpec = null; - } - - mNetworkCallback = new NetworkCallback() { - @Override - public void onLost(Network lostNetwork) { - // If the network disappears while the app is up, exit. - if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED); - } - }; - mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), mNetworkCallback); - - // If the network has disappeared, exit. - final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork); - if (networkCapabilities == null) { - finishAndRemoveTask(); - return; - } - - // Also initializes proxy system properties. - mNetwork = mNetwork.getPrivateDnsBypassingCopy(); - mCm.bindProcessToNetwork(mNetwork); - - // Proxy system properties must be initialized before setContentView is called because - // setContentView initializes the WebView logic which in turn reads the system properties. - setContentView(R.layout.activity_captive_portal_login); - - getActionBar().setDisplayShowHomeEnabled(false); - getActionBar().setElevation(0); // remove shadow - getActionBar().setTitle(getHeaderTitle()); - getActionBar().setSubtitle(""); - - final WebView webview = getWebview(); - webview.clearCache(true); - CookieManager.getInstance().setAcceptThirdPartyCookies(webview, true); - WebSettings webSettings = webview.getSettings(); - webSettings.setJavaScriptEnabled(true); - webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); - webSettings.setUseWideViewPort(true); - webSettings.setLoadWithOverviewMode(true); - webSettings.setSupportZoom(true); - webSettings.setBuiltInZoomControls(true); - webSettings.setDisplayZoomControls(false); - webSettings.setDomStorageEnabled(true); - mWebViewClient = new MyWebViewClient(); - webview.setWebViewClient(mWebViewClient); - webview.setWebChromeClient(new MyWebChromeClient()); - // Start initial page load so WebView finishes loading proxy settings. - // Actual load of mUrl is initiated by MyWebViewClient. - webview.loadData("", "text/html", null); - - mSwipeRefreshLayout = findViewById(R.id.swipe_refresh); - mSwipeRefreshLayout.setOnRefreshListener(() -> { - webview.reload(); - mSwipeRefreshLayout.setRefreshing(true); - }); - - } - - // Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties. - private void setWebViewProxy() { - // TODO: migrate to androidx WebView proxy setting API as soon as it is finalized - try { - final Field loadedApkField = Application.class.getDeclaredField("mLoadedApk"); - final Class<?> loadedApkClass = loadedApkField.getType(); - final Object loadedApk = loadedApkField.get(getApplication()); - Field receiversField = loadedApkClass.getDeclaredField("mReceivers"); - receiversField.setAccessible(true); - ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk); - for (Object receiverMap : receivers.values()) { - for (Object rec : ((ArrayMap) receiverMap).keySet()) { - Class clazz = rec.getClass(); - if (clazz.getName().contains("ProxyChangeListener")) { - Method onReceiveMethod = clazz.getDeclaredMethod("onReceive", Context.class, - Intent.class); - Intent intent = new Intent(Proxy.PROXY_CHANGE_ACTION); - onReceiveMethod.invoke(rec, getApplicationContext(), intent); - Log.v(TAG, "Prompting WebView proxy reload."); - } - } - } - } catch (Exception e) { - Log.e(TAG, "Exception while setting WebView proxy: " + e); - } - } - - private void done(Result result) { - if (isDone.getAndSet(true)) { - // isDone was already true: done() already called - return; - } - if (DBG) { - Log.d(TAG, String.format("Result %s for %s", result.name(), mUrl.toString())); - } - logMetricsEvent(result.metricsEvent); - switch (result) { - case DISMISSED: - mCaptivePortal.reportCaptivePortalDismissed(); - break; - case UNWANTED: - mCaptivePortal.ignoreNetwork(); - break; - case WANTED_AS_IS: - mCaptivePortal.useNetwork(); - break; - } - finishAndRemoveTask(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.captive_portal_login, menu); - return true; - } - - @Override - public void onBackPressed() { - WebView myWebView = findViewById(R.id.webview); - if (myWebView.canGoBack() && mWebViewClient.allowBack()) { - myWebView.goBack(); - } else { - super.onBackPressed(); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - final Result result; - final String action; - final int id = item.getItemId(); - switch (id) { - case R.id.action_use_network: - result = Result.WANTED_AS_IS; - action = "USE_NETWORK"; - break; - case R.id.action_do_not_use_network: - result = Result.UNWANTED; - action = "DO_NOT_USE_NETWORK"; - break; - default: - return super.onOptionsItemSelected(item); - } - if (DBG) { - Log.d(TAG, String.format("onOptionsItemSelect %s for %s", action, mUrl.toString())); - } - done(result); - return true; - } - - @Override - public void onDestroy() { - super.onDestroy(); - final WebView webview = (WebView) findViewById(R.id.webview); - if (webview != null) { - webview.stopLoading(); - webview.setWebViewClient(null); - webview.setWebChromeClient(null); - webview.destroy(); - } - if (mNetworkCallback != null) { - // mNetworkCallback is not null if mUrl is not null. - mCm.unregisterNetworkCallback(mNetworkCallback); - } - if (mLaunchBrowser) { - // Give time for this network to become default. After 500ms just proceed. - for (int i = 0; i < 5; i++) { - // TODO: This misses when mNetwork underlies a VPN. - if (mNetwork.equals(mCm.getActiveNetwork())) break; - try { - Thread.sleep(100); - } catch (InterruptedException e) { - } - } - final String url = mUrl.toString(); - if (DBG) { - Log.d(TAG, "starting activity with intent ACTION_VIEW for " + url); - } - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); - } - } - - private URL getUrl() { - String url = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL); - if (url == null) { - url = mCm.getCaptivePortalServerUrl(); - } - return makeURL(url); - } - - private static URL makeURL(String url) { - try { - return new URL(url); - } catch (MalformedURLException e) { - Log.e(TAG, "Invalid URL " + url); - } - return null; - } - - private static String host(URL url) { - if (url == null) { - return null; - } - return url.getHost(); - } - - private static String sanitizeURL(URL url) { - // In non-Debug build, only show host to avoid leaking private info. - return isDebuggable() ? Objects.toString(url) : host(url); - } - - private static boolean isDebuggable() { - return SystemProperties.getInt("ro.debuggable", 0) == 1; - } - - private void testForCaptivePortal() { - // TODO: reuse NetworkMonitor facilities for consistent captive portal detection. - new Thread(new Runnable() { - public void run() { - // Give time for captive portal to open. - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - } - HttpURLConnection urlConnection = null; - int httpResponseCode = 500; - String locationHeader = null; - try { - urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl); - urlConnection.setInstanceFollowRedirects(false); - urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); - urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); - urlConnection.setUseCaches(false); - if (mUserAgent != null) { - urlConnection.setRequestProperty("User-Agent", mUserAgent); - } - // cannot read request header after connection - String requestHeader = urlConnection.getRequestProperties().toString(); - - urlConnection.getInputStream(); - httpResponseCode = urlConnection.getResponseCode(); - locationHeader = urlConnection.getHeaderField(HTTP_LOCATION_HEADER_NAME); - if (DBG) { - Log.d(TAG, "probe at " + mUrl + - " ret=" + httpResponseCode + - " request=" + requestHeader + - " headers=" + urlConnection.getHeaderFields()); - } - } catch (IOException e) { - } finally { - if (urlConnection != null) urlConnection.disconnect(); - } - if (isDismissed(httpResponseCode, locationHeader, mProbeSpec)) { - done(Result.DISMISSED); - } - } - }).start(); - } - - private static boolean isDismissed( - int httpResponseCode, String locationHeader, CaptivePortalProbeSpec probeSpec) { - return (probeSpec != null) - ? probeSpec.getResult(httpResponseCode, locationHeader).isSuccessful() - : (httpResponseCode == 204); - } - - private class MyWebViewClient extends WebViewClient { - private static final String INTERNAL_ASSETS = "file:///android_asset/"; - - private final String mBrowserBailOutToken = Long.toString(new Random().nextLong()); - private final String mCertificateOutToken = Long.toString(new Random().nextLong()); - // How many Android device-independent-pixels per scaled-pixel - // dp/sp = (px/sp) / (px/dp) = (1/sp) / (1/dp) - private final float mDpPerSp = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1, - getResources().getDisplayMetrics()) / - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, - getResources().getDisplayMetrics()); - private int mPagesLoaded; - private String mMainFrameUrl; - - // If we haven't finished cleaning up the history, don't allow going back. - public boolean allowBack() { - return mPagesLoaded > 1; - } - - private String mSslErrorTitle = null; - private SslErrorHandler mSslErrorHandler = null; - private SslError mSslError = null; - - @Override - public void onPageStarted(WebView view, String urlString, Bitmap favicon) { - if (urlString.contains(mBrowserBailOutToken)) { - mLaunchBrowser = true; - done(Result.WANTED_AS_IS); - return; - } - // The first page load is used only to cause the WebView to - // fetch the proxy settings. Don't update the URL bar, and - // don't check if the captive portal is still there. - if (mPagesLoaded == 0) { - return; - } - final URL url = makeURL(urlString); - Log.d(TAG, "onPageStarted: " + sanitizeURL(url)); - // For internally generated pages, leave URL bar listing prior URL as this is the URL - // the page refers to. - if (!urlString.startsWith(INTERNAL_ASSETS)) { - String subtitle = (url != null) ? getHeaderSubtitle(url) : urlString; - getActionBar().setSubtitle(subtitle); - } - getProgressBar().setVisibility(View.VISIBLE); - testForCaptivePortal(); - } - - @Override - public void onPageFinished(WebView view, String url) { - mPagesLoaded++; - getProgressBar().setVisibility(View.INVISIBLE); - mSwipeRefreshLayout.setRefreshing(false); - if (mPagesLoaded == 1) { - // Now that WebView has loaded at least one page we know it has read in the proxy - // settings. Now prompt the WebView read the Network-specific proxy settings. - setWebViewProxy(); - // Load the real page. - view.loadUrl(mUrl.toString()); - return; - } else if (mPagesLoaded == 2) { - // Prevent going back to empty first page. - // Fix for missing focus, see b/62449959 for details. Remove it once we get a - // newer version of WebView (60.x.y). - view.requestFocus(); - view.clearHistory(); - } - testForCaptivePortal(); - } - - // Convert Android scaled-pixels (sp) to HTML size. - private String sp(int sp) { - // Convert sp to dp's. - float dp = sp * mDpPerSp; - // Apply a scale factor to make things look right. - dp *= 1.3; - // Convert dp's to HTML size. - // HTML px's are scaled just like dp's, so just add "px" suffix. - return Integer.toString((int)dp) + "px"; - } - - // Check if webview is trying to load the main frame and record its url. - @Override - public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { - if (request.isForMainFrame()) { - mMainFrameUrl = request.getUrl().toString(); - } - return false; - } - - // A web page consisting of a large broken lock icon to indicate SSL failure. - - @Override - public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - final URL errorUrl = makeURL(error.getUrl()); - final URL mainFrameUrl = makeURL(mMainFrameUrl); - Log.d(TAG, String.format("SSL error: %s, url: %s, certificate: %s", - sslErrorName(error), sanitizeURL(errorUrl), error.getCertificate())); - if (errorUrl == null - // Ignore SSL errors from resources by comparing the main frame url with SSL - // error url. - || !errorUrl.equals(mainFrameUrl)) { - Log.d(TAG, "onReceivedSslError: mMainFrameUrl = " + mMainFrameUrl); - handler.cancel(); - return; - } - logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR); - final String sslErrorPage = makeSslErrorPage(); - view.loadDataWithBaseURL(INTERNAL_ASSETS, sslErrorPage, "text/HTML", "UTF-8", null); - mSslErrorTitle = view.getTitle() == null ? "" : view.getTitle(); - mSslErrorHandler = handler; - mSslError = error; - } - - private String makeSslErrorPage() { - final String warningMsg = getString(R.string.ssl_error_warning); - final String exampleMsg = getString(R.string.ssl_error_example); - final String continueMsg = getString(R.string.ssl_error_continue); - final String certificateMsg = getString(R.string.ssl_error_view_certificate); - return String.join("\n", - "<html>", - "<head>", - " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">", - " <style>", - " body {", - " background-color:#fafafa;", - " margin:auto;", - " width:80%;", - " margin-top: 96px", - " }", - " img {", - " height:48px;", - " width:48px;", - " }", - " div.warn {", - " font-size:" + sp(16) + ";", - " line-height:1.28;", - " margin-top:16px;", - " opacity:0.87;", - " }", - " div.example {", - " font-size:" + sp(14) + ";", - " line-height:1.21905;", - " margin-top:16px;", - " opacity:0.54;", - " }", - " a {", - " color:#4285F4;", - " display:inline-block;", - " font-size:" + sp(14) + ";", - " font-weight:bold;", - " height:48px;", - " margin-top:24px;", - " text-decoration:none;", - " text-transform:uppercase;", - " }", - " a.certificate {", - " margin-top:0px;", - " }", - " </style>", - "</head>", - "<body>", - " <p><img src=quantum_ic_warning_amber_96.png><br>", - " <div class=warn>" + warningMsg + "</div>", - " <div class=example>" + exampleMsg + "</div>", - " <a href=" + mBrowserBailOutToken + ">" + continueMsg + "</a><br>", - " <a class=certificate href=" + mCertificateOutToken + ">" + certificateMsg + - "</a>", - "</body>", - "</html>"); - } - - @Override - public boolean shouldOverrideUrlLoading (WebView view, String url) { - if (url.startsWith("tel:")) { - startActivity(new Intent(Intent.ACTION_DIAL, Uri.parse(url))); - return true; - } - if (url.contains(mCertificateOutToken) && mSslError != null) { - showSslAlertDialog(mSslErrorHandler, mSslError, mSslErrorTitle); - return true; - } - return false; - } - private void showSslAlertDialog(SslErrorHandler handler, SslError error, String title) { - final LayoutInflater factory = LayoutInflater.from(CaptivePortalLoginActivity.this); - final View sslWarningView = factory.inflate(R.layout.ssl_warning, null); - - // Set Security certificate - setViewSecurityCertificate(sslWarningView.findViewById(R.id.certificate_layout), error); - ((TextView) sslWarningView.findViewById(R.id.ssl_error_type)) - .setText(sslErrorName(error)); - ((TextView) sslWarningView.findViewById(R.id.title)).setText(mSslErrorTitle); - ((TextView) sslWarningView.findViewById(R.id.address)).setText(error.getUrl()); - - AlertDialog sslAlertDialog = new AlertDialog.Builder(CaptivePortalLoginActivity.this) - .setTitle(R.string.ssl_security_warning_title) - .setView(sslWarningView) - .setPositiveButton(R.string.ok, (DialogInterface dialog, int whichButton) -> { - // handler.cancel is called via OnCancelListener. - dialog.cancel(); - }) - .setOnCancelListener((DialogInterface dialogInterface) -> handler.cancel()) - .create(); - sslAlertDialog.show(); - } - - private void setViewSecurityCertificate(LinearLayout certificateLayout, SslError error) { - ((TextView) certificateLayout.findViewById(R.id.ssl_error_msg)) - .setText(sslErrorMessage(error)); - SslCertificate cert = error.getCertificate(); - // TODO: call the method directly once inflateCertificateView is @SystemApi - try { - final View certificateView = (View) SslCertificate.class.getMethod( - "inflateCertificateView", Context.class) - .invoke(cert, CaptivePortalLoginActivity.this); - certificateLayout.addView(certificateView); - } catch (ReflectiveOperationException | SecurityException e) { - Log.e(TAG, "Could not create certificate view", e); - } - } - } - - private class MyWebChromeClient extends WebChromeClient { - @Override - public void onProgressChanged(WebView view, int newProgress) { - getProgressBar().setProgress(newProgress); - } - } - - private ProgressBar getProgressBar() { - return findViewById(R.id.progress_bar); - } - - private WebView getWebview() { - return findViewById(R.id.webview); - } - - private String getHeaderTitle() { - NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork); - final String ssid = getSsid(); - if (TextUtils.isEmpty(ssid) - || nc == null || !nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { - return getString(R.string.action_bar_label); - } - return getString(R.string.action_bar_title, ssid); - } - - // TODO: remove once SSID is obtained from NetworkCapabilities - private String getSsid() { - if (mWifiManager == null) { - return null; - } - final WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); - return removeDoubleQuotes(wifiInfo.getSSID()); - } - - private static String removeDoubleQuotes(String string) { - if (string == null) return null; - final int length = string.length(); - if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) { - return string.substring(1, length - 1); - } - return string; - } - - private String getHeaderSubtitle(URL url) { - String host = host(url); - final String https = "https"; - if (https.equals(url.getProtocol())) { - return https + "://" + host; - } - return host; - } - - private void logMetricsEvent(int event) { - mCaptivePortal.logEvent(event, getPackageName()); - } - - private static final SparseArray<String> SSL_ERRORS = new SparseArray<>(); - static { - SSL_ERRORS.put(SslError.SSL_NOTYETVALID, "SSL_NOTYETVALID"); - SSL_ERRORS.put(SslError.SSL_EXPIRED, "SSL_EXPIRED"); - SSL_ERRORS.put(SslError.SSL_IDMISMATCH, "SSL_IDMISMATCH"); - SSL_ERRORS.put(SslError.SSL_UNTRUSTED, "SSL_UNTRUSTED"); - SSL_ERRORS.put(SslError.SSL_DATE_INVALID, "SSL_DATE_INVALID"); - SSL_ERRORS.put(SslError.SSL_INVALID, "SSL_INVALID"); - } - - private static String sslErrorName(SslError error) { - return SSL_ERRORS.get(error.getPrimaryError(), "UNKNOWN"); - } - - private static final SparseArray<Integer> SSL_ERROR_MSGS = new SparseArray<>(); - static { - SSL_ERROR_MSGS.put(SslError.SSL_NOTYETVALID, R.string.ssl_error_not_yet_valid); - SSL_ERROR_MSGS.put(SslError.SSL_EXPIRED, R.string.ssl_error_expired); - SSL_ERROR_MSGS.put(SslError.SSL_IDMISMATCH, R.string.ssl_error_mismatch); - SSL_ERROR_MSGS.put(SslError.SSL_UNTRUSTED, R.string.ssl_error_untrusted); - SSL_ERROR_MSGS.put(SslError.SSL_DATE_INVALID, R.string.ssl_error_date_invalid); - SSL_ERROR_MSGS.put(SslError.SSL_INVALID, R.string.ssl_error_invalid); - } - - private static Integer sslErrorMessage(SslError error) { - return SSL_ERROR_MSGS.get(error.getPrimaryError(), R.string.ssl_error_unknown); - } -} diff --git a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt index 4a02ee688cf4..d06e5ec634de 100644 --- a/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt +++ b/packages/EasterEgg/src/com/android/egg/paint/BrushPropertyDrawable.kt @@ -62,7 +62,7 @@ class BrushPropertyDrawable : Drawable { return _size } - override fun draw(c: Canvas?) { + override fun draw(c: Canvas) { c?.let { val w = bounds.width().toFloat() val h = bounds.height().toFloat() @@ -90,4 +90,4 @@ class BrushPropertyDrawable : Drawable { // } -}
\ No newline at end of file +} diff --git a/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt b/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt index 164fc5a5af3d..9855565c1335 100644 --- a/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt +++ b/packages/EasterEgg/src/com/android/egg/paint/CutoutAvoidingToolbar.kt @@ -26,15 +26,16 @@ class CutoutAvoidingToolbar : LinearLayout { private var _insets: WindowInsets? = null constructor(context: Context) : super(context) { - init(null, 0) } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - init(attrs, 0) } - constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { - init(attrs, defStyle) + constructor( + context: Context, + attrs: AttributeSet, + defStyle: Int + ) : super(context, attrs, defStyle) { } override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { @@ -81,8 +82,4 @@ class CutoutAvoidingToolbar : LinearLayout { requestLayout() } } - - private fun init(attrs: AttributeSet?, defStyle: Int) { - } - } diff --git a/packages/EasterEgg/src/com/android/egg/paint/Painting.kt b/packages/EasterEgg/src/com/android/egg/paint/Painting.kt index a4a3d3d835e0..9e55d2c74be7 100644 --- a/packages/EasterEgg/src/com/android/egg/paint/Painting.kt +++ b/packages/EasterEgg/src/com/android/egg/paint/Painting.kt @@ -17,7 +17,6 @@ package com.android.egg.paint import android.content.Context -import android.content.res.Resources import android.graphics.* import android.provider.Settings import android.util.AttributeSet @@ -26,7 +25,6 @@ import android.view.MotionEvent import android.view.View import android.view.WindowInsets import java.util.concurrent.TimeUnit -import android.util.Log import android.provider.Settings.System import org.json.JSONObject @@ -86,11 +84,11 @@ public class Painting : View, SpotFilter.Plotter { } var bitmap: Bitmap? = null - var paperColor : Int = 0xFFFFFFFF.toInt() + var paperColor: Int = 0xFFFFFFFF.toInt() private var _paintCanvas: Canvas? = null private val _bitmapLock = Object() - + private var _drawPaint = Paint(Paint.ANTI_ALIAS_FLAG) private var _lastX = 0f private var _lastY = 0f @@ -113,7 +111,9 @@ public class Painting : View, SpotFilter.Plotter { FADE_TO_BLACK_CF synchronized(_bitmapLock) { - c.drawBitmap(bitmap, 0f, 0f, pt) + bitmap?.let { + c.drawBitmap(bitmap!!, 0f, 0f, pt) + } } invalidate() } @@ -122,18 +122,22 @@ public class Painting : View, SpotFilter.Plotter { } constructor(context: Context) : super(context) { - init(null, 0) + init() } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - init(attrs, 0) + init() } - constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { - init(attrs, defStyle) + constructor( + context: Context, + attrs: AttributeSet, + defStyle: Int + ) : super(context, attrs, defStyle) { + init() } - private fun init(attrs: AttributeSet?, defStyle: Int) { + private fun init() { loadDevicePressureData() } @@ -264,7 +268,7 @@ public class Painting : View, SpotFilter.Plotter { super.onDraw(canvas) bitmap?.let { - canvas.drawBitmap(bitmap, 0f, 0f, _drawPaint); + canvas.drawBitmap(bitmap!!, 0f, 0f, _drawPaint) } } @@ -330,14 +334,14 @@ public class Painting : View, SpotFilter.Plotter { } if (bits.width != oldBits.height || bits.height != oldBits.width) { matrix.postScale( - bits.width.toFloat()/oldBits.height, - bits.height.toFloat()/oldBits.width) + bits.width.toFloat() / oldBits.height, + bits.height.toFloat() / oldBits.width) } - c.matrix = matrix + c.setMatrix(matrix) } // paint the old artwork atop the new c.drawBitmap(oldBits, 0f, 0f, _drawPaint) - c.matrix = Matrix() + c.setMatrix(Matrix()) } else { c.drawColor(paperColor) } @@ -350,9 +354,10 @@ public class Painting : View, SpotFilter.Plotter { val invertPaint = Paint() invertPaint.colorFilter = INVERT_CF synchronized(_bitmapLock) { - _paintCanvas?.drawBitmap(bitmap, 0f, 0f, invertPaint) + bitmap?.let { + _paintCanvas?.drawBitmap(bitmap!!, 0f, 0f, invertPaint) + } } invalidate() } } - diff --git a/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt index 86b11e7be81e..460fa3a7241f 100644 --- a/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt +++ b/packages/EasterEgg/src/com/android/egg/paint/ToolbarView.kt @@ -17,19 +17,10 @@ package com.android.egg.paint import android.content.Context -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Paint -import android.graphics.Rect -import android.graphics.drawable.Drawable -import android.text.TextPaint -import android.transition.ChangeBounds import android.transition.Transition import android.transition.TransitionListenerAdapter -import android.transition.TransitionManager import android.util.AttributeSet import android.view.* -import android.view.animation.OvershootInterpolator import android.widget.FrameLayout class ToolbarView : FrameLayout { @@ -44,15 +35,16 @@ class ToolbarView : FrameLayout { } constructor(context: Context) : super(context) { - init(null, 0) } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - init(attrs, 0) } - constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { - init(attrs, defStyle) + constructor( + context: Context, + attrs: AttributeSet, + defStyle: Int + ) : super(context, attrs, defStyle) { } override fun onApplyWindowInsets(insets: WindowInsets?): WindowInsets { @@ -70,8 +62,4 @@ class ToolbarView : FrameLayout { return super.onApplyWindowInsets(insets) } - - private fun init(attrs: AttributeSet?, defStyle: Int) { - } - } diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml deleted file mode 100644 index 45e557c00333..000000000000 --- a/packages/ExtServices/AndroidManifest.xml +++ /dev/null @@ -1,70 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - package="android.ext.services" - android:versionCode="1" - android:versionName="1" - coreApp="true"> - - <uses-permission android:name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" /> - - <application android:label="@string/app_name" - android:defaultToDeviceProtectedStorage="true" - android:directBootAware="true"> - - <service android:name=".storage.CacheQuotaServiceImpl" - android:permission="android.permission.BIND_CACHE_QUOTA_SERVICE"> - <intent-filter> - <action android:name="android.app.usage.CacheQuotaService" /> - </intent-filter> - </service> - - <service android:name=".resolver.LRResolverRankerService" - android:permission="android.permission.BIND_RESOLVER_RANKER_SERVICE" - android:priority="-1" > - <intent-filter> - <action android:name="android.service.resolver.ResolverRankerService" /> - </intent-filter> - </service> - - <service android:name=".notification.Assistant" - android:label="@string/notification_assistant" - android:permission="android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE" - android:exported="true"> - <intent-filter> - <action android:name="android.service.notification.NotificationAssistantService" /> - </intent-filter> - </service> - - <service android:name=".autofill.AutofillFieldClassificationServiceImpl" - android:permission="android.permission.BIND_AUTOFILL_FIELD_CLASSIFICATION_SERVICE"> - <intent-filter> - <action android:name="android.service.autofill.AutofillFieldClassificationService" /> - </intent-filter> - <meta-data - android:name="android.autofill.field_classification.default_algorithm" - android:resource="@string/autofill_field_classification_default_algorithm" /> - <meta-data - android:name="android.autofill.field_classification.available_algorithms" - android:resource="@array/autofill_field_classification_available_algorithms" /> - </service> - - <library android:name="android.ext.services"/> - </application> - -</manifest> diff --git a/packages/ExtServices/MODULE_LICENSE_APACHE2 b/packages/ExtServices/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29bb2d1..000000000000 --- a/packages/ExtServices/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/packages/ExtServices/NOTICE b/packages/ExtServices/NOTICE deleted file mode 100644 index c5b1efa7aac7..000000000000 --- a/packages/ExtServices/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-2008, The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/packages/ExtServices/proguard.proguard b/packages/ExtServices/proguard.proguard deleted file mode 100644 index e5dfbe1c453d..000000000000 --- a/packages/ExtServices/proguard.proguard +++ /dev/null @@ -1,7 +0,0 @@ --keepparameternames --keepattributes Exceptions,InnerClasses,Signature,Deprecated, - SourceFile,LineNumberTable,*Annotation*,EnclosingMethod - --keep public class * { - public protected *; -} diff --git a/packages/ExtServices/res/values/strings.xml b/packages/ExtServices/res/values/strings.xml deleted file mode 100644 index 72647ab8ae3f..000000000000 --- a/packages/ExtServices/res/values/strings.xml +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 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. ---> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_name">Android Services Library</string> - - <string name="notification_assistant">Notification Assistant</string> - <string name="prompt_block_reason">Too many dismissals:views</string> - - <string name="autofill_field_classification_default_algorithm">EDIT_DISTANCE</string> - <string-array name="autofill_field_classification_available_algorithms"> - <item>EDIT_DISTANCE</item> - </string-array> -</resources> diff --git a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java b/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java deleted file mode 100644 index 9ba7e092f34b..000000000000 --- a/packages/ExtServices/src/android/ext/services/autofill/AutofillFieldClassificationServiceImpl.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.ext.services.autofill; - -import static android.ext.services.autofill.EditDistanceScorer.DEFAULT_ALGORITHM; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Bundle; -import android.service.autofill.AutofillFieldClassificationService; -import android.util.Log; -import android.view.autofill.AutofillValue; - -import com.android.internal.util.ArrayUtils; - -import java.util.List; - -public class AutofillFieldClassificationServiceImpl extends AutofillFieldClassificationService { - - private static final String TAG = "AutofillFieldClassificationServiceImpl"; - - @Nullable - @Override - public float[][] onGetScores(@Nullable String algorithmName, - @Nullable Bundle algorithmArgs, @NonNull List<AutofillValue> actualValues, - @NonNull List<String> userDataValues) { - if (ArrayUtils.isEmpty(actualValues) || ArrayUtils.isEmpty(userDataValues)) { - Log.w(TAG, "getScores(): empty currentvalues (" + actualValues + ") or userValues (" - + userDataValues + ")"); - return null; - } - if (algorithmName != null && !algorithmName.equals(DEFAULT_ALGORITHM)) { - Log.w(TAG, "Ignoring invalid algorithm (" + algorithmName + ") and using " - + DEFAULT_ALGORITHM + " instead"); - } - - return EditDistanceScorer.getScores(actualValues, userDataValues); - } -} diff --git a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java b/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java deleted file mode 100644 index 302b16022c26..000000000000 --- a/packages/ExtServices/src/android/ext/services/autofill/EditDistanceScorer.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2017 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.ext.services.autofill; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.Log; -import android.view.autofill.AutofillValue; - -import com.android.internal.annotations.VisibleForTesting; - -import java.util.List; - -final class EditDistanceScorer { - - private static final String TAG = "EditDistanceScorer"; - - // TODO(b/70291841): STOPSHIP - set to false before launching - private static final boolean DEBUG = true; - - static final String DEFAULT_ALGORITHM = "EDIT_DISTANCE"; - - /** - * Gets the field classification score of 2 values based on the edit distance between them. - * - * <p>The score is defined as: @(max_length - edit_distance) / max_length - */ - @VisibleForTesting - static float getScore(@Nullable AutofillValue actualValue, @Nullable String userDataValue) { - if (actualValue == null || !actualValue.isText() || userDataValue == null) return 0; - - final String actualValueText = actualValue.getTextValue().toString(); - final int actualValueLength = actualValueText.length(); - final int userDatalength = userDataValue.length(); - if (userDatalength == 0) { - return (actualValueLength == 0) ? 1 : 0; - } - - final int distance = editDistance(actualValueText.toLowerCase(), - userDataValue.toLowerCase()); - final int maxLength = Math.max(actualValueLength, userDatalength); - return ((float) maxLength - distance) / maxLength; - } - - /** - * Computes the edit distance (number of insertions, deletions or substitutions to edit one - * string into the other) between two strings. In particular, this will compute the Levenshtein - * distance. - * - * <p>See http://en.wikipedia.org/wiki/Levenshtein_distance for details. - * - * @param s the first string to compare - * @param t the second string to compare - * @return the edit distance between the two strings - */ - // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java - public static int editDistance(@NonNull String s, @NonNull String t) { - return editDistance(s, t, Integer.MAX_VALUE); - } - - /** - * Computes the edit distance (number of insertions, deletions or substitutions to edit one - * string into the other) between two strings. In particular, this will compute the Levenshtein - * distance. - * - * <p>See http://en.wikipedia.org/wiki/Levenshtein_distance for details. - * - * @param s the first string to compare - * @param t the second string to compare - * @param max the maximum edit distance that we care about; if for example the string length - * delta is greater than this we don't bother computing the exact edit distance since the - * caller has indicated they're not interested in the result - * @return the edit distance between the two strings, or some other value greater than that if - * the edit distance is at least as big as the {@code max} parameter - */ - // Note: copied verbatim from com.android.tools.lint.detector.api.LintUtils.java - private static int editDistance(@NonNull String s, @NonNull String t, int max) { - if (s.equals(t)) { - return 0; - } - - if (Math.abs(s.length() - t.length()) > max) { - // The string lengths differ more than the allowed edit distance; - // no point in even attempting to compute the edit distance (requires - // O(n*m) storage and O(n*m) speed, where n and m are the string lengths) - return Integer.MAX_VALUE; - } - - int m = s.length(); - int n = t.length(); - int[][] d = new int[m + 1][n + 1]; - for (int i = 0; i <= m; i++) { - d[i][0] = i; - } - for (int j = 0; j <= n; j++) { - d[0][j] = j; - } - for (int j = 1; j <= n; j++) { - for (int i = 1; i <= m; i++) { - if (s.charAt(i - 1) == t.charAt(j - 1)) { - d[i][j] = d[i - 1][j - 1]; - } else { - int deletion = d[i - 1][j] + 1; - int insertion = d[i][j - 1] + 1; - int substitution = d[i - 1][j - 1] + 1; - d[i][j] = Math.min(deletion, Math.min(insertion, substitution)); - } - } - } - - return d[m][n]; - } - /** - * Gets the scores in a batch. - */ - static float[][] getScores(@NonNull List<AutofillValue> actualValues, - @NonNull List<String> userDataValues) { - final int actualValuesSize = actualValues.size(); - final int userDataValuesSize = userDataValues.size(); - if (DEBUG) { - Log.d(TAG, "getScores() will return a " + actualValuesSize + "x" - + userDataValuesSize + " matrix for " + DEFAULT_ALGORITHM); - } - final float[][] scores = new float[actualValuesSize][userDataValuesSize]; - - for (int i = 0; i < actualValuesSize; i++) { - for (int j = 0; j < userDataValuesSize; j++) { - final float score = getScore(actualValues.get(i), userDataValues.get(j)); - scores[i][j] = score; - } - } - return scores; - } - -} diff --git a/packages/ExtServices/src/android/ext/services/notification/Assistant.java b/packages/ExtServices/src/android/ext/services/notification/Assistant.java deleted file mode 100644 index f8788226fc51..000000000000 --- a/packages/ExtServices/src/android/ext/services/notification/Assistant.java +++ /dev/null @@ -1,382 +0,0 @@ -/** - * Copyright (C) 2017 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.ext.services.notification; - -import static android.app.NotificationManager.IMPORTANCE_MIN; -import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; - -import android.app.INotificationManager; -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.ext.services.R; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.storage.StorageManager; -import android.provider.Settings; -import android.service.notification.Adjustment; -import android.service.notification.NotificationAssistantService; -import android.service.notification.NotificationStats; -import android.service.notification.StatusBarNotification; -import android.util.ArrayMap; -import android.util.AtomicFile; -import android.util.Log; -import android.util.Slog; -import android.util.Xml; - -import com.android.internal.util.FastXmlSerializer; -import com.android.internal.util.XmlUtils; - -import libcore.io.IoUtils; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Map; - -/** - * Notification assistant that provides guidance on notification channel blocking - */ -public class Assistant extends NotificationAssistantService { - private static final String TAG = "ExtAssistant"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private static final String TAG_ASSISTANT = "assistant"; - private static final String TAG_IMPRESSION = "impression-set"; - private static final String ATT_KEY = "key"; - private static final int DB_VERSION = 1; - private static final String ATTR_VERSION = "version"; - - private static final ArrayList<Integer> PREJUDICAL_DISMISSALS = new ArrayList<>(); - static { - PREJUDICAL_DISMISSALS.add(REASON_CANCEL); - PREJUDICAL_DISMISSALS.add(REASON_LISTENER_CANCEL); - } - - private float mDismissToViewRatioLimit; - private int mStreakLimit; - - // key : impressions tracker - // TODO: prune deleted channels and apps - final ArrayMap<String, ChannelImpressions> mkeyToImpressions = new ArrayMap<>(); - // SBN key : channel id - ArrayMap<String, String> mLiveNotifications = new ArrayMap<>(); - - private Ranking mFakeRanking = null; - private AtomicFile mFile = null; - - public Assistant() { - } - - @Override - public void onCreate() { - super.onCreate(); - // Contexts are correctly hooked up by the creation step, which is required for the observer - // to be hooked up/initialized. - new SettingsObserver(mHandler); - } - - private void loadFile() { - if (DEBUG) Slog.d(TAG, "loadFile"); - AsyncTask.execute(() -> { - InputStream infile = null; - try { - infile = mFile.openRead(); - readXml(infile); - } catch (FileNotFoundException e) { - Log.d(TAG, "File doesn't exist or isn't readable yet"); - } catch (IOException e) { - Log.e(TAG, "Unable to read channel impressions", e); - } catch (NumberFormatException | XmlPullParserException e) { - Log.e(TAG, "Unable to parse channel impressions", e); - } finally { - IoUtils.closeQuietly(infile); - } - }); - } - - protected void readXml(InputStream stream) - throws XmlPullParserException, NumberFormatException, IOException { - final XmlPullParser parser = Xml.newPullParser(); - parser.setInput(stream, StandardCharsets.UTF_8.name()); - final int outerDepth = parser.getDepth(); - while (XmlUtils.nextElementWithin(parser, outerDepth)) { - if (!TAG_ASSISTANT.equals(parser.getName())) { - continue; - } - final int impressionOuterDepth = parser.getDepth(); - while (XmlUtils.nextElementWithin(parser, impressionOuterDepth)) { - if (!TAG_IMPRESSION.equals(parser.getName())) { - continue; - } - String key = parser.getAttributeValue(null, ATT_KEY); - ChannelImpressions ci = createChannelImpressionsWithThresholds(); - ci.populateFromXml(parser); - synchronized (mkeyToImpressions) { - ci.append(mkeyToImpressions.get(key)); - mkeyToImpressions.put(key, ci); - } - } - } - } - - private void saveFile() throws IOException { - AsyncTask.execute(() -> { - final FileOutputStream stream; - try { - stream = mFile.startWrite(); - } catch (IOException e) { - Slog.w(TAG, "Failed to save policy file", e); - return; - } - try { - final XmlSerializer out = new FastXmlSerializer(); - out.setOutput(stream, StandardCharsets.UTF_8.name()); - writeXml(out); - mFile.finishWrite(stream); - } catch (IOException e) { - Slog.w(TAG, "Failed to save impressions file, restoring backup", e); - mFile.failWrite(stream); - } - }); - } - - protected void writeXml(XmlSerializer out) throws IOException { - out.startDocument(null, true); - out.startTag(null, TAG_ASSISTANT); - out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION)); - synchronized (mkeyToImpressions) { - for (Map.Entry<String, ChannelImpressions> entry - : mkeyToImpressions.entrySet()) { - // TODO: ensure channel still exists - out.startTag(null, TAG_IMPRESSION); - out.attribute(null, ATT_KEY, entry.getKey()); - entry.getValue().writeXml(out); - out.endTag(null, TAG_IMPRESSION); - } - } - out.endTag(null, TAG_ASSISTANT); - out.endDocument(); - } - - @Override - public Adjustment onNotificationEnqueued(StatusBarNotification sbn) { - if (DEBUG) Log.i(TAG, "ENQUEUED " + sbn.getKey()); - return null; - } - - @Override - public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { - if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey()); - try { - Ranking ranking = getRanking(sbn.getKey(), rankingMap); - if (ranking != null && ranking.getChannel() != null) { - String key = getKey( - sbn.getPackageName(), sbn.getUserId(), ranking.getChannel().getId()); - ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, - createChannelImpressionsWithThresholds()); - if (ranking.getImportance() > IMPORTANCE_MIN && ci.shouldTriggerBlock()) { - adjustNotification(createNegativeAdjustment( - sbn.getPackageName(), sbn.getKey(), sbn.getUserId())); - } - mkeyToImpressions.put(key, ci); - mLiveNotifications.put(sbn.getKey(), ranking.getChannel().getId()); - } - } catch (Throwable e) { - Log.e(TAG, "Error occurred processing post", e); - } - } - - @Override - public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap, - NotificationStats stats, int reason) { - try { - boolean updatedImpressions = false; - String channelId = mLiveNotifications.remove(sbn.getKey()); - String key = getKey(sbn.getPackageName(), sbn.getUserId(), channelId); - synchronized (mkeyToImpressions) { - ChannelImpressions ci = mkeyToImpressions.getOrDefault(key, - createChannelImpressionsWithThresholds()); - if (stats.hasSeen()) { - ci.incrementViews(); - updatedImpressions = true; - } - if (PREJUDICAL_DISMISSALS.contains(reason)) { - if ((!sbn.isAppGroup() || sbn.getNotification().isGroupChild()) - && !stats.hasInteracted() - && stats.getDismissalSurface() != NotificationStats.DISMISSAL_AOD - && stats.getDismissalSurface() != NotificationStats.DISMISSAL_PEEK - && stats.getDismissalSurface() != NotificationStats.DISMISSAL_OTHER) { - if (DEBUG) Log.i(TAG, "increment dismissals " + key); - ci.incrementDismissals(); - updatedImpressions = true; - } else { - if (DEBUG) Slog.i(TAG, "reset streak " + key); - if (ci.getStreak() > 0) { - updatedImpressions = true; - } - ci.resetStreak(); - } - } - mkeyToImpressions.put(key, ci); - } - if (updatedImpressions) { - saveFile(); - } - } catch (Throwable e) { - Slog.e(TAG, "Error occurred processing removal", e); - } - } - - @Override - public void onNotificationSnoozedUntilContext(StatusBarNotification sbn, - String snoozeCriterionId) { - } - - @Override - public void onListenerConnected() { - if (DEBUG) Log.i(TAG, "CONNECTED"); - try { - mFile = new AtomicFile(new File(new File( - Environment.getDataUserCePackageDirectory( - StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()), - "assistant"), "blocking_helper_stats.xml")); - loadFile(); - for (StatusBarNotification sbn : getActiveNotifications()) { - onNotificationPosted(sbn); - } - } catch (Throwable e) { - Log.e(TAG, "Error occurred on connection", e); - } - } - - protected String getKey(String pkg, int userId, String channelId) { - return pkg + "|" + userId + "|" + channelId; - } - - private Ranking getRanking(String key, RankingMap rankingMap) { - if (mFakeRanking != null) { - return mFakeRanking; - } - Ranking ranking = new Ranking(); - rankingMap.getRanking(key, ranking); - return ranking; - } - - private Adjustment createNegativeAdjustment(String packageName, String key, int user) { - if (DEBUG) Log.d(TAG, "User probably doesn't want " + key); - Bundle signals = new Bundle(); - signals.putInt(Adjustment.KEY_USER_SENTIMENT, USER_SENTIMENT_NEGATIVE); - return new Adjustment(packageName, key, signals, - getContext().getString(R.string.prompt_block_reason), user); - } - - // for testing - - protected void setFile(AtomicFile file) { - mFile = file; - } - - protected void setFakeRanking(Ranking ranking) { - mFakeRanking = ranking; - } - - protected void setNoMan(INotificationManager noMan) { - mNoMan = noMan; - } - - protected void setContext(Context context) { - mSystemContext = context; - } - - protected ChannelImpressions getImpressions(String key) { - synchronized (mkeyToImpressions) { - return mkeyToImpressions.get(key); - } - } - - protected void insertImpressions(String key, ChannelImpressions ci) { - synchronized (mkeyToImpressions) { - mkeyToImpressions.put(key, ci); - } - } - - private ChannelImpressions createChannelImpressionsWithThresholds() { - ChannelImpressions impressions = new ChannelImpressions(); - impressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit); - return impressions; - } - - /** - * Observer for updates on blocking helper threshold values. - */ - private final class SettingsObserver extends ContentObserver { - private final Uri STREAK_LIMIT_URI = - Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT); - private final Uri DISMISS_TO_VIEW_RATIO_LIMIT_URI = - Settings.Global.getUriFor( - Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT); - - public SettingsObserver(Handler handler) { - super(handler); - ContentResolver resolver = getApplicationContext().getContentResolver(); - resolver.registerContentObserver( - DISMISS_TO_VIEW_RATIO_LIMIT_URI, false, this, getUserId()); - resolver.registerContentObserver(STREAK_LIMIT_URI, false, this, getUserId()); - - // Update all uris on creation. - update(null); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - update(uri); - } - - private void update(Uri uri) { - ContentResolver resolver = getApplicationContext().getContentResolver(); - if (uri == null || DISMISS_TO_VIEW_RATIO_LIMIT_URI.equals(uri)) { - mDismissToViewRatioLimit = Settings.Global.getFloat( - resolver, Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, - ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT); - } - if (uri == null || STREAK_LIMIT_URI.equals(uri)) { - mStreakLimit = Settings.Global.getInt( - resolver, Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, - ChannelImpressions.DEFAULT_STREAK_LIMIT); - } - - // Update all existing channel impression objects with any new limits/thresholds. - synchronized (mkeyToImpressions) { - for (ChannelImpressions channelImpressions: mkeyToImpressions.values()) { - channelImpressions.updateThresholds(mDismissToViewRatioLimit, mStreakLimit); - } - } - } - } -}
\ No newline at end of file diff --git a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java b/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java deleted file mode 100644 index 29ee920d4dde..000000000000 --- a/packages/ExtServices/src/android/ext/services/notification/ChannelImpressions.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Copyright (C) 2017 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.ext.services.notification; - -import android.os.Parcel; -import android.os.Parcelable; -import android.text.TextUtils; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlSerializer; - -import java.io.IOException; - -public final class ChannelImpressions implements Parcelable { - private static final String TAG = "ExtAssistant.CI"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - static final float DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT = .8f; - static final int DEFAULT_STREAK_LIMIT = 2; - static final String ATT_DISMISSALS = "dismisses"; - static final String ATT_VIEWS = "views"; - static final String ATT_STREAK = "streak"; - - private int mDismissals = 0; - private int mViews = 0; - private int mStreak = 0; - - private float mDismissToViewRatioLimit; - private int mStreakLimit; - - public ChannelImpressions() { - mDismissToViewRatioLimit = DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT; - mStreakLimit = DEFAULT_STREAK_LIMIT; - } - - protected ChannelImpressions(Parcel in) { - mDismissals = in.readInt(); - mViews = in.readInt(); - mStreak = in.readInt(); - mDismissToViewRatioLimit = in.readFloat(); - mStreakLimit = in.readInt(); - } - - public int getStreak() { - return mStreak; - } - - public int getDismissals() { - return mDismissals; - } - - public int getViews() { - return mViews; - } - - public void incrementDismissals() { - mDismissals++; - mStreak++; - } - - void updateThresholds(float dismissToViewRatioLimit, int streakLimit) { - mDismissToViewRatioLimit = dismissToViewRatioLimit; - mStreakLimit = streakLimit; - } - - @VisibleForTesting - float getDismissToViewRatioLimit() { - return mDismissToViewRatioLimit; - } - - @VisibleForTesting - int getStreakLimit() { - return mStreakLimit; - } - - public void append(ChannelImpressions additionalImpressions) { - if (additionalImpressions != null) { - mViews += additionalImpressions.getViews(); - mStreak += additionalImpressions.getStreak(); - mDismissals += additionalImpressions.getDismissals(); - } - } - - public void incrementViews() { - mViews++; - } - - public void resetStreak() { - mStreak = 0; - } - - public boolean shouldTriggerBlock() { - if (getViews() == 0) { - return false; - } - if (DEBUG) { - Log.d(TAG, "should trigger? " + getDismissals() + " " + getViews() + " " + getStreak()); - } - return ((float) getDismissals() / getViews()) > mDismissToViewRatioLimit - && getStreak() > mStreakLimit; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mDismissals); - dest.writeInt(mViews); - dest.writeInt(mStreak); - dest.writeFloat(mDismissToViewRatioLimit); - dest.writeInt(mStreakLimit); - } - - @Override - public int describeContents() { - return 0; - } - - public static final Creator<ChannelImpressions> CREATOR = new Creator<ChannelImpressions>() { - @Override - public ChannelImpressions createFromParcel(Parcel in) { - return new ChannelImpressions(in); - } - - @Override - public ChannelImpressions[] newArray(int size) { - return new ChannelImpressions[size]; - } - }; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ChannelImpressions that = (ChannelImpressions) o; - - if (mDismissals != that.mDismissals) return false; - if (mViews != that.mViews) return false; - return mStreak == that.mStreak; - } - - @Override - public int hashCode() { - int result = mDismissals; - result = 31 * result + mViews; - result = 31 * result + mStreak; - return result; - } - - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("ChannelImpressions{"); - sb.append("mDismissals=").append(mDismissals); - sb.append(", mViews=").append(mViews); - sb.append(", mStreak=").append(mStreak); - sb.append(", thresholds=(").append(mDismissToViewRatioLimit); - sb.append(",").append(mStreakLimit); - sb.append(")}"); - return sb.toString(); - } - - protected void populateFromXml(XmlPullParser parser) { - mDismissals = safeInt(parser, ATT_DISMISSALS, 0); - mStreak = safeInt(parser, ATT_STREAK, 0); - mViews = safeInt(parser, ATT_VIEWS, 0); - } - - protected void writeXml(XmlSerializer out) throws IOException { - if (mDismissals != 0) { - out.attribute(null, ATT_DISMISSALS, String.valueOf(mDismissals)); - } - if (mStreak != 0) { - out.attribute(null, ATT_STREAK, String.valueOf(mStreak)); - } - if (mViews != 0) { - out.attribute(null, ATT_VIEWS, String.valueOf(mViews)); - } - } - - private static int safeInt(XmlPullParser parser, String att, int defValue) { - final String val = parser.getAttributeValue(null, att); - return tryParseInt(val, defValue); - } - - private static int tryParseInt(String value, int defValue) { - if (TextUtils.isEmpty(value)) return defValue; - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return defValue; - } - } -} diff --git a/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java b/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java deleted file mode 100644 index 9d7a5689dcd1..000000000000 --- a/packages/ExtServices/src/android/ext/services/resolver/LRResolverRankerService.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright (C) 2017 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.ext.services.resolver; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Environment; -import android.os.IBinder; -import android.os.storage.StorageManager; -import android.service.resolver.ResolverRankerService; -import android.service.resolver.ResolverTarget; -import android.util.ArrayMap; -import android.util.Log; - -import java.io.File; -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * A Logistic Regression based {@link android.service.resolver.ResolverRankerService}, to be used - * in {@link ResolverComparator}. - */ -public final class LRResolverRankerService extends ResolverRankerService { - private static final String TAG = "LRResolverRankerService"; - - private static final boolean DEBUG = false; - - private static final String PARAM_SHARED_PREF_NAME = "resolver_ranker_params"; - private static final String BIAS_PREF_KEY = "bias"; - private static final String VERSION_PREF_KEY = "version"; - - private static final String LAUNCH_SCORE = "launch"; - private static final String TIME_SPENT_SCORE = "timeSpent"; - private static final String RECENCY_SCORE = "recency"; - private static final String CHOOSER_SCORE = "chooser"; - - // parameters for a pre-trained model, to initialize the app ranker. When updating the - // pre-trained model, please update these params, as well as initModel(). - private static final int CURRENT_VERSION = 1; - private static final float LEARNING_RATE = 0.0001f; - private static final float REGULARIZER_PARAM = 0.0001f; - - private SharedPreferences mParamSharedPref; - private ArrayMap<String, Float> mFeatureWeights; - private float mBias; - - @Override - public IBinder onBind(Intent intent) { - initModel(); - return super.onBind(intent); - } - - @Override - public void onPredictSharingProbabilities(List<ResolverTarget> targets) { - final int size = targets.size(); - for (int i = 0; i < size; ++i) { - ResolverTarget target = targets.get(i); - ArrayMap<String, Float> features = getFeatures(target); - target.setSelectProbability(predict(features)); - } - } - - @Override - public void onTrainRankingModel(List<ResolverTarget> targets, int selectedPosition) { - final int size = targets.size(); - if (selectedPosition < 0 || selectedPosition >= size) { - if (DEBUG) { - Log.d(TAG, "Invalid Position of Selected App " + selectedPosition); - } - return; - } - final ArrayMap<String, Float> positive = getFeatures(targets.get(selectedPosition)); - final float positiveProbability = targets.get(selectedPosition).getSelectProbability(); - final int targetSize = targets.size(); - for (int i = 0; i < targetSize; ++i) { - if (i == selectedPosition) { - continue; - } - final ArrayMap<String, Float> negative = getFeatures(targets.get(i)); - final float negativeProbability = targets.get(i).getSelectProbability(); - if (negativeProbability > positiveProbability) { - update(negative, negativeProbability, false); - update(positive, positiveProbability, true); - } - } - commitUpdate(); - } - - private void initModel() { - mParamSharedPref = getParamSharedPref(); - mFeatureWeights = new ArrayMap<>(4); - if (mParamSharedPref == null || - mParamSharedPref.getInt(VERSION_PREF_KEY, 0) < CURRENT_VERSION) { - // Initializing the app ranker to a pre-trained model. When updating the pre-trained - // model, please increment CURRENT_VERSION, and update LEARNING_RATE and - // REGULARIZER_PARAM. - mBias = -1.6568f; - mFeatureWeights.put(LAUNCH_SCORE, 2.5543f); - mFeatureWeights.put(TIME_SPENT_SCORE, 2.8412f); - mFeatureWeights.put(RECENCY_SCORE, 0.269f); - mFeatureWeights.put(CHOOSER_SCORE, 4.2222f); - } else { - mBias = mParamSharedPref.getFloat(BIAS_PREF_KEY, 0.0f); - mFeatureWeights.put(LAUNCH_SCORE, mParamSharedPref.getFloat(LAUNCH_SCORE, 0.0f)); - mFeatureWeights.put( - TIME_SPENT_SCORE, mParamSharedPref.getFloat(TIME_SPENT_SCORE, 0.0f)); - mFeatureWeights.put(RECENCY_SCORE, mParamSharedPref.getFloat(RECENCY_SCORE, 0.0f)); - mFeatureWeights.put(CHOOSER_SCORE, mParamSharedPref.getFloat(CHOOSER_SCORE, 0.0f)); - } - } - - private ArrayMap<String, Float> getFeatures(ResolverTarget target) { - ArrayMap<String, Float> features = new ArrayMap<>(4); - features.put(RECENCY_SCORE, target.getRecencyScore()); - features.put(TIME_SPENT_SCORE, target.getTimeSpentScore()); - features.put(LAUNCH_SCORE, target.getLaunchScore()); - features.put(CHOOSER_SCORE, target.getChooserScore()); - return features; - } - - private float predict(ArrayMap<String, Float> target) { - if (target == null) { - return 0.0f; - } - final int featureSize = target.size(); - float sum = 0.0f; - for (int i = 0; i < featureSize; i++) { - String featureName = target.keyAt(i); - float weight = mFeatureWeights.getOrDefault(featureName, 0.0f); - sum += weight * target.valueAt(i); - } - return (float) (1.0 / (1.0 + Math.exp(-mBias - sum))); - } - - private void update(ArrayMap<String, Float> target, float predict, boolean isSelected) { - if (target == null) { - return; - } - final int featureSize = target.size(); - float error = isSelected ? 1.0f - predict : -predict; - for (int i = 0; i < featureSize; i++) { - String featureName = target.keyAt(i); - float currentWeight = mFeatureWeights.getOrDefault(featureName, 0.0f); - mBias += LEARNING_RATE * error; - currentWeight = currentWeight - LEARNING_RATE * REGULARIZER_PARAM * currentWeight + - LEARNING_RATE * error * target.valueAt(i); - mFeatureWeights.put(featureName, currentWeight); - } - if (DEBUG) { - Log.d(TAG, "Weights: " + mFeatureWeights + " Bias: " + mBias); - } - } - - private void commitUpdate() { - try { - SharedPreferences.Editor editor = mParamSharedPref.edit(); - editor.putFloat(BIAS_PREF_KEY, mBias); - final int size = mFeatureWeights.size(); - for (int i = 0; i < size; i++) { - editor.putFloat(mFeatureWeights.keyAt(i), mFeatureWeights.valueAt(i)); - } - editor.putInt(VERSION_PREF_KEY, CURRENT_VERSION); - editor.apply(); - } catch (Exception e) { - Log.e(TAG, "Failed to commit update" + e); - } - } - - private SharedPreferences getParamSharedPref() { - // The package info in the context isn't initialized in the way it is for normal apps, - // so the standard, name-based context.getSharedPreferences doesn't work. Instead, we - // build the path manually below using the same policy that appears in ContextImpl. - if (DEBUG) { - Log.d(TAG, "Context Package Name: " + getPackageName()); - } - final File prefsFile = new File(new File( - Environment.getDataUserCePackageDirectory( - StorageManager.UUID_PRIVATE_INTERNAL, getUserId(), getPackageName()), - "shared_prefs"), - PARAM_SHARED_PREF_NAME + ".xml"); - return getSharedPreferences(prefsFile, Context.MODE_PRIVATE); - } -}
\ No newline at end of file diff --git a/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java b/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java deleted file mode 100644 index 862f50b2b627..000000000000 --- a/packages/ExtServices/src/android/ext/services/storage/CacheQuotaServiceImpl.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) 2017 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.ext.services.storage; - -import android.app.usage.CacheQuotaHint; -import android.app.usage.CacheQuotaService; -import android.os.Environment; -import android.os.storage.StorageManager; -import android.os.storage.VolumeInfo; -import android.util.ArrayMap; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - -/** - * CacheQuotaServiceImpl implements the CacheQuotaService with a strategy for populating the quota - * of {@link CacheQuotaHint}. - */ -public class CacheQuotaServiceImpl extends CacheQuotaService { - private static final double CACHE_RESERVE_RATIO = 0.15; - - @Override - public List<CacheQuotaHint> onComputeCacheQuotaHints(List<CacheQuotaHint> requests) { - ArrayMap<String, List<CacheQuotaHint>> byUuid = new ArrayMap<>(); - final int requestCount = requests.size(); - for (int i = 0; i < requestCount; i++) { - CacheQuotaHint request = requests.get(i); - String uuid = request.getVolumeUuid(); - List<CacheQuotaHint> listForUuid = byUuid.get(uuid); - if (listForUuid == null) { - listForUuid = new ArrayList<>(); - byUuid.put(uuid, listForUuid); - } - listForUuid.add(request); - } - - List<CacheQuotaHint> processed = new ArrayList<>(); - byUuid.entrySet().forEach( - requestListEntry -> { - // Collapse all usage stats to the same uid. - Map<Integer, List<CacheQuotaHint>> byUid = requestListEntry.getValue() - .stream() - .collect(Collectors.groupingBy(CacheQuotaHint::getUid)); - byUid.values().forEach(uidGroupedList -> { - int size = uidGroupedList.size(); - if (size < 2) { - return; - } - CacheQuotaHint first = uidGroupedList.get(0); - for (int i = 1; i < size; i++) { - /* Note: We can't use the UsageStats built-in addition function because - UIDs may span multiple packages and usage stats adding has - matching package names as a precondition. */ - first.getUsageStats().mTotalTimeInForeground += - uidGroupedList.get(i).getUsageStats().mTotalTimeInForeground; - } - }); - - // Because the foreground stats have been added to the first element, we need - // a list of only the first values (which contain the merged foreground time). - List<CacheQuotaHint> flattenedRequests = - byUid.values() - .stream() - .map(entryList -> entryList.get(0)) - .filter(entry -> entry.getUsageStats().mTotalTimeInForeground != 0) - .sorted(sCacheQuotaRequestComparator) - .collect(Collectors.toList()); - - // Because the elements are sorted, we can use the index to also be the sorted - // index for cache quota calculation. - double sum = getSumOfFairShares(flattenedRequests.size()); - String uuid = requestListEntry.getKey(); - long reservedSize = getReservedCacheSize(uuid); - for (int count = 0; count < flattenedRequests.size(); count++) { - double share = getFairShareForPosition(count) / sum; - CacheQuotaHint entry = flattenedRequests.get(count); - CacheQuotaHint.Builder builder = new CacheQuotaHint.Builder(entry); - builder.setQuota(Math.round(share * reservedSize)); - processed.add(builder.build()); - } - } - ); - - return processed.stream() - .filter(request -> request.getQuota() > 0).collect(Collectors.toList()); - } - - private double getFairShareForPosition(int position) { - double value = 1.0 / Math.log(position + 3) - 0.285; - return (value > 0.01) ? value : 0.01; - } - - private double getSumOfFairShares(int size) { - double sum = 0; - for (int i = 0; i < size; i++) { - sum += getFairShareForPosition(i); - } - return sum; - } - - private long getReservedCacheSize(String uuid) { - // TODO: Revisit the cache size after running more storage tests. - // TODO: Figure out how to ensure ExtServices has the permissions to call - // StorageStatsManager, because this is ignoring the cache... - StorageManager storageManager = getSystemService(StorageManager.class); - long freeBytes = 0; - if (uuid == StorageManager.UUID_PRIVATE_INTERNAL) { // regular equals because of null - freeBytes = Environment.getDataDirectory().getUsableSpace(); - } else { - final VolumeInfo vol = storageManager.findVolumeByUuid(uuid); - freeBytes = vol.getPath().getUsableSpace(); - } - return Math.round(freeBytes * CACHE_RESERVE_RATIO); - } - - // Compares based upon foreground time. - private static Comparator<CacheQuotaHint> sCacheQuotaRequestComparator = - new Comparator<CacheQuotaHint>() { - @Override - public int compare(CacheQuotaHint o, CacheQuotaHint t1) { - long x = t1.getUsageStats().getTotalTimeInForeground(); - long y = o.getUsageStats().getTotalTimeInForeground(); - return (x < y) ? -1 : ((x == y) ? 0 : 1); - } - }; -} diff --git a/packages/ExtServices/tests/Android.bp b/packages/ExtServices/tests/Android.bp deleted file mode 100644 index db160277b82e..000000000000 --- a/packages/ExtServices/tests/Android.bp +++ /dev/null @@ -1,19 +0,0 @@ -android_test { - name: "ExtServicesUnitTests", - certificate: "platform", - libs: [ - "android.test.runner", - "android.test.base", - ], - static_libs: [ - "androidx.test.rules", - "mockito-target-minus-junit4", - "androidx.test.espresso.core", - "truth-prebuilt", - "testables", - ], - // Include all test java files. - srcs: ["src/**/*.java"], - platform_apis: true, - instrumentation_for: "ExtServices", -} diff --git a/packages/ExtServices/tests/AndroidTest.xml b/packages/ExtServices/tests/AndroidTest.xml deleted file mode 100644 index cd26ebc407c1..000000000000 --- a/packages/ExtServices/tests/AndroidTest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Runs Tests for ExtServices"> - <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> - <option name="test-file-name" value="ExtServicesUnitTests.apk" /> - </target_preparer> - - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="framework-base-presubmit" /> - <option name="test-tag" value="ExtServicesUnitTests" /> - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="android.ext.services.tests.unit" /> - <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> - <option name="hidden-api-checks" value="false"/> - </test> -</configuration>
\ No newline at end of file diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java deleted file mode 100644 index 48c076e67e78..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/autofill/AutofillFieldClassificationServiceImplTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.ext.services.autofill; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; - -import static com.google.common.truth.Truth.assertThat; - -import android.view.autofill.AutofillValue; - -/** - * Contains the base tests that does not rely on the specific algorithm implementation. - */ -public class AutofillFieldClassificationServiceImplTest { - - private final AutofillFieldClassificationServiceImpl mService = - new AutofillFieldClassificationServiceImpl(); - - @Test - public void testOnGetScores_nullActualValues() { - assertThat(mService.onGetScores(null, null, null, Arrays.asList("whatever"))).isNull(); - } - - @Test - public void testOnGetScores_emptyActualValues() { - assertThat(mService.onGetScores(null, null, Collections.emptyList(), - Arrays.asList("whatever"))).isNull(); - } - - @Test - public void testOnGetScores_nullUserDataValues() { - assertThat(mService.onGetScores(null, null, - Arrays.asList(AutofillValue.forText("whatever")), null)).isNull(); - } - - @Test - public void testOnGetScores_emptyUserDataValues() { - assertThat(mService.onGetScores(null, null, - Arrays.asList(AutofillValue.forText("whatever")), Collections.emptyList())) - .isNull(); - } -} diff --git a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java b/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java deleted file mode 100644 index afe223641d37..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/autofill/EditDistanceScorerTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.ext.services.autofill; - -import static android.ext.services.autofill.EditDistanceScorer.getScore; -import static android.ext.services.autofill.EditDistanceScorer.getScores; -import static android.view.autofill.AutofillValue.forText; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; - -import android.view.autofill.AutofillValue; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.List; - -public class EditDistanceScorerTest { - - @Test - public void testGetScore_nullValue() { - assertFloat(getScore(null, "D'OH!"), 0); - } - - @Test - public void testGetScore_nonTextValue() { - assertFloat(getScore(AutofillValue.forToggle(true), "D'OH!"), 0); - } - - @Test - public void testGetScore_nullUserData() { - assertFloat(getScore(AutofillValue.forText("D'OH!"), null), 0); - } - - @Test - public void testGetScore_fullMatch() { - assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'OH!"), 1); - assertFloat(getScore(AutofillValue.forText(""), ""), 1); - } - - @Test - public void testGetScore_fullMatchMixedCase() { - assertFloat(getScore(AutofillValue.forText("D'OH!"), "D'oH!"), 1); - } - - @Test - public void testGetScore_mismatchDifferentSizes() { - assertFloat(getScore(AutofillValue.forText("X"), "Xy"), 0.50F); - assertFloat(getScore(AutofillValue.forText("Xy"), "X"), 0.50F); - assertFloat(getScore(AutofillValue.forText("One"), "MoreThanOne"), 0.27F); - assertFloat(getScore(AutofillValue.forText("MoreThanOne"), "One"), 0.27F); - assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Parkway"), - "1600 Amphitheatre Pkwy"), 0.88F); - assertFloat(getScore(AutofillValue.forText("1600 Amphitheatre Pkwy"), - "1600 Amphitheatre Parkway"), 0.88F); - } - - @Test - public void testGetScore_partialMatch() { - assertFloat(getScore(AutofillValue.forText("Dude"), "Dxxx"), 0.25F); - assertFloat(getScore(AutofillValue.forText("Dude"), "DUxx"), 0.50F); - assertFloat(getScore(AutofillValue.forText("Dude"), "DUDx"), 0.75F); - assertFloat(getScore(AutofillValue.forText("Dxxx"), "Dude"), 0.25F); - assertFloat(getScore(AutofillValue.forText("DUxx"), "Dude"), 0.50F); - assertFloat(getScore(AutofillValue.forText("DUDx"), "Dude"), 0.75F); - } - - @Test - public void testGetScores() { - final List<AutofillValue> actualValues = Arrays.asList(forText("A"), forText("b")); - final List<String> userDataValues = Arrays.asList("a", "B", "ab", "c"); - final float[][] expectedScores = new float[][] { - new float[] { 1F, 0F, 0.5F, 0F }, - new float[] { 0F, 1F, 0.5F, 0F } - }; - final float[][] actualScores = getScores(actualValues, userDataValues); - - // Unfortunately, Truth does not have an easy way to compare float matrices and show useful - // messages in case of error, so we need to check. - assertWithMessage("actual=%s, expected=%s", toString(actualScores), - toString(expectedScores)).that(actualScores.length).isEqualTo(2); - assertWithMessage("actual=%s, expected=%s", toString(actualScores), - toString(expectedScores)).that(actualScores[0].length).isEqualTo(4); - assertWithMessage("actual=%s, expected=%s", toString(actualScores), - toString(expectedScores)).that(actualScores[1].length).isEqualTo(4); - for (int i = 0; i < actualScores.length; i++) { - final float[] line = actualScores[i]; - for (int j = 0; j < line.length; j++) { - float cell = line[j]; - assertWithMessage("wrong score at [%s, %s]", i, j).that(cell).isWithin(0.01F) - .of(expectedScores[i][j]); - } - } - } - - public static void assertFloat(float actualValue, float expectedValue) { - assertThat(actualValue).isWithin(0.01F).of(expectedValue); - } - - public static String toString(float[][] matrix) { - final StringBuilder string = new StringBuilder("[ "); - for (int i = 0; i < matrix.length; i++) { - string.append(Arrays.toString(matrix[i])).append(" "); - } - return string.append(" ]").toString(); - } -} diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java deleted file mode 100644 index 6ef25e553204..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantTest.java +++ /dev/null @@ -1,447 +0,0 @@ -/** - * Copyright (C) 2017 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.ext.services.notification; - -import static android.app.NotificationManager.IMPORTANCE_DEFAULT; -import static android.app.NotificationManager.IMPORTANCE_LOW; -import static android.app.NotificationManager.IMPORTANCE_MIN; - -import static junit.framework.Assert.assertEquals; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Application; -import android.app.INotificationManager; -import android.app.Notification; -import android.app.NotificationChannel; -import android.content.ContentResolver; -import android.content.Intent; -import android.os.UserHandle; -import android.provider.Settings; -import android.service.notification.Adjustment; -import android.service.notification.NotificationListenerService; -import android.service.notification.NotificationListenerService.Ranking; -import android.service.notification.NotificationListenerService.RankingMap; -import android.service.notification.NotificationStats; -import android.service.notification.StatusBarNotification; -import android.test.ServiceTestCase; -import android.testing.TestableContext; -import android.util.AtomicFile; - -import androidx.test.InstrumentationRegistry; - -import com.android.internal.util.FastXmlSerializer; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.xmlpull.v1.XmlSerializer; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileOutputStream; - -public class AssistantTest extends ServiceTestCase<Assistant> { - - private static final String PKG1 = "pkg1"; - private static final int UID1 = 1; - private static final NotificationChannel P1C1 = - new NotificationChannel("one", "", IMPORTANCE_LOW); - private static final NotificationChannel P1C2 = - new NotificationChannel("p1c2", "", IMPORTANCE_DEFAULT); - private static final NotificationChannel P1C3 = - new NotificationChannel("p1c3", "", IMPORTANCE_MIN); - private static final String PKG2 = "pkg2"; - - private static final int UID2 = 2; - private static final NotificationChannel P2C1 = - new NotificationChannel("one", "", IMPORTANCE_LOW); - - @Mock INotificationManager mNoMan; - @Mock AtomicFile mFile; - - Assistant mAssistant; - Application mApplication; - - @Rule - public final TestableContext mContext = - new TestableContext(InstrumentationRegistry.getContext(), null); - - public AssistantTest() { - super(Assistant.class); - } - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Intent startIntent = - new Intent("android.service.notification.NotificationAssistantService"); - startIntent.setPackage("android.ext.services"); - - // To bypass real calls to global settings values, set the Settings values here. - Settings.Global.putFloat(mContext.getContentResolver(), - Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, 0.8f); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, 2); - mApplication = (Application) InstrumentationRegistry.getInstrumentation(). - getTargetContext().getApplicationContext(); - // Force the test to use the correct application instead of trying to use a mock application - setApplication(mApplication); - bindService(startIntent); - mAssistant = getService(); - mAssistant.setNoMan(mNoMan); - mAssistant.setFile(mFile); - when(mFile.startWrite()).thenReturn(mock(FileOutputStream.class)); - } - - private StatusBarNotification generateSbn(String pkg, int uid, NotificationChannel channel, - String tag, String groupKey) { - Notification n = new Notification.Builder(mContext, channel.getId()) - .setContentTitle("foo") - .setGroup(groupKey) - .build(); - - StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 0, tag, uid, uid, n, - UserHandle.SYSTEM, null, 0); - - return sbn; - } - - private Ranking generateRanking(StatusBarNotification sbn, NotificationChannel channel) { - Ranking mockRanking = mock(Ranking.class); - when(mockRanking.getChannel()).thenReturn(channel); - when(mockRanking.getImportance()).thenReturn(channel.getImportance()); - when(mockRanking.getKey()).thenReturn(sbn.getKey()); - when(mockRanking.getOverrideGroupKey()).thenReturn(null); - return mockRanking; - } - - private void almostBlockChannel(String pkg, int uid, NotificationChannel channel) { - for (int i = 0; i < ChannelImpressions.DEFAULT_STREAK_LIMIT; i++) { - dismissBadNotification(pkg, uid, channel, String.valueOf(i)); - } - } - - private void dismissBadNotification(String pkg, int uid, NotificationChannel channel, - String tag) { - StatusBarNotification sbn = generateSbn(pkg, uid, channel, tag, null); - mAssistant.setFakeRanking(generateRanking(sbn, channel)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.setFakeRanking(mock(Ranking.class)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - } - - @Test - public void testNoAdjustmentForInitialPost() throws Exception { - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, null, null); - - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - dismissBadNotification(PKG1, UID1, P1C1, "trigger!"); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - ArgumentCaptor<Adjustment> captor = ArgumentCaptor.forClass(Adjustment.class); - verify(mNoMan, times(1)).applyAdjustmentFromAssistant(any(), captor.capture()); - assertEquals(sbn.getKey(), captor.getValue().getKey()); - assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, - captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT)); - } - - @Test - public void testMinCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C3); - dismissBadNotification(PKG1, UID1, P1C3, "trigger!"); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C3, "new one!", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C3)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testGroupChildCanTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP"); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group"); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - ArgumentCaptor<Adjustment> captor = ArgumentCaptor.forClass(Adjustment.class); - verify(mNoMan, times(1)).applyAdjustmentFromAssistant(any(), captor.capture()); - assertEquals(sbn.getKey(), captor.getValue().getKey()); - assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, - captor.getValue().getSignals().getInt(Adjustment.KEY_USER_SENTIMENT)); - } - - @Test - public void testGroupSummaryCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", "I HAVE A GROUP"); - sbn.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY; - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", "group"); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testAodCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_AOD); - stats.setSeen(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testInteractedCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - stats.setExpanded(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testAppDismissedCannotTriggerAdjustment() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C1, "no", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C1)); - NotificationStats stats = new NotificationStats(); - stats.setDismissalSurface(NotificationStats.DISMISSAL_SHADE); - stats.setSeen(); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - mAssistant.onNotificationRemoved( - sbn, mock(RankingMap.class), stats, NotificationListenerService.REASON_APP_CANCEL); - - sbn = generateSbn(PKG1, UID1, P1C1, "new one!", null); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testAppSeparation() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - dismissBadNotification(PKG1, UID1, P1C1, "trigger!"); - - StatusBarNotification sbn = generateSbn(PKG2, UID2, P2C1, "new app!", null); - mAssistant.setFakeRanking(generateRanking(sbn, P2C1)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testChannelSeparation() throws Exception { - almostBlockChannel(PKG1, UID1, P1C1); - dismissBadNotification(PKG1, UID1, P1C1, "trigger!"); - - StatusBarNotification sbn = generateSbn(PKG1, UID1, P1C2, "new app!", null); - mAssistant.setFakeRanking(generateRanking(sbn, P1C2)); - mAssistant.onNotificationPosted(sbn, mock(RankingMap.class)); - - verify(mNoMan, never()).applyAdjustmentFromAssistant(any(), any()); - } - - @Test - public void testReadXml() throws Exception { - String key1 = mAssistant.getKey("pkg1", 1, "channel1"); - int streak1 = 2; - int views1 = 5; - int dismiss1 = 9; - - int streak1a = 3; - int views1a = 10; - int dismiss1a = 99; - String key1a = mAssistant.getKey("pkg1", 1, "channel1a"); - - int streak2 = 7; - int views2 = 77; - int dismiss2 = 777; - String key2 = mAssistant.getKey("pkg2", 2, "channel2"); - - String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>" - + "<assistant version=\"1\">\n" - + "<impression-set key=\"" + key1 + "\" " - + "dismisses=\"" + dismiss1 + "\" views=\"" + views1 - + "\" streak=\"" + streak1 + "\"/>\n" - + "<impression-set key=\"" + key1a + "\" " - + "dismisses=\"" + dismiss1a + "\" views=\"" + views1a - + "\" streak=\"" + streak1a + "\"/>\n" - + "<impression-set key=\"" + key2 + "\" " - + "dismisses=\"" + dismiss2 + "\" views=\"" + views2 - + "\" streak=\"" + streak2 + "\"/>\n" - + "</assistant>\n"; - mAssistant.readXml(new BufferedInputStream(new ByteArrayInputStream(xml.getBytes()))); - - ChannelImpressions c1 = mAssistant.getImpressions(key1); - assertEquals(2, c1.getStreak()); - assertEquals(5, c1.getViews()); - assertEquals(9, c1.getDismissals()); - - ChannelImpressions c1a = mAssistant.getImpressions(key1a); - assertEquals(3, c1a.getStreak()); - assertEquals(10, c1a.getViews()); - assertEquals(99, c1a.getDismissals()); - - ChannelImpressions c2 = mAssistant.getImpressions(key2); - assertEquals(7, c2.getStreak()); - assertEquals(77, c2.getViews()); - assertEquals(777, c2.getDismissals()); - } - - @Test - public void testRoundTripXml() throws Exception { - String key1 = mAssistant.getKey("pkg1", 1, "channel1"); - ChannelImpressions ci1 = new ChannelImpressions(); - String key2 = mAssistant.getKey("pkg1", 1, "channel2"); - ChannelImpressions ci2 = new ChannelImpressions(); - for (int i = 0; i < 3; i++) { - ci2.incrementViews(); - ci2.incrementDismissals(); - } - ChannelImpressions ci3 = new ChannelImpressions(); - String key3 = mAssistant.getKey("pkg3", 3, "channel2"); - for (int i = 0; i < 9; i++) { - ci3.incrementViews(); - if (i % 3 == 0) { - ci3.incrementDismissals(); - } - } - - mAssistant.insertImpressions(key1, ci1); - mAssistant.insertImpressions(key2, ci2); - mAssistant.insertImpressions(key3, ci3); - - XmlSerializer serializer = new FastXmlSerializer(); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); - mAssistant.writeXml(serializer); - - Assistant assistant = new Assistant(); - assistant.readXml(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray()))); - - assertEquals(ci1, assistant.getImpressions(key1)); - assertEquals(ci2, assistant.getImpressions(key2)); - assertEquals(ci3, assistant.getImpressions(key3)); - } - - @Test - public void testSettingsProviderUpdate() { - ContentResolver resolver = mApplication.getContentResolver(); - - // Set up channels - String key = mAssistant.getKey("pkg1", 1, "channel1"); - ChannelImpressions ci = new ChannelImpressions(); - for (int i = 0; i < 3; i++) { - ci.incrementViews(); - if (i % 2 == 0) { - ci.incrementDismissals(); - } - } - - mAssistant.insertImpressions(key, ci); - - // With default values, the blocking helper shouldn't be triggered. - assertEquals(false, ci.shouldTriggerBlock()); - - // Update settings values. - float newDismissToViewRatioLimit = 0f; - int newStreakLimit = 0; - Settings.Global.putFloat(resolver, - Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT, - newDismissToViewRatioLimit); - Settings.Global.putInt(resolver, - Settings.Global.BLOCKING_HELPER_STREAK_LIMIT, newStreakLimit); - - // Notify for the settings values we updated. - resolver.notifyChange( - Settings.Global.getUriFor(Settings.Global.BLOCKING_HELPER_STREAK_LIMIT), null); - resolver.notifyChange( - Settings.Global.getUriFor( - Settings.Global.BLOCKING_HELPER_DISMISS_TO_VIEW_RATIO_LIMIT), - null); - - // With the new threshold, the blocking helper should be triggered. - assertEquals(true, ci.shouldTriggerBlock()); - } -} diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java deleted file mode 100644 index 3253802bec03..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/notification/ChannelImpressionsTest.java +++ /dev/null @@ -1,161 +0,0 @@ -/** - * Copyright (C) 2017 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.ext.services.notification; - -import static android.ext.services.notification.ChannelImpressions.DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT; -import static android.ext.services.notification.ChannelImpressions.DEFAULT_STREAK_LIMIT; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -public class ChannelImpressionsTest { - - @Test - public void testNoResultNoBlock() { - ChannelImpressions ci = new ChannelImpressions(); - assertFalse(ci.shouldTriggerBlock()); - } - - @Test - public void testNoStreakNoBlock() { - ChannelImpressions ci = new ChannelImpressions(); - - for (int i = 0; i < DEFAULT_STREAK_LIMIT - 1; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - } - - assertFalse(ci.shouldTriggerBlock()); - } - - @Test - public void testNoStreakNoBlock_breakStreak() { - ChannelImpressions ci = new ChannelImpressions(); - - for (int i = 0; i < DEFAULT_STREAK_LIMIT; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - if (i == DEFAULT_STREAK_LIMIT - 1) { - ci.resetStreak(); - } - } - - assertFalse(ci.shouldTriggerBlock()); - } - - @Test - public void testStreakBlock() { - ChannelImpressions ci = new ChannelImpressions(); - - for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - } - - assertTrue(ci.shouldTriggerBlock()); - } - - @Test - public void testRatio_NoBlockEvenWithStreak() { - ChannelImpressions ci = new ChannelImpressions(); - - for (int i = 0; i < DEFAULT_STREAK_LIMIT; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - ci.incrementViews(); - } - - assertFalse(ci.shouldTriggerBlock()); - } - - @Test - public void testAppend() { - ChannelImpressions ci = new ChannelImpressions(); - ci.incrementViews(); - ci.incrementDismissals(); - - ChannelImpressions ci2 = new ChannelImpressions(); - ci2.incrementViews(); - ci2.incrementDismissals(); - ci2.incrementViews(); - - ci.append(ci2); - assertEquals(3, ci.getViews()); - assertEquals(2, ci.getDismissals()); - assertEquals(2, ci.getStreak()); - - assertEquals(2, ci2.getViews()); - assertEquals(1, ci2.getDismissals()); - assertEquals(1, ci2.getStreak()); - - // no crash - ci.append(null); - } - - @Test - public void testUpdateThresholds_streakLimitsCorrectlyApplied() { - int updatedStreakLimit = DEFAULT_STREAK_LIMIT + 3; - ChannelImpressions ci = new ChannelImpressions(); - ci.updateThresholds(DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT, updatedStreakLimit); - - for (int i = 0; i <= updatedStreakLimit; i++) { - ci.incrementViews(); - ci.incrementDismissals(); - } - - ChannelImpressions ci2 = new ChannelImpressions(); - ci2.updateThresholds(DEFAULT_DISMISS_TO_VIEW_RATIO_LIMIT, updatedStreakLimit); - - for (int i = 0; i < updatedStreakLimit; i++) { - ci2.incrementViews(); - ci2.incrementDismissals(); - } - - assertTrue(ci.shouldTriggerBlock()); - assertFalse(ci2.shouldTriggerBlock()); - } - - @Test - public void testUpdateThresholds_ratioLimitsCorrectlyApplied() { - float updatedDismissRatio = .99f; - ChannelImpressions ci = new ChannelImpressions(); - ci.updateThresholds(updatedDismissRatio, DEFAULT_STREAK_LIMIT); - - // N views, N-1 dismissals, which doesn't satisfy the ratio = 1 criteria. - for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) { - ci.incrementViews(); - if (i != DEFAULT_STREAK_LIMIT) { - ci.incrementDismissals(); - } - } - - ChannelImpressions ci2 = new ChannelImpressions(); - ci2.updateThresholds(updatedDismissRatio, DEFAULT_STREAK_LIMIT); - - for (int i = 0; i <= DEFAULT_STREAK_LIMIT; i++) { - ci2.incrementViews(); - ci2.incrementDismissals(); - } - - assertFalse(ci.shouldTriggerBlock()); - assertTrue(ci2.shouldTriggerBlock()); - } -} diff --git a/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java deleted file mode 100644 index df4738ff1948..000000000000 --- a/packages/ExtServices/tests/src/android/ext/services/storage/CacheQuotaServiceImplTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2017 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.ext.services.storage; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.when; - -import android.app.usage.CacheQuotaHint; -import android.app.usage.UsageStats; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.Intent; -import android.os.storage.StorageManager; -import android.os.storage.VolumeInfo; -import android.test.ServiceTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -public class CacheQuotaServiceImplTest extends ServiceTestCase<CacheQuotaServiceImpl> { - private static final String sTestVolUuid = "uuid"; - private static final String sSecondTestVolUuid = "otherUuid"; - - @Mock private Context mContext; - @Mock private File mFile; - @Mock private VolumeInfo mVolume; - @Mock(answer = Answers.RETURNS_DEEP_STUBS) private StorageManager mStorageManager; - - public CacheQuotaServiceImplTest() { - super(CacheQuotaServiceImpl.class); - } - - @Before - public void setUp() throws Exception { - super.setUp(); - MockitoAnnotations.initMocks(this); - mContext = Mockito.spy(new ContextWrapper(getSystemContext())); - setContext(mContext); - when(mContext.getSystemService(Context.STORAGE_SERVICE)).thenReturn(mStorageManager); - - when(mFile.getUsableSpace()).thenReturn(10000L); - when(mVolume.getPath()).thenReturn(mFile); - when(mStorageManager.findVolumeByUuid(sTestVolUuid)).thenReturn(mVolume); - when(mStorageManager.findVolumeByUuid(sSecondTestVolUuid)).thenReturn(mVolume); - - Intent intent = new Intent(getContext(), CacheQuotaServiceImpl.class); - startService(intent); - } - - @Test - public void testNoApps() { - CacheQuotaServiceImpl service = getService(); - assertEquals(service.onComputeCacheQuotaHints(new ArrayList()).size(), 0); - } - - @Test - public void testOneApp() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - CacheQuotaHint request = makeNewRequest("com.test", sTestVolUuid, 1001, 100L); - requests.add(request); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - assertThat(output).hasSize(1); - assertThat(output.get(0).getQuota()).isEqualTo(1500L); - } - - @Test - public void testTwoAppsOneVolume() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L)); - requests.add(makeNewRequest("com.test2", sTestVolUuid, 1002, 99L)); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - // Note that the sizes are just the cache area split up. - assertThat(output).hasSize(2); - assertThat(output.get(0).getQuota()).isEqualTo(883); - assertThat(output.get(1).getQuota()).isEqualTo(1500 - 883); - } - - @Test - public void testTwoAppsTwoVolumes() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L)); - requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1002, 99L)); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - assertThat(output).hasSize(2); - assertThat(output.get(0).getQuota()).isEqualTo(1500); - assertThat(output.get(1).getQuota()).isEqualTo(1500); - } - - @Test - public void testMultipleAppsPerUidIsCollated() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L)); - requests.add(makeNewRequest("com.test2", sTestVolUuid, 1001, 99L)); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - assertThat(output).hasSize(1); - assertThat(output.get(0).getQuota()).isEqualTo(1500); - } - - @Test - public void testTwoAppsTwoVolumesTwoUuidsShouldBESeparate() throws Exception { - ArrayList<CacheQuotaHint> requests = new ArrayList<>(); - requests.add(makeNewRequest("com.test", sTestVolUuid, 1001, 100L)); - requests.add(makeNewRequest("com.test2", sSecondTestVolUuid, 1001, 99L)); - - List<CacheQuotaHint> output = getService().onComputeCacheQuotaHints(requests); - - assertThat(output).hasSize(2); - assertThat(output.get(0).getQuota()).isEqualTo(1500); - assertThat(output.get(1).getQuota()).isEqualTo(1500); - } - - private CacheQuotaHint makeNewRequest(String packageName, String uuid, int uid, long foregroundTime) { - UsageStats stats = new UsageStats(); - stats.mPackageName = packageName; - stats.mTotalTimeInForeground = foregroundTime; - return new CacheQuotaHint.Builder() - .setVolumeUuid(uuid).setUid(uid).setUsageStats(stats).setQuota(-1).build(); - } -} diff --git a/packages/NetworkPermissionConfig/Android.bp b/packages/NetworkPermissionConfig/Android.bp deleted file mode 100644 index 6e50459a1dd3..000000000000 --- a/packages/NetworkPermissionConfig/Android.bp +++ /dev/null @@ -1,41 +0,0 @@ -// -// 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. -// - -java_defaults { - name: "NetworkPermissionConfigDefaults", - // TODO: mark app as hasCode=false in manifest once soong stops complaining about apps without - // a classes.dex. - srcs: ["src/**/*.java"], - platform_apis: true, - min_sdk_version: "28", - privileged: true, - manifest: "AndroidManifest.xml", -} - -// Stub APK to define permissions for NetworkStack -android_app { - name: "NetworkPermissionConfig", - defaults: ["NetworkPermissionConfigDefaults"], - certificate: "networkstack", -} - -// Alternative stub APK signed with platform certificate. To use with InProcessNetworkStack. -android_app { - name: "PlatformNetworkPermissionConfig", - defaults: ["NetworkPermissionConfigDefaults"], - certificate: "platform", - overrides: ["NetworkPermissionConfig"], -} diff --git a/packages/NetworkPermissionConfig/AndroidManifest.xml b/packages/NetworkPermissionConfig/AndroidManifest.xml deleted file mode 100644 index 34f987c8f0d4..000000000000 --- a/packages/NetworkPermissionConfig/AndroidManifest.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * 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. - */ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack.permissionconfig" - android:sharedUserId="android.uid.networkstack" - android:versionCode="11" - android:versionName="Q-initial"> - <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" /> - <!-- - This package only exists to define the below permissions, and enforce that they are only - granted to apps sharing the same signature. - Permissions defined here are intended to be used only by the NetworkStack: both - NetworkStack and this stub APK are to be signed with a dedicated certificate to ensure - that, with the below permissions being signature permissions. - - This APK *must* be installed, even if the NetworkStack app is not installed, because otherwise, - any application will be able to define this permission and the system will give that application - full access to the network stack. - --> - <permission android:name="android.permission.MAINLINE_NETWORK_STACK" - android:protectionLevel="signature"/> - - <application android:name="com.android.server.NetworkPermissionConfig"/> -</manifest> diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp deleted file mode 100644 index 3b644e9a6b71..000000000000 --- a/packages/NetworkStack/Android.bp +++ /dev/null @@ -1,132 +0,0 @@ -// -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -java_library { - name: "captiveportal-lib", - srcs: ["common/**/*.java"], - libs: [ - "androidx.annotation_annotation", - ], - sdk_version: "system_current", -} - -java_defaults { - name: "NetworkStackCommon", - sdk_version: "system_current", - min_sdk_version: "28", -} - -// Library including the network stack, used to compile both variants of the network stack -android_library { - name: "NetworkStackBase", - defaults: ["NetworkStackCommon"], - srcs: [ - "src/**/*.java", - ":framework-networkstack-shared-srcs", - ":services-networkstack-shared-srcs", - ":statslog-networkstack-java-gen", - ], - static_libs: [ - "androidx.annotation_annotation", - "ipmemorystore-client", - "netd_aidl_interface-V2-java", - "networkstack-aidl-interfaces-V3-java", - "datastallprotosnano", - "networkstackprotosnano", - "captiveportal-lib", - ], - manifest: "AndroidManifestBase.xml", -} - -cc_library_shared { - name: "libnetworkstackutilsjni", - srcs: [ - "jni/network_stack_utils_jni.cpp" - ], - sdk_version: "current", - shared_libs: [ - "liblog", - "libnativehelper_compat_libc++", - ], - - // We cannot use plain "libc++" here to link libc++ dynamically because it results in: - // java.lang.UnsatisfiedLinkError: dlopen failed: library "libc++_shared.so" not found - // even if "libc++" is added into jni_libs below. Adding "libc++_shared" into jni_libs doesn't - // build because soong complains of: - // module NetworkStack missing dependencies: libc++_shared - // - // So, link libc++ statically. This means that we also need to ensure that all the C++ libraries - // we depend on do not dynamically link libc++. This is currently the case, because liblog is - // C-only and libnativehelper_compat_libc also uses stl: "c++_static". - // - // TODO: find a better solution for this in R. - stl: "c++_static", - cflags: [ - "-Wall", - "-Werror", - "-Wno-unused-parameter", - ], -} - -java_defaults { - name: "NetworkStackAppCommon", - defaults: ["NetworkStackCommon"], - privileged: true, - static_libs: [ - "NetworkStackBase", - ], - jni_libs: [ - "libnativehelper_compat_libc++", - "libnetworkstackutilsjni", - ], - // Resources already included in NetworkStackBase - resource_dirs: [], - jarjar_rules: "jarjar-rules-shared.txt", - optimize: { - proguard_flags_files: ["proguard.flags"], - }, -} - -// Non-updatable network stack running in the system server process for devices not using the module -android_app { - name: "InProcessNetworkStack", - defaults: ["NetworkStackAppCommon"], - certificate: "platform", - manifest: "AndroidManifest_InProcess.xml", - // InProcessNetworkStack is a replacement for NetworkStack - overrides: ["NetworkStack"], - // The permission configuration *must* be included to ensure security of the device - required: ["PlatformNetworkPermissionConfig"], -} - -// Updatable network stack packaged as an application -android_app { - name: "NetworkStack", - defaults: ["NetworkStackAppCommon"], - certificate: "networkstack", - manifest: "AndroidManifest.xml", - use_embedded_native_libs: true, - // The permission configuration *must* be included to ensure security of the device - required: ["NetworkPermissionConfig"], -} - -genrule { - name: "statslog-networkstack-java-gen", - tools: ["stats-log-api-gen"], - cmd: "$(location stats-log-api-gen) --java $(out) --module network_stack" + - " --javaPackage com.android.networkstack.metrics --javaClass NetworkStackStatsLog", - out: ["com/android/networkstack/metrics/NetworkStackStatsLog.java"], -} diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml deleted file mode 100644 index de45784eb97f..000000000000 --- a/packages/NetworkStack/AndroidManifest.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * 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. - */ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack" - android:sharedUserId="android.uid.networkstack"> - <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" /> - - <!-- Permissions must be defined here, and not in the base manifest, as the network stack - running in the system server process does not need any permission, and having privileged - permissions added would cause crashes on startup unless they are also added to the - privileged permissions whitelist for that package. --> - <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> - <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> - <!-- Send latency broadcast as current user --> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> - <uses-permission android:name="android.permission.WAKE_LOCK" /> - <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> - <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> - <!-- Signature permission defined in NetworkStackStub --> - <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" /> - <application - android:extractNativeLibs="false" - android:persistent="true"> - <service android:name="com.android.server.NetworkStackService"> - <intent-filter> - <action android:name="android.net.INetworkStackConnector"/> - </intent-filter> - </service> - </application> -</manifest> diff --git a/packages/NetworkStack/AndroidManifestBase.xml b/packages/NetworkStack/AndroidManifestBase.xml deleted file mode 100644 index d00a55143605..000000000000 --- a/packages/NetworkStack/AndroidManifestBase.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * 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. - */ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack" - android:versionCode="11" - android:versionName="Q-initial"> - <application - android:label="NetworkStack" - android:defaultToDeviceProtectedStorage="true" - android:directBootAware="true" - android:usesCleartextTraffic="true"> - - <service android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService" - android:permission="android.permission.BIND_JOB_SERVICE" > - </service> - </application> -</manifest> diff --git a/packages/NetworkStack/AndroidManifest_InProcess.xml b/packages/NetworkStack/AndroidManifest_InProcess.xml deleted file mode 100644 index 275cd02a6cc0..000000000000 --- a/packages/NetworkStack/AndroidManifest_InProcess.xml +++ /dev/null @@ -1,31 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -/* - * 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. - */ ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.networkstack.inprocess" - android:sharedUserId="android.uid.system" - android:process="system"> - <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="28" /> - <application> - <service android:name="com.android.server.NetworkStackService" android:process="system"> - <intent-filter> - <action android:name="android.net.INetworkStackConnector.InProcess"/> - </intent-filter> - </service> - </application> -</manifest> diff --git a/packages/NetworkStack/OWNERS b/packages/NetworkStack/OWNERS deleted file mode 100644 index 0e1e65df818f..000000000000 --- a/packages/NetworkStack/OWNERS +++ /dev/null @@ -1,6 +0,0 @@ -codewiz@google.com -jchalard@google.com -junyulai@google.com -lorenzo@google.com -reminv@google.com -satk@google.com diff --git a/packages/NetworkStack/TEST_MAPPING b/packages/NetworkStack/TEST_MAPPING deleted file mode 100644 index fe9731fe577e..000000000000 --- a/packages/NetworkStack/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presubmit": [ - { - "name": "NetworkStackTests" - } - ] -}
\ No newline at end of file diff --git a/packages/NetworkStack/common/CaptivePortalProbeResult.java b/packages/NetworkStack/common/CaptivePortalProbeResult.java deleted file mode 100644 index 48cd48b30085..000000000000 --- a/packages/NetworkStack/common/CaptivePortalProbeResult.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.captiveportal; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -/** - * Result of calling isCaptivePortal(). - * @hide - */ -public final class CaptivePortalProbeResult { - public static final int SUCCESS_CODE = 204; - public static final int FAILED_CODE = 599; - public static final int PORTAL_CODE = 302; - // Set partial connectivity http response code to -1 to prevent conflict with the other http - // response codes. Besides the default http response code of probe result is set as 599 in - // NetworkMonitor#sendParallelHttpProbes(), so response code will be set as -1 only when - // NetworkMonitor detects partial connectivity. - /** - * @hide - */ - public static final int PARTIAL_CODE = -1; - - @NonNull - public static final CaptivePortalProbeResult FAILED = new CaptivePortalProbeResult(FAILED_CODE); - @NonNull - public static final CaptivePortalProbeResult SUCCESS = - new CaptivePortalProbeResult(SUCCESS_CODE); - public static final CaptivePortalProbeResult PARTIAL = - new CaptivePortalProbeResult(PARTIAL_CODE); - - private final int mHttpResponseCode; // HTTP response code returned from Internet probe. - @Nullable - public final String redirectUrl; // Redirect destination returned from Internet probe. - @Nullable - public final String detectUrl; // URL where a 204 response code indicates - // captive portal has been appeased. - @Nullable - public final CaptivePortalProbeSpec probeSpec; - - public CaptivePortalProbeResult(int httpResponseCode) { - this(httpResponseCode, null, null); - } - - public CaptivePortalProbeResult(int httpResponseCode, @Nullable String redirectUrl, - @Nullable String detectUrl) { - this(httpResponseCode, redirectUrl, detectUrl, null); - } - - public CaptivePortalProbeResult(int httpResponseCode, @Nullable String redirectUrl, - @Nullable String detectUrl, @Nullable CaptivePortalProbeSpec probeSpec) { - mHttpResponseCode = httpResponseCode; - this.redirectUrl = redirectUrl; - this.detectUrl = detectUrl; - this.probeSpec = probeSpec; - } - - public boolean isSuccessful() { - return mHttpResponseCode == SUCCESS_CODE; - } - - public boolean isPortal() { - return !isSuccessful() && (mHttpResponseCode >= 200) && (mHttpResponseCode <= 399); - } - - public boolean isFailed() { - return !isSuccessful() && !isPortal(); - } - - public boolean isPartialConnectivity() { - return mHttpResponseCode == PARTIAL_CODE; - } -} diff --git a/packages/NetworkStack/common/CaptivePortalProbeSpec.java b/packages/NetworkStack/common/CaptivePortalProbeSpec.java deleted file mode 100644 index bf983a50ab51..000000000000 --- a/packages/NetworkStack/common/CaptivePortalProbeSpec.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.captiveportal; - -import static android.net.captiveportal.CaptivePortalProbeResult.PORTAL_CODE; -import static android.net.captiveportal.CaptivePortalProbeResult.SUCCESS_CODE; - -import android.text.TextUtils; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; - -import java.net.MalformedURLException; -import java.net.URL; -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; - -/** @hide */ -public abstract class CaptivePortalProbeSpec { - private static final String TAG = CaptivePortalProbeSpec.class.getSimpleName(); - private static final String REGEX_SEPARATOR = "@@/@@"; - private static final String SPEC_SEPARATOR = "@@,@@"; - - private final String mEncodedSpec; - private final URL mUrl; - - CaptivePortalProbeSpec(@NonNull String encodedSpec, @NonNull URL url) { - mEncodedSpec = checkNotNull(encodedSpec); - mUrl = checkNotNull(url); - } - - /** - * Parse a {@link CaptivePortalProbeSpec} from a {@link String}. - * - * <p>The valid format is a URL followed by two regular expressions, each separated by "@@/@@". - * @throws MalformedURLException The URL has invalid format for {@link URL#URL(String)}. - * @throws ParseException The string is empty, does not match the above format, or a regular - * expression is invalid for {@link Pattern#compile(String)}. - * @hide - */ - @VisibleForTesting - @NonNull - public static CaptivePortalProbeSpec parseSpec(@NonNull String spec) throws ParseException, - MalformedURLException { - if (TextUtils.isEmpty(spec)) { - throw new ParseException("Empty probe spec", 0 /* errorOffset */); - } - - String[] splits = TextUtils.split(spec, REGEX_SEPARATOR); - if (splits.length != 3) { - throw new ParseException("Probe spec does not have 3 parts", 0 /* errorOffset */); - } - - final int statusRegexPos = splits[0].length() + REGEX_SEPARATOR.length(); - final int locationRegexPos = statusRegexPos + splits[1].length() + REGEX_SEPARATOR.length(); - final Pattern statusRegex = parsePatternIfNonEmpty(splits[1], statusRegexPos); - final Pattern locationRegex = parsePatternIfNonEmpty(splits[2], locationRegexPos); - - return new RegexMatchProbeSpec(spec, new URL(splits[0]), statusRegex, locationRegex); - } - - @Nullable - private static Pattern parsePatternIfNonEmpty(@Nullable String pattern, int pos) - throws ParseException { - if (TextUtils.isEmpty(pattern)) { - return null; - } - try { - return Pattern.compile(pattern); - } catch (PatternSyntaxException e) { - throw new ParseException( - String.format("Invalid status pattern [%s]: %s", pattern, e), - pos /* errorOffset */); - } - } - - /** - * Parse a {@link CaptivePortalProbeSpec} from a {@link String}, or return a fallback spec - * based on the status code of the provided URL if the spec cannot be parsed. - */ - @Nullable - public static CaptivePortalProbeSpec parseSpecOrNull(@Nullable String spec) { - if (spec != null) { - try { - return parseSpec(spec); - } catch (ParseException | MalformedURLException e) { - Log.e(TAG, "Invalid probe spec: " + spec, e); - // Fall through - } - } - return null; - } - - /** - * Parse a config String to build an array of {@link CaptivePortalProbeSpec}. - * - * <p>Each spec is separated by @@,@@ and follows the format for {@link #parseSpec(String)}. - * <p>This method does not throw but ignores any entry that could not be parsed. - */ - @NonNull - public static Collection<CaptivePortalProbeSpec> parseCaptivePortalProbeSpecs( - @NonNull String settingsVal) { - List<CaptivePortalProbeSpec> specs = new ArrayList<>(); - if (settingsVal != null) { - for (String spec : TextUtils.split(settingsVal, SPEC_SEPARATOR)) { - try { - specs.add(parseSpec(spec)); - } catch (ParseException | MalformedURLException e) { - Log.e(TAG, "Invalid probe spec: " + spec, e); - } - } - } - - if (specs.isEmpty()) { - Log.e(TAG, String.format("could not create any validation spec from %s", settingsVal)); - } - return specs; - } - - /** - * Get the probe result from HTTP status and location header. - */ - @NonNull - public abstract CaptivePortalProbeResult getResult(int status, @Nullable String locationHeader); - - @NonNull - public String getEncodedSpec() { - return mEncodedSpec; - } - - @NonNull - public URL getUrl() { - return mUrl; - } - - /** - * Implementation of {@link CaptivePortalProbeSpec} that is based on configurable regular - * expressions for the HTTP status code and location header (if any). Matches indicate that - * the page is not a portal. - * This probe cannot fail: it always returns SUCCESS_CODE or PORTAL_CODE - */ - private static class RegexMatchProbeSpec extends CaptivePortalProbeSpec { - @Nullable - final Pattern mStatusRegex; - @Nullable - final Pattern mLocationHeaderRegex; - - RegexMatchProbeSpec( - String spec, URL url, Pattern statusRegex, Pattern locationHeaderRegex) { - super(spec, url); - mStatusRegex = statusRegex; - mLocationHeaderRegex = locationHeaderRegex; - } - - @Override - public CaptivePortalProbeResult getResult(int status, String locationHeader) { - final boolean statusMatch = safeMatch(String.valueOf(status), mStatusRegex); - final boolean locationMatch = safeMatch(locationHeader, mLocationHeaderRegex); - final int returnCode = statusMatch && locationMatch ? SUCCESS_CODE : PORTAL_CODE; - return new CaptivePortalProbeResult( - returnCode, locationHeader, getUrl().toString(), this); - } - } - - private static boolean safeMatch(@Nullable String value, @Nullable Pattern pattern) { - // No value is a match ("no location header" passes the location rule for non-redirects) - return pattern == null || TextUtils.isEmpty(value) || pattern.matcher(value).matches(); - } - - // Throws NullPointerException if the input is null. - private static <T> T checkNotNull(T object) { - if (object == null) throw new NullPointerException(); - return object; - } -} diff --git a/packages/NetworkStack/jarjar-rules-shared.txt b/packages/NetworkStack/jarjar-rules-shared.txt deleted file mode 100644 index 7346b1ae81e6..000000000000 --- a/packages/NetworkStack/jarjar-rules-shared.txt +++ /dev/null @@ -1,10 +0,0 @@ -rule com.android.internal.util.** android.net.networkstack.util.@1 - -rule android.net.shared.Inet4AddressUtils* android.net.networkstack.shared.Inet4AddressUtils@1 -rule android.net.shared.InetAddressUtils* android.net.networkstack.shared.InetAddressUtils@1 - -# Ignore DhcpResultsParcelable, but jarjar DhcpResults -# TODO: move DhcpResults into services.net and delete from here -rule android.net.DhcpResultsParcelable* @0 -rule android.net.DhcpResults* android.net.networkstack.DhcpResults@1 -rule android.net.LocalLog* android.net.networkstack.LocalLog@1 diff --git a/packages/NetworkStack/jni/network_stack_utils_jni.cpp b/packages/NetworkStack/jni/network_stack_utils_jni.cpp deleted file mode 100644 index f2ba5757ed80..000000000000 --- a/packages/NetworkStack/jni/network_stack_utils_jni.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2019, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "NetworkStackUtils-JNI" - -#include <errno.h> -#include <jni.h> -#include <linux/filter.h> -#include <linux/if_arp.h> -#include <net/if.h> -#include <netinet/ether.h> -#include <netinet/icmp6.h> -#include <netinet/ip.h> -#include <netinet/ip6.h> -#include <netinet/udp.h> -#include <stdlib.h> - -#include <string> - -#include <nativehelper/JNIHelp.h> -#include <android/log.h> - -namespace android { -constexpr const char NETWORKSTACKUTILS_PKG_NAME[] = "android/net/util/NetworkStackUtils"; - -static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type); -static const uint32_t kEtherHeaderLen = sizeof(ether_header); -static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol); -static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off); -static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt); -static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr); -static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type); -static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source); -static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest); -static const uint16_t kDhcpClientPort = 68; - -static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst) { - if (env->GetArrayLength(addr) != len) { - return false; - } - env->GetByteArrayRegion(addr, 0, len, reinterpret_cast<jbyte*>(dst)); - return true; -} - -static void network_stack_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr, - jbyteArray ipv4Addr, jstring ifname, jobject javaFd) { - arpreq req = {}; - sockaddr_in& netAddrStruct = *reinterpret_cast<sockaddr_in*>(&req.arp_pa); - sockaddr& ethAddrStruct = req.arp_ha; - - ethAddrStruct.sa_family = ARPHRD_ETHER; - if (!checkLenAndCopy(env, ethAddr, ETH_ALEN, ethAddrStruct.sa_data)) { - jniThrowException(env, "java/io/IOException", "Invalid ethAddr length"); - return; - } - - netAddrStruct.sin_family = AF_INET; - if (!checkLenAndCopy(env, ipv4Addr, sizeof(in_addr), &netAddrStruct.sin_addr)) { - jniThrowException(env, "java/io/IOException", "Invalid ipv4Addr length"); - return; - } - - int ifLen = env->GetStringLength(ifname); - // IFNAMSIZ includes the terminating NULL character - if (ifLen >= IFNAMSIZ) { - jniThrowException(env, "java/io/IOException", "ifname too long"); - return; - } - env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev); - - req.arp_flags = ATF_COM; // Completed entry (ha valid) - int fd = jniGetFDFromFileDescriptor(env, javaFd); - if (fd < 0) { - jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor"); - return; - } - // See also: man 7 arp - if (ioctl(fd, SIOCSARP, &req)) { - jniThrowExceptionFmt(env, "java/io/IOException", "ioctl error: %s", strerror(errno)); - return; - } -} - -static void network_stack_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd) { - static sock_filter filter_code[] = { - // Check the protocol is UDP. - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6), - - // Check this is not a fragment. - BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset), - BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 4, 0), - - // Get the IP header length. - BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen), - - // Check the destination port. - BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1), - - // Accept or reject. - BPF_STMT(BPF_RET | BPF_K, 0xffff), - BPF_STMT(BPF_RET | BPF_K, 0) - }; - static const sock_fprog filter = { - sizeof(filter_code) / sizeof(filter_code[0]), - filter_code, - }; - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); - } -} - -static void network_stack_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject javaFd, - jint hardwareAddressType) { - if (hardwareAddressType != ARPHRD_ETHER) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "attachRaFilter only supports ARPHRD_ETHER"); - return; - } - - static sock_filter filter_code[] = { - // Check IPv6 Next Header is ICMPv6. - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3), - - // Check ICMPv6 type is Router Advertisement. - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1), - - // Accept or reject. - BPF_STMT(BPF_RET | BPF_K, 0xffff), - BPF_STMT(BPF_RET | BPF_K, 0) - }; - static const sock_fprog filter = { - sizeof(filter_code) / sizeof(filter_code[0]), - filter_code, - }; - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); - } -} - -// TODO: Move all this filter code into libnetutils. -static void network_stack_utils_attachControlPacketFilter( - JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) { - if (hardwareAddressType != ARPHRD_ETHER) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "attachControlPacketFilter only supports ARPHRD_ETHER"); - return; - } - - // Capture all: - // - ARPs - // - DHCPv4 packets - // - Router Advertisements & Solicitations - // - Neighbor Advertisements & Solicitations - // - // tcpdump: - // arp or - // '(ip and udp port 68)' or - // '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)' - static sock_filter filter_code[] = { - // Load the link layer next payload field. - BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kEtherTypeOffset), - - // Accept all ARP. - // TODO: Figure out how to better filter ARPs on noisy networks. - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0), - - // If IPv4: - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9), - - // Check the protocol is UDP. - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 14), - - // Check this is not a fragment. - BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset), - BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 12, 0), - - // Get the IP header length. - BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen), - - // Check the source port. - BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPSrcPortIndirectOffset), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 8, 0), - - // Check the destination port. - BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 6, 7), - - // IPv6 ... - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6), - // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ... - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader), - BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 4), - // ... and check the ICMPv6 type is one of RS/RA/NS/NA. - BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset), - BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, ND_ROUTER_SOLICIT, 0, 2), - BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0), - - // Accept or reject. - BPF_STMT(BPF_RET | BPF_K, 0xffff), - BPF_STMT(BPF_RET | BPF_K, 0) - }; - static const sock_fprog filter = { - sizeof(filter_code) / sizeof(filter_code[0]), - filter_code, - }; - - int fd = jniGetFDFromFileDescriptor(env, javaFd); - if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { - jniThrowExceptionFmt(env, "java/net/SocketException", - "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); - } -} - -/* - * JNI registration. - */ -static const JNINativeMethod gNetworkStackUtilsMethods[] = { - /* name, signature, funcPtr */ - { "addArpEntry", "([B[BLjava/lang/String;Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_addArpEntry }, - { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_attachDhcpFilter }, - { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachRaFilter }, - { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachControlPacketFilter }, -}; - -extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { - JNIEnv *env; - if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "ERROR: GetEnv failed"); - return JNI_ERR; - } - - if (jniRegisterNativeMethods(env, NETWORKSTACKUTILS_PKG_NAME, - gNetworkStackUtilsMethods, NELEM(gNetworkStackUtilsMethods)) < 0) { - return JNI_ERR; - } - - return JNI_VERSION_1_6; - -} -}; // namespace android diff --git a/packages/NetworkStack/proguard.flags b/packages/NetworkStack/proguard.flags deleted file mode 100644 index c60f6c338d83..000000000000 --- a/packages/NetworkStack/proguard.flags +++ /dev/null @@ -1,9 +0,0 @@ --keepclassmembers class android.net.ip.IpClient { - static final int CMD_*; - static final int EVENT_*; -} - --keepclassmembers class android.net.dhcp.DhcpClient { - static final int CMD_*; - static final int EVENT_*; -} diff --git a/packages/NetworkStack/res/values-mcc460/config.xml b/packages/NetworkStack/res/values-mcc460/config.xml deleted file mode 100644 index fd4a8481ab22..000000000000 --- a/packages/NetworkStack/res/values-mcc460/config.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <!-- Network validation URL configuration for devices using a Chinese SIM (MCC 460). - The below URLs are often whitelisted by captive portals, so they should not be used in the - general case as this could degrade the user experience (portals not detected properly). - However in China the default URLs are not accessible in general. The below alternatives - should allow users to connect to local networks normally. --> - <string name="default_captive_portal_https_url" translatable="false">https://connectivitycheck.gstatic.com/generate_204</string> - <string-array name="default_captive_portal_fallback_urls" translatable="false"> - <item>http://www.googleapis.cn/generate_204</item> - </string-array> -</resources> diff --git a/packages/NetworkStack/res/values/config.xml b/packages/NetworkStack/res/values/config.xml deleted file mode 100644 index 478ed6b06596..000000000000 --- a/packages/NetworkStack/res/values/config.xml +++ /dev/null @@ -1,46 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - <!-- - OEMs that wish to change the below settings must do so via a runtime resource overlay package - and *NOT* by changing this file. This file is part of the NetworkStack mainline module. - The overlays must apply to the config_* values, not the default_* values. The default_* - values are meant to be the default when no other configuration is specified. - --> - - <!-- DNS probe timeout for network validation. Enough for 3 DNS queries 5 seconds apart. --> - <integer name="default_captive_portal_dns_probe_timeout">12500</integer> - - <!-- HTTP URL for network validation, to use for detecting captive portals. --> - <string name="default_captive_portal_http_url" translatable="false">http://connectivitycheck.gstatic.com/generate_204</string> - - <!-- HTTPS URL for network validation, to use for confirming internet connectivity. --> - <string name="default_captive_portal_https_url" translatable="false">https://www.google.com/generate_204</string> - - <!-- List of fallback URLs to use for detecting captive portals. --> - <string-array name="default_captive_portal_fallback_urls" translatable="false"> - <item>http://www.google.com/gen_204</item> - <item>http://play.googleapis.com/generate_204</item> - </string-array> - - <!-- List of fallback probe specs to use for detecting captive portals. - This is an alternative to fallback URLs that provides more flexibility on detection rules. - Empty, so unused by default. --> - <string-array name="default_captive_portal_fallback_probe_specs" translatable="false"> - </string-array> - - <!-- Configuration hooks for the above settings. - Empty by default but may be overridden by RROs. --> - <integer name="config_captive_portal_dns_probe_timeout"></integer> - <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook --> - <string name="config_captive_portal_http_url" translatable="false"></string> - <!--suppress CheckTagEmptyBody: overlayable resource to use as configuration hook --> - <string name="config_captive_portal_https_url" translatable="false"></string> - <string-array name="config_captive_portal_fallback_urls" translatable="false"> - </string-array> - <string-array name="config_captive_portal_fallback_probe_specs" translatable="false"> - </string-array> - - <!-- Customized default DNS Servers address. --> - <string-array name="config_default_dns_servers" translatable="false"> - </string-array> -</resources> diff --git a/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java b/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java deleted file mode 100644 index 41715b2a4798..000000000000 --- a/packages/NetworkStack/src/android/net/NetworkStackIpMemoryStore.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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.content.Context; - -import java.util.concurrent.ExecutionException; -import java.util.function.Consumer; - -/** - * service used to communicate with the ip memory store service in network stack, - * which is running in the same module. - * @see com.android.server.connectivity.ipmemorystore.IpMemoryStoreService - * @hide - */ -public class NetworkStackIpMemoryStore extends IpMemoryStoreClient { - @NonNull private final IIpMemoryStore mService; - - public NetworkStackIpMemoryStore(@NonNull final Context context, - @NonNull final IIpMemoryStore service) { - super(context); - mService = service; - } - - @Override - protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException { - cb.accept(mService); - } -} diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java deleted file mode 100644 index f05431968684..000000000000 --- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java +++ /dev/null @@ -1,1981 +0,0 @@ -/* - * Copyright (C) 2016 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.apf; - -import static android.net.util.SocketUtils.makePacketSocketAddress; -import static android.system.OsConstants.AF_PACKET; -import static android.system.OsConstants.ARPHRD_ETHER; -import static android.system.OsConstants.ETH_P_ARP; -import static android.system.OsConstants.ETH_P_IP; -import static android.system.OsConstants.ETH_P_IPV6; -import static android.system.OsConstants.IPPROTO_ICMPV6; -import static android.system.OsConstants.IPPROTO_TCP; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_RAW; - -import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE; -import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION; - -import android.annotation.Nullable; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NattKeepalivePacketDataParcelable; -import android.net.TcpKeepalivePacketDataParcelable; -import android.net.apf.ApfGenerator.IllegalInstructionException; -import android.net.apf.ApfGenerator.Register; -import android.net.ip.IpClient.IpClientCallbacksWrapper; -import android.net.metrics.ApfProgramEvent; -import android.net.metrics.ApfStats; -import android.net.metrics.IpConnectivityLog; -import android.net.metrics.RaEvent; -import android.net.util.InterfaceParams; -import android.net.util.NetworkStackUtils; -import android.os.PowerManager; -import android.os.SystemClock; -import android.system.ErrnoException; -import android.system.Os; -import android.text.format.DateUtils; -import android.util.Log; -import android.util.Pair; -import android.util.SparseArray; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.HexDump; -import com.android.internal.util.IndentingPrintWriter; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.SocketAddress; -import java.net.SocketException; -import java.net.UnknownHostException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.ArrayList; -import java.util.Arrays; - -/** - * For networks that support packet filtering via APF programs, {@code ApfFilter} - * listens for IPv6 ICMPv6 router advertisements (RAs) and generates APF programs to - * filter out redundant duplicate ones. - * - * Threading model: - * A collection of RAs we've received is kept in mRas. Generating APF programs uses mRas to - * know what RAs to filter for, thus generating APF programs is dependent on mRas. - * mRas can be accessed by multiple threads: - * - ReceiveThread, which listens for RAs and adds them to mRas, and generates APF programs. - * - callers of: - * - setMulticastFilter(), which can cause an APF program to be generated. - * - dump(), which dumps mRas among other things. - * - shutdown(), which clears mRas. - * So access to mRas is synchronized. - * - * @hide - */ -public class ApfFilter { - - // Helper class for specifying functional filter parameters. - public static class ApfConfiguration { - public ApfCapabilities apfCapabilities; - public boolean multicastFilter; - public boolean ieee802_3Filter; - public int[] ethTypeBlackList; - } - - // Enums describing the outcome of receiving an RA packet. - private static enum ProcessRaResult { - MATCH, // Received RA matched a known RA - DROPPED, // Received RA ignored due to MAX_RAS - PARSE_ERROR, // Received RA could not be parsed - ZERO_LIFETIME, // Received RA had 0 lifetime - UPDATE_NEW_RA, // APF program updated for new RA - UPDATE_EXPIRY // APF program updated for expiry - } - - /** - * APF packet counters. - * - * Packet counters are 32bit big-endian values, and allocated near the end of the APF data - * buffer, using negative byte offsets, where -4 is equivalent to maximumApfProgramSize - 4, - * the last writable 32bit word. - */ - @VisibleForTesting - public static enum Counter { - RESERVED_OOB, // Points to offset 0 from the end of the buffer (out-of-bounds) - TOTAL_PACKETS, - PASSED_ARP, - PASSED_DHCP, - PASSED_IPV4, - PASSED_IPV6_NON_ICMP, - PASSED_IPV4_UNICAST, - PASSED_IPV6_ICMP, - PASSED_IPV6_UNICAST_NON_ICMP, - PASSED_ARP_NON_IPV4, - PASSED_ARP_UNKNOWN, - PASSED_ARP_UNICAST_REPLY, - PASSED_NON_IP_UNICAST, - DROPPED_ETH_BROADCAST, - DROPPED_RA, - DROPPED_GARP_REPLY, - DROPPED_ARP_OTHER_HOST, - DROPPED_IPV4_L2_BROADCAST, - DROPPED_IPV4_BROADCAST_ADDR, - DROPPED_IPV4_BROADCAST_NET, - DROPPED_IPV4_MULTICAST, - DROPPED_IPV6_ROUTER_SOLICITATION, - DROPPED_IPV6_MULTICAST_NA, - DROPPED_IPV6_MULTICAST, - DROPPED_IPV6_MULTICAST_PING, - DROPPED_IPV6_NON_ICMP_MULTICAST, - DROPPED_802_3_FRAME, - DROPPED_ETHERTYPE_BLACKLISTED, - DROPPED_ARP_REPLY_SPA_NO_HOST, - DROPPED_IPV4_KEEPALIVE_ACK, - DROPPED_IPV6_KEEPALIVE_ACK, - DROPPED_IPV4_NATT_KEEPALIVE; - - // Returns the negative byte offset from the end of the APF data segment for - // a given counter. - public int offset() { - return - this.ordinal() * 4; // Currently, all counters are 32bit long. - } - - // Returns the total size of the data segment in bytes. - public static int totalSize() { - return (Counter.class.getEnumConstants().length - 1) * 4; - } - } - - /** - * When APFv4 is supported, loads R1 with the offset of the specified counter. - */ - private void maybeSetupCounter(ApfGenerator gen, Counter c) { - if (mApfCapabilities.hasDataAccess()) { - gen.addLoadImmediate(Register.R1, c.offset()); - } - } - - // When APFv4 is supported, these point to the trampolines generated by emitEpilogue(). - // Otherwise, they're just aliases for PASS_LABEL and DROP_LABEL. - private final String mCountAndPassLabel; - private final String mCountAndDropLabel; - - // Thread to listen for RAs. - @VisibleForTesting - class ReceiveThread extends Thread { - private final byte[] mPacket = new byte[1514]; - private final FileDescriptor mSocket; - private final long mStart = SystemClock.elapsedRealtime(); - - private int mReceivedRas = 0; - private int mMatchingRas = 0; - private int mDroppedRas = 0; - private int mParseErrors = 0; - private int mZeroLifetimeRas = 0; - private int mProgramUpdates = 0; - - private volatile boolean mStopped; - - public ReceiveThread(FileDescriptor socket) { - mSocket = socket; - } - - public void halt() { - mStopped = true; - // Interrupts the read() call the thread is blocked in. - NetworkStackUtils.closeSocketQuietly(mSocket); - } - - @Override - public void run() { - log("begin monitoring"); - while (!mStopped) { - try { - int length = Os.read(mSocket, mPacket, 0, mPacket.length); - updateStats(processRa(mPacket, length)); - } catch (IOException|ErrnoException e) { - if (!mStopped) { - Log.e(TAG, "Read error", e); - } - } - } - logStats(); - } - - private void updateStats(ProcessRaResult result) { - mReceivedRas++; - switch(result) { - case MATCH: - mMatchingRas++; - return; - case DROPPED: - mDroppedRas++; - return; - case PARSE_ERROR: - mParseErrors++; - return; - case ZERO_LIFETIME: - mZeroLifetimeRas++; - return; - case UPDATE_EXPIRY: - mMatchingRas++; - mProgramUpdates++; - return; - case UPDATE_NEW_RA: - mProgramUpdates++; - return; - } - } - - private void logStats() { - final long nowMs = SystemClock.elapsedRealtime(); - synchronized (this) { - final ApfStats stats = new ApfStats.Builder() - .setReceivedRas(mReceivedRas) - .setMatchingRas(mMatchingRas) - .setDroppedRas(mDroppedRas) - .setParseErrors(mParseErrors) - .setZeroLifetimeRas(mZeroLifetimeRas) - .setProgramUpdates(mProgramUpdates) - .setDurationMs(nowMs - mStart) - .setMaxProgramSize(mApfCapabilities.maximumApfProgramSize) - .setProgramUpdatesAll(mNumProgramUpdates) - .setProgramUpdatesAllowingMulticast(mNumProgramUpdatesAllowingMulticast) - .build(); - mMetricsLog.log(stats); - logApfProgramEventLocked(nowMs / DateUtils.SECOND_IN_MILLIS); - } - } - } - - private static final String TAG = "ApfFilter"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - private static final int ETH_HEADER_LEN = 14; - private static final int ETH_DEST_ADDR_OFFSET = 0; - private static final int ETH_ETHERTYPE_OFFSET = 12; - private static final int ETH_TYPE_MIN = 0x0600; - private static final int ETH_TYPE_MAX = 0xFFFF; - private static final byte[] ETH_BROADCAST_MAC_ADDRESS = - {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; - // TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN. - private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2; - private static final int IPV4_FRAGMENT_OFFSET_OFFSET = ETH_HEADER_LEN + 6; - // Endianness is not an issue for this constant because the APF interpreter always operates in - // network byte order. - private static final int IPV4_FRAGMENT_OFFSET_MASK = 0x1fff; - private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; - private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; - private static final int IPV4_ANY_HOST_ADDRESS = 0; - private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255 - private static final int IPV4_HEADER_LEN = 20; // Without options - - // Traffic class and Flow label are not byte aligned. Luckily we - // don't care about either value so we'll consider bytes 1-3 of the - // IPv6 header as don't care. - private static final int IPV6_FLOW_LABEL_OFFSET = ETH_HEADER_LEN + 1; - private static final int IPV6_FLOW_LABEL_LEN = 3; - private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; - private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; - private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24; - private static final int IPV6_HEADER_LEN = 40; - // The IPv6 all nodes address ff02::1 - private static final byte[] IPV6_ALL_NODES_ADDRESS = - { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; - - private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; - - // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT - private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2; - private static final int UDP_HEADER_LEN = 8; - - private static final int TCP_HEADER_SIZE_OFFSET = 12; - - private static final int DHCP_CLIENT_PORT = 68; - // NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT - private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28; - - private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN; - private static final byte[] ARP_IPV4_HEADER = { - 0, 1, // Hardware type: Ethernet (1) - 8, 0, // Protocol type: IP (0x0800) - 6, // Hardware size: 6 - 4, // Protocol size: 4 - }; - private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6; - // Opcode: ARP request (0x0001), ARP reply (0x0002) - private static final short ARP_OPCODE_REQUEST = 1; - private static final short ARP_OPCODE_REPLY = 2; - private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14; - private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24; - // Do not log ApfProgramEvents whose actual lifetimes was less than this. - private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2; - // Limit on the Black List size to cap on program usage for this - // TODO: Select a proper max length - private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20; - - private final ApfCapabilities mApfCapabilities; - private final IpClientCallbacksWrapper mIpClientCallback; - private final InterfaceParams mInterfaceParams; - private final IpConnectivityLog mMetricsLog; - - @VisibleForTesting - byte[] mHardwareAddress; - @VisibleForTesting - ReceiveThread mReceiveThread; - @GuardedBy("this") - private long mUniqueCounter; - @GuardedBy("this") - private boolean mMulticastFilter; - @GuardedBy("this") - private boolean mInDozeMode; - private final boolean mDrop802_3Frames; - private final int[] mEthTypeBlackList; - - // Detects doze mode state transitions. - private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) { - PowerManager powerManager = - (PowerManager) context.getSystemService(Context.POWER_SERVICE); - final boolean deviceIdle = powerManager.isDeviceIdleMode(); - setDozeMode(deviceIdle); - } - } - }; - private final Context mContext; - - // Our IPv4 address, if we have just one, otherwise null. - @GuardedBy("this") - private byte[] mIPv4Address; - // The subnet prefix length of our IPv4 network. Only valid if mIPv4Address is not null. - @GuardedBy("this") - private int mIPv4PrefixLength; - - @VisibleForTesting - ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams, - IpClientCallbacksWrapper ipClientCallback, IpConnectivityLog log) { - mApfCapabilities = config.apfCapabilities; - mIpClientCallback = ipClientCallback; - mInterfaceParams = ifParams; - mMulticastFilter = config.multicastFilter; - mDrop802_3Frames = config.ieee802_3Filter; - mContext = context; - - if (mApfCapabilities.hasDataAccess()) { - mCountAndPassLabel = "countAndPass"; - mCountAndDropLabel = "countAndDrop"; - } else { - // APFv4 unsupported: turn jumps to the counter trampolines to immediately PASS or DROP, - // preserving the original pre-APFv4 behavior. - mCountAndPassLabel = ApfGenerator.PASS_LABEL; - mCountAndDropLabel = ApfGenerator.DROP_LABEL; - } - - // Now fill the black list from the passed array - mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList); - - mMetricsLog = log; - - // TODO: ApfFilter should not generate programs until IpClient sends provisioning success. - maybeStartFilter(); - - // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter. - mContext.registerReceiver(mDeviceIdleReceiver, - new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); - } - - public synchronized void setDataSnapshot(byte[] data) { - mDataSnapshot = data; - } - - private void log(String s) { - Log.d(TAG, "(" + mInterfaceParams.name + "): " + s); - } - - @GuardedBy("this") - private long getUniqueNumberLocked() { - return mUniqueCounter++; - } - - @GuardedBy("this") - private static int[] filterEthTypeBlackList(int[] ethTypeBlackList) { - ArrayList<Integer> bl = new ArrayList<Integer>(); - - for (int p : ethTypeBlackList) { - // Check if the protocol is a valid ether type - if ((p < ETH_TYPE_MIN) || (p > ETH_TYPE_MAX)) { - continue; - } - - // Check if the protocol is not repeated in the passed array - if (bl.contains(p)) { - continue; - } - - // Check if list reach its max size - if (bl.size() == APF_MAX_ETH_TYPE_BLACK_LIST_LEN) { - Log.w(TAG, "Passed EthType Black List size too large (" + bl.size() + - ") using top " + APF_MAX_ETH_TYPE_BLACK_LIST_LEN + " protocols"); - break; - } - - // Now add the protocol to the list - bl.add(p); - } - - return bl.stream().mapToInt(Integer::intValue).toArray(); - } - - /** - * Attempt to start listening for RAs and, if RAs are received, generating and installing - * filters to ignore useless RAs. - */ - @VisibleForTesting - void maybeStartFilter() { - FileDescriptor socket; - try { - mHardwareAddress = mInterfaceParams.macAddr.toByteArray(); - synchronized(this) { - // Clear the APF memory to reset all counters upon connecting to the first AP - // in an SSID. This is limited to APFv4 devices because this large write triggers - // a crash on some older devices (b/78905546). - if (mApfCapabilities.hasDataAccess()) { - byte[] zeroes = new byte[mApfCapabilities.maximumApfProgramSize]; - mIpClientCallback.installPacketFilter(zeroes); - } - - // Install basic filters - installNewProgramLocked(); - } - socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6); - SocketAddress addr = makePacketSocketAddress( - (short) ETH_P_IPV6, mInterfaceParams.index); - Os.bind(socket, addr); - NetworkStackUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat); - } catch(SocketException|ErrnoException e) { - Log.e(TAG, "Error starting filter", e); - return; - } - mReceiveThread = new ReceiveThread(socket); - mReceiveThread.start(); - } - - // Returns seconds since device boot. - @VisibleForTesting - protected long currentTimeSeconds() { - return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS; - } - - public static class InvalidRaException extends Exception { - public InvalidRaException(String m) { - super(m); - } - } - - // A class to hold information about an RA. - @VisibleForTesting - class Ra { - // From RFC4861: - private static final int ICMP6_RA_HEADER_LEN = 16; - private static final int ICMP6_RA_CHECKSUM_OFFSET = - ETH_HEADER_LEN + IPV6_HEADER_LEN + 2; - private static final int ICMP6_RA_CHECKSUM_LEN = 2; - private static final int ICMP6_RA_OPTION_OFFSET = - ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN; - private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET = - ETH_HEADER_LEN + IPV6_HEADER_LEN + 6; - private static final int ICMP6_RA_ROUTER_LIFETIME_LEN = 2; - // Prefix information option. - private static final int ICMP6_PREFIX_OPTION_TYPE = 3; - private static final int ICMP6_PREFIX_OPTION_LEN = 32; - private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4; - private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN = 4; - private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8; - private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN = 4; - - // From RFC6106: Recursive DNS Server option - private static final int ICMP6_RDNSS_OPTION_TYPE = 25; - // From RFC6106: DNS Search List option - private static final int ICMP6_DNSSL_OPTION_TYPE = 31; - - // From RFC4191: Route Information option - private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24; - // Above three options all have the same format: - private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4; - private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4; - - // Note: mPacket's position() cannot be assumed to be reset. - private final ByteBuffer mPacket; - // List of binary ranges that include the whole packet except the lifetimes. - // Pairs consist of offset and length. - private final ArrayList<Pair<Integer, Integer>> mNonLifetimes = - new ArrayList<Pair<Integer, Integer>>(); - // Minimum lifetime in packet - long mMinLifetime; - // When the packet was last captured, in seconds since Unix Epoch - long mLastSeen; - - // For debugging only. Offsets into the packet where PIOs are. - private final ArrayList<Integer> mPrefixOptionOffsets = new ArrayList<>(); - - // For debugging only. Offsets into the packet where RDNSS options are. - private final ArrayList<Integer> mRdnssOptionOffsets = new ArrayList<>(); - - // For debugging only. How many times this RA was seen. - int seenCount = 0; - - // For debugging only. Returns the hex representation of the last matching packet. - String getLastMatchingPacket() { - return HexDump.toHexString(mPacket.array(), 0, mPacket.capacity(), - false /* lowercase */); - } - - // For debugging only. Returns the string representation of the IPv6 address starting at - // position pos in the packet. - private String IPv6AddresstoString(int pos) { - try { - byte[] array = mPacket.array(); - // Can't just call copyOfRange() and see if it throws, because if it reads past the - // end it pads with zeros instead of throwing. - if (pos < 0 || pos + 16 > array.length || pos + 16 < pos) { - return "???"; - } - byte[] addressBytes = Arrays.copyOfRange(array, pos, pos + 16); - InetAddress address = (Inet6Address) InetAddress.getByAddress(addressBytes); - return address.getHostAddress(); - } catch (UnsupportedOperationException e) { - // array() failed. Cannot happen, mPacket is array-backed and read-write. - return "???"; - } catch (ClassCastException|UnknownHostException e) { - // Cannot happen. - return "???"; - } - } - - // Can't be static because it's in a non-static inner class. - // TODO: Make this static once RA is its own class. - private void prefixOptionToString(StringBuffer sb, int offset) { - String prefix = IPv6AddresstoString(offset + 16); - int length = getUint8(mPacket, offset + 2); - long valid = getUint32(mPacket, offset + 4); - long preferred = getUint32(mPacket, offset + 8); - sb.append(String.format("%s/%d %ds/%ds ", prefix, length, valid, preferred)); - } - - private void rdnssOptionToString(StringBuffer sb, int offset) { - int optLen = getUint8(mPacket, offset + 1) * 8; - if (optLen < 24) return; // Malformed or empty. - long lifetime = getUint32(mPacket, offset + 4); - int numServers = (optLen - 8) / 16; - sb.append("DNS ").append(lifetime).append("s"); - for (int server = 0; server < numServers; server++) { - sb.append(" ").append(IPv6AddresstoString(offset + 8 + 16 * server)); - } - } - - public String toString() { - try { - StringBuffer sb = new StringBuffer(); - sb.append(String.format("RA %s -> %s %ds ", - IPv6AddresstoString(IPV6_SRC_ADDR_OFFSET), - IPv6AddresstoString(IPV6_DEST_ADDR_OFFSET), - getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET))); - for (int i: mPrefixOptionOffsets) { - prefixOptionToString(sb, i); - } - for (int i: mRdnssOptionOffsets) { - rdnssOptionToString(sb, i); - } - return sb.toString(); - } catch (BufferUnderflowException|IndexOutOfBoundsException e) { - return "<Malformed RA>"; - } - } - - /** - * Add a binary range of the packet that does not include a lifetime to mNonLifetimes. - * Assumes mPacket.position() is as far as we've parsed the packet. - * @param lastNonLifetimeStart offset within packet of where the last binary range of - * data not including a lifetime. - * @param lifetimeOffset offset from mPacket.position() to the next lifetime data. - * @param lifetimeLength length of the next lifetime data. - * @return offset within packet of where the next binary range of data not including - * a lifetime. This can be passed into the next invocation of this function - * via {@code lastNonLifetimeStart}. - */ - private int addNonLifetime(int lastNonLifetimeStart, int lifetimeOffset, - int lifetimeLength) { - lifetimeOffset += mPacket.position(); - mNonLifetimes.add(new Pair<Integer, Integer>(lastNonLifetimeStart, - lifetimeOffset - lastNonLifetimeStart)); - return lifetimeOffset + lifetimeLength; - } - - private int addNonLifetimeU32(int lastNonLifetimeStart) { - return addNonLifetime(lastNonLifetimeStart, - ICMP6_4_BYTE_LIFETIME_OFFSET, ICMP6_4_BYTE_LIFETIME_LEN); - } - - // Note that this parses RA and may throw InvalidRaException (from - // Buffer.position(int) or due to an invalid-length option) or IndexOutOfBoundsException - // (from ByteBuffer.get(int) ) if parsing encounters something non-compliant with - // specifications. - Ra(byte[] packet, int length) throws InvalidRaException { - if (length < ICMP6_RA_OPTION_OFFSET) { - throw new InvalidRaException("Not an ICMP6 router advertisement"); - } - - mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length)); - mLastSeen = currentTimeSeconds(); - - // Sanity check packet in case a packet arrives before we attach RA filter - // to our packet socket. b/29586253 - if (getUint16(mPacket, ETH_ETHERTYPE_OFFSET) != ETH_P_IPV6 || - getUint8(mPacket, IPV6_NEXT_HEADER_OFFSET) != IPPROTO_ICMPV6 || - getUint8(mPacket, ICMP6_TYPE_OFFSET) != ICMPV6_ROUTER_ADVERTISEMENT) { - throw new InvalidRaException("Not an ICMP6 router advertisement"); - } - - - RaEvent.Builder builder = new RaEvent.Builder(); - - // Ignore the flow label and low 4 bits of traffic class. - int lastNonLifetimeStart = addNonLifetime(0, - IPV6_FLOW_LABEL_OFFSET, - IPV6_FLOW_LABEL_LEN); - - // Ignore the checksum. - lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, - ICMP6_RA_CHECKSUM_OFFSET, - ICMP6_RA_CHECKSUM_LEN); - - // Parse router lifetime - lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, - ICMP6_RA_ROUTER_LIFETIME_OFFSET, - ICMP6_RA_ROUTER_LIFETIME_LEN); - builder.updateRouterLifetime(getUint16(mPacket, ICMP6_RA_ROUTER_LIFETIME_OFFSET)); - - // Ensures that the RA is not truncated. - mPacket.position(ICMP6_RA_OPTION_OFFSET); - while (mPacket.hasRemaining()) { - final int position = mPacket.position(); - final int optionType = getUint8(mPacket, position); - final int optionLength = getUint8(mPacket, position + 1) * 8; - long lifetime; - switch (optionType) { - case ICMP6_PREFIX_OPTION_TYPE: - // Parse valid lifetime - lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, - ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, - ICMP6_PREFIX_OPTION_VALID_LIFETIME_LEN); - lifetime = getUint32(mPacket, - position + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET); - builder.updatePrefixValidLifetime(lifetime); - // Parse preferred lifetime - lastNonLifetimeStart = addNonLifetime(lastNonLifetimeStart, - ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET, - ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_LEN); - lifetime = getUint32(mPacket, - position + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET); - builder.updatePrefixPreferredLifetime(lifetime); - mPrefixOptionOffsets.add(position); - break; - // These three options have the same lifetime offset and size, and - // are processed with the same specialized addNonLifetimeU32: - case ICMP6_RDNSS_OPTION_TYPE: - mRdnssOptionOffsets.add(position); - lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart); - lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET); - builder.updateRdnssLifetime(lifetime); - break; - case ICMP6_ROUTE_INFO_OPTION_TYPE: - lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart); - lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET); - builder.updateRouteInfoLifetime(lifetime); - break; - case ICMP6_DNSSL_OPTION_TYPE: - lastNonLifetimeStart = addNonLifetimeU32(lastNonLifetimeStart); - lifetime = getUint32(mPacket, position + ICMP6_4_BYTE_LIFETIME_OFFSET); - builder.updateDnsslLifetime(lifetime); - break; - default: - // RFC4861 section 4.2 dictates we ignore unknown options for fowards - // compatibility. - break; - } - if (optionLength <= 0) { - throw new InvalidRaException(String.format( - "Invalid option length opt=%d len=%d", optionType, optionLength)); - } - mPacket.position(position + optionLength); - } - // Mark non-lifetime bytes since last lifetime. - addNonLifetime(lastNonLifetimeStart, 0, 0); - mMinLifetime = minLifetime(packet, length); - mMetricsLog.log(builder.build()); - } - - // Ignoring lifetimes (which may change) does {@code packet} match this RA? - boolean matches(byte[] packet, int length) { - if (length != mPacket.capacity()) return false; - byte[] referencePacket = mPacket.array(); - for (Pair<Integer, Integer> nonLifetime : mNonLifetimes) { - for (int i = nonLifetime.first; i < (nonLifetime.first + nonLifetime.second); i++) { - if (packet[i] != referencePacket[i]) return false; - } - } - return true; - } - - // What is the minimum of all lifetimes within {@code packet} in seconds? - // Precondition: matches(packet, length) already returned true. - long minLifetime(byte[] packet, int length) { - long minLifetime = Long.MAX_VALUE; - // Wrap packet in ByteBuffer so we can read big-endian values easily - ByteBuffer byteBuffer = ByteBuffer.wrap(packet); - for (int i = 0; (i + 1) < mNonLifetimes.size(); i++) { - int offset = mNonLifetimes.get(i).first + mNonLifetimes.get(i).second; - - // The flow label is in mNonLifetimes, but it's not a lifetime. - if (offset == IPV6_FLOW_LABEL_OFFSET) { - continue; - } - - // The checksum is in mNonLifetimes, but it's not a lifetime. - if (offset == ICMP6_RA_CHECKSUM_OFFSET) { - continue; - } - - final int lifetimeLength = mNonLifetimes.get(i+1).first - offset; - final long optionLifetime; - switch (lifetimeLength) { - case 2: - optionLifetime = getUint16(byteBuffer, offset); - break; - case 4: - optionLifetime = getUint32(byteBuffer, offset); - break; - default: - throw new IllegalStateException("bogus lifetime size " + lifetimeLength); - } - minLifetime = Math.min(minLifetime, optionLifetime); - } - return minLifetime; - } - - // How many seconds does this RA's have to live, taking into account the fact - // that we might have seen it a while ago. - long currentLifetime() { - return mMinLifetime - (currentTimeSeconds() - mLastSeen); - } - - boolean isExpired() { - // TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll - // have to calculate the filter lifetime specially as a fraction of 0 is still 0. - return currentLifetime() <= 0; - } - - // Append a filter for this RA to {@code gen}. Jump to DROP_LABEL if it should be dropped. - // Jump to the next filter if packet doesn't match this RA. - @GuardedBy("ApfFilter.this") - long generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { - String nextFilterLabel = "Ra" + getUniqueNumberLocked(); - // Skip if packet is not the right size - gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT); - gen.addJumpIfR0NotEquals(mPacket.capacity(), nextFilterLabel); - int filterLifetime = (int)(currentLifetime() / FRACTION_OF_LIFETIME_TO_FILTER); - // Skip filter if expired - gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT); - gen.addJumpIfR0GreaterThan(filterLifetime, nextFilterLabel); - for (int i = 0; i < mNonLifetimes.size(); i++) { - // Generate code to match the packet bytes - Pair<Integer, Integer> nonLifetime = mNonLifetimes.get(i); - // Don't generate JNEBS instruction for 0 bytes as it always fails the - // ASSERT_FORWARD_IN_PROGRAM(pc + cmp_imm - 1) check where cmp_imm is - // the number of bytes to compare. nonLifetime is zero between the - // valid and preferred lifetimes in the prefix option. - if (nonLifetime.second != 0) { - gen.addLoadImmediate(Register.R0, nonLifetime.first); - gen.addJumpIfBytesNotEqual(Register.R0, - Arrays.copyOfRange(mPacket.array(), nonLifetime.first, - nonLifetime.first + nonLifetime.second), - nextFilterLabel); - } - // Generate code to test the lifetimes haven't gone down too far - if ((i + 1) < mNonLifetimes.size()) { - Pair<Integer, Integer> nextNonLifetime = mNonLifetimes.get(i + 1); - int offset = nonLifetime.first + nonLifetime.second; - - // Skip the Flow label. - if (offset == IPV6_FLOW_LABEL_OFFSET) { - continue; - } - // Skip the checksum. - if (offset == ICMP6_RA_CHECKSUM_OFFSET) { - continue; - } - int length = nextNonLifetime.first - offset; - switch (length) { - case 4: gen.addLoad32(Register.R0, offset); break; - case 2: gen.addLoad16(Register.R0, offset); break; - default: throw new IllegalStateException("bogus lifetime size " + length); - } - gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel); - } - } - maybeSetupCounter(gen, Counter.DROPPED_RA); - gen.addJump(mCountAndDropLabel); - gen.defineLabel(nextFilterLabel); - return filterLifetime; - } - } - - // TODO: Refactor these subclasses to avoid so much repetition. - private abstract static class KeepalivePacket { - // Note that the offset starts from IP header. - // These must be added ether header length when generating program. - static final int IP_HEADER_OFFSET = 0; - static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12; - - // Append a filter for this keepalive ack to {@code gen}. - // Jump to drop if it matches the keepalive ack. - // Jump to the next filter if packet doesn't match the keepalive ack. - abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException; - } - - // A class to hold NAT-T keepalive ack information. - private class NattKeepaliveResponse extends KeepalivePacket { - static final int UDP_LENGTH_OFFSET = 4; - static final int UDP_HEADER_LEN = 8; - - protected class NattKeepaliveResponseData { - public final byte[] srcAddress; - public final int srcPort; - public final byte[] dstAddress; - public final int dstPort; - - NattKeepaliveResponseData(final NattKeepalivePacketDataParcelable sentKeepalivePacket) { - srcAddress = sentKeepalivePacket.dstAddress; - srcPort = sentKeepalivePacket.dstPort; - dstAddress = sentKeepalivePacket.srcAddress; - dstPort = sentKeepalivePacket.srcPort; - } - } - - protected final NattKeepaliveResponseData mPacket; - protected final byte[] mSrcDstAddr; - protected final byte[] mPortFingerprint; - // NAT-T keepalive packet - protected final byte[] mPayload = {(byte) 0xff}; - - NattKeepaliveResponse(final NattKeepalivePacketDataParcelable sentKeepalivePacket) { - mPacket = new NattKeepaliveResponseData(sentKeepalivePacket); - mSrcDstAddr = concatArrays(mPacket.srcAddress, mPacket.dstAddress); - mPortFingerprint = generatePortFingerprint(mPacket.srcPort, mPacket.dstPort); - } - - byte[] generatePortFingerprint(int srcPort, int dstPort) { - final ByteBuffer fp = ByteBuffer.allocate(4); - fp.order(ByteOrder.BIG_ENDIAN); - fp.putShort((short) srcPort); - fp.putShort((short) dstPort); - return fp.array(); - } - - @Override - void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { - final String nextFilterLabel = "natt_keepalive_filter" + getUniqueNumberLocked(); - - gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel); - - // A NAT-T keepalive packet contains 1 byte payload with the value 0xff - // Check payload length is 1 - gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addAdd(UDP_HEADER_LEN); - gen.addSwap(); - gen.addLoad16(Register.R0, IPV4_TOTAL_LENGTH_OFFSET); - gen.addNeg(Register.R1); - gen.addAddR1(); - gen.addJumpIfR0NotEquals(1, nextFilterLabel); - - // Check that the ports match - gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addAdd(ETH_HEADER_LEN); - gen.addJumpIfBytesNotEqual(Register.R0, mPortFingerprint, nextFilterLabel); - - // Payload offset = R0 + UDP header length - gen.addAdd(UDP_HEADER_LEN); - gen.addJumpIfBytesNotEqual(Register.R0, mPayload, nextFilterLabel); - - maybeSetupCounter(gen, Counter.DROPPED_IPV4_NATT_KEEPALIVE); - gen.addJump(mCountAndDropLabel); - gen.defineLabel(nextFilterLabel); - } - - public String toString() { - try { - return String.format("%s -> %s", - NetworkStackUtils.addressAndPortToString( - InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort), - NetworkStackUtils.addressAndPortToString( - InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort)); - } catch (UnknownHostException e) { - return "Unknown host"; - } - } - } - - // A class to hold TCP keepalive ack information. - private abstract static class TcpKeepaliveAck extends KeepalivePacket { - protected static class TcpKeepaliveAckData { - public final byte[] srcAddress; - public final int srcPort; - public final byte[] dstAddress; - public final int dstPort; - public final int seq; - public final int ack; - - // Create the characteristics of the ack packet from the sent keepalive packet. - TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { - srcAddress = sentKeepalivePacket.dstAddress; - srcPort = sentKeepalivePacket.dstPort; - dstAddress = sentKeepalivePacket.srcAddress; - dstPort = sentKeepalivePacket.srcPort; - seq = sentKeepalivePacket.ack; - ack = sentKeepalivePacket.seq + 1; - } - } - - protected final TcpKeepaliveAckData mPacket; - protected final byte[] mSrcDstAddr; - protected final byte[] mPortSeqAckFingerprint; - - TcpKeepaliveAck(final TcpKeepaliveAckData packet, final byte[] srcDstAddr) { - mPacket = packet; - mSrcDstAddr = srcDstAddr; - mPortSeqAckFingerprint = generatePortSeqAckFingerprint(mPacket.srcPort, - mPacket.dstPort, mPacket.seq, mPacket.ack); - } - - static byte[] generatePortSeqAckFingerprint(int srcPort, int dstPort, int seq, int ack) { - final ByteBuffer fp = ByteBuffer.allocate(12); - fp.order(ByteOrder.BIG_ENDIAN); - fp.putShort((short) srcPort); - fp.putShort((short) dstPort); - fp.putInt(seq); - fp.putInt(ack); - return fp.array(); - } - - public String toString() { - try { - return String.format("%s -> %s , seq=%d, ack=%d", - NetworkStackUtils.addressAndPortToString( - InetAddress.getByAddress(mPacket.srcAddress), mPacket.srcPort), - NetworkStackUtils.addressAndPortToString( - InetAddress.getByAddress(mPacket.dstAddress), mPacket.dstPort), - Integer.toUnsignedLong(mPacket.seq), - Integer.toUnsignedLong(mPacket.ack)); - } catch (UnknownHostException e) { - return "Unknown host"; - } - } - - // Append a filter for this keepalive ack to {@code gen}. - // Jump to drop if it matches the keepalive ack. - // Jump to the next filter if packet doesn't match the keepalive ack. - abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException; - } - - private class TcpKeepaliveAckV4 extends TcpKeepaliveAck { - - TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { - this(new TcpKeepaliveAckData(sentKeepalivePacket)); - } - TcpKeepaliveAckV4(final TcpKeepaliveAckData packet) { - super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */); - } - - @Override - void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { - final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked(); - - gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel); - - // Skip to the next filter if it's not zero-sized : - // TCP_HEADER_SIZE + IPV4_HEADER_SIZE - ipv4_total_length == 0 - // Load the IP header size into R1 - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - // Load the TCP header size into R0 (it's indexed by R1) - gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET); - // Size offset is in the top nibble, but it must be multiplied by 4, and the two - // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2. - gen.addRightShift(2); - // R0 += R1 -> R0 contains TCP + IP headers length - gen.addAddR1(); - // Load IPv4 total length - gen.addLoad16(Register.R1, IPV4_TOTAL_LENGTH_OFFSET); - gen.addNeg(Register.R0); - gen.addAddR1(); - gen.addJumpIfR0NotEquals(0, nextFilterLabel); - // Add IPv4 header length - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN); - gen.addAddR1(); - gen.addJumpIfBytesNotEqual(Register.R0, mPortSeqAckFingerprint, nextFilterLabel); - - maybeSetupCounter(gen, Counter.DROPPED_IPV4_KEEPALIVE_ACK); - gen.addJump(mCountAndDropLabel); - gen.defineLabel(nextFilterLabel); - } - } - - private class TcpKeepaliveAckV6 extends TcpKeepaliveAck { - TcpKeepaliveAckV6(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { - this(new TcpKeepaliveAckData(sentKeepalivePacket)); - } - TcpKeepaliveAckV6(final TcpKeepaliveAckData packet) { - super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */); - } - - @Override - void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException { - throw new UnsupportedOperationException("IPv6 TCP Keepalive is not supported yet"); - } - } - - // Maximum number of RAs to filter for. - private static final int MAX_RAS = 10; - - @GuardedBy("this") - private ArrayList<Ra> mRas = new ArrayList<>(); - @GuardedBy("this") - private SparseArray<KeepalivePacket> mKeepalivePackets = new SparseArray<>(); - - // There is always some marginal benefit to updating the installed APF program when an RA is - // seen because we can extend the program's lifetime slightly, but there is some cost to - // updating the program, so don't bother unless the program is going to expire soon. This - // constant defines "soon" in seconds. - private static final long MAX_PROGRAM_LIFETIME_WORTH_REFRESHING = 30; - // We don't want to filter an RA for it's whole lifetime as it'll be expired by the time we ever - // see a refresh. Using half the lifetime might be a good idea except for the fact that - // packets may be dropped, so let's use 6. - private static final int FRACTION_OF_LIFETIME_TO_FILTER = 6; - - // When did we last install a filter program? In seconds since Unix Epoch. - @GuardedBy("this") - private long mLastTimeInstalledProgram; - // How long should the last installed filter program live for? In seconds. - @GuardedBy("this") - private long mLastInstalledProgramMinLifetime; - @GuardedBy("this") - private ApfProgramEvent.Builder mLastInstallEvent; - - // For debugging only. The last program installed. - @GuardedBy("this") - private byte[] mLastInstalledProgram; - - /** - * For debugging only. Contains the latest APF buffer snapshot captured from the firmware. - * - * A typical size for this buffer is 4KB. It is present only if the WiFi HAL supports - * IWifiStaIface#readApfPacketFilterData(), and the APF interpreter advertised support for - * the opcodes to access the data buffer (LDDW and STDW). - */ - @GuardedBy("this") @Nullable - private byte[] mDataSnapshot; - - // How many times the program was updated since we started. - @GuardedBy("this") - private int mNumProgramUpdates = 0; - // How many times the program was updated since we started for allowing multicast traffic. - @GuardedBy("this") - private int mNumProgramUpdatesAllowingMulticast = 0; - - /** - * Generate filter code to process ARP packets. Execution of this code ends in either the - * DROP_LABEL or PASS_LABEL and does not fall off the end. - * Preconditions: - * - Packet being filtered is ARP - */ - @GuardedBy("this") - private void generateArpFilterLocked(ApfGenerator gen) throws IllegalInstructionException { - // Here's a basic summary of what the ARP filter program does: - // - // if not ARP IPv4 - // pass - // if not ARP IPv4 reply or request - // pass - // if ARP reply source ip is 0.0.0.0 - // drop - // if unicast ARP reply - // pass - // if interface has no IPv4 address - // if target ip is 0.0.0.0 - // drop - // else - // if target ip is not the interface ip - // drop - // pass - - final String checkTargetIPv4 = "checkTargetIPv4"; - - // Pass if not ARP IPv4. - gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET); - maybeSetupCounter(gen, Counter.PASSED_ARP_NON_IPV4); - gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel); - - // Pass if unknown ARP opcode. - gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET); - gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check - maybeSetupCounter(gen, Counter.PASSED_ARP_UNKNOWN); - gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel); - - // Drop if ARP reply source IP is 0.0.0.0 - gen.addLoad32(Register.R0, ARP_SOURCE_IP_ADDRESS_OFFSET); - maybeSetupCounter(gen, Counter.DROPPED_ARP_REPLY_SPA_NO_HOST); - gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel); - - // Pass if unicast reply. - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); - maybeSetupCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY); - gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel); - - // Either a unicast request, a unicast reply, or a broadcast reply. - gen.defineLabel(checkTargetIPv4); - if (mIPv4Address == null) { - // When there is no IPv4 address, drop GARP replies (b/29404209). - gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); - maybeSetupCounter(gen, Counter.DROPPED_GARP_REPLY); - gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel); - } else { - // When there is an IPv4 address, drop unicast/broadcast requests - // and broadcast replies with a different target IPv4 address. - gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET); - maybeSetupCounter(gen, Counter.DROPPED_ARP_OTHER_HOST); - gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, mCountAndDropLabel); - } - - maybeSetupCounter(gen, Counter.PASSED_ARP); - gen.addJump(mCountAndPassLabel); - } - - /** - * Generate filter code to process IPv4 packets. Execution of this code ends in either the - * DROP_LABEL or PASS_LABEL and does not fall off the end. - * Preconditions: - * - Packet being filtered is IPv4 - */ - @GuardedBy("this") - private void generateIPv4FilterLocked(ApfGenerator gen) throws IllegalInstructionException { - // Here's a basic summary of what the IPv4 filter program does: - // - // if filtering multicast (i.e. multicast lock not held): - // if it's DHCP destined to our MAC: - // pass - // if it's L2 broadcast: - // drop - // if it's IPv4 multicast: - // drop - // if it's IPv4 broadcast: - // drop - // if keepalive ack - // drop - // pass - - if (mMulticastFilter) { - final String skipDhcpv4Filter = "skip_dhcp_v4_filter"; - - // Pass DHCP addressed to us. - // Check it's UDP. - gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET); - gen.addJumpIfR0NotEquals(IPPROTO_UDP, skipDhcpv4Filter); - // Check it's not a fragment. This matches the BPF filter installed by the DHCP client. - gen.addLoad16(Register.R0, IPV4_FRAGMENT_OFFSET_OFFSET); - gen.addJumpIfR0AnyBitsSet(IPV4_FRAGMENT_OFFSET_MASK, skipDhcpv4Filter); - // Check it's addressed to DHCP client port. - gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addLoad16Indexed(Register.R0, UDP_DESTINATION_PORT_OFFSET); - gen.addJumpIfR0NotEquals(DHCP_CLIENT_PORT, skipDhcpv4Filter); - // Check it's DHCP to our MAC address. - gen.addLoadImmediate(Register.R0, DHCP_CLIENT_MAC_OFFSET); - // NOTE: Relies on R1 containing IPv4 header offset. - gen.addAddR1(); - gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter); - maybeSetupCounter(gen, Counter.PASSED_DHCP); - gen.addJump(mCountAndPassLabel); - - // Drop all multicasts/broadcasts. - gen.defineLabel(skipDhcpv4Filter); - - // If IPv4 destination address is in multicast range, drop. - gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET); - gen.addAnd(0xf0); - maybeSetupCounter(gen, Counter.DROPPED_IPV4_MULTICAST); - gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel); - - // If IPv4 broadcast packet, drop regardless of L2 (b/30231088). - maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR); - gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET); - gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel); - if (mIPv4Address != null && mIPv4PrefixLength < 31) { - maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET); - int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength); - gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel); - } - - // If any TCP keepalive filter matches, drop - generateV4KeepaliveFilters(gen); - - // If any NAT-T keepalive filter matches, drop - generateV4NattKeepaliveFilters(gen); - - // Otherwise, this is an IPv4 unicast, pass - // If L2 broadcast packet, drop. - // TODO: can we invert this condition to fall through to the common pass case below? - maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST); - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel); - maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST); - gen.addJump(mCountAndDropLabel); - } else { - generateV4KeepaliveFilters(gen); - generateV4NattKeepaliveFilters(gen); - } - - // Otherwise, pass - maybeSetupCounter(gen, Counter.PASSED_IPV4); - gen.addJump(mCountAndPassLabel); - } - - private void generateKeepaliveFilters(ApfGenerator gen, Class<?> filterType, int proto, - int offset, String label) throws IllegalInstructionException { - final boolean haveKeepaliveResponses = NetworkStackUtils.any(mKeepalivePackets, - ack -> filterType.isInstance(ack)); - - // If no keepalive packets of this type - if (!haveKeepaliveResponses) return; - - // If not the right proto, skip keepalive filters - gen.addLoad8(Register.R0, offset); - gen.addJumpIfR0NotEquals(proto, label); - - // Drop Keepalive responses - for (int i = 0; i < mKeepalivePackets.size(); ++i) { - final KeepalivePacket response = mKeepalivePackets.valueAt(i); - if (filterType.isInstance(response)) response.generateFilterLocked(gen); - } - - gen.defineLabel(label); - } - - private void generateV4KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException { - generateKeepaliveFilters(gen, TcpKeepaliveAckV4.class, IPPROTO_TCP, IPV4_PROTOCOL_OFFSET, - "skip_v4_keepalive_filter"); - } - - private void generateV4NattKeepaliveFilters(ApfGenerator gen) - throws IllegalInstructionException { - generateKeepaliveFilters(gen, NattKeepaliveResponse.class, - IPPROTO_UDP, IPV4_PROTOCOL_OFFSET, "skip_v4_nattkeepalive_filter"); - } - - /** - * Generate filter code to process IPv6 packets. Execution of this code ends in either the - * DROP_LABEL or PASS_LABEL, or falls off the end for ICMPv6 packets. - * Preconditions: - * - Packet being filtered is IPv6 - */ - @GuardedBy("this") - private void generateIPv6FilterLocked(ApfGenerator gen) throws IllegalInstructionException { - // Here's a basic summary of what the IPv6 filter program does: - // - // if we're dropping multicast - // if it's not IPCMv6 or it's ICMPv6 but we're in doze mode: - // if it's multicast: - // drop - // pass - // if it's ICMPv6 RS to any: - // drop - // if it's ICMPv6 NA to ff02::1: - // drop - // if keepalive ack - // drop - - gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET); - - // Drop multicast if the multicast filter is enabled. - if (mMulticastFilter) { - final String skipIPv6MulticastFilterLabel = "skipIPv6MulticastFilter"; - final String dropAllIPv6MulticastsLabel = "dropAllIPv6Multicast"; - - // While in doze mode, drop ICMPv6 multicast pings, let the others pass. - // While awake, let all ICMPv6 multicasts through. - if (mInDozeMode) { - // Not ICMPv6? -> Proceed to multicast filtering - gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, dropAllIPv6MulticastsLabel); - - // ICMPv6 but not ECHO? -> Skip the multicast filter. - // (ICMPv6 ECHO requests will go through the multicast filter below). - gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); - gen.addJumpIfR0NotEquals(ICMPV6_ECHO_REQUEST_TYPE, skipIPv6MulticastFilterLabel); - } else { - gen.addJumpIfR0Equals(IPPROTO_ICMPV6, skipIPv6MulticastFilterLabel); - } - - // Drop all other packets sent to ff00::/8 (multicast prefix). - gen.defineLabel(dropAllIPv6MulticastsLabel); - maybeSetupCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST); - gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET); - gen.addJumpIfR0Equals(0xff, mCountAndDropLabel); - // If any keepalive filter matches, drop - generateV6KeepaliveFilters(gen); - // Not multicast. Pass. - maybeSetupCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP); - gen.addJump(mCountAndPassLabel); - gen.defineLabel(skipIPv6MulticastFilterLabel); - } else { - generateV6KeepaliveFilters(gen); - // If not ICMPv6, pass. - maybeSetupCounter(gen, Counter.PASSED_IPV6_NON_ICMP); - gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, mCountAndPassLabel); - } - - // If we got this far, the packet is ICMPv6. Drop some specific types. - - // Add unsolicited multicast neighbor announcements filter - String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA"; - gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET); - // Drop all router solicitations (b/32833400) - maybeSetupCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION); - gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel); - // If not neighbor announcements, skip filter. - gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel); - // If to ff02::1, drop. - // TODO: Drop only if they don't contain the address of on-link neighbours. - gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS, - skipUnsolicitedMulticastNALabel); - maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA); - gen.addJump(mCountAndDropLabel); - gen.defineLabel(skipUnsolicitedMulticastNALabel); - } - - private void generateV6KeepaliveFilters(ApfGenerator gen) throws IllegalInstructionException { - generateKeepaliveFilters(gen, TcpKeepaliveAckV6.class, IPPROTO_TCP, IPV6_NEXT_HEADER_OFFSET, - "skip_v6_keepalive_filter"); - } - - /** - * Begin generating an APF program to: - * <ul> - * <li>Drop/Pass 802.3 frames (based on policy) - * <li>Drop packets with EtherType within the Black List - * <li>Drop ARP requests not for us, if mIPv4Address is set, - * <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC, - * <li>Drop IPv4 multicast packets, if mMulticastFilter, - * <li>Pass all other IPv4 packets, - * <li>Drop all broadcast non-IP non-ARP packets. - * <li>Pass all non-ICMPv6 IPv6 packets, - * <li>Pass all non-IPv4 and non-IPv6 packets, - * <li>Drop IPv6 ICMPv6 NAs to ff02::1. - * <li>Drop IPv6 ICMPv6 RSs. - * <li>Filter IPv4 packets (see generateIPv4FilterLocked()) - * <li>Filter IPv6 packets (see generateIPv6FilterLocked()) - * <li>Let execution continue off the end of the program for IPv6 ICMPv6 packets. This allows - * insertion of RA filters here, or if there aren't any, just passes the packets. - * </ul> - */ - @GuardedBy("this") - private ApfGenerator emitPrologueLocked() throws IllegalInstructionException { - // This is guaranteed to succeed because of the check in maybeCreate. - ApfGenerator gen = new ApfGenerator(mApfCapabilities.apfVersionSupported); - - if (mApfCapabilities.hasDataAccess()) { - // Increment TOTAL_PACKETS - maybeSetupCounter(gen, Counter.TOTAL_PACKETS); - gen.addLoadData(Register.R0, 0); // load counter - gen.addAdd(1); - gen.addStoreData(Register.R0, 0); // write-back counter - } - - // Here's a basic summary of what the initial program does: - // - // if it's a 802.3 Frame (ethtype < 0x0600): - // drop or pass based on configurations - // if it has a ether-type that belongs to the black list - // drop - // if it's ARP: - // insert ARP filter to drop or pass these appropriately - // if it's IPv4: - // insert IPv4 filter to drop or pass these appropriately - // if it's not IPv6: - // if it's broadcast: - // drop - // pass - // insert IPv6 filter to drop, pass, or fall off the end for ICMPv6 packets - - gen.addLoad16(Register.R0, ETH_ETHERTYPE_OFFSET); - - if (mDrop802_3Frames) { - // drop 802.3 frames (ethtype < 0x0600) - maybeSetupCounter(gen, Counter.DROPPED_802_3_FRAME); - gen.addJumpIfR0LessThan(ETH_TYPE_MIN, mCountAndDropLabel); - } - - // Handle ether-type black list - maybeSetupCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED); - for (int p : mEthTypeBlackList) { - gen.addJumpIfR0Equals(p, mCountAndDropLabel); - } - - // Add ARP filters: - String skipArpFiltersLabel = "skipArpFilters"; - gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel); - generateArpFilterLocked(gen); - gen.defineLabel(skipArpFiltersLabel); - - // Add IPv4 filters: - String skipIPv4FiltersLabel = "skipIPv4Filters"; - // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not - // execute the ARP filter, since that filter does not fall through, but either drops or - // passes. - gen.addJumpIfR0NotEquals(ETH_P_IP, skipIPv4FiltersLabel); - generateIPv4FilterLocked(gen); - gen.defineLabel(skipIPv4FiltersLabel); - - // Check for IPv6: - // NOTE: Relies on R0 containing ethertype. This is safe because if we got here, we did not - // execute the ARP or IPv4 filters, since those filters do not fall through, but either - // drop or pass. - String ipv6FilterLabel = "IPv6Filters"; - gen.addJumpIfR0Equals(ETH_P_IPV6, ipv6FilterLabel); - - // Drop non-IP non-ARP broadcasts, pass the rest - gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); - maybeSetupCounter(gen, Counter.PASSED_NON_IP_UNICAST); - gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel); - maybeSetupCounter(gen, Counter.DROPPED_ETH_BROADCAST); - gen.addJump(mCountAndDropLabel); - - // Add IPv6 filters: - gen.defineLabel(ipv6FilterLabel); - generateIPv6FilterLocked(gen); - return gen; - } - - /** - * Append packet counting epilogue to the APF program. - * - * Currently, the epilogue consists of two trampolines which count passed and dropped packets - * before jumping to the actual PASS and DROP labels. - */ - @GuardedBy("this") - private void emitEpilogue(ApfGenerator gen) throws IllegalInstructionException { - // If APFv4 is unsupported, no epilogue is necessary: if execution reached this far, it - // will just fall-through to the PASS label. - if (!mApfCapabilities.hasDataAccess()) return; - - // Execution will reach the bottom of the program if none of the filters match, - // which will pass the packet to the application processor. - maybeSetupCounter(gen, Counter.PASSED_IPV6_ICMP); - - // Append the count & pass trampoline, which increments the counter at the data address - // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting - // the entire sequence inline for every counter. - gen.defineLabel(mCountAndPassLabel); - gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0) - gen.addAdd(1); // R0++ - gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0 - gen.addJump(gen.PASS_LABEL); - - // Same as above for the count & drop trampoline. - gen.defineLabel(mCountAndDropLabel); - gen.addLoadData(Register.R0, 0); // R0 = *(R1 + 0) - gen.addAdd(1); // R0++ - gen.addStoreData(Register.R0, 0); // *(R1 + 0) = R0 - gen.addJump(gen.DROP_LABEL); - } - - /** - * Generate and install a new filter program. - */ - @GuardedBy("this") - @VisibleForTesting - void installNewProgramLocked() { - purgeExpiredRasLocked(); - ArrayList<Ra> rasToFilter = new ArrayList<>(); - final byte[] program; - long programMinLifetime = Long.MAX_VALUE; - long maximumApfProgramSize = mApfCapabilities.maximumApfProgramSize; - if (mApfCapabilities.hasDataAccess()) { - // Reserve space for the counters. - maximumApfProgramSize -= Counter.totalSize(); - } - - try { - // Step 1: Determine how many RA filters we can fit in the program. - ApfGenerator gen = emitPrologueLocked(); - - // The epilogue normally goes after the RA filters, but add it early to include its - // length when estimating the total. - emitEpilogue(gen); - - // Can't fit the program even without any RA filters? - if (gen.programLengthOverEstimate() > maximumApfProgramSize) { - Log.e(TAG, "Program exceeds maximum size " + maximumApfProgramSize); - return; - } - - for (Ra ra : mRas) { - ra.generateFilterLocked(gen); - // Stop if we get too big. - if (gen.programLengthOverEstimate() > maximumApfProgramSize) break; - rasToFilter.add(ra); - } - - // Step 2: Actually generate the program - gen = emitPrologueLocked(); - for (Ra ra : rasToFilter) { - programMinLifetime = Math.min(programMinLifetime, ra.generateFilterLocked(gen)); - } - emitEpilogue(gen); - program = gen.generate(); - } catch (IllegalInstructionException|IllegalStateException e) { - Log.e(TAG, "Failed to generate APF program.", e); - return; - } - final long now = currentTimeSeconds(); - mLastTimeInstalledProgram = now; - mLastInstalledProgramMinLifetime = programMinLifetime; - mLastInstalledProgram = program; - mNumProgramUpdates++; - - if (VDBG) { - hexDump("Installing filter: ", program, program.length); - } - mIpClientCallback.installPacketFilter(program); - logApfProgramEventLocked(now); - mLastInstallEvent = new ApfProgramEvent.Builder() - .setLifetime(programMinLifetime) - .setFilteredRas(rasToFilter.size()) - .setCurrentRas(mRas.size()) - .setProgramLength(program.length) - .setFlags(mIPv4Address != null, mMulticastFilter); - } - - @GuardedBy("this") - private void logApfProgramEventLocked(long now) { - if (mLastInstallEvent == null) { - return; - } - ApfProgramEvent.Builder ev = mLastInstallEvent; - mLastInstallEvent = null; - final long actualLifetime = now - mLastTimeInstalledProgram; - ev.setActualLifetime(actualLifetime); - if (actualLifetime < APF_PROGRAM_EVENT_LIFETIME_THRESHOLD) { - return; - } - mMetricsLog.log(ev.build()); - } - - /** - * Returns {@code true} if a new program should be installed because the current one dies soon. - */ - private boolean shouldInstallnewProgram() { - long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime; - return expiry < currentTimeSeconds() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING; - } - - private void hexDump(String msg, byte[] packet, int length) { - log(msg + HexDump.toHexString(packet, 0, length, false /* lowercase */)); - } - - @GuardedBy("this") - private void purgeExpiredRasLocked() { - for (int i = 0; i < mRas.size();) { - if (mRas.get(i).isExpired()) { - log("Expiring " + mRas.get(i)); - mRas.remove(i); - } else { - i++; - } - } - } - - /** - * Process an RA packet, updating the list of known RAs and installing a new APF program - * if the current APF program should be updated. - * @return a ProcessRaResult enum describing what action was performed. - */ - @VisibleForTesting - synchronized ProcessRaResult processRa(byte[] packet, int length) { - if (VDBG) hexDump("Read packet = ", packet, length); - - // Have we seen this RA before? - for (int i = 0; i < mRas.size(); i++) { - Ra ra = mRas.get(i); - if (ra.matches(packet, length)) { - if (VDBG) log("matched RA " + ra); - // Update lifetimes. - ra.mLastSeen = currentTimeSeconds(); - ra.mMinLifetime = ra.minLifetime(packet, length); - ra.seenCount++; - - // Keep mRas in LRU order so as to prioritize generating filters for recently seen - // RAs. LRU prioritizes this because RA filters are generated in order from mRas - // until the filter program exceeds the maximum filter program size allowed by the - // chipset, so RAs appearing earlier in mRas are more likely to make it into the - // filter program. - // TODO: consider sorting the RAs in order of increasing expiry time as well. - // Swap to front of array. - mRas.add(0, mRas.remove(i)); - - // If the current program doesn't expire for a while, don't update. - if (shouldInstallnewProgram()) { - installNewProgramLocked(); - return ProcessRaResult.UPDATE_EXPIRY; - } - return ProcessRaResult.MATCH; - } - } - purgeExpiredRasLocked(); - // TODO: figure out how to proceed when we've received more then MAX_RAS RAs. - if (mRas.size() >= MAX_RAS) { - return ProcessRaResult.DROPPED; - } - final Ra ra; - try { - ra = new Ra(packet, length); - } catch (Exception e) { - Log.e(TAG, "Error parsing RA", e); - return ProcessRaResult.PARSE_ERROR; - } - // Ignore 0 lifetime RAs. - if (ra.isExpired()) { - return ProcessRaResult.ZERO_LIFETIME; - } - log("Adding " + ra); - mRas.add(ra); - installNewProgramLocked(); - return ProcessRaResult.UPDATE_NEW_RA; - } - - /** - * Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet - * filtering using APF programs. - */ - public static ApfFilter maybeCreate(Context context, ApfConfiguration config, - InterfaceParams ifParams, IpClientCallbacksWrapper ipClientCallback) { - if (context == null || config == null || ifParams == null) return null; - ApfCapabilities apfCapabilities = config.apfCapabilities; - if (apfCapabilities == null) return null; - if (apfCapabilities.apfVersionSupported == 0) return null; - if (apfCapabilities.maximumApfProgramSize < 512) { - Log.e(TAG, "Unacceptably small APF limit: " + apfCapabilities.maximumApfProgramSize); - return null; - } - // For now only support generating programs for Ethernet frames. If this restriction is - // lifted: - // 1. the program generator will need its offsets adjusted. - // 2. the packet filter attached to our packet socket will need its offset adjusted. - if (apfCapabilities.apfPacketFormat != ARPHRD_ETHER) return null; - if (!ApfGenerator.supportsVersion(apfCapabilities.apfVersionSupported)) { - Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported); - return null; - } - - return new ApfFilter(context, config, ifParams, ipClientCallback, new IpConnectivityLog()); - } - - public synchronized void shutdown() { - if (mReceiveThread != null) { - log("shutting down"); - mReceiveThread.halt(); // Also closes socket. - mReceiveThread = null; - } - mRas.clear(); - mContext.unregisterReceiver(mDeviceIdleReceiver); - } - - public synchronized void setMulticastFilter(boolean isEnabled) { - if (mMulticastFilter == isEnabled) return; - mMulticastFilter = isEnabled; - if (!isEnabled) { - mNumProgramUpdatesAllowingMulticast++; - } - installNewProgramLocked(); - } - - @VisibleForTesting - public synchronized void setDozeMode(boolean isEnabled) { - if (mInDozeMode == isEnabled) return; - mInDozeMode = isEnabled; - installNewProgramLocked(); - } - - /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */ - private static LinkAddress findIPv4LinkAddress(LinkProperties lp) { - LinkAddress ipv4Address = null; - for (LinkAddress address : lp.getLinkAddresses()) { - if (!(address.getAddress() instanceof Inet4Address)) { - continue; - } - if (ipv4Address != null && !ipv4Address.isSameAddressAs(address)) { - // More than one IPv4 address, abort. - return null; - } - ipv4Address = address; - } - return ipv4Address; - } - - public synchronized void setLinkProperties(LinkProperties lp) { - // NOTE: Do not keep a copy of LinkProperties as it would further duplicate state. - final LinkAddress ipv4Address = findIPv4LinkAddress(lp); - final byte[] addr = (ipv4Address != null) ? ipv4Address.getAddress().getAddress() : null; - final int prefix = (ipv4Address != null) ? ipv4Address.getPrefixLength() : 0; - if ((prefix == mIPv4PrefixLength) && Arrays.equals(addr, mIPv4Address)) { - return; - } - mIPv4Address = addr; - mIPv4PrefixLength = prefix; - installNewProgramLocked(); - } - - /** - * Add TCP keepalive ack packet filter. - * This will add a filter to drop acks to the keepalive packet passed as an argument. - * - * @param slot The index used to access the filter. - * @param sentKeepalivePacket The attributes of the sent keepalive packet. - */ - public synchronized void addTcpKeepalivePacketFilter(final int slot, - final TcpKeepalivePacketDataParcelable sentKeepalivePacket) { - log("Adding keepalive ack(" + slot + ")"); - if (null != mKeepalivePackets.get(slot)) { - throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied"); - } - final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6; - mKeepalivePackets.put(slot, (ipVersion == 4) - ? new TcpKeepaliveAckV4(sentKeepalivePacket) - : new TcpKeepaliveAckV6(sentKeepalivePacket)); - installNewProgramLocked(); - } - - /** - * Add NAT-T keepalive packet filter. - * This will add a filter to drop NAT-T keepalive packet which is passed as an argument. - * - * @param slot The index used to access the filter. - * @param sentKeepalivePacket The attributes of the sent keepalive packet. - */ - public synchronized void addNattKeepalivePacketFilter(final int slot, - final NattKeepalivePacketDataParcelable sentKeepalivePacket) { - log("Adding NAT-T keepalive packet(" + slot + ")"); - if (null != mKeepalivePackets.get(slot)) { - throw new IllegalArgumentException("NAT-T Keepalive slot " + slot + " is occupied"); - } - if (sentKeepalivePacket.srcAddress.length != 4) { - throw new IllegalArgumentException("NAT-T keepalive is only supported on IPv4"); - } - mKeepalivePackets.put(slot, new NattKeepaliveResponse(sentKeepalivePacket)); - installNewProgramLocked(); - } - - /** - * Remove keepalive packet filter. - * - * @param slot The index used to access the filter. - */ - public synchronized void removeKeepalivePacketFilter(int slot) { - log("Removing keepalive packet(" + slot + ")"); - mKeepalivePackets.remove(slot); - installNewProgramLocked(); - } - - static public long counterValue(byte[] data, Counter counter) - throws ArrayIndexOutOfBoundsException { - // Follow the same wrap-around addressing scheme of the interpreter. - int offset = counter.offset(); - if (offset < 0) { - offset = data.length + offset; - } - - // Decode 32bit big-endian integer into a long so we can count up beyond 2^31. - long value = 0; - for (int i = 0; i < 4; i++) { - value = value << 8 | (data[offset] & 0xFF); - offset++; - } - return value; - } - - public synchronized void dump(IndentingPrintWriter pw) { - pw.println("Capabilities: " + mApfCapabilities); - pw.println("Receive thread: " + (mReceiveThread != null ? "RUNNING" : "STOPPED")); - pw.println("Multicast: " + (mMulticastFilter ? "DROP" : "ALLOW")); - try { - pw.println("IPv4 address: " + InetAddress.getByAddress(mIPv4Address).getHostAddress()); - } catch (UnknownHostException|NullPointerException e) {} - - if (mLastTimeInstalledProgram == 0) { - pw.println("No program installed."); - return; - } - pw.println("Program updates: " + mNumProgramUpdates); - pw.println(String.format( - "Last program length %d, installed %ds ago, lifetime %ds", - mLastInstalledProgram.length, currentTimeSeconds() - mLastTimeInstalledProgram, - mLastInstalledProgramMinLifetime)); - - pw.println("RA filters:"); - pw.increaseIndent(); - for (Ra ra: mRas) { - pw.println(ra); - pw.increaseIndent(); - pw.println(String.format( - "Seen: %d, last %ds ago", ra.seenCount, currentTimeSeconds() - ra.mLastSeen)); - if (DBG) { - pw.println("Last match:"); - pw.increaseIndent(); - pw.println(ra.getLastMatchingPacket()); - pw.decreaseIndent(); - } - pw.decreaseIndent(); - } - pw.decreaseIndent(); - - pw.println("TCP Keepalive filters:"); - pw.increaseIndent(); - for (int i = 0; i < mKeepalivePackets.size(); ++i) { - final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i); - if (keepalivePacket instanceof TcpKeepaliveAck) { - pw.print("Slot "); - pw.print(mKeepalivePackets.keyAt(i)); - pw.print(": "); - pw.println(keepalivePacket); - } - } - pw.decreaseIndent(); - - pw.println("NAT-T Keepalive filters:"); - pw.increaseIndent(); - for (int i = 0; i < mKeepalivePackets.size(); ++i) { - final KeepalivePacket keepalivePacket = mKeepalivePackets.valueAt(i); - if (keepalivePacket instanceof NattKeepaliveResponse) { - pw.print("Slot "); - pw.print(mKeepalivePackets.keyAt(i)); - pw.print(": "); - pw.println(keepalivePacket); - } - } - pw.decreaseIndent(); - - if (DBG) { - pw.println("Last program:"); - pw.increaseIndent(); - pw.println(HexDump.toHexString(mLastInstalledProgram, false /* lowercase */)); - pw.decreaseIndent(); - } - - pw.println("APF packet counters: "); - pw.increaseIndent(); - if (!mApfCapabilities.hasDataAccess()) { - pw.println("APF counters not supported"); - } else if (mDataSnapshot == null) { - pw.println("No last snapshot."); - } else { - try { - Counter[] counters = Counter.class.getEnumConstants(); - for (Counter c : Arrays.asList(counters).subList(1, counters.length)) { - long value = counterValue(mDataSnapshot, c); - // Only print non-zero counters - if (value != 0) { - pw.println(c.toString() + ": " + value); - } - } - } catch (ArrayIndexOutOfBoundsException e) { - pw.println("Uh-oh: " + e); - } - if (VDBG) { - pw.println("Raw data dump: "); - pw.println(HexDump.dumpHexString(mDataSnapshot)); - } - } - pw.decreaseIndent(); - } - - // TODO: move to android.net.NetworkUtils - @VisibleForTesting - public static int ipv4BroadcastAddress(byte[] addrBytes, int prefixLength) { - return bytesToBEInt(addrBytes) | (int) (Integer.toUnsignedLong(-1) >>> prefixLength); - } - - private static int uint8(byte b) { - return b & 0xff; - } - - private static int getUint16(ByteBuffer buffer, int position) { - return buffer.getShort(position) & 0xffff; - } - - private static long getUint32(ByteBuffer buffer, int position) { - return Integer.toUnsignedLong(buffer.getInt(position)); - } - - private static int getUint8(ByteBuffer buffer, int position) { - return uint8(buffer.get(position)); - } - - private static int bytesToBEInt(byte[] bytes) { - return (uint8(bytes[0]) << 24) - + (uint8(bytes[1]) << 16) - + (uint8(bytes[2]) << 8) - + (uint8(bytes[3])); - } - - private static byte[] concatArrays(final byte[]... arr) { - int size = 0; - for (byte[] a : arr) { - size += a.length; - } - final byte[] result = new byte[size]; - int offset = 0; - for (byte[] a : arr) { - System.arraycopy(a, 0, result, offset, a.length); - offset += a.length; - } - return result; - } -} diff --git a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java deleted file mode 100644 index 44ce2db8547c..000000000000 --- a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java +++ /dev/null @@ -1,937 +0,0 @@ -/* - * Copyright (C) 2016 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.apf; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * APF assembler/generator. A tool for generating an APF program. - * - * Call add*() functions to add instructions to the program, then call - * {@link generate} to get the APF bytecode for the program. - * - * @hide - */ -public class ApfGenerator { - /** - * This exception is thrown when an attempt is made to generate an illegal instruction. - */ - public static class IllegalInstructionException extends Exception { - IllegalInstructionException(String msg) { - super(msg); - } - } - private enum Opcodes { - LABEL(-1), - LDB(1), // Load 1 byte from immediate offset, e.g. "ldb R0, [5]" - LDH(2), // Load 2 bytes from immediate offset, e.g. "ldh R0, [5]" - LDW(3), // Load 4 bytes from immediate offset, e.g. "ldw R0, [5]" - LDBX(4), // Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0" - LDHX(5), // Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0" - LDWX(6), // Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0" - ADD(7), // Add, e.g. "add R0,5" - MUL(8), // Multiply, e.g. "mul R0,5" - DIV(9), // Divide, e.g. "div R0,5" - AND(10), // And, e.g. "and R0,5" - OR(11), // Or, e.g. "or R0,5" - SH(12), // Left shift, e.g, "sh R0, 5" or "sh R0, -5" (shifts right) - LI(13), // Load immediate, e.g. "li R0,5" (immediate encoded as signed value) - JMP(14), // Jump, e.g. "jmp label" - JEQ(15), // Compare equal and branch, e.g. "jeq R0,5,label" - JNE(16), // Compare not equal and branch, e.g. "jne R0,5,label" - JGT(17), // Compare greater than and branch, e.g. "jgt R0,5,label" - JLT(18), // Compare less than and branch, e.g. "jlt R0,5,label" - JSET(19), // Compare any bits set and branch, e.g. "jset R0,5,label" - JNEBS(20), // Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455" - EXT(21), // Followed by immediate indicating ExtendedOpcodes. - LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1" - STDW(23); // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1" - - final int value; - - private Opcodes(int value) { - this.value = value; - } - } - // Extended opcodes. Primary opcode is Opcodes.EXT. ExtendedOpcodes are encoded in the immediate - // field. - private enum ExtendedOpcodes { - LDM(0), // Load from memory, e.g. "ldm R0,5" - STM(16), // Store to memory, e.g. "stm R0,5" - NOT(32), // Not, e.g. "not R0" - NEG(33), // Negate, e.g. "neg R0" - SWAP(34), // Swap, e.g. "swap R0,R1" - MOVE(35); // Move, e.g. "move R0,R1" - - final int value; - - private ExtendedOpcodes(int value) { - this.value = value; - } - } - public enum Register { - R0(0), - R1(1); - - final int value; - - private Register(int value) { - this.value = value; - } - } - private class Instruction { - private final byte mOpcode; // A "Opcode" value. - private final byte mRegister; // A "Register" value. - private boolean mHasImm; - private byte mImmSize; - private boolean mImmSigned; - private int mImm; - // When mOpcode is a jump: - private byte mTargetLabelSize; - private String mTargetLabel; - // When mOpcode == Opcodes.LABEL: - private String mLabel; - // When mOpcode == Opcodes.JNEBS: - private byte[] mCompareBytes; - // Offset in bytes from the beginning of this program. Set by {@link ApfGenerator#generate}. - int offset; - - Instruction(Opcodes opcode, Register register) { - mOpcode = (byte)opcode.value; - mRegister = (byte)register.value; - } - - Instruction(Opcodes opcode) { - this(opcode, Register.R0); - } - - void setImm(int imm, boolean signed) { - mHasImm = true; - mImm = imm; - mImmSigned = signed; - mImmSize = calculateImmSize(imm, signed); - } - - void setUnsignedImm(int imm) { - setImm(imm, false); - } - - void setSignedImm(int imm) { - setImm(imm, true); - } - - void setLabel(String label) throws IllegalInstructionException { - if (mLabels.containsKey(label)) { - throw new IllegalInstructionException("duplicate label " + label); - } - if (mOpcode != Opcodes.LABEL.value) { - throw new IllegalStateException("adding label to non-label instruction"); - } - mLabel = label; - mLabels.put(label, this); - } - - void setTargetLabel(String label) { - mTargetLabel = label; - mTargetLabelSize = 4; // May shrink later on in generate(). - } - - void setCompareBytes(byte[] bytes) { - if (mOpcode != Opcodes.JNEBS.value) { - throw new IllegalStateException("adding compare bytes to non-JNEBS instruction"); - } - mCompareBytes = bytes; - } - - /** - * @return size of instruction in bytes. - */ - int size() { - if (mOpcode == Opcodes.LABEL.value) { - return 0; - } - int size = 1; - if (mHasImm) { - size += generatedImmSize(); - } - if (mTargetLabel != null) { - size += generatedImmSize(); - } - if (mCompareBytes != null) { - size += mCompareBytes.length; - } - return size; - } - - /** - * Resize immediate value field so that it's only as big as required to - * contain the offset of the jump destination. - * @return {@code true} if shrunk. - */ - boolean shrink() throws IllegalInstructionException { - if (mTargetLabel == null) { - return false; - } - int oldSize = size(); - int oldTargetLabelSize = mTargetLabelSize; - mTargetLabelSize = calculateImmSize(calculateTargetLabelOffset(), false); - if (mTargetLabelSize > oldTargetLabelSize) { - throw new IllegalStateException("instruction grew"); - } - return size() < oldSize; - } - - /** - * Assemble value for instruction size field. - */ - private byte generateImmSizeField() { - byte immSize = generatedImmSize(); - // Encode size field to fit in 2 bits: 0->0, 1->1, 2->2, 3->4. - return immSize == 4 ? 3 : immSize; - } - - /** - * Assemble first byte of generated instruction. - */ - private byte generateInstructionByte() { - byte sizeField = generateImmSizeField(); - return (byte)((mOpcode << 3) | (sizeField << 1) | mRegister); - } - - /** - * Write {@code value} at offset {@code writingOffset} into {@code bytecode}. - * {@link generatedImmSize} bytes are written. {@code value} is truncated to - * {@code generatedImmSize} bytes. {@code value} is treated simply as a - * 32-bit value, so unsigned values should be zero extended and the truncation - * should simply throw away their zero-ed upper bits, and signed values should - * be sign extended and the truncation should simply throw away their signed - * upper bits. - */ - private int writeValue(int value, byte[] bytecode, int writingOffset) { - for (int i = generatedImmSize() - 1; i >= 0; i--) { - bytecode[writingOffset++] = (byte)((value >> (i * 8)) & 255); - } - return writingOffset; - } - - /** - * Generate bytecode for this instruction at offset {@link offset}. - */ - void generate(byte[] bytecode) throws IllegalInstructionException { - if (mOpcode == Opcodes.LABEL.value) { - return; - } - int writingOffset = offset; - bytecode[writingOffset++] = generateInstructionByte(); - if (mTargetLabel != null) { - writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset); - } - if (mHasImm) { - writingOffset = writeValue(mImm, bytecode, writingOffset); - } - if (mCompareBytes != null) { - System.arraycopy(mCompareBytes, 0, bytecode, writingOffset, mCompareBytes.length); - writingOffset += mCompareBytes.length; - } - if ((writingOffset - offset) != size()) { - throw new IllegalStateException("wrote " + (writingOffset - offset) + - " but should have written " + size()); - } - } - - /** - * Calculate the size of either the immediate field or the target label field, if either is - * present. Most instructions have either an immediate or a target label field, but for the - * instructions that have both, the size of the target label field must be the same as the - * size of the immediate field, because there is only one length field in the instruction - * byte, hence why this function simply takes the maximum of the two sizes, so neither is - * truncated. - */ - private byte generatedImmSize() { - return mImmSize > mTargetLabelSize ? mImmSize : mTargetLabelSize; - } - - private int calculateTargetLabelOffset() throws IllegalInstructionException { - Instruction targetLabelInstruction; - if (mTargetLabel == DROP_LABEL) { - targetLabelInstruction = mDropLabel; - } else if (mTargetLabel == PASS_LABEL) { - targetLabelInstruction = mPassLabel; - } else { - targetLabelInstruction = mLabels.get(mTargetLabel); - } - if (targetLabelInstruction == null) { - throw new IllegalInstructionException("label not found: " + mTargetLabel); - } - // Calculate distance from end of this instruction to instruction.offset. - final int targetLabelOffset = targetLabelInstruction.offset - (offset + size()); - if (targetLabelOffset < 0) { - throw new IllegalInstructionException("backward branches disallowed; label: " + - mTargetLabel); - } - return targetLabelOffset; - } - - private byte calculateImmSize(int imm, boolean signed) { - if (imm == 0) { - return 0; - } - if (signed && (imm >= -128 && imm <= 127) || - !signed && (imm >= 0 && imm <= 255)) { - return 1; - } - if (signed && (imm >= -32768 && imm <= 32767) || - !signed && (imm >= 0 && imm <= 65535)) { - return 2; - } - return 4; - } - } - - /** - * Jump to this label to terminate the program and indicate the packet - * should be dropped. - */ - public static final String DROP_LABEL = "__DROP__"; - - /** - * Jump to this label to terminate the program and indicate the packet - * should be passed to the AP. - */ - public static final String PASS_LABEL = "__PASS__"; - - /** - * Number of memory slots available for access via APF stores to memory and loads from memory. - * The memory slots are numbered 0 to {@code MEMORY_SLOTS} - 1. This must be kept in sync with - * the APF interpreter. - */ - public static final int MEMORY_SLOTS = 16; - - /** - * Memory slot number that is prefilled with the IPv4 header length. - * Note that this memory slot may be overwritten by a program that - * executes stores to this memory slot. This must be kept in sync with - * the APF interpreter. - */ - public static final int IPV4_HEADER_SIZE_MEMORY_SLOT = 13; - - /** - * Memory slot number that is prefilled with the size of the packet being filtered in bytes. - * Note that this memory slot may be overwritten by a program that - * executes stores to this memory slot. This must be kept in sync with the APF interpreter. - */ - public static final int PACKET_SIZE_MEMORY_SLOT = 14; - - /** - * Memory slot number that is prefilled with the age of the filter in seconds. The age of the - * filter is the time since the filter was installed until now. - * Note that this memory slot may be overwritten by a program that - * executes stores to this memory slot. This must be kept in sync with the APF interpreter. - */ - public static final int FILTER_AGE_MEMORY_SLOT = 15; - - /** - * First memory slot containing prefilled values. Can be used in range comparisons to determine - * if memory slot index is within prefilled slots. - */ - public static final int FIRST_PREFILLED_MEMORY_SLOT = IPV4_HEADER_SIZE_MEMORY_SLOT; - - /** - * Last memory slot containing prefilled values. Can be used in range comparisons to determine - * if memory slot index is within prefilled slots. - */ - public static final int LAST_PREFILLED_MEMORY_SLOT = FILTER_AGE_MEMORY_SLOT; - - // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h - private static final int MIN_APF_VERSION = 2; - - private final ArrayList<Instruction> mInstructions = new ArrayList<Instruction>(); - private final HashMap<String, Instruction> mLabels = new HashMap<String, Instruction>(); - private final Instruction mDropLabel = new Instruction(Opcodes.LABEL); - private final Instruction mPassLabel = new Instruction(Opcodes.LABEL); - private final int mVersion; - private boolean mGenerated; - - /** - * Creates an ApfGenerator instance which is able to emit instructions for the specified - * {@code version} of the APF interpreter. Throws {@code IllegalInstructionException} if - * the requested version is unsupported. - */ - ApfGenerator(int version) throws IllegalInstructionException { - mVersion = version; - requireApfVersion(MIN_APF_VERSION); - } - - /** - * Returns true if the ApfGenerator supports the specified {@code version}, otherwise false. - */ - public static boolean supportsVersion(int version) { - return version >= MIN_APF_VERSION; - } - - private void requireApfVersion(int minimumVersion) throws IllegalInstructionException { - if (mVersion < minimumVersion) { - throw new IllegalInstructionException("Requires APF >= " + minimumVersion); - } - } - - private void addInstruction(Instruction instruction) { - if (mGenerated) { - throw new IllegalStateException("Program already generated"); - } - mInstructions.add(instruction); - } - - /** - * Define a label at the current end of the program. Jumps can jump to this label. Labels are - * their own separate instructions, though with size 0. This facilitates having labels with - * no corresponding code to execute, for example a label at the end of a program. For example - * an {@link ApfGenerator} might be passed to a function that adds a filter like so: - * <pre> - * load from packet - * compare loaded data, jump if not equal to "next_filter" - * load from packet - * compare loaded data, jump if not equal to "next_filter" - * jump to drop label - * define "next_filter" here - * </pre> - * In this case "next_filter" may not have any generated code associated with it. - */ - public ApfGenerator defineLabel(String name) throws IllegalInstructionException { - Instruction instruction = new Instruction(Opcodes.LABEL); - instruction.setLabel(name); - addInstruction(instruction); - return this; - } - - /** - * Add an unconditional jump instruction to the end of the program. - */ - public ApfGenerator addJump(String target) { - Instruction instruction = new Instruction(Opcodes.JMP); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to load the byte at offset {@code offset} - * bytes from the beginning of the packet into {@code register}. - */ - public ApfGenerator addLoad8(Register register, int offset) { - Instruction instruction = new Instruction(Opcodes.LDB, register); - instruction.setUnsignedImm(offset); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to load 16-bits at offset {@code offset} - * bytes from the beginning of the packet into {@code register}. - */ - public ApfGenerator addLoad16(Register register, int offset) { - Instruction instruction = new Instruction(Opcodes.LDH, register); - instruction.setUnsignedImm(offset); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to load 32-bits at offset {@code offset} - * bytes from the beginning of the packet into {@code register}. - */ - public ApfGenerator addLoad32(Register register, int offset) { - Instruction instruction = new Instruction(Opcodes.LDW, register); - instruction.setUnsignedImm(offset); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to load a byte from the packet into - * {@code register}. The offset of the loaded byte from the beginning of the packet is - * the sum of {@code offset} and the value in register R1. - */ - public ApfGenerator addLoad8Indexed(Register register, int offset) { - Instruction instruction = new Instruction(Opcodes.LDBX, register); - instruction.setUnsignedImm(offset); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to load 16-bits from the packet into - * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is - * the sum of {@code offset} and the value in register R1. - */ - public ApfGenerator addLoad16Indexed(Register register, int offset) { - Instruction instruction = new Instruction(Opcodes.LDHX, register); - instruction.setUnsignedImm(offset); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to load 32-bits from the packet into - * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is - * the sum of {@code offset} and the value in register R1. - */ - public ApfGenerator addLoad32Indexed(Register register, int offset) { - Instruction instruction = new Instruction(Opcodes.LDWX, register); - instruction.setUnsignedImm(offset); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to add {@code value} to register R0. - */ - public ApfGenerator addAdd(int value) { - Instruction instruction = new Instruction(Opcodes.ADD); - instruction.setSignedImm(value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to multiply register R0 by {@code value}. - */ - public ApfGenerator addMul(int value) { - Instruction instruction = new Instruction(Opcodes.MUL); - instruction.setSignedImm(value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to divide register R0 by {@code value}. - */ - public ApfGenerator addDiv(int value) { - Instruction instruction = new Instruction(Opcodes.DIV); - instruction.setSignedImm(value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to logically and register R0 with {@code value}. - */ - public ApfGenerator addAnd(int value) { - Instruction instruction = new Instruction(Opcodes.AND); - instruction.setUnsignedImm(value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to logically or register R0 with {@code value}. - */ - public ApfGenerator addOr(int value) { - Instruction instruction = new Instruction(Opcodes.OR); - instruction.setUnsignedImm(value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to shift left register R0 by {@code value} bits. - */ - public ApfGenerator addLeftShift(int value) { - Instruction instruction = new Instruction(Opcodes.SH); - instruction.setSignedImm(value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to shift right register R0 by {@code value} - * bits. - */ - public ApfGenerator addRightShift(int value) { - Instruction instruction = new Instruction(Opcodes.SH); - instruction.setSignedImm(-value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to add register R1 to register R0. - */ - public ApfGenerator addAddR1() { - Instruction instruction = new Instruction(Opcodes.ADD, Register.R1); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to multiply register R0 by register R1. - */ - public ApfGenerator addMulR1() { - Instruction instruction = new Instruction(Opcodes.MUL, Register.R1); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to divide register R0 by register R1. - */ - public ApfGenerator addDivR1() { - Instruction instruction = new Instruction(Opcodes.DIV, Register.R1); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to logically and register R0 with register R1 - * and store the result back into register R0. - */ - public ApfGenerator addAndR1() { - Instruction instruction = new Instruction(Opcodes.AND, Register.R1); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to logically or register R0 with register R1 - * and store the result back into register R0. - */ - public ApfGenerator addOrR1() { - Instruction instruction = new Instruction(Opcodes.OR, Register.R1); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to shift register R0 left by the value in - * register R1. - */ - public ApfGenerator addLeftShiftR1() { - Instruction instruction = new Instruction(Opcodes.SH, Register.R1); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to move {@code value} into {@code register}. - */ - public ApfGenerator addLoadImmediate(Register register, int value) { - Instruction instruction = new Instruction(Opcodes.LI, register); - instruction.setSignedImm(value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value equals {@code value}. - */ - public ApfGenerator addJumpIfR0Equals(int value, String target) { - Instruction instruction = new Instruction(Opcodes.JEQ); - instruction.setUnsignedImm(value); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value does not equal {@code value}. - */ - public ApfGenerator addJumpIfR0NotEquals(int value, String target) { - Instruction instruction = new Instruction(Opcodes.JNE); - instruction.setUnsignedImm(value); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value is greater than {@code value}. - */ - public ApfGenerator addJumpIfR0GreaterThan(int value, String target) { - Instruction instruction = new Instruction(Opcodes.JGT); - instruction.setUnsignedImm(value); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value is less than {@code value}. - */ - public ApfGenerator addJumpIfR0LessThan(int value, String target) { - Instruction instruction = new Instruction(Opcodes.JLT); - instruction.setUnsignedImm(value); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value has any bits set that are also set in {@code value}. - */ - public ApfGenerator addJumpIfR0AnyBitsSet(int value, String target) { - Instruction instruction = new Instruction(Opcodes.JSET); - instruction.setUnsignedImm(value); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value equals register R1's value. - */ - public ApfGenerator addJumpIfR0EqualsR1(String target) { - Instruction instruction = new Instruction(Opcodes.JEQ, Register.R1); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value does not equal register R1's value. - */ - public ApfGenerator addJumpIfR0NotEqualsR1(String target) { - Instruction instruction = new Instruction(Opcodes.JNE, Register.R1); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value is greater than register R1's value. - */ - public ApfGenerator addJumpIfR0GreaterThanR1(String target) { - Instruction instruction = new Instruction(Opcodes.JGT, Register.R1); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value is less than register R1's value. - */ - public ApfGenerator addJumpIfR0LessThanR1(String target) { - Instruction instruction = new Instruction(Opcodes.JLT, Register.R1); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if register R0's - * value has any bits set that are also set in R1's value. - */ - public ApfGenerator addJumpIfR0AnyBitsSetR1(String target) { - Instruction instruction = new Instruction(Opcodes.JSET, Register.R1); - instruction.setTargetLabel(target); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to jump to {@code target} if the bytes of the - * packet at an offset specified by {@code register} match {@code bytes}. - */ - public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target) - throws IllegalInstructionException { - if (register == Register.R1) { - throw new IllegalInstructionException("JNEBS fails with R1"); - } - Instruction instruction = new Instruction(Opcodes.JNEBS, register); - instruction.setUnsignedImm(bytes.length); - instruction.setTargetLabel(target); - instruction.setCompareBytes(bytes); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to load memory slot {@code slot} into - * {@code register}. - */ - public ApfGenerator addLoadFromMemory(Register register, int slot) - throws IllegalInstructionException { - if (slot < 0 || slot > (MEMORY_SLOTS - 1)) { - throw new IllegalInstructionException("illegal memory slot number: " + slot); - } - Instruction instruction = new Instruction(Opcodes.EXT, register); - instruction.setUnsignedImm(ExtendedOpcodes.LDM.value + slot); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to store {@code register} into memory slot - * {@code slot}. - */ - public ApfGenerator addStoreToMemory(Register register, int slot) - throws IllegalInstructionException { - if (slot < 0 || slot > (MEMORY_SLOTS - 1)) { - throw new IllegalInstructionException("illegal memory slot number: " + slot); - } - Instruction instruction = new Instruction(Opcodes.EXT, register); - instruction.setUnsignedImm(ExtendedOpcodes.STM.value + slot); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to logically not {@code register}. - */ - public ApfGenerator addNot(Register register) { - Instruction instruction = new Instruction(Opcodes.EXT, register); - instruction.setUnsignedImm(ExtendedOpcodes.NOT.value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to negate {@code register}. - */ - public ApfGenerator addNeg(Register register) { - Instruction instruction = new Instruction(Opcodes.EXT, register); - instruction.setUnsignedImm(ExtendedOpcodes.NEG.value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to swap the values in register R0 and register R1. - */ - public ApfGenerator addSwap() { - Instruction instruction = new Instruction(Opcodes.EXT); - instruction.setUnsignedImm(ExtendedOpcodes.SWAP.value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to move the value into - * {@code register} from the other register. - */ - public ApfGenerator addMove(Register register) { - Instruction instruction = new Instruction(Opcodes.EXT, register); - instruction.setUnsignedImm(ExtendedOpcodes.MOVE.value); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to load 32 bits from the data memory into - * {@code register}. The source address is computed by adding the signed immediate - * @{code offset} to the other register. - * Requires APF v3 or greater. - */ - public ApfGenerator addLoadData(Register destinationRegister, int offset) - throws IllegalInstructionException { - requireApfVersion(3); - Instruction instruction = new Instruction(Opcodes.LDDW, destinationRegister); - instruction.setSignedImm(offset); - addInstruction(instruction); - return this; - } - - /** - * Add an instruction to the end of the program to store 32 bits from {@code register} into the - * data memory. The destination address is computed by adding the signed immediate - * @{code offset} to the other register. - * Requires APF v3 or greater. - */ - public ApfGenerator addStoreData(Register sourceRegister, int offset) - throws IllegalInstructionException { - requireApfVersion(3); - Instruction instruction = new Instruction(Opcodes.STDW, sourceRegister); - instruction.setSignedImm(offset); - addInstruction(instruction); - return this; - } - - /** - * Updates instruction offset fields using latest instruction sizes. - * @return current program length in bytes. - */ - private int updateInstructionOffsets() { - int offset = 0; - for (Instruction instruction : mInstructions) { - instruction.offset = offset; - offset += instruction.size(); - } - return offset; - } - - /** - * Returns an overestimate of the size of the generated program. {@link #generate} may return - * a program that is smaller. - */ - public int programLengthOverEstimate() { - return updateInstructionOffsets(); - } - - /** - * Generate the bytecode for the APF program. - * @return the bytecode. - * @throws IllegalStateException if a label is referenced but not defined. - */ - public byte[] generate() throws IllegalInstructionException { - // Enforce that we can only generate once because we cannot unshrink instructions and - // PASS/DROP labels may move further away requiring unshrinking if we add further - // instructions. - if (mGenerated) { - throw new IllegalStateException("Can only generate() once!"); - } - mGenerated = true; - int total_size; - boolean shrunk; - // Shrink the immediate value fields of instructions. - // As we shrink the instructions some branch offset - // fields may shrink also, thereby shrinking the - // instructions further. Loop until we've reached the - // minimum size. Rarely will this loop more than a few times. - // Limit iterations to avoid O(n^2) behavior. - int iterations_remaining = 10; - do { - total_size = updateInstructionOffsets(); - // Update drop and pass label offsets. - mDropLabel.offset = total_size + 1; - mPassLabel.offset = total_size; - // Limit run-time in aberant circumstances. - if (iterations_remaining-- == 0) break; - // Attempt to shrink instructions. - shrunk = false; - for (Instruction instruction : mInstructions) { - if (instruction.shrink()) { - shrunk = true; - } - } - } while (shrunk); - // Generate bytecode for instructions. - byte[] bytecode = new byte[total_size]; - for (Instruction instruction : mInstructions) { - instruction.generate(bytecode); - } - return bytecode; - } -} - diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java deleted file mode 100644 index b2eb4e21b591..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpAckPacket.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; - -/** - * This class implements the DHCP-ACK packet. - */ -class DhcpAckPacket extends DhcpPacket { - - /** - * The address of the server which sent this packet. - */ - private final Inet4Address mSrcIp; - - DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress, - Inet4Address relayIp, Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) { - super(transId, secs, clientIp, yourIp, serverAddress, relayIp, clientMac, broadcast); - mBroadcast = broadcast; - mSrcIp = serverAddress; - } - - public String toString() { - String s = super.toString(); - String dnsServers = " DNS servers: "; - - for (Inet4Address dnsServer: mDnsServers) { - dnsServers += dnsServer.toString() + " "; - } - - return s + " ACK: your new IP " + mYourIp + - ", netmask " + mSubnetMask + - ", gateways " + mGateways + dnsServers + - ", lease time " + mLeaseTime; - } - - /** - * Fills in a packet with the requested ACK parameters. - */ - public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) { - ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); - Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp; - Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp; - - fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result, - DHCP_BOOTREPLY, mBroadcast); - result.flip(); - return result; - } - - /** - * Adds the optional parameters to the client-generated ACK packet. - */ - void finishPacket(ByteBuffer buffer) { - addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_ACK); - addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier); - - addCommonServerTlvs(buffer); - addTlvEnd(buffer); - } - - /** - * Un-boxes an Integer, returning 0 if a null reference is supplied. - */ - private static final int getInt(Integer v) { - if (v == null) { - return 0; - } else { - return v.intValue(); - } - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java deleted file mode 100644 index ca6c17a65e99..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java +++ /dev/null @@ -1,1070 +0,0 @@ -/* - * Copyright (C) 2015 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.dhcp; - -import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS; -import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER; -import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME; -import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME; -import static android.net.dhcp.DhcpPacket.DHCP_MTU; -import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME; -import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME; -import static android.net.dhcp.DhcpPacket.DHCP_ROUTER; -import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK; -import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO; -import static android.net.dhcp.DhcpPacket.INADDR_ANY; -import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; -import static android.net.util.NetworkStackUtils.closeSocketQuietly; -import static android.net.util.SocketUtils.makePacketSocketAddress; -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.AF_PACKET; -import static android.system.OsConstants.ETH_P_IP; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_DGRAM; -import static android.system.OsConstants.SOCK_RAW; -import static android.system.OsConstants.SOL_SOCKET; -import static android.system.OsConstants.SO_BROADCAST; -import static android.system.OsConstants.SO_RCVBUF; -import static android.system.OsConstants.SO_REUSEADDR; - -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; - -import android.content.Context; -import android.net.DhcpResults; -import android.net.InetAddresses; -import android.net.TrafficStats; -import android.net.ip.IpClient; -import android.net.metrics.DhcpClientEvent; -import android.net.metrics.DhcpErrorEvent; -import android.net.metrics.IpConnectivityLog; -import android.net.util.InterfaceParams; -import android.net.util.NetworkStackUtils; -import android.net.util.SocketUtils; -import android.os.Message; -import android.os.SystemClock; -import android.system.ErrnoException; -import android.system.Os; -import android.util.EventLog; -import android.util.Log; -import android.util.SparseArray; - -import com.android.internal.util.HexDump; -import com.android.internal.util.MessageUtils; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.internal.util.TrafficStatsConstants; -import com.android.internal.util.WakeupMessage; -import com.android.networkstack.R; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.Inet4Address; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Random; - -/** - * A DHCPv4 client. - * - * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android - * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine. - * - * TODO: - * - * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour). - * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not - * do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a - * given SSID), it requests the last-leased IP address on the same interface, causing a delay if - * the server NAKs or a timeout if it doesn't. - * - * Known differences from current behaviour: - * - * - Does not request the "static routes" option. - * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now. - * - Requests the "broadcast" option, but does nothing with it. - * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0). - * - * @hide - */ -public class DhcpClient extends StateMachine { - - private static final String TAG = "DhcpClient"; - private static final boolean DBG = true; - private static final boolean STATE_DBG = Log.isLoggable(TAG, Log.DEBUG); - private static final boolean MSG_DBG = Log.isLoggable(TAG, Log.DEBUG); - private static final boolean PACKET_DBG = Log.isLoggable(TAG, Log.DEBUG); - - // Metrics events: must be kept in sync with server-side aggregation code. - /** Represents transitions from DhcpInitState to DhcpBoundState */ - private static final String EVENT_INITIAL_BOUND = "InitialBoundState"; - /** Represents transitions from and to DhcpBoundState via DhcpRenewingState */ - private static final String EVENT_RENEWING_BOUND = "RenewingBoundState"; - - // Timers and timeouts. - private static final int SECONDS = 1000; - private static final int FIRST_TIMEOUT_MS = 2 * SECONDS; - private static final int MAX_TIMEOUT_MS = 128 * SECONDS; - - // This is not strictly needed, since the client is asynchronous and implements exponential - // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was - // a blocking operation with a 30-second timeout. We pick 36 seconds so we can send packets at - // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter. - private static final int DHCP_TIMEOUT_MS = 36 * SECONDS; - - // DhcpClient uses IpClient's handler. - private static final int PUBLIC_BASE = IpClient.DHCPCLIENT_CMD_BASE; - - // Below constants are picked up by MessageUtils and exempt from ProGuard optimization. - /* Commands from controller to start/stop DHCP */ - public static final int CMD_START_DHCP = PUBLIC_BASE + 1; - public static final int CMD_STOP_DHCP = PUBLIC_BASE + 2; - - /* Notification from DHCP state machine prior to DHCP discovery/renewal */ - public static final int CMD_PRE_DHCP_ACTION = PUBLIC_BASE + 3; - /* Notification from DHCP state machine post DHCP discovery/renewal. Indicates - * success/failure */ - public static final int CMD_POST_DHCP_ACTION = PUBLIC_BASE + 4; - /* Notification from DHCP state machine before quitting */ - public static final int CMD_ON_QUIT = PUBLIC_BASE + 5; - - /* Command from controller to indicate DHCP discovery/renewal can continue - * after pre DHCP action is complete */ - public static final int CMD_PRE_DHCP_ACTION_COMPLETE = PUBLIC_BASE + 6; - - /* Command and event notification to/from IpManager requesting the setting - * (or clearing) of an IPv4 LinkAddress. - */ - public static final int CMD_CLEAR_LINKADDRESS = PUBLIC_BASE + 7; - public static final int CMD_CONFIGURE_LINKADDRESS = PUBLIC_BASE + 8; - public static final int EVENT_LINKADDRESS_CONFIGURED = PUBLIC_BASE + 9; - - /* Message.arg1 arguments to CMD_POST_DHCP_ACTION notification */ - public static final int DHCP_SUCCESS = 1; - public static final int DHCP_FAILURE = 2; - - // Internal messages. - private static final int PRIVATE_BASE = IpClient.DHCPCLIENT_CMD_BASE + 100; - private static final int CMD_KICK = PRIVATE_BASE + 1; - private static final int CMD_RECEIVED_PACKET = PRIVATE_BASE + 2; - private static final int CMD_TIMEOUT = PRIVATE_BASE + 3; - private static final int CMD_RENEW_DHCP = PRIVATE_BASE + 4; - private static final int CMD_REBIND_DHCP = PRIVATE_BASE + 5; - private static final int CMD_EXPIRE_DHCP = PRIVATE_BASE + 6; - - // For message logging. - private static final Class[] sMessageClasses = { DhcpClient.class }; - private static final SparseArray<String> sMessageNames = - MessageUtils.findMessageNames(sMessageClasses); - - // DHCP parameters that we request. - /* package */ static final byte[] REQUESTED_PARAMS = new byte[] { - DHCP_SUBNET_MASK, - DHCP_ROUTER, - DHCP_DNS_SERVER, - DHCP_DOMAIN_NAME, - DHCP_MTU, - DHCP_BROADCAST_ADDRESS, // TODO: currently ignored. - DHCP_LEASE_TIME, - DHCP_RENEWAL_TIME, - DHCP_REBINDING_TIME, - DHCP_VENDOR_INFO, - }; - - // DHCP flag that means "yes, we support unicast." - private static final boolean DO_UNICAST = false; - - // System services / libraries we use. - private final Context mContext; - private final Random mRandom; - private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); - - // Sockets. - // - We use a packet socket to receive, because servers send us packets bound for IP addresses - // which we have not yet configured, and the kernel protocol stack drops these. - // - We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can - // be off-link as well as on-link). - private FileDescriptor mPacketSock; - private FileDescriptor mUdpSock; - private ReceiveThread mReceiveThread; - - // State variables. - private final StateMachine mController; - private final WakeupMessage mKickAlarm; - private final WakeupMessage mTimeoutAlarm; - private final WakeupMessage mRenewAlarm; - private final WakeupMessage mRebindAlarm; - private final WakeupMessage mExpiryAlarm; - private final String mIfaceName; - - private boolean mRegisteredForPreDhcpNotification; - private InterfaceParams mIface; - // TODO: MacAddress-ify more of this class hierarchy. - private byte[] mHwAddr; - private SocketAddress mInterfaceBroadcastAddr; - private int mTransactionId; - private long mTransactionStartMillis; - private DhcpResults mDhcpLease; - private long mDhcpLeaseExpiry; - private DhcpResults mOffer; - - // Milliseconds SystemClock timestamps used to record transition times to DhcpBoundState. - private long mLastInitEnterTime; - private long mLastBoundExitTime; - - // States. - private State mStoppedState = new StoppedState(); - private State mDhcpState = new DhcpState(); - private State mDhcpInitState = new DhcpInitState(); - private State mDhcpSelectingState = new DhcpSelectingState(); - private State mDhcpRequestingState = new DhcpRequestingState(); - private State mDhcpHaveLeaseState = new DhcpHaveLeaseState(); - private State mConfiguringInterfaceState = new ConfiguringInterfaceState(); - private State mDhcpBoundState = new DhcpBoundState(); - private State mDhcpRenewingState = new DhcpRenewingState(); - private State mDhcpRebindingState = new DhcpRebindingState(); - private State mDhcpInitRebootState = new DhcpInitRebootState(); - private State mDhcpRebootingState = new DhcpRebootingState(); - private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState); - private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState); - - private WakeupMessage makeWakeupMessage(String cmdName, int cmd) { - cmdName = DhcpClient.class.getSimpleName() + "." + mIfaceName + "." + cmdName; - return new WakeupMessage(mContext, getHandler(), cmdName, cmd); - } - - // TODO: Take an InterfaceParams instance instead of an interface name String. - private DhcpClient(Context context, StateMachine controller, String iface) { - super(TAG, controller.getHandler()); - - mContext = context; - mController = controller; - mIfaceName = iface; - - addState(mStoppedState); - addState(mDhcpState); - addState(mDhcpInitState, mDhcpState); - addState(mWaitBeforeStartState, mDhcpState); - addState(mDhcpSelectingState, mDhcpState); - addState(mDhcpRequestingState, mDhcpState); - addState(mDhcpHaveLeaseState, mDhcpState); - addState(mConfiguringInterfaceState, mDhcpHaveLeaseState); - addState(mDhcpBoundState, mDhcpHaveLeaseState); - addState(mWaitBeforeRenewalState, mDhcpHaveLeaseState); - addState(mDhcpRenewingState, mDhcpHaveLeaseState); - addState(mDhcpRebindingState, mDhcpHaveLeaseState); - addState(mDhcpInitRebootState, mDhcpState); - addState(mDhcpRebootingState, mDhcpState); - - setInitialState(mStoppedState); - - mRandom = new Random(); - - // Used to schedule packet retransmissions. - mKickAlarm = makeWakeupMessage("KICK", CMD_KICK); - // Used to time out PacketRetransmittingStates. - mTimeoutAlarm = makeWakeupMessage("TIMEOUT", CMD_TIMEOUT); - // Used to schedule DHCP reacquisition. - mRenewAlarm = makeWakeupMessage("RENEW", CMD_RENEW_DHCP); - mRebindAlarm = makeWakeupMessage("REBIND", CMD_REBIND_DHCP); - mExpiryAlarm = makeWakeupMessage("EXPIRY", CMD_EXPIRE_DHCP); - } - - public void registerForPreDhcpNotification() { - mRegisteredForPreDhcpNotification = true; - } - - public static DhcpClient makeDhcpClient( - Context context, StateMachine controller, InterfaceParams ifParams) { - DhcpClient client = new DhcpClient(context, controller, ifParams.name); - client.mIface = ifParams; - client.start(); - return client; - } - - private boolean initInterface() { - if (mIface == null) mIface = InterfaceParams.getByName(mIfaceName); - if (mIface == null) { - Log.e(TAG, "Can't determine InterfaceParams for " + mIfaceName); - return false; - } - - mHwAddr = mIface.macAddr.toByteArray(); - mInterfaceBroadcastAddr = makePacketSocketAddress(mIface.index, DhcpPacket.ETHER_BROADCAST); - return true; - } - - private void startNewTransaction() { - mTransactionId = mRandom.nextInt(); - mTransactionStartMillis = SystemClock.elapsedRealtime(); - } - - private boolean initSockets() { - return initPacketSocket() && initUdpSocket(); - } - - private boolean initPacketSocket() { - try { - mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP); - SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index); - Os.bind(mPacketSock, addr); - NetworkStackUtils.attachDhcpFilter(mPacketSock); - } catch(SocketException|ErrnoException e) { - Log.e(TAG, "Error creating packet socket", e); - return false; - } - return true; - } - - private boolean initUdpSocket() { - final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_DHCP); - try { - mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName); - Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1); - Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1); - Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0); - Os.bind(mUdpSock, IPV4_ADDR_ANY, DhcpPacket.DHCP_CLIENT); - } catch(SocketException|ErrnoException e) { - Log.e(TAG, "Error creating UDP socket", e); - return false; - } finally { - TrafficStats.setThreadStatsTag(oldTag); - } - return true; - } - - private boolean connectUdpSock(Inet4Address to) { - try { - Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER); - return true; - } catch (SocketException|ErrnoException e) { - Log.e(TAG, "Error connecting UDP socket", e); - return false; - } - } - - private void closeSockets() { - closeSocketQuietly(mUdpSock); - closeSocketQuietly(mPacketSock); - } - - class ReceiveThread extends Thread { - - private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH]; - private volatile boolean mStopped = false; - - public void halt() { - mStopped = true; - closeSockets(); // Interrupts the read() call the thread is blocked in. - } - - @Override - public void run() { - if (DBG) Log.d(TAG, "Receive thread started"); - while (!mStopped) { - int length = 0; // Or compiler can't tell it's initialized if a parse error occurs. - try { - length = Os.read(mPacketSock, mPacket, 0, mPacket.length); - DhcpPacket packet = null; - packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2); - if (DBG) Log.d(TAG, "Received packet: " + packet); - sendMessage(CMD_RECEIVED_PACKET, packet); - } catch (IOException|ErrnoException e) { - if (!mStopped) { - Log.e(TAG, "Read error", e); - logError(DhcpErrorEvent.RECEIVE_ERROR); - } - } catch (DhcpPacket.ParseException e) { - Log.e(TAG, "Can't parse packet: " + e.getMessage()); - if (PACKET_DBG) { - Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length)); - } - if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) { - int snetTagId = 0x534e4554; - String bugId = "31850211"; - int uid = -1; - String data = DhcpPacket.ParseException.class.getName(); - EventLog.writeEvent(snetTagId, bugId, uid, data); - } - logError(e.errorCode); - } - } - if (DBG) Log.d(TAG, "Receive thread stopped"); - } - } - - private short getSecs() { - return (short) ((SystemClock.elapsedRealtime() - mTransactionStartMillis) / 1000); - } - - private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) { - try { - if (encap == DhcpPacket.ENCAP_L2) { - if (DBG) Log.d(TAG, "Broadcasting " + description); - Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr); - } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) { - if (DBG) Log.d(TAG, "Broadcasting " + description); - // We only send L3-encapped broadcasts in DhcpRebindingState, - // where we have an IP address and an unconnected UDP socket. - // - // N.B.: We only need this codepath because DhcpRequestPacket - // hardcodes the source IP address to 0.0.0.0. We could reuse - // the packet socket if this ever changes. - Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER); - } else { - // It's safe to call getpeername here, because we only send unicast packets if we - // have an IP address, and we connect the UDP socket in DhcpBoundState#enter. - if (DBG) Log.d(TAG, String.format("Unicasting %s to %s", - description, Os.getpeername(mUdpSock))); - Os.write(mUdpSock, buf); - } - } catch(ErrnoException|IOException e) { - Log.e(TAG, "Can't send packet: ", e); - return false; - } - return true; - } - - private boolean sendDiscoverPacket() { - ByteBuffer packet = DhcpPacket.buildDiscoverPacket( - DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, - DO_UNICAST, REQUESTED_PARAMS); - return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); - } - - private boolean sendRequestPacket( - Inet4Address clientAddress, Inet4Address requestedAddress, - Inet4Address serverAddress, Inet4Address to) { - // TODO: should we use the transaction ID from the server? - final int encap = INADDR_ANY.equals(clientAddress) - ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP; - - ByteBuffer packet = DhcpPacket.buildRequestPacket( - encap, mTransactionId, getSecs(), clientAddress, - DO_UNICAST, mHwAddr, requestedAddress, - serverAddress, REQUESTED_PARAMS, null); - String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; - String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + - " request=" + requestedAddress.getHostAddress() + - " serverid=" + serverStr; - return transmitPacket(packet, description, encap, to); - } - - private void scheduleLeaseTimers() { - if (mDhcpLeaseExpiry == 0) { - Log.d(TAG, "Infinite lease, no timer scheduling needed"); - return; - } - - final long now = SystemClock.elapsedRealtime(); - - // TODO: consider getting the renew and rebind timers from T1 and T2. - // See also: - // https://tools.ietf.org/html/rfc2131#section-4.4.5 - // https://tools.ietf.org/html/rfc1533#section-9.9 - // https://tools.ietf.org/html/rfc1533#section-9.10 - final long remainingDelay = mDhcpLeaseExpiry - now; - final long renewDelay = remainingDelay / 2; - final long rebindDelay = remainingDelay * 7 / 8; - mRenewAlarm.schedule(now + renewDelay); - mRebindAlarm.schedule(now + rebindDelay); - mExpiryAlarm.schedule(now + remainingDelay); - Log.d(TAG, "Scheduling renewal in " + (renewDelay / 1000) + "s"); - Log.d(TAG, "Scheduling rebind in " + (rebindDelay / 1000) + "s"); - Log.d(TAG, "Scheduling expiry in " + (remainingDelay / 1000) + "s"); - } - - private void notifySuccess() { - mController.sendMessage( - CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease)); - } - - private void notifyFailure() { - mController.sendMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0, null); - } - - private void acceptDhcpResults(DhcpResults results, String msg) { - mDhcpLease = results; - if (mDhcpLease.dnsServers.isEmpty()) { - // supplement customized dns servers - String[] dnsServersList = - mContext.getResources().getStringArray(R.array.config_default_dns_servers); - for (final String dnsServer : dnsServersList) { - try { - mDhcpLease.dnsServers.add(InetAddresses.parseNumericAddress(dnsServer)); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Invalid default DNS server: " + dnsServer, e); - } - } - } - mOffer = null; - Log.d(TAG, msg + " lease: " + mDhcpLease); - notifySuccess(); - } - - private void clearDhcpState() { - mDhcpLease = null; - mDhcpLeaseExpiry = 0; - mOffer = null; - } - - /** - * Quit the DhcpStateMachine. - * - * @hide - */ - public void doQuit() { - Log.d(TAG, "doQuit"); - quit(); - } - - @Override - protected void onQuitting() { - Log.d(TAG, "onQuitting"); - mController.sendMessage(CMD_ON_QUIT); - } - - abstract class LoggingState extends State { - private long mEnterTimeMs; - - @Override - public void enter() { - if (STATE_DBG) Log.d(TAG, "Entering state " + getName()); - mEnterTimeMs = SystemClock.elapsedRealtime(); - } - - @Override - public void exit() { - long durationMs = SystemClock.elapsedRealtime() - mEnterTimeMs; - logState(getName(), (int) durationMs); - } - - private String messageName(int what) { - return sMessageNames.get(what, Integer.toString(what)); - } - - private String messageToString(Message message) { - long now = SystemClock.uptimeMillis(); - return new StringBuilder(" ") - .append(message.getWhen() - now) - .append(messageName(message.what)) - .append(" ").append(message.arg1) - .append(" ").append(message.arg2) - .append(" ").append(message.obj) - .toString(); - } - - @Override - public boolean processMessage(Message message) { - if (MSG_DBG) { - Log.d(TAG, getName() + messageToString(message)); - } - return NOT_HANDLED; - } - - @Override - public String getName() { - // All DhcpClient's states are inner classes with a well defined name. - // Use getSimpleName() and avoid super's getName() creating new String instances. - return getClass().getSimpleName(); - } - } - - // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with - // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState. - abstract class WaitBeforeOtherState extends LoggingState { - protected State mOtherState; - - @Override - public void enter() { - super.enter(); - mController.sendMessage(CMD_PRE_DHCP_ACTION); - } - - @Override - public boolean processMessage(Message message) { - super.processMessage(message); - switch (message.what) { - case CMD_PRE_DHCP_ACTION_COMPLETE: - transitionTo(mOtherState); - return HANDLED; - default: - return NOT_HANDLED; - } - } - } - - class StoppedState extends State { - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_START_DHCP: - if (mRegisteredForPreDhcpNotification) { - transitionTo(mWaitBeforeStartState); - } else { - transitionTo(mDhcpInitState); - } - return HANDLED; - default: - return NOT_HANDLED; - } - } - } - - class WaitBeforeStartState extends WaitBeforeOtherState { - public WaitBeforeStartState(State otherState) { - super(); - mOtherState = otherState; - } - } - - class WaitBeforeRenewalState extends WaitBeforeOtherState { - public WaitBeforeRenewalState(State otherState) { - super(); - mOtherState = otherState; - } - } - - class DhcpState extends State { - @Override - public void enter() { - clearDhcpState(); - if (initInterface() && initSockets()) { - mReceiveThread = new ReceiveThread(); - mReceiveThread.start(); - } else { - notifyFailure(); - transitionTo(mStoppedState); - } - } - - @Override - public void exit() { - if (mReceiveThread != null) { - mReceiveThread.halt(); // Also closes sockets. - mReceiveThread = null; - } - clearDhcpState(); - } - - @Override - public boolean processMessage(Message message) { - super.processMessage(message); - switch (message.what) { - case CMD_STOP_DHCP: - transitionTo(mStoppedState); - return HANDLED; - default: - return NOT_HANDLED; - } - } - } - - public boolean isValidPacket(DhcpPacket packet) { - // TODO: check checksum. - int xid = packet.getTransactionId(); - if (xid != mTransactionId) { - Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId); - return false; - } - if (!Arrays.equals(packet.getClientMac(), mHwAddr)) { - Log.d(TAG, "MAC addr mismatch: got " + - HexDump.toHexString(packet.getClientMac()) + ", expected " + - HexDump.toHexString(packet.getClientMac())); - return false; - } - return true; - } - - public void setDhcpLeaseExpiry(DhcpPacket packet) { - long leaseTimeMillis = packet.getLeaseTimeMillis(); - mDhcpLeaseExpiry = - (leaseTimeMillis > 0) ? SystemClock.elapsedRealtime() + leaseTimeMillis : 0; - } - - /** - * Retransmits packets using jittered exponential backoff with an optional timeout. Packet - * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass - * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout - * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the - * state. - * - * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a - * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET - * sent by the receive thread. They may also set mTimeout and implement timeout. - */ - abstract class PacketRetransmittingState extends LoggingState { - - private int mTimer; - protected int mTimeout = 0; - - @Override - public void enter() { - super.enter(); - initTimer(); - maybeInitTimeout(); - sendMessage(CMD_KICK); - } - - @Override - public boolean processMessage(Message message) { - super.processMessage(message); - switch (message.what) { - case CMD_KICK: - sendPacket(); - scheduleKick(); - return HANDLED; - case CMD_RECEIVED_PACKET: - receivePacket((DhcpPacket) message.obj); - return HANDLED; - case CMD_TIMEOUT: - timeout(); - return HANDLED; - default: - return NOT_HANDLED; - } - } - - @Override - public void exit() { - super.exit(); - mKickAlarm.cancel(); - mTimeoutAlarm.cancel(); - } - - abstract protected boolean sendPacket(); - abstract protected void receivePacket(DhcpPacket packet); - protected void timeout() {} - - protected void initTimer() { - mTimer = FIRST_TIMEOUT_MS; - } - - protected int jitterTimer(int baseTimer) { - int maxJitter = baseTimer / 10; - int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter; - return baseTimer + jitter; - } - - protected void scheduleKick() { - long now = SystemClock.elapsedRealtime(); - long timeout = jitterTimer(mTimer); - long alarmTime = now + timeout; - mKickAlarm.schedule(alarmTime); - mTimer *= 2; - if (mTimer > MAX_TIMEOUT_MS) { - mTimer = MAX_TIMEOUT_MS; - } - } - - protected void maybeInitTimeout() { - if (mTimeout > 0) { - long alarmTime = SystemClock.elapsedRealtime() + mTimeout; - mTimeoutAlarm.schedule(alarmTime); - } - } - } - - class DhcpInitState extends PacketRetransmittingState { - public DhcpInitState() { - super(); - } - - @Override - public void enter() { - super.enter(); - startNewTransaction(); - mLastInitEnterTime = SystemClock.elapsedRealtime(); - } - - protected boolean sendPacket() { - return sendDiscoverPacket(); - } - - protected void receivePacket(DhcpPacket packet) { - if (!isValidPacket(packet)) return; - if (!(packet instanceof DhcpOfferPacket)) return; - mOffer = packet.toDhcpResults(); - if (mOffer != null) { - Log.d(TAG, "Got pending lease: " + mOffer); - transitionTo(mDhcpRequestingState); - } - } - } - - // Not implemented. We request the first offer we receive. - class DhcpSelectingState extends LoggingState { - } - - class DhcpRequestingState extends PacketRetransmittingState { - public DhcpRequestingState() { - mTimeout = DHCP_TIMEOUT_MS / 2; - } - - protected boolean sendPacket() { - return sendRequestPacket( - INADDR_ANY, // ciaddr - (Inet4Address) mOffer.ipAddress.getAddress(), // DHCP_REQUESTED_IP - (Inet4Address) mOffer.serverAddress, // DHCP_SERVER_IDENTIFIER - INADDR_BROADCAST); // packet destination address - } - - protected void receivePacket(DhcpPacket packet) { - if (!isValidPacket(packet)) return; - if ((packet instanceof DhcpAckPacket)) { - DhcpResults results = packet.toDhcpResults(); - if (results != null) { - setDhcpLeaseExpiry(packet); - acceptDhcpResults(results, "Confirmed"); - transitionTo(mConfiguringInterfaceState); - } - } else if (packet instanceof DhcpNakPacket) { - // TODO: Wait a while before returning into INIT state. - Log.d(TAG, "Received NAK, returning to INIT"); - mOffer = null; - transitionTo(mDhcpInitState); - } - } - - @Override - protected void timeout() { - // After sending REQUESTs unsuccessfully for a while, go back to init. - transitionTo(mDhcpInitState); - } - } - - class DhcpHaveLeaseState extends State { - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_EXPIRE_DHCP: - Log.d(TAG, "Lease expired!"); - notifyFailure(); - transitionTo(mDhcpInitState); - return HANDLED; - default: - return NOT_HANDLED; - } - } - - @Override - public void exit() { - // Clear any extant alarms. - mRenewAlarm.cancel(); - mRebindAlarm.cancel(); - mExpiryAlarm.cancel(); - clearDhcpState(); - // Tell IpManager to clear the IPv4 address. There is no need to - // wait for confirmation since any subsequent packets are sent from - // INADDR_ANY anyway (DISCOVER, REQUEST). - mController.sendMessage(CMD_CLEAR_LINKADDRESS); - } - } - - class ConfiguringInterfaceState extends LoggingState { - @Override - public void enter() { - super.enter(); - mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress); - } - - @Override - public boolean processMessage(Message message) { - super.processMessage(message); - switch (message.what) { - case EVENT_LINKADDRESS_CONFIGURED: - transitionTo(mDhcpBoundState); - return HANDLED; - default: - return NOT_HANDLED; - } - } - } - - class DhcpBoundState extends LoggingState { - @Override - public void enter() { - super.enter(); - if (mDhcpLease.serverAddress != null && !connectUdpSock(mDhcpLease.serverAddress)) { - // There's likely no point in going into DhcpInitState here, we'll probably - // just repeat the transaction, get the same IP address as before, and fail. - // - // NOTE: It is observed that connectUdpSock() basically never fails, due to - // SO_BINDTODEVICE. Examining the local socket address shows it will happily - // return an IPv4 address from another interface, or even return "0.0.0.0". - // - // TODO: Consider deleting this check, following testing on several kernels. - notifyFailure(); - transitionTo(mStoppedState); - } - - scheduleLeaseTimers(); - logTimeToBoundState(); - } - - @Override - public void exit() { - super.exit(); - mLastBoundExitTime = SystemClock.elapsedRealtime(); - } - - @Override - public boolean processMessage(Message message) { - super.processMessage(message); - switch (message.what) { - case CMD_RENEW_DHCP: - if (mRegisteredForPreDhcpNotification) { - transitionTo(mWaitBeforeRenewalState); - } else { - transitionTo(mDhcpRenewingState); - } - return HANDLED; - default: - return NOT_HANDLED; - } - } - - private void logTimeToBoundState() { - long now = SystemClock.elapsedRealtime(); - if (mLastBoundExitTime > mLastInitEnterTime) { - logState(EVENT_RENEWING_BOUND, (int) (now - mLastBoundExitTime)); - } else { - logState(EVENT_INITIAL_BOUND, (int) (now - mLastInitEnterTime)); - } - } - } - - abstract class DhcpReacquiringState extends PacketRetransmittingState { - protected String mLeaseMsg; - - @Override - public void enter() { - super.enter(); - startNewTransaction(); - } - - abstract protected Inet4Address packetDestination(); - - protected boolean sendPacket() { - return sendRequestPacket( - (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr - INADDR_ANY, // DHCP_REQUESTED_IP - null, // DHCP_SERVER_IDENTIFIER - packetDestination()); // packet destination address - } - - protected void receivePacket(DhcpPacket packet) { - if (!isValidPacket(packet)) return; - if ((packet instanceof DhcpAckPacket)) { - final DhcpResults results = packet.toDhcpResults(); - if (results != null) { - if (!mDhcpLease.ipAddress.equals(results.ipAddress)) { - Log.d(TAG, "Renewed lease not for our current IP address!"); - notifyFailure(); - transitionTo(mDhcpInitState); - } - setDhcpLeaseExpiry(packet); - // Updating our notion of DhcpResults here only causes the - // DNS servers and routes to be updated in LinkProperties - // in IpManager and by any overridden relevant handlers of - // the registered IpManager.Callback. IP address changes - // are not supported here. - acceptDhcpResults(results, mLeaseMsg); - transitionTo(mDhcpBoundState); - } - } else if (packet instanceof DhcpNakPacket) { - Log.d(TAG, "Received NAK, returning to INIT"); - notifyFailure(); - transitionTo(mDhcpInitState); - } - } - } - - class DhcpRenewingState extends DhcpReacquiringState { - public DhcpRenewingState() { - mLeaseMsg = "Renewed"; - } - - @Override - public boolean processMessage(Message message) { - if (super.processMessage(message) == HANDLED) { - return HANDLED; - } - - switch (message.what) { - case CMD_REBIND_DHCP: - transitionTo(mDhcpRebindingState); - return HANDLED; - default: - return NOT_HANDLED; - } - } - - @Override - protected Inet4Address packetDestination() { - // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but... - // http://b/25343517 . Try to make things work anyway by using broadcast renews. - return (mDhcpLease.serverAddress != null) ? - mDhcpLease.serverAddress : INADDR_BROADCAST; - } - } - - class DhcpRebindingState extends DhcpReacquiringState { - public DhcpRebindingState() { - mLeaseMsg = "Rebound"; - } - - @Override - public void enter() { - super.enter(); - - // We need to broadcast and possibly reconnect the socket to a - // completely different server. - closeSocketQuietly(mUdpSock); - if (!initUdpSocket()) { - Log.e(TAG, "Failed to recreate UDP socket"); - transitionTo(mDhcpInitState); - } - } - - @Override - protected Inet4Address packetDestination() { - return INADDR_BROADCAST; - } - } - - class DhcpInitRebootState extends LoggingState { - } - - class DhcpRebootingState extends LoggingState { - } - - private void logError(int errorCode) { - mMetricsLog.log(mIfaceName, new DhcpErrorEvent(errorCode)); - } - - private void logState(String name, int durationMs) { - final DhcpClientEvent event = new DhcpClientEvent.Builder() - .setMsg(name) - .setDurationMs(durationMs) - .build(); - mMetricsLog.log(mIfaceName, event); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java deleted file mode 100644 index 7ecdea7b89da..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpDeclinePacket.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; - -/** - * This class implements the DHCP-DECLINE packet. - */ -class DhcpDeclinePacket extends DhcpPacket { - /** - * Generates a DECLINE packet with the specified parameters. - */ - DhcpDeclinePacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, - Inet4Address nextIp, Inet4Address relayIp, - byte[] clientMac) { - super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false); - } - - public String toString() { - String s = super.toString(); - return s + " DECLINE"; - } - - /** - * Fills in a packet with the requested DECLINE attributes. - */ - public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) { - ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); - - fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result, - DHCP_BOOTREQUEST, false); - result.flip(); - return result; - } - - /** - * Adds optional parameters to the DECLINE packet. - */ - void finishPacket(ByteBuffer buffer) { - addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DECLINE); - addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId()); - // RFC 2131 says we MUST NOT include our common client TLVs or the parameter request list. - addTlvEnd(buffer); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java deleted file mode 100644 index 11f2b6118e24..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpDiscoverPacket.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; - -/** - * This class implements the DHCP-DISCOVER packet. - */ -class DhcpDiscoverPacket extends DhcpPacket { - /** - * The IP address of the client which sent this packet. - */ - final Inet4Address mSrcIp; - - /** - * Generates a DISCOVER packet with the specified parameters. - */ - DhcpDiscoverPacket(int transId, short secs, Inet4Address relayIp, byte[] clientMac, - boolean broadcast, Inet4Address srcIp) { - super(transId, secs, INADDR_ANY, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast); - mSrcIp = srcIp; - } - - public String toString() { - String s = super.toString(); - return s + " DISCOVER " + - (mBroadcast ? "broadcast " : "unicast "); - } - - /** - * Fills in a packet with the requested DISCOVER parameters. - */ - public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) { - ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); - fillInPacket(encap, INADDR_BROADCAST, mSrcIp, destUdp, srcUdp, result, DHCP_BOOTREQUEST, - mBroadcast); - result.flip(); - return result; - } - - /** - * Adds optional parameters to a DISCOVER packet. - */ - void finishPacket(ByteBuffer buffer) { - addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DISCOVER); - addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId()); - addCommonClientTlvs(buffer); - addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams); - addTlvEnd(buffer); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java deleted file mode 100644 index 7a83466c6e05..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpInformPacket.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; - -/** - * This class implements the (unused) DHCP-INFORM packet. - */ -class DhcpInformPacket extends DhcpPacket { - /** - * Generates an INFORM packet with the specified parameters. - */ - DhcpInformPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, - Inet4Address nextIp, Inet4Address relayIp, - byte[] clientMac) { - super(transId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, false); - } - - public String toString() { - String s = super.toString(); - return s + " INFORM"; - } - - /** - * Builds an INFORM packet. - */ - public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) { - ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); - - fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result, - DHCP_BOOTREQUEST, false); - result.flip(); - return result; - } - - /** - * Adds additional parameters to the INFORM packet. - */ - void finishPacket(ByteBuffer buffer) { - addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_INFORM); - addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId()); - addCommonClientTlvs(buffer); - addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams); - addTlvEnd(buffer); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java deleted file mode 100644 index 6849cfadc22a..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpLease.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.MacAddress; -import android.os.SystemClock; -import android.text.TextUtils; - -import com.android.internal.util.HexDump; - -import java.net.Inet4Address; -import java.util.Arrays; -import java.util.Objects; - -/** - * An IPv4 address assignment done through DHCPv4. - * @hide - */ -public class DhcpLease { - public static final long EXPIRATION_NEVER = Long.MAX_VALUE; - public static final String HOSTNAME_NONE = null; - - @Nullable - private final byte[] mClientId; - @NonNull - private final MacAddress mHwAddr; - @NonNull - private final Inet4Address mNetAddr; - /** - * Expiration time for the lease, to compare with {@link SystemClock#elapsedRealtime()}. - */ - private final long mExpTime; - @Nullable - private final String mHostname; - - public DhcpLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr, - @NonNull Inet4Address netAddr, long expTime, @Nullable String hostname) { - mClientId = (clientId == null ? null : Arrays.copyOf(clientId, clientId.length)); - mHwAddr = hwAddr; - mNetAddr = netAddr; - mExpTime = expTime; - mHostname = hostname; - } - - /** - * Get the clientId associated with this lease, if any. - * - * <p>If the lease is not associated to a clientId, this returns null. - */ - @Nullable - public byte[] getClientId() { - if (mClientId == null) { - return null; - } - return Arrays.copyOf(mClientId, mClientId.length); - } - - @NonNull - public MacAddress getHwAddr() { - return mHwAddr; - } - - @Nullable - public String getHostname() { - return mHostname; - } - - @NonNull - public Inet4Address getNetAddr() { - return mNetAddr; - } - - public long getExpTime() { - return mExpTime; - } - - /** - * Push back the expiration time of this lease. If the provided time is sooner than the original - * expiration time, the lease time will not be updated. - * - * <p>The lease hostname is updated with the provided one if set. - * @return A {@link DhcpLease} with expiration time set to max(expTime, currentExpTime) - */ - public DhcpLease renewedLease(long expTime, @Nullable String hostname) { - return new DhcpLease(mClientId, mHwAddr, mNetAddr, Math.max(expTime, mExpTime), - (hostname == null ? mHostname : hostname)); - } - - /** - * Determine whether this lease matches a client with the specified parameters. - * @param clientId clientId of the client if any, or null otherwise. - * @param hwAddr Hardware address of the client. - */ - public boolean matchesClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) { - if (mClientId != null) { - return Arrays.equals(mClientId, clientId); - } else { - return clientId == null && mHwAddr.equals(hwAddr); - } - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof DhcpLease)) { - return false; - } - final DhcpLease other = (DhcpLease) obj; - return Arrays.equals(mClientId, other.mClientId) - && mHwAddr.equals(other.mHwAddr) - && mNetAddr.equals(other.mNetAddr) - && mExpTime == other.mExpTime - && TextUtils.equals(mHostname, other.mHostname); - } - - @Override - public int hashCode() { - return Objects.hash(mClientId, mHwAddr, mNetAddr, mHostname, mExpTime); - } - - static String clientIdToString(byte[] bytes) { - if (bytes == null) { - return "null"; - } - return HexDump.toHexString(bytes); - } - - static String inet4AddrToString(@Nullable Inet4Address addr) { - return (addr == null) ? "null" : addr.getHostAddress(); - } - - @Override - public String toString() { - return String.format("clientId: %s, hwAddr: %s, netAddr: %s, expTime: %d, hostname: %s", - clientIdToString(mClientId), mHwAddr.toString(), inet4AddrToString(mNetAddr), - mExpTime, mHostname); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java b/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java deleted file mode 100644 index 0a15cd7d3bd9..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpLeaseRepository.java +++ /dev/null @@ -1,546 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import static android.net.dhcp.DhcpLease.EXPIRATION_NEVER; -import static android.net.dhcp.DhcpLease.inet4AddrToString; -import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; -import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; -import static android.net.shared.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH; - -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_BITS; - -import static java.lang.Math.min; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.IpPrefix; -import android.net.MacAddress; -import android.net.dhcp.DhcpServer.Clock; -import android.net.util.SharedLog; -import android.util.ArrayMap; - -import java.net.Inet4Address; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.function.Function; - -/** - * A repository managing IPv4 address assignments through DHCPv4. - * - * <p>This class is not thread-safe. All public methods should be called on a common thread or - * use some synchronization mechanism. - * - * <p>Methods are optimized for a small number of allocated leases, assuming that most of the time - * only 2~10 addresses will be allocated, which is the common case. Managing a large number of - * addresses is supported but will be slower: some operations have complexity in O(num_leases). - * @hide - */ -class DhcpLeaseRepository { - public static final byte[] CLIENTID_UNSPEC = null; - public static final Inet4Address INETADDR_UNSPEC = null; - - @NonNull - private final SharedLog mLog; - @NonNull - private final Clock mClock; - - @NonNull - private IpPrefix mPrefix; - @NonNull - private Set<Inet4Address> mReservedAddrs; - private int mSubnetAddr; - private int mSubnetMask; - private int mNumAddresses; - private long mLeaseTimeMs; - - /** - * Next timestamp when committed or declined leases should be checked for expired ones. This - * will always be lower than or equal to the time for the first lease to expire: it's OK not to - * update this when removing entries, but it must always be updated when adding/updating. - */ - private long mNextExpirationCheck = EXPIRATION_NEVER; - - static class DhcpLeaseException extends Exception { - DhcpLeaseException(String message) { - super(message); - } - } - - static class OutOfAddressesException extends DhcpLeaseException { - OutOfAddressesException(String message) { - super(message); - } - } - - static class InvalidAddressException extends DhcpLeaseException { - InvalidAddressException(String message) { - super(message); - } - } - - static class InvalidSubnetException extends DhcpLeaseException { - InvalidSubnetException(String message) { - super(message); - } - } - - /** - * Leases by IP address - */ - private final ArrayMap<Inet4Address, DhcpLease> mCommittedLeases = new ArrayMap<>(); - - /** - * Map address -> expiration timestamp in ms. Addresses are guaranteed to be valid as defined - * by {@link #isValidAddress(Inet4Address)}, but are not necessarily otherwise available for - * assignment. - */ - private final LinkedHashMap<Inet4Address, Long> mDeclinedAddrs = new LinkedHashMap<>(); - - DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs, - long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) { - updateParams(prefix, reservedAddrs, leaseTimeMs); - mLog = log; - mClock = clock; - } - - public void updateParams(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs, - long leaseTimeMs) { - mPrefix = prefix; - mReservedAddrs = Collections.unmodifiableSet(new HashSet<>(reservedAddrs)); - mSubnetMask = prefixLengthToV4NetmaskIntHTH(prefix.getPrefixLength()); - mSubnetAddr = inet4AddressToIntHTH((Inet4Address) prefix.getAddress()) & mSubnetMask; - mNumAddresses = 1 << (IPV4_ADDR_BITS - prefix.getPrefixLength()); - mLeaseTimeMs = leaseTimeMs; - - cleanMap(mCommittedLeases); - cleanMap(mDeclinedAddrs); - } - - /** - * From a map keyed by {@link Inet4Address}, remove entries where the key is invalid (as - * specified by {@link #isValidAddress(Inet4Address)}), or is a reserved address. - */ - private <T> void cleanMap(Map<Inet4Address, T> map) { - final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator(); - while (it.hasNext()) { - final Inet4Address addr = it.next().getKey(); - if (!isValidAddress(addr) || mReservedAddrs.contains(addr)) { - it.remove(); - } - } - } - - /** - * Get a DHCP offer, to reply to a DHCPDISCOVER. Follows RFC2131 #4.3.1. - * - * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC} - * @param relayAddr Internet address of the relay (giaddr), can be {@link Inet4Address#ANY} - * @param reqAddr Requested address by the client (option 50), or {@link #INETADDR_UNSPEC} - * @param hostname Client-provided hostname, or {@link DhcpLease#HOSTNAME_NONE} - * @throws OutOfAddressesException The server does not have any available address - * @throws InvalidSubnetException The lease was requested from an unsupported subnet - */ - @NonNull - public DhcpLease getOffer(@Nullable byte[] clientId, @NonNull MacAddress hwAddr, - @NonNull Inet4Address relayAddr, @Nullable Inet4Address reqAddr, - @Nullable String hostname) throws OutOfAddressesException, InvalidSubnetException { - final long currentTime = mClock.elapsedRealtime(); - final long expTime = currentTime + mLeaseTimeMs; - - removeExpiredLeases(currentTime); - checkValidRelayAddr(relayAddr); - - final DhcpLease currentLease = findByClient(clientId, hwAddr); - final DhcpLease newLease; - if (currentLease != null) { - newLease = currentLease.renewedLease(expTime, hostname); - mLog.log("Offering extended lease " + newLease); - // Do not update lease time in the map: the offer is not committed yet. - } else if (reqAddr != null && isValidAddress(reqAddr) && isAvailable(reqAddr)) { - newLease = new DhcpLease(clientId, hwAddr, reqAddr, expTime, hostname); - mLog.log("Offering requested lease " + newLease); - } else { - newLease = makeNewOffer(clientId, hwAddr, expTime, hostname); - mLog.log("Offering new generated lease " + newLease); - } - return newLease; - } - - private void checkValidRelayAddr(@Nullable Inet4Address relayAddr) - throws InvalidSubnetException { - // As per #4.3.1, addresses are assigned based on the relay address if present. This - // implementation only assigns addresses if the relayAddr is inside our configured subnet. - // This also applies when the client requested a specific address for consistency between - // requests, and with older behavior. - if (isIpAddrOutsidePrefix(mPrefix, relayAddr)) { - throw new InvalidSubnetException("Lease requested by relay from outside of subnet"); - } - } - - private static boolean isIpAddrOutsidePrefix(@NonNull IpPrefix prefix, - @Nullable Inet4Address addr) { - return addr != null && !addr.equals(IPV4_ADDR_ANY) && !prefix.contains(addr); - } - - @Nullable - private DhcpLease findByClient(@Nullable byte[] clientId, @NonNull MacAddress hwAddr) { - for (DhcpLease lease : mCommittedLeases.values()) { - if (lease.matchesClient(clientId, hwAddr)) { - return lease; - } - } - - // Note this differs from dnsmasq behavior, which would match by hwAddr if clientId was - // given but no lease keyed on clientId matched. This would prevent one interface from - // obtaining multiple leases with different clientId. - return null; - } - - /** - * Make a lease conformant to a client DHCPREQUEST or renew the client's existing lease, - * commit it to the repository and return it. - * - * <p>This method always succeeds and commits the lease if it does not throw, and has no side - * effects if it throws. - * - * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC} - * @param reqAddr Requested address by the client (option 50), or {@link #INETADDR_UNSPEC} - * @param sidSet Whether the server identifier was set in the request - * @return The newly created or renewed lease - * @throws InvalidAddressException The client provided an address that conflicts with its - * current configuration, or other committed/reserved leases. - */ - @NonNull - public DhcpLease requestLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr, - @NonNull Inet4Address clientAddr, @NonNull Inet4Address relayAddr, - @Nullable Inet4Address reqAddr, boolean sidSet, @Nullable String hostname) - throws InvalidAddressException, InvalidSubnetException { - final long currentTime = mClock.elapsedRealtime(); - removeExpiredLeases(currentTime); - checkValidRelayAddr(relayAddr); - final DhcpLease assignedLease = findByClient(clientId, hwAddr); - - final Inet4Address leaseAddr = reqAddr != null ? reqAddr : clientAddr; - if (assignedLease != null) { - if (sidSet && reqAddr != null) { - // Client in SELECTING state; remove any current lease before creating a new one. - mCommittedLeases.remove(assignedLease.getNetAddr()); - } else if (!assignedLease.getNetAddr().equals(leaseAddr)) { - // reqAddr null (RENEWING/REBINDING): client renewing its own lease for clientAddr. - // reqAddr set with sid not set (INIT-REBOOT): client verifying configuration. - // In both cases, throw if clientAddr or reqAddr does not match the known lease. - throw new InvalidAddressException("Incorrect address for client in " - + (reqAddr != null ? "INIT-REBOOT" : "RENEWING/REBINDING")); - } - } - - // In the init-reboot case, RFC2131 #4.3.2 says that the server must not reply if - // assignedLease == null, but dnsmasq will let the client use the requested address if - // available, when configured with --dhcp-authoritative. This is preferable to avoid issues - // if the server lost the lease DB: the client would not get a reply because the server - // does not know their lease. - // Similarly in RENEWING/REBINDING state, create a lease when possible if the - // client-provided lease is unknown. - final DhcpLease lease = - checkClientAndMakeLease(clientId, hwAddr, leaseAddr, hostname, currentTime); - mLog.logf("DHCPREQUEST assignedLease %s, reqAddr=%s, sidSet=%s: created/renewed lease %s", - assignedLease, inet4AddrToString(reqAddr), sidSet, lease); - return lease; - } - - /** - * Check that the client can request the specified address, make or renew the lease if yes, and - * commit it. - * - * <p>This method always succeeds and returns the lease if it does not throw, and has no - * side-effect if it throws. - * - * @return The newly created or renewed, committed lease - * @throws InvalidAddressException The client provided an address that conflicts with its - * current configuration, or other committed/reserved leases. - */ - private DhcpLease checkClientAndMakeLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr, - @NonNull Inet4Address addr, @Nullable String hostname, long currentTime) - throws InvalidAddressException { - final long expTime = currentTime + mLeaseTimeMs; - final DhcpLease currentLease = mCommittedLeases.getOrDefault(addr, null); - if (currentLease != null && !currentLease.matchesClient(clientId, hwAddr)) { - throw new InvalidAddressException("Address in use"); - } - - final DhcpLease lease; - if (currentLease == null) { - if (isValidAddress(addr) && !mReservedAddrs.contains(addr)) { - lease = new DhcpLease(clientId, hwAddr, addr, expTime, hostname); - } else { - throw new InvalidAddressException("Lease not found and address unavailable"); - } - } else { - lease = currentLease.renewedLease(expTime, hostname); - } - commitLease(lease); - return lease; - } - - private void commitLease(@NonNull DhcpLease lease) { - mCommittedLeases.put(lease.getNetAddr(), lease); - maybeUpdateEarliestExpiration(lease.getExpTime()); - } - - /** - * Delete a committed lease from the repository. - * - * @return true if a lease matching parameters was found. - */ - public boolean releaseLease(@Nullable byte[] clientId, @NonNull MacAddress hwAddr, - @NonNull Inet4Address addr) { - final DhcpLease currentLease = mCommittedLeases.getOrDefault(addr, null); - if (currentLease == null) { - mLog.w("Could not release unknown lease for " + inet4AddrToString(addr)); - return false; - } - if (currentLease.matchesClient(clientId, hwAddr)) { - mCommittedLeases.remove(addr); - mLog.log("Released lease " + currentLease); - return true; - } - mLog.w(String.format("Not releasing lease %s: does not match client (cid %s, hwAddr %s)", - currentLease, DhcpLease.clientIdToString(clientId), hwAddr)); - return false; - } - - public void markLeaseDeclined(@NonNull Inet4Address addr) { - if (mDeclinedAddrs.containsKey(addr) || !isValidAddress(addr)) { - mLog.logf("Not marking %s as declined: already declined or not assignable", - inet4AddrToString(addr)); - return; - } - final long expTime = mClock.elapsedRealtime() + mLeaseTimeMs; - mDeclinedAddrs.put(addr, expTime); - mLog.logf("Marked %s as declined expiring %d", inet4AddrToString(addr), expTime); - maybeUpdateEarliestExpiration(expTime); - } - - /** - * Get the list of currently valid committed leases in the repository. - */ - @NonNull - public List<DhcpLease> getCommittedLeases() { - removeExpiredLeases(mClock.elapsedRealtime()); - return new ArrayList<>(mCommittedLeases.values()); - } - - /** - * Get the set of addresses that have been marked as declined in the repository. - */ - @NonNull - public Set<Inet4Address> getDeclinedAddresses() { - removeExpiredLeases(mClock.elapsedRealtime()); - return new HashSet<>(mDeclinedAddrs.keySet()); - } - - /** - * Given the expiration time of a new committed lease or declined address, update - * {@link #mNextExpirationCheck} so it stays lower than or equal to the time for the first lease - * to expire. - */ - private void maybeUpdateEarliestExpiration(long expTime) { - if (expTime < mNextExpirationCheck) { - mNextExpirationCheck = expTime; - } - } - - /** - * Remove expired entries from a map keyed by {@link Inet4Address}. - * - * @param tag Type of lease in the map, for logging - * @param getExpTime Functor returning the expiration time for an object in the map. - * Must not return null. - * @return The lowest expiration time among entries remaining in the map - */ - private <T> long removeExpired(long currentTime, @NonNull Map<Inet4Address, T> map, - @NonNull String tag, @NonNull Function<T, Long> getExpTime) { - final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator(); - long firstExpiration = EXPIRATION_NEVER; - while (it.hasNext()) { - final Entry<Inet4Address, T> lease = it.next(); - final long expTime = getExpTime.apply(lease.getValue()); - if (expTime <= currentTime) { - mLog.logf("Removing expired %s lease for %s (expTime=%s, currentTime=%s)", - tag, lease.getKey(), expTime, currentTime); - it.remove(); - } else { - firstExpiration = min(firstExpiration, expTime); - } - } - return firstExpiration; - } - - /** - * Go through committed and declined leases and remove the expired ones. - */ - private void removeExpiredLeases(long currentTime) { - if (currentTime < mNextExpirationCheck) { - return; - } - - final long commExp = removeExpired( - currentTime, mCommittedLeases, "committed", DhcpLease::getExpTime); - final long declExp = removeExpired( - currentTime, mDeclinedAddrs, "declined", Function.identity()); - - mNextExpirationCheck = min(commExp, declExp); - } - - private boolean isAvailable(@NonNull Inet4Address addr) { - return !mReservedAddrs.contains(addr) && !mCommittedLeases.containsKey(addr); - } - - /** - * Get the 0-based index of an address in the subnet. - * - * <p>Given ordering of addresses 5.6.7.8 < 5.6.7.9 < 5.6.8.0, the index on a subnet is defined - * so that the first address is 0, the second 1, etc. For example on a /16, 192.168.0.0 -> 0, - * 192.168.0.1 -> 1, 192.168.1.0 -> 256 - * - */ - private int getAddrIndex(int addr) { - return addr & ~mSubnetMask; - } - - private int getAddrByIndex(int index) { - return mSubnetAddr | index; - } - - /** - * Get a valid address starting from the supplied one. - * - * <p>This only checks that the address is numerically valid for assignment, not whether it is - * already in use. The return value is always inside the configured prefix, even if the supplied - * address is not. - * - * <p>If the provided address is valid, it is returned as-is. Otherwise, the next valid - * address (with the ordering in {@link #getAddrIndex(int)}) is returned. - */ - private int getValidAddress(int addr) { - final int lastByteMask = 0xff; - int addrIndex = getAddrIndex(addr); // 0-based index of the address in the subnet - - // Some OSes do not handle addresses in .255 or .0 correctly: avoid those. - final int lastByte = getAddrByIndex(addrIndex) & lastByteMask; - if (lastByte == lastByteMask) { - // Avoid .255 address, and .0 address that follows - addrIndex = (addrIndex + 2) % mNumAddresses; - } else if (lastByte == 0) { - // Avoid .0 address - addrIndex = (addrIndex + 1) % mNumAddresses; - } - - // Do not use first or last address of range - if (addrIndex == 0 || addrIndex == mNumAddresses - 1) { - // Always valid and not end of range since prefixLength is at most 30 in serving params - addrIndex = 1; - } - return getAddrByIndex(addrIndex); - } - - /** - * Returns whether the address is in the configured subnet and part of the assignable range. - */ - private boolean isValidAddress(Inet4Address addr) { - final int intAddr = inet4AddressToIntHTH(addr); - return getValidAddress(intAddr) == intAddr; - } - - private int getNextAddress(int addr) { - final int addrIndex = getAddrIndex(addr); - final int nextAddress = getAddrByIndex((addrIndex + 1) % mNumAddresses); - return getValidAddress(nextAddress); - } - - /** - * Calculate a first candidate address for a client by hashing the hardware address. - * - * <p>This will be a valid address as checked by {@link #getValidAddress(int)}, but may be - * in use. - * - * @return An IPv4 address encoded as 32-bit int - */ - private int getFirstClientAddress(MacAddress hwAddr) { - // This follows dnsmasq behavior. Advantages are: clients will often get the same - // offers for different DISCOVER even if the lease was not yet accepted or has expired, - // and address generation will generally not need to loop through many allocated addresses - // until it finds a free one. - int hash = 0; - for (byte b : hwAddr.toByteArray()) { - hash += b + (b << 8) + (b << 16); - } - // This implementation will not always result in the same IPs as dnsmasq would give out in - // Android <= P, because it includes invalid and reserved addresses in mNumAddresses while - // the configured ranges for dnsmasq did not. - final int addrIndex = hash % mNumAddresses; - return getValidAddress(getAddrByIndex(addrIndex)); - } - - /** - * Create a lease that can be offered to respond to a client DISCOVER. - * - * <p>This method always succeeds and returns the lease if it does not throw. If no non-declined - * address is available, it will try to offer the oldest declined address if valid. - * - * @throws OutOfAddressesException The server has no address left to offer - */ - private DhcpLease makeNewOffer(@Nullable byte[] clientId, @NonNull MacAddress hwAddr, - long expTime, @Nullable String hostname) throws OutOfAddressesException { - int intAddr = getFirstClientAddress(hwAddr); - // Loop until a free address is found, or there are no more addresses. - // There is slightly less than this many usable addresses, but some extra looping is OK - for (int i = 0; i < mNumAddresses; i++) { - final Inet4Address addr = intToInet4AddressHTH(intAddr); - if (isAvailable(addr) && !mDeclinedAddrs.containsKey(addr)) { - return new DhcpLease(clientId, hwAddr, addr, expTime, hostname); - } - intAddr = getNextAddress(intAddr); - } - - // Try freeing DECLINEd addresses if out of addresses. - final Iterator<Inet4Address> it = mDeclinedAddrs.keySet().iterator(); - while (it.hasNext()) { - final Inet4Address addr = it.next(); - it.remove(); - mLog.logf("Out of addresses in address pool: dropped declined addr %s", - inet4AddrToString(addr)); - // isValidAddress() is always verified for entries in mDeclinedAddrs. - // However declined addresses may have been requested (typically by the machine that was - // already using the address) after being declined. - if (isAvailable(addr)) { - return new DhcpLease(clientId, hwAddr, addr, expTime, hostname); - } - } - - throw new OutOfAddressesException("No address available for offer"); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java deleted file mode 100644 index 1da0b7300559..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpNakPacket.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; - -/** - * This class implements the DHCP-NAK packet. - */ -class DhcpNakPacket extends DhcpPacket { - /** - * Generates a NAK packet with the specified parameters. - */ - DhcpNakPacket(int transId, short secs, Inet4Address relayIp, byte[] clientMac, - boolean broadcast) { - super(transId, secs, INADDR_ANY /* clientIp */, INADDR_ANY /* yourIp */, - INADDR_ANY /* nextIp */, relayIp, clientMac, broadcast); - } - - public String toString() { - String s = super.toString(); - return s + " NAK, reason " + (mMessage == null ? "(none)" : mMessage); - } - - /** - * Fills in a packet with the requested NAK attributes. - */ - public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) { - ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); - // Constructor does not set values for layers <= 3: use empty values - Inet4Address destIp = INADDR_ANY; - Inet4Address srcIp = INADDR_ANY; - - fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result, DHCP_BOOTREPLY, mBroadcast); - result.flip(); - return result; - } - - /** - * Adds the optional parameters to the client-generated NAK packet. - */ - void finishPacket(ByteBuffer buffer) { - addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_NAK); - addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier); - addTlv(buffer, DHCP_MESSAGE, mMessage); - addTlvEnd(buffer); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java deleted file mode 100644 index 0eba77e4a682..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpOfferPacket.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; - -/** - * This class implements the DHCP-OFFER packet. - */ -class DhcpOfferPacket extends DhcpPacket { - /** - * The IP address of the server which sent this packet. - */ - private final Inet4Address mSrcIp; - - /** - * Generates a OFFER packet with the specified parameters. - */ - DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress, - Inet4Address relayIp, Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) { - super(transId, secs, clientIp, yourIp, serverAddress, relayIp, clientMac, broadcast); - mSrcIp = serverAddress; - } - - public String toString() { - String s = super.toString(); - String dnsServers = ", DNS servers: "; - - if (mDnsServers != null) { - for (Inet4Address dnsServer: mDnsServers) { - dnsServers += dnsServer + " "; - } - } - - return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask + - dnsServers + ", gateways " + mGateways + - " lease time " + mLeaseTime + ", domain " + mDomainName; - } - - /** - * Fills in a packet with the specified OFFER attributes. - */ - public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) { - ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); - Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp; - Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp; - - fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result, - DHCP_BOOTREPLY, mBroadcast); - result.flip(); - return result; - } - - /** - * Adds the optional parameters to the server-generated OFFER packet. - */ - void finishPacket(ByteBuffer buffer) { - addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_OFFER); - addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier); - - addCommonServerTlvs(buffer); - addTlvEnd(buffer); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java deleted file mode 100644 index a15d42381ff0..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacket.java +++ /dev/null @@ -1,1397 +0,0 @@ -package android.net.dhcp; - -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL; -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; - -import android.annotation.Nullable; -import android.net.DhcpResults; -import android.net.LinkAddress; -import android.net.metrics.DhcpErrorEvent; -import android.net.shared.Inet4AddressUtils; -import android.os.Build; -import android.os.SystemProperties; -import android.system.OsConstants; -import android.text.TextUtils; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.UnsupportedEncodingException; -import java.net.Inet4Address; -import java.net.UnknownHostException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.ShortBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Defines basic data and operations needed to build and use packets for the - * DHCP protocol. Subclasses create the specific packets used at each - * stage of the negotiation. - * - * @hide - */ -public abstract class DhcpPacket { - protected static final String TAG = "DhcpPacket"; - - // TODO: use NetworkStackConstants.IPV4_MIN_MTU once this class is moved to the network stack. - private static final int IPV4_MIN_MTU = 68; - - // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the - // CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the - // DHCP client timeout. - public static final int MINIMUM_LEASE = 60; - public static final int INFINITE_LEASE = (int) 0xffffffff; - - public static final Inet4Address INADDR_ANY = IPV4_ADDR_ANY; - public static final Inet4Address INADDR_BROADCAST = IPV4_ADDR_ALL; - public static final byte[] ETHER_BROADCAST = new byte[] { - (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xff, (byte) 0xff, (byte) 0xff, - }; - - /** - * Packet encapsulations. - */ - public static final int ENCAP_L2 = 0; // EthernetII header included - public static final int ENCAP_L3 = 1; // IP/UDP header included - public static final int ENCAP_BOOTP = 2; // BOOTP contents only - - /** - * Minimum length of a DHCP packet, excluding options, in the above encapsulations. - */ - public static final int MIN_PACKET_LENGTH_BOOTP = 236; // See diagram in RFC 2131, section 2. - public static final int MIN_PACKET_LENGTH_L3 = MIN_PACKET_LENGTH_BOOTP + 20 + 8; - public static final int MIN_PACKET_LENGTH_L2 = MIN_PACKET_LENGTH_L3 + 14; - - public static final int HWADDR_LEN = 16; - public static final int MAX_OPTION_LEN = 255; - - /** - * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum - * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280, - * and does not recover if the MTU is brought above 1280 again. We set the maximum to 1500 - * because in general it is risky to assume that the hardware is able to send/receive packets - * larger than 1500 bytes even if the network supports it. - */ - private static final int MIN_MTU = 1280; - private static final int MAX_MTU = 1500; - - /** - * IP layer definitions. - */ - private static final byte IP_TYPE_UDP = (byte) 0x11; - - /** - * IP: Version 4, Header Length 20 bytes - */ - private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45; - - /** - * IP: Flags 0, Fragment Offset 0, Don't Fragment - */ - private static final short IP_FLAGS_OFFSET = (short) 0x4000; - - /** - * IP: TOS - */ - private static final byte IP_TOS_LOWDELAY = (byte) 0x10; - - /** - * IP: TTL -- use default 64 from RFC1340 - */ - private static final byte IP_TTL = (byte) 0x40; - - /** - * The client DHCP port. - */ - static final short DHCP_CLIENT = (short) 68; - - /** - * The server DHCP port. - */ - static final short DHCP_SERVER = (short) 67; - - /** - * The message op code indicating a request from a client. - */ - protected static final byte DHCP_BOOTREQUEST = (byte) 1; - - /** - * The message op code indicating a response from the server. - */ - protected static final byte DHCP_BOOTREPLY = (byte) 2; - - /** - * The code type used to identify an Ethernet MAC address in the - * Client-ID field. - */ - protected static final byte CLIENT_ID_ETHER = (byte) 1; - - /** - * The maximum length of a packet that can be constructed. - */ - protected static final int MAX_LENGTH = 1500; - - /** - * The magic cookie that identifies this as a DHCP packet instead of BOOTP. - */ - private static final int DHCP_MAGIC_COOKIE = 0x63825363; - - /** - * DHCP Optional Type: DHCP Subnet Mask - */ - protected static final byte DHCP_SUBNET_MASK = 1; - protected Inet4Address mSubnetMask; - - /** - * DHCP Optional Type: DHCP Router - */ - protected static final byte DHCP_ROUTER = 3; - protected List <Inet4Address> mGateways; - - /** - * DHCP Optional Type: DHCP DNS Server - */ - protected static final byte DHCP_DNS_SERVER = 6; - protected List<Inet4Address> mDnsServers; - - /** - * DHCP Optional Type: DHCP Host Name - */ - protected static final byte DHCP_HOST_NAME = 12; - protected String mHostName; - - /** - * DHCP Optional Type: DHCP DOMAIN NAME - */ - protected static final byte DHCP_DOMAIN_NAME = 15; - protected String mDomainName; - - /** - * DHCP Optional Type: DHCP Interface MTU - */ - protected static final byte DHCP_MTU = 26; - protected Short mMtu; - - /** - * DHCP Optional Type: DHCP BROADCAST ADDRESS - */ - protected static final byte DHCP_BROADCAST_ADDRESS = 28; - protected Inet4Address mBroadcastAddress; - - /** - * DHCP Optional Type: Vendor specific information - */ - protected static final byte DHCP_VENDOR_INFO = 43; - protected String mVendorInfo; - - /** - * Value of the vendor specific option used to indicate that the network is metered - */ - public static final String VENDOR_INFO_ANDROID_METERED = "ANDROID_METERED"; - - /** - * DHCP Optional Type: Option overload option - */ - protected static final byte DHCP_OPTION_OVERLOAD = 52; - - /** - * Possible values of the option overload option. - */ - private static final byte OPTION_OVERLOAD_FILE = 1; - private static final byte OPTION_OVERLOAD_SNAME = 2; - private static final byte OPTION_OVERLOAD_BOTH = 3; - - /** - * DHCP Optional Type: DHCP Requested IP Address - */ - protected static final byte DHCP_REQUESTED_IP = 50; - protected Inet4Address mRequestedIp; - - /** - * DHCP Optional Type: DHCP Lease Time - */ - protected static final byte DHCP_LEASE_TIME = 51; - protected Integer mLeaseTime; - - /** - * DHCP Optional Type: DHCP Message Type - */ - protected static final byte DHCP_MESSAGE_TYPE = 53; - // the actual type values - protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1; - protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2; - protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3; - protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4; - protected static final byte DHCP_MESSAGE_TYPE_ACK = 5; - protected static final byte DHCP_MESSAGE_TYPE_NAK = 6; - protected static final byte DHCP_MESSAGE_TYPE_RELEASE = 7; - protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8; - - /** - * DHCP Optional Type: DHCP Server Identifier - */ - protected static final byte DHCP_SERVER_IDENTIFIER = 54; - protected Inet4Address mServerIdentifier; - - /** - * DHCP Optional Type: DHCP Parameter List - */ - protected static final byte DHCP_PARAMETER_LIST = 55; - protected byte[] mRequestedParams; - - /** - * DHCP Optional Type: DHCP MESSAGE - */ - protected static final byte DHCP_MESSAGE = 56; - protected String mMessage; - - /** - * DHCP Optional Type: Maximum DHCP Message Size - */ - protected static final byte DHCP_MAX_MESSAGE_SIZE = 57; - protected Short mMaxMessageSize; - - /** - * DHCP Optional Type: DHCP Renewal Time Value - */ - protected static final byte DHCP_RENEWAL_TIME = 58; - protected Integer mT1; - - /** - * DHCP Optional Type: Rebinding Time Value - */ - protected static final byte DHCP_REBINDING_TIME = 59; - protected Integer mT2; - - /** - * DHCP Optional Type: Vendor Class Identifier - */ - protected static final byte DHCP_VENDOR_CLASS_ID = 60; - protected String mVendorId; - - /** - * DHCP Optional Type: DHCP Client Identifier - */ - protected static final byte DHCP_CLIENT_IDENTIFIER = 61; - protected byte[] mClientId; - - /** - * DHCP zero-length option code: pad - */ - protected static final byte DHCP_OPTION_PAD = 0x00; - - /** - * DHCP zero-length option code: end of options - */ - protected static final byte DHCP_OPTION_END = (byte) 0xff; - - /** - * The transaction identifier used in this particular DHCP negotiation - */ - protected final int mTransId; - - /** - * The seconds field in the BOOTP header. Per RFC, should be nonzero in client requests only. - */ - protected final short mSecs; - - /** - * The IP address of the client host. This address is typically - * proposed by the client (from an earlier DHCP negotiation) or - * supplied by the server. - */ - protected final Inet4Address mClientIp; - protected final Inet4Address mYourIp; - private final Inet4Address mNextIp; - protected final Inet4Address mRelayIp; - - /** - * Does the client request a broadcast response? - */ - protected boolean mBroadcast; - - /** - * The six-octet MAC of the client. - */ - protected final byte[] mClientMac; - - /** - * The server host name from server. - */ - protected String mServerHostName; - - /** - * Asks the packet object to create a ByteBuffer serialization of - * the packet for transmission. - */ - public abstract ByteBuffer buildPacket(int encap, short destUdp, - short srcUdp); - - /** - * Allows the concrete class to fill in packet-type-specific details, - * typically optional parameters at the end of the packet. - */ - abstract void finishPacket(ByteBuffer buffer); - - // Set in unit tests, to ensure that the test does not break when run on different devices and - // on different releases. - static String testOverrideVendorId = null; - static String testOverrideHostname = null; - - protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, - Inet4Address nextIp, Inet4Address relayIp, - byte[] clientMac, boolean broadcast) { - mTransId = transId; - mSecs = secs; - mClientIp = clientIp; - mYourIp = yourIp; - mNextIp = nextIp; - mRelayIp = relayIp; - mClientMac = clientMac; - mBroadcast = broadcast; - } - - /** - * Returns the transaction ID. - */ - public int getTransactionId() { - return mTransId; - } - - /** - * Returns the client MAC. - */ - public byte[] getClientMac() { - return mClientMac; - } - - // TODO: refactor DhcpClient to set clientId when constructing packets and remove - // hasExplicitClientId logic - /** - * Returns whether a client ID was set in the options for this packet. - */ - public boolean hasExplicitClientId() { - return mClientId != null; - } - - /** - * Convenience method to return the client ID if it was set explicitly, or null otherwise. - */ - @Nullable - public byte[] getExplicitClientIdOrNull() { - return hasExplicitClientId() ? getClientId() : null; - } - - /** - * Returns the client ID. If not set explicitly, this follows RFC 2132 and creates a client ID - * based on the hardware address. - */ - public byte[] getClientId() { - final byte[] clientId; - if (hasExplicitClientId()) { - clientId = Arrays.copyOf(mClientId, mClientId.length); - } else { - clientId = new byte[mClientMac.length + 1]; - clientId[0] = CLIENT_ID_ETHER; - System.arraycopy(mClientMac, 0, clientId, 1, mClientMac.length); - } - return clientId; - } - - /** - * Returns whether a parameter is included in the parameter request list option of this packet. - * - * <p>If there is no parameter request list option in the packet, false is returned. - * - * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}. - */ - public boolean hasRequestedParam(byte paramId) { - if (mRequestedParams == null) { - return false; - } - - for (byte reqParam : mRequestedParams) { - if (reqParam == paramId) { - return true; - } - } - return false; - } - - /** - * Creates a new L3 packet (including IP header) containing the - * DHCP udp packet. This method relies upon the delegated method - * finishPacket() to insert the per-packet contents. - */ - protected void fillInPacket(int encap, Inet4Address destIp, - Inet4Address srcIp, short destUdp, short srcUdp, ByteBuffer buf, - byte requestCode, boolean broadcast) { - byte[] destIpArray = destIp.getAddress(); - byte[] srcIpArray = srcIp.getAddress(); - int ipHeaderOffset = 0; - int ipLengthOffset = 0; - int ipChecksumOffset = 0; - int endIpHeader = 0; - int udpHeaderOffset = 0; - int udpLengthOffset = 0; - int udpChecksumOffset = 0; - - buf.clear(); - buf.order(ByteOrder.BIG_ENDIAN); - - if (encap == ENCAP_L2) { - buf.put(ETHER_BROADCAST); - buf.put(mClientMac); - buf.putShort((short) OsConstants.ETH_P_IP); - } - - // if a full IP packet needs to be generated, put the IP & UDP - // headers in place, and pre-populate with artificial values - // needed to seed the IP checksum. - if (encap <= ENCAP_L3) { - ipHeaderOffset = buf.position(); - buf.put(IP_VERSION_HEADER_LEN); - buf.put(IP_TOS_LOWDELAY); // tos: IPTOS_LOWDELAY - ipLengthOffset = buf.position(); - buf.putShort((short)0); // length - buf.putShort((short)0); // id - buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment - buf.put(IP_TTL); // TTL: use default 64 from RFC1340 - buf.put(IP_TYPE_UDP); - ipChecksumOffset = buf.position(); - buf.putShort((short) 0); // checksum - - buf.put(srcIpArray); - buf.put(destIpArray); - endIpHeader = buf.position(); - - // UDP header - udpHeaderOffset = buf.position(); - buf.putShort(srcUdp); - buf.putShort(destUdp); - udpLengthOffset = buf.position(); - buf.putShort((short) 0); // length - udpChecksumOffset = buf.position(); - buf.putShort((short) 0); // UDP checksum -- initially zero - } - - // DHCP payload - buf.put(requestCode); - buf.put((byte) 1); // Hardware Type: Ethernet - buf.put((byte) mClientMac.length); // Hardware Address Length - buf.put((byte) 0); // Hop Count - buf.putInt(mTransId); // Transaction ID - buf.putShort(mSecs); // Elapsed Seconds - - if (broadcast) { - buf.putShort((short) 0x8000); // Flags - } else { - buf.putShort((short) 0x0000); // Flags - } - - buf.put(mClientIp.getAddress()); - buf.put(mYourIp.getAddress()); - buf.put(mNextIp.getAddress()); - buf.put(mRelayIp.getAddress()); - buf.put(mClientMac); - buf.position(buf.position() + - (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes - + 64 // empty server host name (64 bytes) - + 128); // empty boot file name (128 bytes) - buf.putInt(DHCP_MAGIC_COOKIE); // magic number - finishPacket(buf); - - // round up to an even number of octets - if ((buf.position() & 1) == 1) { - buf.put((byte) 0); - } - - // If an IP packet is being built, the IP & UDP checksums must be - // computed. - if (encap <= ENCAP_L3) { - // fix UDP header: insert length - short udpLen = (short)(buf.position() - udpHeaderOffset); - buf.putShort(udpLengthOffset, udpLen); - // fix UDP header: checksum - // checksum for UDP at udpChecksumOffset - int udpSeed = 0; - - // apply IPv4 pseudo-header. Read IP address src and destination - // values from the IP header and accumulate checksum. - udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2)); - udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4)); - udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6)); - udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8)); - - // accumulate extra data for the pseudo-header - udpSeed += IP_TYPE_UDP; - udpSeed += udpLen; - // and compute UDP checksum - buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed, - udpHeaderOffset, - buf.position())); - // fix IP header: insert length - buf.putShort(ipLengthOffset, (short)(buf.position() - ipHeaderOffset)); - // fixup IP-header checksum - buf.putShort(ipChecksumOffset, - (short) checksum(buf, 0, ipHeaderOffset, endIpHeader)); - } - } - - /** - * Converts a signed short value to an unsigned int value. Needed - * because Java does not have unsigned types. - */ - private static int intAbs(short v) { - return v & 0xFFFF; - } - - /** - * Performs an IP checksum (used in IP header and across UDP - * payload) on the specified portion of a ByteBuffer. The seed - * allows the checksum to commence with a specified value. - */ - private int checksum(ByteBuffer buf, int seed, int start, int end) { - int sum = seed; - int bufPosition = buf.position(); - - // set position of original ByteBuffer, so that the ShortBuffer - // will be correctly initialized - buf.position(start); - ShortBuffer shortBuf = buf.asShortBuffer(); - - // re-set ByteBuffer position - buf.position(bufPosition); - - short[] shortArray = new short[(end - start) / 2]; - shortBuf.get(shortArray); - - for (short s : shortArray) { - sum += intAbs(s); - } - - start += shortArray.length * 2; - - // see if a singleton byte remains - if (end != start) { - short b = buf.get(start); - - // make it unsigned - if (b < 0) { - b += 256; - } - - sum += b * 256; - } - - sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF); - sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF); - int negated = ~sum; - return intAbs((short) negated); - } - - /** - * Adds an optional parameter containing a single byte value. - */ - protected static void addTlv(ByteBuffer buf, byte type, byte value) { - buf.put(type); - buf.put((byte) 1); - buf.put(value); - } - - /** - * Adds an optional parameter containing an array of bytes. - * - * <p>This method is a no-op if the payload argument is null. - */ - protected static void addTlv(ByteBuffer buf, byte type, @Nullable byte[] payload) { - if (payload != null) { - if (payload.length > MAX_OPTION_LEN) { - throw new IllegalArgumentException("DHCP option too long: " - + payload.length + " vs. " + MAX_OPTION_LEN); - } - buf.put(type); - buf.put((byte) payload.length); - buf.put(payload); - } - } - - /** - * Adds an optional parameter containing an IP address. - * - * <p>This method is a no-op if the address argument is null. - */ - protected static void addTlv(ByteBuffer buf, byte type, @Nullable Inet4Address addr) { - if (addr != null) { - addTlv(buf, type, addr.getAddress()); - } - } - - /** - * Adds an optional parameter containing a list of IP addresses. - * - * <p>This method is a no-op if the addresses argument is null or empty. - */ - protected static void addTlv(ByteBuffer buf, byte type, @Nullable List<Inet4Address> addrs) { - if (addrs == null || addrs.size() == 0) return; - - int optionLen = 4 * addrs.size(); - if (optionLen > MAX_OPTION_LEN) { - throw new IllegalArgumentException("DHCP option too long: " - + optionLen + " vs. " + MAX_OPTION_LEN); - } - - buf.put(type); - buf.put((byte)(optionLen)); - - for (Inet4Address addr : addrs) { - buf.put(addr.getAddress()); - } - } - - /** - * Adds an optional parameter containing a short integer. - * - * <p>This method is a no-op if the value argument is null. - */ - protected static void addTlv(ByteBuffer buf, byte type, @Nullable Short value) { - if (value != null) { - buf.put(type); - buf.put((byte) 2); - buf.putShort(value.shortValue()); - } - } - - /** - * Adds an optional parameter containing a simple integer. - * - * <p>This method is a no-op if the value argument is null. - */ - protected static void addTlv(ByteBuffer buf, byte type, @Nullable Integer value) { - if (value != null) { - buf.put(type); - buf.put((byte) 4); - buf.putInt(value.intValue()); - } - } - - /** - * Adds an optional parameter containing an ASCII string. - * - * <p>This method is a no-op if the string argument is null. - */ - protected static void addTlv(ByteBuffer buf, byte type, @Nullable String str) { - if (str != null) { - try { - addTlv(buf, type, str.getBytes("US-ASCII")); - } catch (UnsupportedEncodingException e) { - throw new IllegalArgumentException("String is not US-ASCII: " + str); - } - } - } - - /** - * Adds the special end-of-optional-parameters indicator. - */ - protected static void addTlvEnd(ByteBuffer buf) { - buf.put((byte) 0xFF); - } - - private String getVendorId() { - if (testOverrideVendorId != null) return testOverrideVendorId; - return "android-dhcp-" + Build.VERSION.RELEASE; - } - - private String getHostname() { - if (testOverrideHostname != null) return testOverrideHostname; - return SystemProperties.get("net.hostname"); - } - - /** - * Adds common client TLVs. - * - * TODO: Does this belong here? The alternative would be to modify all the buildXyzPacket - * methods to take them. - */ - protected void addCommonClientTlvs(ByteBuffer buf) { - addTlv(buf, DHCP_MAX_MESSAGE_SIZE, (short) MAX_LENGTH); - addTlv(buf, DHCP_VENDOR_CLASS_ID, getVendorId()); - final String hn = getHostname(); - if (!TextUtils.isEmpty(hn)) addTlv(buf, DHCP_HOST_NAME, hn); - } - - protected void addCommonServerTlvs(ByteBuffer buf) { - addTlv(buf, DHCP_LEASE_TIME, mLeaseTime); - if (mLeaseTime != null && mLeaseTime != INFINITE_LEASE) { - // The client should renew at 1/2 the lease-expiry interval - addTlv(buf, DHCP_RENEWAL_TIME, (int) (Integer.toUnsignedLong(mLeaseTime) / 2)); - // Default rebinding time is set as below by RFC2131 - addTlv(buf, DHCP_REBINDING_TIME, - (int) (Integer.toUnsignedLong(mLeaseTime) * 875L / 1000L)); - } - addTlv(buf, DHCP_SUBNET_MASK, mSubnetMask); - addTlv(buf, DHCP_BROADCAST_ADDRESS, mBroadcastAddress); - addTlv(buf, DHCP_ROUTER, mGateways); - addTlv(buf, DHCP_DNS_SERVER, mDnsServers); - addTlv(buf, DHCP_DOMAIN_NAME, mDomainName); - addTlv(buf, DHCP_HOST_NAME, mHostName); - addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo); - if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) { - addTlv(buf, DHCP_MTU, mMtu); - } - } - - /** - * Converts a MAC from an array of octets to an ASCII string. - */ - public static String macToString(byte[] mac) { - String macAddr = ""; - - for (int i = 0; i < mac.length; i++) { - String hexString = "0" + Integer.toHexString(mac[i]); - - // substring operation grabs the last 2 digits: this - // allows signed bytes to be converted correctly. - macAddr += hexString.substring(hexString.length() - 2); - - if (i != (mac.length - 1)) { - macAddr += ":"; - } - } - - return macAddr; - } - - public String toString() { - String macAddr = macToString(mClientMac); - - return macAddr; - } - - /** - * Reads a four-octet value from a ByteBuffer and construct - * an IPv4 address from that value. - */ - private static Inet4Address readIpAddress(ByteBuffer packet) { - Inet4Address result = null; - byte[] ipAddr = new byte[4]; - packet.get(ipAddr); - - try { - result = (Inet4Address) Inet4Address.getByAddress(ipAddr); - } catch (UnknownHostException ex) { - // ipAddr is numeric, so this should not be - // triggered. However, if it is, just nullify - result = null; - } - - return result; - } - - /** - * Reads a string of specified length from the buffer. - */ - private static String readAsciiString(ByteBuffer buf, int byteCount, boolean nullOk) { - byte[] bytes = new byte[byteCount]; - buf.get(bytes); - int length = bytes.length; - if (!nullOk) { - // Stop at the first null byte. This is because some DHCP options (e.g., the domain - // name) are passed to netd via FrameworkListener, which refuses arguments containing - // null bytes. We don't do this by default because vendorInfo is an opaque string which - // could in theory contain null bytes. - for (length = 0; length < bytes.length; length++) { - if (bytes[length] == 0) { - break; - } - } - } - return new String(bytes, 0, length, StandardCharsets.US_ASCII); - } - - private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) { - return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT); - } - - private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) { - return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER); - } - - public static class ParseException extends Exception { - public final int errorCode; - public ParseException(int errorCode, String msg, Object... args) { - super(String.format(msg, args)); - this.errorCode = errorCode; - } - } - - /** - * Creates a concrete DhcpPacket from the supplied ByteBuffer. The - * buffer may have an L2 encapsulation (which is the full EthernetII - * format starting with the source-address MAC) or an L3 encapsulation - * (which starts with the IP header). - * <br> - * A subset of the optional parameters are parsed and are stored - * in object fields. - */ - @VisibleForTesting - static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException - { - // bootp parameters - int transactionId; - short secs; - Inet4Address clientIp; - Inet4Address yourIp; - Inet4Address nextIp; - Inet4Address relayIp; - byte[] clientMac; - byte[] clientId = null; - List<Inet4Address> dnsServers = new ArrayList<>(); - List<Inet4Address> gateways = new ArrayList<>(); // aka router - Inet4Address serverIdentifier = null; - Inet4Address netMask = null; - String message = null; - String vendorId = null; - String vendorInfo = null; - byte[] expectedParams = null; - String hostName = null; - String domainName = null; - Inet4Address ipSrc = null; - Inet4Address ipDst = null; - Inet4Address bcAddr = null; - Inet4Address requestedIp = null; - String serverHostName; - byte optionOverload = 0; - - // The following are all unsigned integers. Internally we store them as signed integers of - // the same length because that way we're guaranteed that they can't be out of the range of - // the unsigned field in the packet. Callers wanting to pass in an unsigned value will need - // to cast it. - Short mtu = null; - Short maxMessageSize = null; - Integer leaseTime = null; - Integer T1 = null; - Integer T2 = null; - - // dhcp options - byte dhcpType = (byte) 0xFF; - - packet.order(ByteOrder.BIG_ENDIAN); - - // check to see if we need to parse L2, IP, and UDP encaps - if (pktType == ENCAP_L2) { - if (packet.remaining() < MIN_PACKET_LENGTH_L2) { - throw new ParseException(DhcpErrorEvent.L2_TOO_SHORT, - "L2 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L2); - } - - byte[] l2dst = new byte[6]; - byte[] l2src = new byte[6]; - - packet.get(l2dst); - packet.get(l2src); - - short l2type = packet.getShort(); - - if (l2type != OsConstants.ETH_P_IP) { - throw new ParseException(DhcpErrorEvent.L2_WRONG_ETH_TYPE, - "Unexpected L2 type 0x%04x, expected 0x%04x", l2type, OsConstants.ETH_P_IP); - } - } - - if (pktType <= ENCAP_L3) { - if (packet.remaining() < MIN_PACKET_LENGTH_L3) { - throw new ParseException(DhcpErrorEvent.L3_TOO_SHORT, - "L3 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L3); - } - - byte ipTypeAndLength = packet.get(); - int ipVersion = (ipTypeAndLength & 0xf0) >> 4; - if (ipVersion != 4) { - throw new ParseException( - DhcpErrorEvent.L3_NOT_IPV4, "Invalid IP version %d", ipVersion); - } - - // System.out.println("ipType is " + ipType); - byte ipDiffServicesField = packet.get(); - short ipTotalLength = packet.getShort(); - short ipIdentification = packet.getShort(); - byte ipFlags = packet.get(); - byte ipFragOffset = packet.get(); - byte ipTTL = packet.get(); - byte ipProto = packet.get(); - short ipChksm = packet.getShort(); - - ipSrc = readIpAddress(packet); - ipDst = readIpAddress(packet); - - if (ipProto != IP_TYPE_UDP) { - throw new ParseException( - DhcpErrorEvent.L4_NOT_UDP, "Protocol not UDP: %d", ipProto); - } - - // Skip options. This cannot cause us to read beyond the end of the buffer because the - // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than - // MIN_PACKET_LENGTH_L3. - int optionWords = ((ipTypeAndLength & 0x0f) - 5); - for (int i = 0; i < optionWords; i++) { - packet.getInt(); - } - - // assume UDP - short udpSrcPort = packet.getShort(); - short udpDstPort = packet.getShort(); - short udpLen = packet.getShort(); - short udpChkSum = packet.getShort(); - - // Only accept packets to or from the well-known client port (expressly permitting - // packets from ports other than the well-known server port; http://b/24687559), and - // server-to-server packets, e.g. for relays. - if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) && - !isPacketServerToServer(udpSrcPort, udpDstPort)) { - // This should almost never happen because we use SO_ATTACH_FILTER on the packet - // socket to drop packets that don't have the right source ports. However, it's - // possible that a packet arrives between when the socket is bound and when the - // filter is set. http://b/26696823 . - throw new ParseException(DhcpErrorEvent.L4_WRONG_PORT, - "Unexpected UDP ports %d->%d", udpSrcPort, udpDstPort); - } - } - - // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length. - if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) { - throw new ParseException(DhcpErrorEvent.BOOTP_TOO_SHORT, - "Invalid type or BOOTP packet too short, %d < %d", - packet.remaining(), MIN_PACKET_LENGTH_BOOTP); - } - - byte type = packet.get(); - byte hwType = packet.get(); - int addrLen = packet.get() & 0xff; - byte hops = packet.get(); - transactionId = packet.getInt(); - secs = packet.getShort(); - short bootpFlags = packet.getShort(); - boolean broadcast = (bootpFlags & 0x8000) != 0; - byte[] ipv4addr = new byte[4]; - - try { - packet.get(ipv4addr); - clientIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr); - packet.get(ipv4addr); - yourIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr); - packet.get(ipv4addr); - nextIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr); - packet.get(ipv4addr); - relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr); - } catch (UnknownHostException ex) { - throw new ParseException(DhcpErrorEvent.L3_INVALID_IP, - "Invalid IPv4 address: %s", Arrays.toString(ipv4addr)); - } - - // Some DHCP servers have been known to announce invalid client hardware address values such - // as 0xff. The legacy DHCP client accepted these becuause it does not check the length at - // all but only checks that the interface MAC address matches the first bytes of the address - // in the packets. We're a bit stricter: if the length is obviously invalid (i.e., bigger - // than the size of the field), we fudge it to 6 (Ethernet). http://b/23725795 - // TODO: evaluate whether to make this test more liberal. - if (addrLen > HWADDR_LEN) { - addrLen = ETHER_BROADCAST.length; - } - - clientMac = new byte[addrLen]; - packet.get(clientMac); - - // skip over address padding (16 octets allocated) - packet.position(packet.position() + (16 - addrLen)); - serverHostName = readAsciiString(packet, 64, false); - packet.position(packet.position() + 128); - - // Ensure this is a DHCP packet with a magic cookie, and not BOOTP. http://b/31850211 - if (packet.remaining() < 4) { - throw new ParseException(DhcpErrorEvent.DHCP_NO_COOKIE, "not a DHCP message"); - } - - int dhcpMagicCookie = packet.getInt(); - if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) { - throw new ParseException(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE, - "Bad magic cookie 0x%08x, should be 0x%08x", - dhcpMagicCookie, DHCP_MAGIC_COOKIE); - } - - // parse options - boolean notFinishedOptions = true; - - while ((packet.position() < packet.limit()) && notFinishedOptions) { - final byte optionType = packet.get(); // cannot underflow because position < limit - try { - if (optionType == DHCP_OPTION_END) { - notFinishedOptions = false; - } else if (optionType == DHCP_OPTION_PAD) { - // The pad option doesn't have a length field. Nothing to do. - } else { - int optionLen = packet.get() & 0xFF; - int expectedLen = 0; - - switch(optionType) { - case DHCP_SUBNET_MASK: - netMask = readIpAddress(packet); - expectedLen = 4; - break; - case DHCP_ROUTER: - for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) { - gateways.add(readIpAddress(packet)); - } - break; - case DHCP_DNS_SERVER: - for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) { - dnsServers.add(readIpAddress(packet)); - } - break; - case DHCP_HOST_NAME: - expectedLen = optionLen; - hostName = readAsciiString(packet, optionLen, false); - break; - case DHCP_MTU: - expectedLen = 2; - mtu = packet.getShort(); - break; - case DHCP_DOMAIN_NAME: - expectedLen = optionLen; - domainName = readAsciiString(packet, optionLen, false); - break; - case DHCP_BROADCAST_ADDRESS: - bcAddr = readIpAddress(packet); - expectedLen = 4; - break; - case DHCP_REQUESTED_IP: - requestedIp = readIpAddress(packet); - expectedLen = 4; - break; - case DHCP_LEASE_TIME: - leaseTime = Integer.valueOf(packet.getInt()); - expectedLen = 4; - break; - case DHCP_MESSAGE_TYPE: - dhcpType = packet.get(); - expectedLen = 1; - break; - case DHCP_SERVER_IDENTIFIER: - serverIdentifier = readIpAddress(packet); - expectedLen = 4; - break; - case DHCP_PARAMETER_LIST: - expectedParams = new byte[optionLen]; - packet.get(expectedParams); - expectedLen = optionLen; - break; - case DHCP_MESSAGE: - expectedLen = optionLen; - message = readAsciiString(packet, optionLen, false); - break; - case DHCP_MAX_MESSAGE_SIZE: - expectedLen = 2; - maxMessageSize = Short.valueOf(packet.getShort()); - break; - case DHCP_RENEWAL_TIME: - expectedLen = 4; - T1 = Integer.valueOf(packet.getInt()); - break; - case DHCP_REBINDING_TIME: - expectedLen = 4; - T2 = Integer.valueOf(packet.getInt()); - break; - case DHCP_VENDOR_CLASS_ID: - expectedLen = optionLen; - // Embedded nulls are safe as this does not get passed to netd. - vendorId = readAsciiString(packet, optionLen, true); - break; - case DHCP_CLIENT_IDENTIFIER: { // Client identifier - byte[] id = new byte[optionLen]; - packet.get(id); - expectedLen = optionLen; - } break; - case DHCP_VENDOR_INFO: - expectedLen = optionLen; - // Embedded nulls are safe as this does not get passed to netd. - vendorInfo = readAsciiString(packet, optionLen, true); - break; - case DHCP_OPTION_OVERLOAD: - expectedLen = 1; - optionOverload = packet.get(); - optionOverload &= OPTION_OVERLOAD_BOTH; - break; - default: - // ignore any other parameters - for (int i = 0; i < optionLen; i++) { - expectedLen++; - byte throwaway = packet.get(); - } - } - - if (expectedLen != optionLen) { - final int errorCode = DhcpErrorEvent.errorCodeWithOption( - DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH, optionType); - throw new ParseException(errorCode, - "Invalid length %d for option %d, expected %d", - optionLen, optionType, expectedLen); - } - } - } catch (BufferUnderflowException e) { - final int errorCode = DhcpErrorEvent.errorCodeWithOption( - DhcpErrorEvent.BUFFER_UNDERFLOW, optionType); - throw new ParseException(errorCode, "BufferUnderflowException"); - } - } - - DhcpPacket newPacket; - - switch(dhcpType) { - case (byte) 0xFF: - throw new ParseException(DhcpErrorEvent.DHCP_NO_MSG_TYPE, - "No DHCP message type option"); - case DHCP_MESSAGE_TYPE_DISCOVER: - newPacket = new DhcpDiscoverPacket(transactionId, secs, relayIp, clientMac, - broadcast, ipSrc); - break; - case DHCP_MESSAGE_TYPE_OFFER: - newPacket = new DhcpOfferPacket( - transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac); - break; - case DHCP_MESSAGE_TYPE_REQUEST: - newPacket = new DhcpRequestPacket( - transactionId, secs, clientIp, relayIp, clientMac, broadcast); - break; - case DHCP_MESSAGE_TYPE_DECLINE: - newPacket = new DhcpDeclinePacket( - transactionId, secs, clientIp, yourIp, nextIp, relayIp, - clientMac); - break; - case DHCP_MESSAGE_TYPE_ACK: - newPacket = new DhcpAckPacket( - transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac); - break; - case DHCP_MESSAGE_TYPE_NAK: - newPacket = new DhcpNakPacket( - transactionId, secs, relayIp, clientMac, broadcast); - break; - case DHCP_MESSAGE_TYPE_RELEASE: - if (serverIdentifier == null) { - throw new ParseException(DhcpErrorEvent.MISC_ERROR, - "DHCPRELEASE without server identifier"); - } - newPacket = new DhcpReleasePacket( - transactionId, serverIdentifier, clientIp, relayIp, clientMac); - break; - case DHCP_MESSAGE_TYPE_INFORM: - newPacket = new DhcpInformPacket( - transactionId, secs, clientIp, yourIp, nextIp, relayIp, - clientMac); - break; - default: - throw new ParseException(DhcpErrorEvent.DHCP_UNKNOWN_MSG_TYPE, - "Unimplemented DHCP type %d", dhcpType); - } - - newPacket.mBroadcastAddress = bcAddr; - newPacket.mClientId = clientId; - newPacket.mDnsServers = dnsServers; - newPacket.mDomainName = domainName; - newPacket.mGateways = gateways; - newPacket.mHostName = hostName; - newPacket.mLeaseTime = leaseTime; - newPacket.mMessage = message; - newPacket.mMtu = mtu; - newPacket.mRequestedIp = requestedIp; - newPacket.mRequestedParams = expectedParams; - newPacket.mServerIdentifier = serverIdentifier; - newPacket.mSubnetMask = netMask; - newPacket.mMaxMessageSize = maxMessageSize; - newPacket.mT1 = T1; - newPacket.mT2 = T2; - newPacket.mVendorId = vendorId; - newPacket.mVendorInfo = vendorInfo; - if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) { - newPacket.mServerHostName = serverHostName; - } else { - newPacket.mServerHostName = ""; - } - return newPacket; - } - - /** - * Parse a packet from an array of bytes, stopping at the given length. - */ - public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType) - throws ParseException { - ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN); - try { - return decodeFullPacket(buffer, pktType); - } catch (ParseException e) { - throw e; - } catch (Exception e) { - throw new ParseException(DhcpErrorEvent.PARSING_ERROR, e.getMessage()); - } - } - - /** - * Construct a DhcpResults object from a DHCP reply packet. - */ - public DhcpResults toDhcpResults() { - Inet4Address ipAddress = mYourIp; - if (ipAddress.equals(IPV4_ADDR_ANY)) { - ipAddress = mClientIp; - if (ipAddress.equals(IPV4_ADDR_ANY)) { - return null; - } - } - - int prefixLength; - if (mSubnetMask != null) { - try { - prefixLength = Inet4AddressUtils.netmaskToPrefixLength(mSubnetMask); - } catch (IllegalArgumentException e) { - // Non-contiguous netmask. - return null; - } - } else { - prefixLength = Inet4AddressUtils.getImplicitNetmask(ipAddress); - } - - DhcpResults results = new DhcpResults(); - try { - results.ipAddress = new LinkAddress(ipAddress, prefixLength); - } catch (IllegalArgumentException e) { - return null; - } - - if (mGateways.size() > 0) { - results.gateway = mGateways.get(0); - } - - results.dnsServers.addAll(mDnsServers); - results.domains = mDomainName; - results.serverAddress = mServerIdentifier; - results.vendorInfo = mVendorInfo; - results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE; - results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0; - results.serverHostName = mServerHostName; - - return results; - } - - /** - * Returns the parsed lease time, in milliseconds, or 0 for infinite. - */ - public long getLeaseTimeMillis() { - // dhcpcd treats the lack of a lease time option as an infinite lease. - if (mLeaseTime == null || mLeaseTime == INFINITE_LEASE) { - return 0; - } else if (0 <= mLeaseTime && mLeaseTime < MINIMUM_LEASE) { - return MINIMUM_LEASE * 1000; - } else { - return (mLeaseTime & 0xffffffffL) * 1000; - } - } - - /** - * Builds a DHCP-DISCOVER packet from the required specified - * parameters. - */ - public static ByteBuffer buildDiscoverPacket(int encap, int transactionId, - short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams) { - DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */, - clientMac, broadcast, INADDR_ANY /* srcIp */); - pkt.mRequestedParams = expectedParams; - return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); - } - - /** - * Builds a DHCP-OFFER packet from the required specified - * parameters. - */ - public static ByteBuffer buildOfferPacket(int encap, int transactionId, - boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, - Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask, - Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, - Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, - short mtu) { - DhcpPacket pkt = new DhcpOfferPacket( - transactionId, (short) 0, broadcast, serverIpAddr, relayIp, - INADDR_ANY /* clientIp */, yourIp, mac); - pkt.mGateways = gateways; - pkt.mDnsServers = dnsServers; - pkt.mLeaseTime = timeout; - pkt.mDomainName = domainName; - pkt.mHostName = hostname; - pkt.mServerIdentifier = dhcpServerIdentifier; - pkt.mSubnetMask = netMask; - pkt.mBroadcastAddress = bcAddr; - pkt.mMtu = mtu; - if (metered) { - pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; - } - return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); - } - - /** - * Builds a DHCP-ACK packet from the required specified parameters. - */ - public static ByteBuffer buildAckPacket(int encap, int transactionId, - boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, - Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask, - Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, - Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, - short mtu) { - DhcpPacket pkt = new DhcpAckPacket( - transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp, - mac); - pkt.mGateways = gateways; - pkt.mDnsServers = dnsServers; - pkt.mLeaseTime = timeout; - pkt.mDomainName = domainName; - pkt.mHostName = hostname; - pkt.mSubnetMask = netMask; - pkt.mServerIdentifier = dhcpServerIdentifier; - pkt.mBroadcastAddress = bcAddr; - pkt.mMtu = mtu; - if (metered) { - pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED; - } - return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); - } - - /** - * Builds a DHCP-NAK packet from the required specified parameters. - */ - public static ByteBuffer buildNakPacket(int encap, int transactionId, Inet4Address serverIpAddr, - Inet4Address relayIp, byte[] mac, boolean broadcast, String message) { - DhcpPacket pkt = new DhcpNakPacket( - transactionId, (short) 0, relayIp, mac, broadcast); - pkt.mMessage = message; - pkt.mServerIdentifier = serverIpAddr; - return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER); - } - - /** - * Builds a DHCP-REQUEST packet from the required specified parameters. - */ - public static ByteBuffer buildRequestPacket(int encap, - int transactionId, short secs, Inet4Address clientIp, boolean broadcast, - byte[] clientMac, Inet4Address requestedIpAddress, - Inet4Address serverIdentifier, byte[] requestedParams, String hostName) { - DhcpPacket pkt = new DhcpRequestPacket(transactionId, secs, clientIp, - INADDR_ANY /* relayIp */, clientMac, broadcast); - pkt.mRequestedIp = requestedIpAddress; - pkt.mServerIdentifier = serverIdentifier; - pkt.mHostName = hostName; - pkt.mRequestedParams = requestedParams; - ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT); - return result; - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java deleted file mode 100644 index 97d26c7c9c1f..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.util.FdEventsReader; -import android.os.Handler; -import android.system.Os; - -import java.io.FileDescriptor; -import java.net.Inet4Address; -import java.net.InetSocketAddress; - -/** - * A {@link FdEventsReader} to receive and parse {@link DhcpPacket}. - * @hide - */ -abstract class DhcpPacketListener extends FdEventsReader<DhcpPacketListener.Payload> { - static final class Payload { - protected final byte[] mBytes = new byte[DhcpPacket.MAX_LENGTH]; - protected Inet4Address mSrcAddr; - protected int mSrcPort; - } - - DhcpPacketListener(@NonNull Handler handler) { - super(handler, new Payload()); - } - - @Override - protected int recvBufSize(@NonNull Payload buffer) { - return buffer.mBytes.length; - } - - @Override - protected final void handlePacket(@NonNull Payload recvbuf, int length) { - if (recvbuf.mSrcAddr == null) { - return; - } - - try { - final DhcpPacket packet = DhcpPacket.decodeFullPacket(recvbuf.mBytes, length, - DhcpPacket.ENCAP_BOOTP); - onReceive(packet, recvbuf.mSrcAddr, recvbuf.mSrcPort); - } catch (DhcpPacket.ParseException e) { - logParseError(recvbuf.mBytes, length, e); - } - } - - @Override - protected int readPacket(@NonNull FileDescriptor fd, @NonNull Payload packetBuffer) - throws Exception { - final InetSocketAddress addr = new InetSocketAddress(0); - final int read = Os.recvfrom( - fd, packetBuffer.mBytes, 0, packetBuffer.mBytes.length, 0 /* flags */, addr); - - // Buffers with null srcAddr will be dropped in handlePacket() - packetBuffer.mSrcAddr = inet4AddrOrNull(addr); - packetBuffer.mSrcPort = addr.getPort(); - return read; - } - - @Nullable - private static Inet4Address inet4AddrOrNull(@NonNull InetSocketAddress addr) { - return addr.getAddress() instanceof Inet4Address - ? (Inet4Address) addr.getAddress() - : null; - } - - protected abstract void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr, - int srcPort); - protected abstract void logParseError(@NonNull byte[] packet, int length, - @NonNull DhcpPacket.ParseException e); -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java deleted file mode 100644 index 39583032c20d..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpReleasePacket.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; - -/** - * Implements DHCP-RELEASE - */ -class DhcpReleasePacket extends DhcpPacket { - - final Inet4Address mClientAddr; - - /** - * Generates a RELEASE packet with the specified parameters. - */ - public DhcpReleasePacket(int transId, Inet4Address serverId, Inet4Address clientAddr, - Inet4Address relayIp, byte[] clientMac) { - super(transId, (short)0, clientAddr, INADDR_ANY /* yourIp */, INADDR_ANY /* nextIp */, - relayIp, clientMac, false /* broadcast */); - mServerIdentifier = serverId; - mClientAddr = clientAddr; - } - - - @Override - public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) { - ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); - fillInPacket(encap, mServerIdentifier /* destIp */, mClientIp /* srcIp */, destUdp, srcUdp, - result, DHCP_BOOTREPLY, mBroadcast); - result.flip(); - return result; - } - - @Override - void finishPacket(ByteBuffer buffer) { - addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_RELEASE); - addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId()); - addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier); - addCommonClientTlvs(buffer); - addTlvEnd(buffer); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java b/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java deleted file mode 100644 index 231d04576c28..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpRequestPacket.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import android.util.Log; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; - -/** - * This class implements the DHCP-REQUEST packet. - */ -class DhcpRequestPacket extends DhcpPacket { - /** - * Generates a REQUEST packet with the specified parameters. - */ - DhcpRequestPacket(int transId, short secs, Inet4Address clientIp, Inet4Address relayIp, - byte[] clientMac, boolean broadcast) { - super(transId, secs, clientIp, INADDR_ANY, INADDR_ANY, relayIp, clientMac, broadcast); - } - - public String toString() { - String s = super.toString(); - return s + " REQUEST, desired IP " + mRequestedIp + " from host '" - + mHostName + "', param list length " - + (mRequestedParams == null ? 0 : mRequestedParams.length); - } - - /** - * Fills in a packet with the requested REQUEST attributes. - */ - public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) { - ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); - - fillInPacket(encap, INADDR_BROADCAST, INADDR_ANY, destUdp, srcUdp, - result, DHCP_BOOTREQUEST, mBroadcast); - result.flip(); - return result; - } - - /** - * Adds the optional parameters to the client-generated REQUEST packet. - */ - void finishPacket(ByteBuffer buffer) { - addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_REQUEST); - addTlv(buffer, DHCP_CLIENT_IDENTIFIER, getClientId()); - if (!INADDR_ANY.equals(mRequestedIp)) { - addTlv(buffer, DHCP_REQUESTED_IP, mRequestedIp); - } - if (!INADDR_ANY.equals(mServerIdentifier)) { - addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier); - } - addCommonClientTlvs(buffer); - addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams); - addTlvEnd(buffer); - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java deleted file mode 100644 index b8ab94ce3830..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java +++ /dev/null @@ -1,655 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import static android.net.dhcp.DhcpPacket.DHCP_CLIENT; -import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME; -import static android.net.dhcp.DhcpPacket.DHCP_SERVER; -import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP; -import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.shared.Inet4AddressUtils.getBroadcastAddress; -import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address; -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_DGRAM; -import static android.system.OsConstants.SOCK_NONBLOCK; -import static android.system.OsConstants.SOL_SOCKET; -import static android.system.OsConstants.SO_BROADCAST; -import static android.system.OsConstants.SO_REUSEADDR; - -import static com.android.internal.util.TrafficStatsConstants.TAG_SYSTEM_DHCP_SERVER; -import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE; -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ALL; -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; -import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission; - -import static java.lang.Integer.toUnsignedLong; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.INetworkStackStatusCallback; -import android.net.MacAddress; -import android.net.TrafficStats; -import android.net.util.NetworkStackUtils; -import android.net.util.SharedLog; -import android.net.util.SocketUtils; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.system.ErrnoException; -import android.system.Os; -import android.text.TextUtils; -import android.util.Pair; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.HexDump; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.nio.ByteBuffer; -import java.util.ArrayList; - -/** - * A DHCPv4 server. - * - * <p>This server listens for and responds to packets on a single interface. It considers itself - * authoritative for all leases on the subnet, which means that DHCP requests for unknown leases of - * unknown hosts receive a reply instead of being ignored. - * - * <p>The server is single-threaded (including send/receive operations): all internal operations are - * done on the provided {@link Looper}. Public methods are thread-safe and will schedule operations - * on the looper asynchronously. - * @hide - */ -public class DhcpServer extends IDhcpServer.Stub { - private static final String REPO_TAG = "Repository"; - - // Lease time to transmit to client instead of a negative time in case a lease expired before - // the server could send it (if the server process is suspended for example). - private static final int EXPIRED_FALLBACK_LEASE_TIME_SECS = 120; - - private static final int CMD_START_DHCP_SERVER = 1; - private static final int CMD_STOP_DHCP_SERVER = 2; - private static final int CMD_UPDATE_PARAMS = 3; - - @NonNull - private final HandlerThread mHandlerThread; - @NonNull - private final String mIfName; - @NonNull - private final DhcpLeaseRepository mLeaseRepo; - @NonNull - private final SharedLog mLog; - @NonNull - private final Dependencies mDeps; - @NonNull - private final Clock mClock; - - @Nullable - private volatile ServerHandler mHandler; - - // Accessed only on the handler thread - @Nullable - private DhcpPacketListener mPacketListener; - @Nullable - private FileDescriptor mSocket; - @NonNull - private DhcpServingParams mServingParams; - - /** - * Clock to be used by DhcpServer to track time for lease expiration. - * - * <p>The clock should track time as may be measured by clients obtaining a lease. It does not - * need to be monotonous across restarts of the server as long as leases are cleared when the - * server is stopped. - */ - public static class Clock { - /** - * @see SystemClock#elapsedRealtime() - */ - public long elapsedRealtime() { - return SystemClock.elapsedRealtime(); - } - } - - /** - * Dependencies for the DhcpServer. Useful to be mocked in tests. - */ - public interface Dependencies { - /** - * Send a packet to the specified datagram socket. - * - * @param fd File descriptor of the socket. - * @param buffer Data to be sent. - * @param dst Destination address of the packet. - */ - void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer, - @NonNull InetAddress dst) throws ErrnoException, IOException; - - /** - * Create a DhcpLeaseRepository for the server. - * @param servingParams Parameters used to serve DHCP requests. - * @param log Log to be used by the repository. - * @param clock Clock that the repository must use to track time. - */ - DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams, - @NonNull SharedLog log, @NonNull Clock clock); - - /** - * Create a packet listener that will send packets to be processed. - */ - DhcpPacketListener makePacketListener(); - - /** - * Create a clock that the server will use to track time. - */ - Clock makeClock(); - - /** - * Add an entry to the ARP cache table. - * @param fd Datagram socket file descriptor that must use the new entry. - */ - void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr, - @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException; - - /** - * Verify that the caller is allowed to call public methods on DhcpServer. - * @throws SecurityException The caller is not allowed to call public methods on DhcpServer. - */ - void checkCaller() throws SecurityException; - } - - private class DependenciesImpl implements Dependencies { - @Override - public void sendPacket(@NonNull FileDescriptor fd, @NonNull ByteBuffer buffer, - @NonNull InetAddress dst) throws ErrnoException, IOException { - Os.sendto(fd, buffer, 0, dst, DhcpPacket.DHCP_CLIENT); - } - - @Override - public DhcpLeaseRepository makeLeaseRepository(@NonNull DhcpServingParams servingParams, - @NonNull SharedLog log, @NonNull Clock clock) { - return new DhcpLeaseRepository( - DhcpServingParams.makeIpPrefix(servingParams.serverAddr), - servingParams.excludedAddrs, - servingParams.dhcpLeaseTimeSecs * 1000, log.forSubComponent(REPO_TAG), clock); - } - - @Override - public DhcpPacketListener makePacketListener() { - return new PacketListener(); - } - - @Override - public Clock makeClock() { - return new Clock(); - } - - @Override - public void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr, - @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException { - NetworkStackUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd); - } - - @Override - public void checkCaller() { - checkNetworkStackCallingPermission(); - } - } - - private static class MalformedPacketException extends Exception { - MalformedPacketException(String message, Throwable t) { - super(message, t); - } - } - - public DhcpServer(@NonNull String ifName, - @NonNull DhcpServingParams params, @NonNull SharedLog log) { - this(new HandlerThread(DhcpServer.class.getSimpleName() + "." + ifName), - ifName, params, log, null); - } - - @VisibleForTesting - DhcpServer(@NonNull HandlerThread handlerThread, @NonNull String ifName, - @NonNull DhcpServingParams params, @NonNull SharedLog log, - @Nullable Dependencies deps) { - if (deps == null) { - deps = new DependenciesImpl(); - } - mHandlerThread = handlerThread; - mIfName = ifName; - mServingParams = params; - mLog = log; - mDeps = deps; - mClock = deps.makeClock(); - mLeaseRepo = deps.makeLeaseRepository(mServingParams, mLog, mClock); - } - - /** - * Start listening for and responding to packets. - * - * <p>It is not legal to call this method more than once; in particular the server cannot be - * restarted after being stopped. - */ - @Override - public void start(@Nullable INetworkStackStatusCallback cb) { - mDeps.checkCaller(); - mHandlerThread.start(); - mHandler = new ServerHandler(mHandlerThread.getLooper()); - sendMessage(CMD_START_DHCP_SERVER, cb); - } - - /** - * Update serving parameters. All subsequently received requests will be handled with the new - * parameters, and current leases that are incompatible with the new parameters are dropped. - */ - @Override - public void updateParams(@Nullable DhcpServingParamsParcel params, - @Nullable INetworkStackStatusCallback cb) throws RemoteException { - mDeps.checkCaller(); - final DhcpServingParams parsedParams; - try { - // throws InvalidParameterException with null params - parsedParams = DhcpServingParams.fromParcelableObject(params); - } catch (DhcpServingParams.InvalidParameterException e) { - mLog.e("Invalid parameters sent to DhcpServer", e); - if (cb != null) { - cb.onStatusAvailable(STATUS_INVALID_ARGUMENT); - } - return; - } - sendMessage(CMD_UPDATE_PARAMS, new Pair<>(parsedParams, cb)); - } - - /** - * Stop listening for packets. - * - * <p>As the server is stopped asynchronously, some packets may still be processed shortly after - * calling this method. - */ - @Override - public void stop(@Nullable INetworkStackStatusCallback cb) { - mDeps.checkCaller(); - sendMessage(CMD_STOP_DHCP_SERVER, cb); - } - - private void sendMessage(int what, @Nullable Object obj) { - if (mHandler == null) { - mLog.e("Attempting to send a command to stopped DhcpServer: " + what); - return; - } - mHandler.sendMessage(mHandler.obtainMessage(what, obj)); - } - - private class ServerHandler extends Handler { - ServerHandler(@NonNull Looper looper) { - super(looper); - } - - @Override - public void handleMessage(@NonNull Message msg) { - final INetworkStackStatusCallback cb; - switch (msg.what) { - case CMD_UPDATE_PARAMS: - final Pair<DhcpServingParams, INetworkStackStatusCallback> pair = - (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj; - final DhcpServingParams params = pair.first; - mServingParams = params; - mLeaseRepo.updateParams( - DhcpServingParams.makeIpPrefix(mServingParams.serverAddr), - params.excludedAddrs, - params.dhcpLeaseTimeSecs); - - cb = pair.second; - break; - case CMD_START_DHCP_SERVER: - mPacketListener = mDeps.makePacketListener(); - mPacketListener.start(); - cb = (INetworkStackStatusCallback) msg.obj; - break; - case CMD_STOP_DHCP_SERVER: - if (mPacketListener != null) { - mPacketListener.stop(); - mPacketListener = null; - } - mHandlerThread.quitSafely(); - cb = (INetworkStackStatusCallback) msg.obj; - break; - default: - return; - } - if (cb != null) { - try { - cb.onStatusAvailable(STATUS_SUCCESS); - } catch (RemoteException e) { - mLog.e("Could not send status back to caller", e); - } - } - } - } - - @VisibleForTesting - void processPacket(@NonNull DhcpPacket packet, int srcPort) { - final String packetType = packet.getClass().getSimpleName(); - if (srcPort != DHCP_CLIENT) { - mLog.logf("Ignored packet of type %s sent from client port %d", packetType, srcPort); - return; - } - - mLog.log("Received packet of type " + packetType); - final Inet4Address sid = packet.mServerIdentifier; - if (sid != null && !sid.equals(mServingParams.serverAddr.getAddress())) { - mLog.log("Packet ignored due to wrong server identifier: " + sid); - return; - } - - try { - if (packet instanceof DhcpDiscoverPacket) { - processDiscover((DhcpDiscoverPacket) packet); - } else if (packet instanceof DhcpRequestPacket) { - processRequest((DhcpRequestPacket) packet); - } else if (packet instanceof DhcpReleasePacket) { - processRelease((DhcpReleasePacket) packet); - } else { - mLog.e("Unknown packet type: " + packet.getClass().getSimpleName()); - } - } catch (MalformedPacketException e) { - // Not an internal error: only logging exception message, not stacktrace - mLog.e("Ignored malformed packet: " + e.getMessage()); - } - } - - private void logIgnoredPacketInvalidSubnet(DhcpLeaseRepository.InvalidSubnetException e) { - // Not an internal error: only logging exception message, not stacktrace - mLog.e("Ignored packet from invalid subnet: " + e.getMessage()); - } - - private void processDiscover(@NonNull DhcpDiscoverPacket packet) - throws MalformedPacketException { - final DhcpLease lease; - final MacAddress clientMac = getMacAddr(packet); - try { - lease = mLeaseRepo.getOffer(packet.getExplicitClientIdOrNull(), clientMac, - packet.mRelayIp, packet.mRequestedIp, packet.mHostName); - } catch (DhcpLeaseRepository.OutOfAddressesException e) { - transmitNak(packet, "Out of addresses to offer"); - return; - } catch (DhcpLeaseRepository.InvalidSubnetException e) { - logIgnoredPacketInvalidSubnet(e); - return; - } - - transmitOffer(packet, lease, clientMac); - } - - private void processRequest(@NonNull DhcpRequestPacket packet) throws MalformedPacketException { - // If set, packet SID matches with this server's ID as checked in processPacket(). - final boolean sidSet = packet.mServerIdentifier != null; - final DhcpLease lease; - final MacAddress clientMac = getMacAddr(packet); - try { - lease = mLeaseRepo.requestLease(packet.getExplicitClientIdOrNull(), clientMac, - packet.mClientIp, packet.mRelayIp, packet.mRequestedIp, sidSet, - packet.mHostName); - } catch (DhcpLeaseRepository.InvalidAddressException e) { - transmitNak(packet, "Invalid requested address"); - return; - } catch (DhcpLeaseRepository.InvalidSubnetException e) { - logIgnoredPacketInvalidSubnet(e); - return; - } - - transmitAck(packet, lease, clientMac); - } - - private void processRelease(@NonNull DhcpReleasePacket packet) - throws MalformedPacketException { - final byte[] clientId = packet.getExplicitClientIdOrNull(); - final MacAddress macAddr = getMacAddr(packet); - // Don't care about success (there is no ACK/NAK); logging is already done in the repository - mLeaseRepo.releaseLease(clientId, macAddr, packet.mClientIp); - } - - private Inet4Address getAckOrOfferDst(@NonNull DhcpPacket request, @NonNull DhcpLease lease, - boolean broadcastFlag) { - // Unless relayed or broadcast, send to client IP if already configured on the client, or to - // the lease address if the client has no configured address - if (!isEmpty(request.mRelayIp)) { - return request.mRelayIp; - } else if (broadcastFlag) { - return IPV4_ADDR_ALL; - } else if (!isEmpty(request.mClientIp)) { - return request.mClientIp; - } else { - return lease.getNetAddr(); - } - } - - /** - * Determine whether the broadcast flag should be set in the BOOTP packet flags. This does not - * apply to NAK responses, which should always have it set. - */ - private static boolean getBroadcastFlag(@NonNull DhcpPacket request, @NonNull DhcpLease lease) { - // No broadcast flag if the client already has a configured IP to unicast to. RFC2131 #4.1 - // has some contradictions regarding broadcast behavior if a client already has an IP - // configured and sends a request with both ciaddr (renew/rebind) and the broadcast flag - // set. Sending a unicast response to ciaddr matches previous behavior and is more - // efficient. - // If the client has no configured IP, broadcast if requested by the client or if the lease - // address cannot be used to send a unicast reply either. - return isEmpty(request.mClientIp) && (request.mBroadcast || isEmpty(lease.getNetAddr())); - } - - /** - * Get the hostname from a lease if non-empty and requested in the incoming request. - * @param request The incoming request. - * @return The hostname, or null if not requested or empty. - */ - @Nullable - private static String getHostnameIfRequested(@NonNull DhcpPacket request, - @NonNull DhcpLease lease) { - return request.hasRequestedParam(DHCP_HOST_NAME) && !TextUtils.isEmpty(lease.getHostname()) - ? lease.getHostname() - : null; - } - - private boolean transmitOffer(@NonNull DhcpPacket request, @NonNull DhcpLease lease, - @NonNull MacAddress clientMac) { - final boolean broadcastFlag = getBroadcastFlag(request, lease); - final int timeout = getLeaseTimeout(lease); - final Inet4Address prefixMask = - getPrefixMaskAsInet4Address(mServingParams.serverAddr.getPrefixLength()); - final Inet4Address broadcastAddr = getBroadcastAddress( - mServingParams.getServerInet4Addr(), mServingParams.serverAddr.getPrefixLength()); - final String hostname = getHostnameIfRequested(request, lease); - final ByteBuffer offerPacket = DhcpPacket.buildOfferPacket( - ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(), - request.mRelayIp, lease.getNetAddr(), request.mClientMac, timeout, prefixMask, - broadcastAddr, new ArrayList<>(mServingParams.defaultRouters), - new ArrayList<>(mServingParams.dnsServers), - mServingParams.getServerInet4Addr(), null /* domainName */, hostname, - mServingParams.metered, (short) mServingParams.linkMtu); - - return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag); - } - - private boolean transmitAck(@NonNull DhcpPacket request, @NonNull DhcpLease lease, - @NonNull MacAddress clientMac) { - // TODO: replace DhcpPacket's build methods with real builders and use common code with - // transmitOffer above - final boolean broadcastFlag = getBroadcastFlag(request, lease); - final int timeout = getLeaseTimeout(lease); - final String hostname = getHostnameIfRequested(request, lease); - final ByteBuffer ackPacket = DhcpPacket.buildAckPacket(ENCAP_BOOTP, request.mTransId, - broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp, - lease.getNetAddr(), request.mClientIp, request.mClientMac, timeout, - mServingParams.getPrefixMaskAsAddress(), mServingParams.getBroadcastAddress(), - new ArrayList<>(mServingParams.defaultRouters), - new ArrayList<>(mServingParams.dnsServers), - mServingParams.getServerInet4Addr(), null /* domainName */, hostname, - mServingParams.metered, (short) mServingParams.linkMtu); - - return transmitOfferOrAckPacket(ackPacket, request, lease, clientMac, broadcastFlag); - } - - private boolean transmitNak(DhcpPacket request, String message) { - mLog.w("Transmitting NAK: " + message); - // Always set broadcast flag for NAK: client may not have a correct IP - final ByteBuffer nakPacket = DhcpPacket.buildNakPacket( - ENCAP_BOOTP, request.mTransId, mServingParams.getServerInet4Addr(), - request.mRelayIp, request.mClientMac, true /* broadcast */, message); - - final Inet4Address dst = isEmpty(request.mRelayIp) - ? IPV4_ADDR_ALL - : request.mRelayIp; - return transmitPacket(nakPacket, DhcpNakPacket.class.getSimpleName(), dst); - } - - private boolean transmitOfferOrAckPacket(@NonNull ByteBuffer buf, @NonNull DhcpPacket request, - @NonNull DhcpLease lease, @NonNull MacAddress clientMac, boolean broadcastFlag) { - mLog.logf("Transmitting %s with lease %s", request.getClass().getSimpleName(), lease); - // Client may not yet respond to ARP for the lease address, which may be the destination - // address. Add an entry to the ARP cache to save future ARP probes and make sure the - // packet reaches its destination. - if (!addArpEntry(clientMac, lease.getNetAddr())) { - // Logging for error already done - return false; - } - final Inet4Address dst = getAckOrOfferDst(request, lease, broadcastFlag); - return transmitPacket(buf, request.getClass().getSimpleName(), dst); - } - - private boolean transmitPacket(@NonNull ByteBuffer buf, @NonNull String packetTypeTag, - @NonNull Inet4Address dst) { - try { - mDeps.sendPacket(mSocket, buf, dst); - } catch (ErrnoException | IOException e) { - mLog.e("Can't send packet " + packetTypeTag, e); - return false; - } - return true; - } - - private boolean addArpEntry(@NonNull MacAddress macAddr, @NonNull Inet4Address inetAddr) { - try { - mDeps.addArpEntry(inetAddr, macAddr, mIfName, mSocket); - return true; - } catch (IOException e) { - mLog.e("Error adding client to ARP table", e); - return false; - } - } - - /** - * Get the remaining lease time in seconds, starting from {@link Clock#elapsedRealtime()}. - * - * <p>This is an unsigned 32-bit integer, so it cannot be read as a standard (signed) Java int. - * The return value is only intended to be used to populate the lease time field in a DHCP - * response, considering that lease time is an unsigned 32-bit integer field in DHCP packets. - * - * <p>Lease expiration times are tracked internally with millisecond precision: this method - * returns a rounded down value. - */ - private int getLeaseTimeout(@NonNull DhcpLease lease) { - final long remainingTimeSecs = (lease.getExpTime() - mClock.elapsedRealtime()) / 1000; - if (remainingTimeSecs < 0) { - mLog.e("Processing expired lease " + lease); - return EXPIRED_FALLBACK_LEASE_TIME_SECS; - } - - if (remainingTimeSecs >= toUnsignedLong(INFINITE_LEASE)) { - return INFINITE_LEASE; - } - - return (int) remainingTimeSecs; - } - - /** - * Get the client MAC address from a packet. - * - * @throws MalformedPacketException The address in the packet uses an unsupported format. - */ - @NonNull - private MacAddress getMacAddr(@NonNull DhcpPacket packet) throws MalformedPacketException { - try { - return MacAddress.fromBytes(packet.getClientMac()); - } catch (IllegalArgumentException e) { - final String message = "Invalid MAC address in packet: " - + HexDump.dumpHexString(packet.getClientMac()); - throw new MalformedPacketException(message, e); - } - } - - private static boolean isEmpty(@Nullable Inet4Address address) { - return address == null || IPV4_ADDR_ANY.equals(address); - } - - private class PacketListener extends DhcpPacketListener { - PacketListener() { - super(mHandler); - } - - @Override - protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr, - int srcPort) { - processPacket(packet, srcPort); - } - - @Override - protected void logError(@NonNull String msg, Exception e) { - mLog.e("Error receiving packet: " + msg, e); - } - - @Override - protected void logParseError(@NonNull byte[] packet, int length, - @NonNull DhcpPacket.ParseException e) { - mLog.e("Error parsing packet", e); - } - - @Override - protected FileDescriptor createFd() { - // TODO: have and use an API to set a socket tag without going through the thread tag - final int oldTag = TrafficStats.getAndSetThreadStatsTag(TAG_SYSTEM_DHCP_SERVER); - try { - mSocket = Os.socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); - SocketUtils.bindSocketToInterface(mSocket, mIfName); - Os.setsockoptInt(mSocket, SOL_SOCKET, SO_REUSEADDR, 1); - Os.setsockoptInt(mSocket, SOL_SOCKET, SO_BROADCAST, 1); - Os.bind(mSocket, IPV4_ADDR_ANY, DHCP_SERVER); - - return mSocket; - } catch (IOException | ErrnoException e) { - mLog.e("Error creating UDP socket", e); - DhcpServer.this.stop(null); - return null; - } finally { - TrafficStats.setThreadStatsTag(oldTag); - } - } - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } -} diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java deleted file mode 100644 index 230b693a809a..000000000000 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpServingParams.java +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address; -import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; - -import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE; -import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU; -import static com.android.server.util.NetworkStackConstants.IPV4_MIN_MTU; - -import static java.lang.Integer.toUnsignedLong; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.shared.Inet4AddressUtils; -import android.util.ArraySet; - -import java.net.Inet4Address; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** - * Parameters used by the DhcpServer to serve requests. - * - * <p>Instances are immutable. Use {@link DhcpServingParams.Builder} to instantiate. - * @hide - */ -public class DhcpServingParams { - public static final int MTU_UNSET = 0; - public static final int MIN_PREFIX_LENGTH = 16; - public static final int MAX_PREFIX_LENGTH = 30; - - /** Server inet address and prefix to serve */ - @NonNull - public final LinkAddress serverAddr; - - /** - * Default routers to be advertised to DHCP clients. May be empty. - * This set is provided by {@link DhcpServingParams.Builder} and is immutable. - */ - @NonNull - public final Set<Inet4Address> defaultRouters; - - /** - * DNS servers to be advertised to DHCP clients. May be empty. - * This set is provided by {@link DhcpServingParams.Builder} and is immutable. - */ - @NonNull - public final Set<Inet4Address> dnsServers; - - /** - * Excluded addresses that the DHCP server is not allowed to assign to clients. - * This set is provided by {@link DhcpServingParams.Builder} and is immutable. - */ - @NonNull - public final Set<Inet4Address> excludedAddrs; - - // DHCP uses uint32. Use long for clearer code, and check range when building. - public final long dhcpLeaseTimeSecs; - public final int linkMtu; - - /** - * Indicates whether the DHCP server should send the ANDROID_METERED vendor-specific option. - */ - public final boolean metered; - - /** - * Checked exception thrown when some parameters used to build {@link DhcpServingParams} are - * missing or invalid. - */ - public static class InvalidParameterException extends Exception { - public InvalidParameterException(String message) { - super(message); - } - } - - private DhcpServingParams(@NonNull LinkAddress serverAddr, - @NonNull Set<Inet4Address> defaultRouters, - @NonNull Set<Inet4Address> dnsServers, @NonNull Set<Inet4Address> excludedAddrs, - long dhcpLeaseTimeSecs, int linkMtu, boolean metered) { - this.serverAddr = serverAddr; - this.defaultRouters = defaultRouters; - this.dnsServers = dnsServers; - this.excludedAddrs = excludedAddrs; - this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs; - this.linkMtu = linkMtu; - this.metered = metered; - } - - /** - * Create parameters from a stable AIDL-compatible parcel. - * @throws InvalidParameterException The parameters parcelable is null or invalid. - */ - public static DhcpServingParams fromParcelableObject(@Nullable DhcpServingParamsParcel parcel) - throws InvalidParameterException { - if (parcel == null) { - throw new InvalidParameterException("Null serving parameters"); - } - final LinkAddress serverAddr = new LinkAddress( - intToInet4AddressHTH(parcel.serverAddr), - parcel.serverAddrPrefixLength); - return new Builder() - .setServerAddr(serverAddr) - .setDefaultRouters(toInet4AddressSet(parcel.defaultRouters)) - .setDnsServers(toInet4AddressSet(parcel.dnsServers)) - .setExcludedAddrs(toInet4AddressSet(parcel.excludedAddrs)) - .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs) - .setLinkMtu(parcel.linkMtu) - .setMetered(parcel.metered) - .build(); - } - - private static Set<Inet4Address> toInet4AddressSet(@Nullable int[] addrs) { - if (addrs == null) { - return new HashSet<>(0); - } - - final HashSet<Inet4Address> res = new HashSet<>(); - for (int addr : addrs) { - res.add(intToInet4AddressHTH(addr)); - } - return res; - } - - @NonNull - public Inet4Address getServerInet4Addr() { - return (Inet4Address) serverAddr.getAddress(); - } - - /** - * Get the served prefix mask as an IPv4 address. - * - * <p>For example, if the served prefix is 192.168.42.0/24, this will return 255.255.255.0. - */ - @NonNull - public Inet4Address getPrefixMaskAsAddress() { - return getPrefixMaskAsInet4Address(serverAddr.getPrefixLength()); - } - - /** - * Get the server broadcast address. - * - * <p>For example, if the server {@link LinkAddress} is 192.168.42.1/24, this will return - * 192.168.42.255. - */ - @NonNull - public Inet4Address getBroadcastAddress() { - return Inet4AddressUtils.getBroadcastAddress( - getServerInet4Addr(), serverAddr.getPrefixLength()); - } - - /** - * Utility class to create new instances of {@link DhcpServingParams} while checking validity - * of the parameters. - */ - public static class Builder { - private LinkAddress mServerAddr; - private Set<Inet4Address> mDefaultRouters; - private Set<Inet4Address> mDnsServers; - private Set<Inet4Address> mExcludedAddrs; - private long mDhcpLeaseTimeSecs; - private int mLinkMtu = MTU_UNSET; - private boolean mMetered; - - /** - * Set the server address and served prefix for the DHCP server. - * - * <p>This parameter is required. - */ - public Builder setServerAddr(@NonNull LinkAddress serverAddr) { - this.mServerAddr = serverAddr; - return this; - } - - /** - * Set the default routers to be advertised to DHCP clients. - * - * <p>Each router must be inside the served prefix. This may be an empty set, but it must - * always be set explicitly before building the {@link DhcpServingParams}. - */ - public Builder setDefaultRouters(@NonNull Set<Inet4Address> defaultRouters) { - this.mDefaultRouters = defaultRouters; - return this; - } - - /** - * Set the default routers to be advertised to DHCP clients. - * - * <p>Each router must be inside the served prefix. This may be an empty list of routers, - * but it must always be set explicitly before building the {@link DhcpServingParams}. - */ - public Builder setDefaultRouters(@NonNull Inet4Address... defaultRouters) { - return setDefaultRouters(makeArraySet(defaultRouters)); - } - - /** - * Convenience method to build the parameters with no default router. - * - * <p>Equivalent to calling {@link #setDefaultRouters(Inet4Address...)} with no address. - */ - public Builder withNoDefaultRouter() { - return setDefaultRouters(); - } - - /** - * Set the DNS servers to be advertised to DHCP clients. - * - * <p>This may be an empty set, but it must always be set explicitly before building the - * {@link DhcpServingParams}. - */ - public Builder setDnsServers(@NonNull Set<Inet4Address> dnsServers) { - this.mDnsServers = dnsServers; - return this; - } - - /** - * Set the DNS servers to be advertised to DHCP clients. - * - * <p>This may be an empty list of servers, but it must always be set explicitly before - * building the {@link DhcpServingParams}. - */ - public Builder setDnsServers(@NonNull Inet4Address... dnsServers) { - return setDnsServers(makeArraySet(dnsServers)); - } - - /** - * Convenience method to build the parameters with no DNS server. - * - * <p>Equivalent to calling {@link #setDnsServers(Inet4Address...)} with no address. - */ - public Builder withNoDnsServer() { - return setDnsServers(); - } - - /** - * Set excluded addresses that the DHCP server is not allowed to assign to clients. - * - * <p>This parameter is optional. DNS servers and default routers are always excluded - * and do not need to be set here. - */ - public Builder setExcludedAddrs(@NonNull Set<Inet4Address> excludedAddrs) { - this.mExcludedAddrs = excludedAddrs; - return this; - } - - /** - * Set excluded addresses that the DHCP server is not allowed to assign to clients. - * - * <p>This parameter is optional. DNS servers and default routers are always excluded - * and do not need to be set here. - */ - public Builder setExcludedAddrs(@NonNull Inet4Address... excludedAddrs) { - return setExcludedAddrs(makeArraySet(excludedAddrs)); - } - - /** - * Set the lease time for leases assigned by the DHCP server. - * - * <p>This parameter is required. - */ - public Builder setDhcpLeaseTimeSecs(long dhcpLeaseTimeSecs) { - this.mDhcpLeaseTimeSecs = dhcpLeaseTimeSecs; - return this; - } - - /** - * Set the link MTU to be advertised to DHCP clients. - * - * <p>If set to {@link #MTU_UNSET}, no MTU will be advertised to clients. This parameter - * is optional and defaults to {@link #MTU_UNSET}. - */ - public Builder setLinkMtu(int linkMtu) { - this.mLinkMtu = linkMtu; - return this; - } - - /** - * Set whether the DHCP server should send the ANDROID_METERED vendor-specific option. - * - * <p>If not set, the default value is false. - */ - public Builder setMetered(boolean metered) { - this.mMetered = metered; - return this; - } - - /** - * Create a new {@link DhcpServingParams} instance based on parameters set in the builder. - * - * <p>This method has no side-effects. If it does not throw, a valid - * {@link DhcpServingParams} is returned. - * @return The constructed parameters. - * @throws InvalidParameterException At least one parameter is missing or invalid. - */ - @NonNull - public DhcpServingParams build() throws InvalidParameterException { - if (mServerAddr == null) { - throw new InvalidParameterException("Missing serverAddr"); - } - if (mDefaultRouters == null) { - throw new InvalidParameterException("Missing defaultRouters"); - } - if (mDnsServers == null) { - // Empty set is OK, but enforce explicitly setting it - throw new InvalidParameterException("Missing dnsServers"); - } - if (mDhcpLeaseTimeSecs <= 0 || mDhcpLeaseTimeSecs > toUnsignedLong(INFINITE_LEASE)) { - throw new InvalidParameterException("Invalid lease time: " + mDhcpLeaseTimeSecs); - } - if (mLinkMtu != MTU_UNSET && (mLinkMtu < IPV4_MIN_MTU || mLinkMtu > IPV4_MAX_MTU)) { - throw new InvalidParameterException("Invalid link MTU: " + mLinkMtu); - } - if (!mServerAddr.isIpv4()) { - throw new InvalidParameterException("serverAddr must be IPv4"); - } - if (mServerAddr.getPrefixLength() < MIN_PREFIX_LENGTH - || mServerAddr.getPrefixLength() > MAX_PREFIX_LENGTH) { - throw new InvalidParameterException("Prefix length is not in supported range"); - } - - final IpPrefix prefix = makeIpPrefix(mServerAddr); - for (Inet4Address addr : mDefaultRouters) { - if (!prefix.contains(addr)) { - throw new InvalidParameterException(String.format( - "Default router %s is not in server prefix %s", addr, mServerAddr)); - } - } - - final Set<Inet4Address> excl = new HashSet<>(); - if (mExcludedAddrs != null) { - excl.addAll(mExcludedAddrs); - } - excl.add((Inet4Address) mServerAddr.getAddress()); - excl.addAll(mDefaultRouters); - excl.addAll(mDnsServers); - - return new DhcpServingParams(mServerAddr, - Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)), - Collections.unmodifiableSet(new HashSet<>(mDnsServers)), - Collections.unmodifiableSet(excl), - mDhcpLeaseTimeSecs, mLinkMtu, mMetered); - } - } - - /** - * Utility method to create an IpPrefix with the address and prefix length of a LinkAddress. - */ - @NonNull - static IpPrefix makeIpPrefix(@NonNull LinkAddress addr) { - return new IpPrefix(addr.getAddress(), addr.getPrefixLength()); - } - - private static <T> ArraySet<T> makeArraySet(T[] elements) { - final ArraySet<T> set = new ArraySet<>(elements.length); - set.addAll(Arrays.asList(elements)); - return set; - } -} diff --git a/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java b/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java deleted file mode 100644 index eb49218ebbae..000000000000 --- a/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2016 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.ip; - -import static android.net.util.SocketUtils.makePacketSocketAddress; -import static android.system.OsConstants.AF_PACKET; -import static android.system.OsConstants.ARPHRD_ETHER; -import static android.system.OsConstants.ETH_P_ALL; -import static android.system.OsConstants.SOCK_NONBLOCK; -import static android.system.OsConstants.SOCK_RAW; - -import android.net.util.ConnectivityPacketSummary; -import android.net.util.InterfaceParams; -import android.net.util.NetworkStackUtils; -import android.net.util.PacketReader; -import android.os.Handler; -import android.system.ErrnoException; -import android.system.Os; -import android.text.TextUtils; -import android.util.LocalLog; -import android.util.Log; - -import com.android.internal.util.HexDump; - -import java.io.FileDescriptor; -import java.io.IOException; - - -/** - * Critical connectivity packet tracking daemon. - * - * Tracks ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets. - * - * This class's constructor, start() and stop() methods must only be called - * from the same thread on which the passed in |log| is accessed. - * - * Log lines include a hexdump of the packet, which can be decoded via: - * - * echo -n H3XSTR1NG | sed -e 's/\([0-9A-F][0-9A-F]\)/\1 /g' -e 's/^/000000 /' - * | text2pcap - - - * | tcpdump -n -vv -e -r - - * - * @hide - */ -public class ConnectivityPacketTracker { - private static final String TAG = ConnectivityPacketTracker.class.getSimpleName(); - private static final boolean DBG = false; - private static final String MARK_START = "--- START ---"; - private static final String MARK_STOP = "--- STOP ---"; - private static final String MARK_NAMED_START = "--- START (%s) ---"; - private static final String MARK_NAMED_STOP = "--- STOP (%s) ---"; - - private final String mTag; - private final LocalLog mLog; - private final PacketReader mPacketListener; - private boolean mRunning; - private String mDisplayName; - - public ConnectivityPacketTracker(Handler h, InterfaceParams ifParams, LocalLog log) { - if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams"); - - mTag = TAG + "." + ifParams.name; - mLog = log; - mPacketListener = new PacketListener(h, ifParams); - } - - public void start(String displayName) { - mRunning = true; - mDisplayName = displayName; - mPacketListener.start(); - } - - public void stop() { - mPacketListener.stop(); - mRunning = false; - mDisplayName = null; - } - - private final class PacketListener extends PacketReader { - private final InterfaceParams mInterface; - - PacketListener(Handler h, InterfaceParams ifParams) { - super(h, ifParams.defaultMtu); - mInterface = ifParams; - } - - @Override - protected FileDescriptor createFd() { - FileDescriptor s = null; - try { - s = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0); - NetworkStackUtils.attachControlPacketFilter(s, ARPHRD_ETHER); - Os.bind(s, makePacketSocketAddress((short) ETH_P_ALL, mInterface.index)); - } catch (ErrnoException | IOException e) { - logError("Failed to create packet tracking socket: ", e); - closeFd(s); - return null; - } - return s; - } - - @Override - protected void handlePacket(byte[] recvbuf, int length) { - final String summary = ConnectivityPacketSummary.summarize( - mInterface.macAddr, recvbuf, length); - if (summary == null) return; - - if (DBG) Log.d(mTag, summary); - addLogEntry(summary + "\n[" + HexDump.toHexString(recvbuf, 0, length) + "]"); - } - - @Override - protected void onStart() { - final String msg = TextUtils.isEmpty(mDisplayName) - ? MARK_START - : String.format(MARK_NAMED_START, mDisplayName); - mLog.log(msg); - } - - @Override - protected void onStop() { - String msg = TextUtils.isEmpty(mDisplayName) - ? MARK_STOP - : String.format(MARK_NAMED_STOP, mDisplayName); - if (!mRunning) msg += " (packet listener stopped unexpectedly)"; - mLog.log(msg); - } - - @Override - protected void logError(String msg, Exception e) { - Log.e(mTag, msg, e); - addLogEntry(msg + e); - } - - private void addLogEntry(String entry) { - mLog.log(entry); - } - } -} diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java deleted file mode 100644 index 266b1b047a90..000000000000 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ /dev/null @@ -1,1784 +0,0 @@ -/* - * Copyright (C) 2017 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.ip; - -import static android.net.RouteInfo.RTN_UNICAST; -import static android.net.shared.IpConfigurationParcelableUtil.toStableParcelable; - -import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission; - -import android.annotation.NonNull; -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.DhcpResults; -import android.net.INetd; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NattKeepalivePacketDataParcelable; -import android.net.NetworkStackIpMemoryStore; -import android.net.ProvisioningConfigurationParcelable; -import android.net.ProxyInfo; -import android.net.RouteInfo; -import android.net.TcpKeepalivePacketDataParcelable; -import android.net.apf.ApfCapabilities; -import android.net.apf.ApfFilter; -import android.net.dhcp.DhcpClient; -import android.net.metrics.IpConnectivityLog; -import android.net.metrics.IpManagerEvent; -import android.net.shared.InitialConfiguration; -import android.net.shared.ProvisioningConfiguration; -import android.net.util.InterfaceParams; -import android.net.util.SharedLog; -import android.os.ConditionVariable; -import android.os.IBinder; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.text.TextUtils; -import android.util.LocalLog; -import android.util.Log; -import android.util.Pair; -import android.util.SparseArray; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.IState; -import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.MessageUtils; -import com.android.internal.util.Preconditions; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.internal.util.WakeupMessage; -import com.android.server.NetworkObserverRegistry; -import com.android.server.NetworkStackService.NetworkStackServiceManager; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.net.InetAddress; -import java.util.Collection; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; -import java.util.function.Predicate; -import java.util.stream.Collectors; - - -/** - * IpClient - * - * This class provides the interface to IP-layer provisioning and maintenance - * functionality that can be used by transport layers like Wi-Fi, Ethernet, - * et cetera. - * - * [ Lifetime ] - * IpClient is designed to be instantiated as soon as the interface name is - * known and can be as long-lived as the class containing it (i.e. declaring - * it "private final" is okay). - * - * @hide - */ -public class IpClient extends StateMachine { - private static final boolean DBG = false; - - // For message logging. - private static final Class[] sMessageClasses = { IpClient.class, DhcpClient.class }; - private static final SparseArray<String> sWhatToString = - MessageUtils.findMessageNames(sMessageClasses); - // Two static concurrent hashmaps of interface name to logging classes. - // One holds StateMachine logs and the other connectivity packet logs. - private static final ConcurrentHashMap<String, SharedLog> sSmLogs = new ConcurrentHashMap<>(); - private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>(); - private final NetworkStackIpMemoryStore mIpMemoryStore; - - /** - * Dump all state machine and connectivity packet logs to the specified writer. - * @param skippedIfaces Interfaces for which logs should not be dumped. - */ - public static void dumpAllLogs(PrintWriter writer, Set<String> skippedIfaces) { - for (String ifname : sSmLogs.keySet()) { - if (skippedIfaces.contains(ifname)) continue; - - writer.println(String.format("--- BEGIN %s ---", ifname)); - - final SharedLog smLog = sSmLogs.get(ifname); - if (smLog != null) { - writer.println("State machine log:"); - smLog.dump(null, writer, null); - } - - writer.println(""); - - final LocalLog pktLog = sPktLogs.get(ifname); - if (pktLog != null) { - writer.println("Connectivity packet log:"); - pktLog.readOnlyLocalLog().dump(null, writer, null); - } - - writer.println(String.format("--- END %s ---", ifname)); - } - } - - // Use a wrapper class to log in order to ensure complete and detailed - // logging. This method is lighter weight than annotations/reflection - // and has the following benefits: - // - // - No invoked method can be forgotten. - // Any new method added to IpClient.Callback must be overridden - // here or it will never be called. - // - // - No invoking call site can be forgotten. - // Centralized logging in this way means call sites don't need to - // remember to log, and therefore no call site can be forgotten. - // - // - No variation in log format among call sites. - // Encourages logging of any available arguments, and all call sites - // are necessarily logged identically. - // - // NOTE: Log first because passed objects may or may not be thread-safe and - // once passed on to the callback they may be modified by another thread. - // - // TODO: Find an lighter weight approach. - public static class IpClientCallbacksWrapper { - private static final String PREFIX = "INVOKE "; - private final IIpClientCallbacks mCallback; - private final SharedLog mLog; - - @VisibleForTesting - protected IpClientCallbacksWrapper(IIpClientCallbacks callback, SharedLog log) { - mCallback = callback; - mLog = log; - } - - private void log(String msg) { - mLog.log(PREFIX + msg); - } - - private void log(String msg, Throwable e) { - mLog.e(PREFIX + msg, e); - } - - public void onPreDhcpAction() { - log("onPreDhcpAction()"); - try { - mCallback.onPreDhcpAction(); - } catch (RemoteException e) { - log("Failed to call onPreDhcpAction", e); - } - } - - public void onPostDhcpAction() { - log("onPostDhcpAction()"); - try { - mCallback.onPostDhcpAction(); - } catch (RemoteException e) { - log("Failed to call onPostDhcpAction", e); - } - } - - public void onNewDhcpResults(DhcpResults dhcpResults) { - log("onNewDhcpResults({" + dhcpResults + "})"); - try { - mCallback.onNewDhcpResults(toStableParcelable(dhcpResults)); - } catch (RemoteException e) { - log("Failed to call onNewDhcpResults", e); - } - } - - public void onProvisioningSuccess(LinkProperties newLp) { - log("onProvisioningSuccess({" + newLp + "})"); - try { - mCallback.onProvisioningSuccess(newLp); - } catch (RemoteException e) { - log("Failed to call onProvisioningSuccess", e); - } - } - - public void onProvisioningFailure(LinkProperties newLp) { - log("onProvisioningFailure({" + newLp + "})"); - try { - mCallback.onProvisioningFailure(newLp); - } catch (RemoteException e) { - log("Failed to call onProvisioningFailure", e); - } - } - - public void onLinkPropertiesChange(LinkProperties newLp) { - log("onLinkPropertiesChange({" + newLp + "})"); - try { - mCallback.onLinkPropertiesChange(newLp); - } catch (RemoteException e) { - log("Failed to call onLinkPropertiesChange", e); - } - } - - public void onReachabilityLost(String logMsg) { - log("onReachabilityLost(" + logMsg + ")"); - try { - mCallback.onReachabilityLost(logMsg); - } catch (RemoteException e) { - log("Failed to call onReachabilityLost", e); - } - } - - public void onQuit() { - log("onQuit()"); - try { - mCallback.onQuit(); - } catch (RemoteException e) { - log("Failed to call onQuit", e); - } - } - - public void installPacketFilter(byte[] filter) { - log("installPacketFilter(byte[" + filter.length + "])"); - try { - mCallback.installPacketFilter(filter); - } catch (RemoteException e) { - log("Failed to call installPacketFilter", e); - } - } - - public void startReadPacketFilter() { - log("startReadPacketFilter()"); - try { - mCallback.startReadPacketFilter(); - } catch (RemoteException e) { - log("Failed to call startReadPacketFilter", e); - } - } - - public void setFallbackMulticastFilter(boolean enabled) { - log("setFallbackMulticastFilter(" + enabled + ")"); - try { - mCallback.setFallbackMulticastFilter(enabled); - } catch (RemoteException e) { - log("Failed to call setFallbackMulticastFilter", e); - } - } - - public void setNeighborDiscoveryOffload(boolean enable) { - log("setNeighborDiscoveryOffload(" + enable + ")"); - try { - mCallback.setNeighborDiscoveryOffload(enable); - } catch (RemoteException e) { - log("Failed to call setNeighborDiscoveryOffload", e); - } - } - } - - public static final String DUMP_ARG_CONFIRM = "confirm"; - - // Below constants are picked up by MessageUtils and exempt from ProGuard optimization. - private static final int CMD_TERMINATE_AFTER_STOP = 1; - private static final int CMD_STOP = 2; - private static final int CMD_START = 3; - private static final int CMD_CONFIRM = 4; - private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 5; - // Triggered by NetlinkTracker to communicate netlink events. - private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6; - private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 7; - private static final int CMD_UPDATE_HTTP_PROXY = 8; - private static final int CMD_SET_MULTICAST_FILTER = 9; - private static final int EVENT_PROVISIONING_TIMEOUT = 10; - private static final int EVENT_DHCPACTION_TIMEOUT = 11; - private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12; - private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13; - private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14; - private static final int CMD_UPDATE_L2KEY_GROUPHINT = 15; - - // Internal commands to use instead of trying to call transitionTo() inside - // a given State's enter() method. Calling transitionTo() from enter/exit - // encounters a Log.wtf() that can cause trouble on eng builds. - private static final int CMD_JUMP_STARTED_TO_RUNNING = 100; - private static final int CMD_JUMP_RUNNING_TO_STOPPING = 101; - private static final int CMD_JUMP_STOPPING_TO_STOPPED = 102; - - // IpClient shares a handler with DhcpClient: commands must not overlap - public static final int DHCPCLIENT_CMD_BASE = 1000; - - private static final int MAX_LOG_RECORDS = 500; - private static final int MAX_PACKET_RECORDS = 100; - - private static final boolean NO_CALLBACKS = false; - private static final boolean SEND_CALLBACKS = true; - - // This must match the interface prefix in clatd.c. - // TODO: Revert this hack once IpClient and Nat464Xlat work in concert. - private static final String CLAT_PREFIX = "v4-"; - - private static final int IMMEDIATE_FAILURE_DURATION = 0; - - private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1; - private static final int PROV_CHANGE_LOST_PROVISIONING = 2; - private static final int PROV_CHANGE_GAINED_PROVISIONING = 3; - private static final int PROV_CHANGE_STILL_PROVISIONED = 4; - - private final State mStoppedState = new StoppedState(); - private final State mStoppingState = new StoppingState(); - private final State mStartedState = new StartedState(); - private final State mRunningState = new RunningState(); - - private final String mTag; - private final Context mContext; - private final String mInterfaceName; - private final String mClatInterfaceName; - @VisibleForTesting - protected final IpClientCallbacksWrapper mCallback; - private final Dependencies mDependencies; - private final CountDownLatch mShutdownLatch; - private final ConnectivityManager mCm; - private final INetd mNetd; - private final NetworkObserverRegistry mObserverRegistry; - private final IpClientLinkObserver mLinkObserver; - private final WakeupMessage mProvisioningTimeoutAlarm; - private final WakeupMessage mDhcpActionTimeoutAlarm; - private final SharedLog mLog; - private final LocalLog mConnectivityPacketLog; - private final MessageHandlingLogger mMsgStateLogger; - private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); - private final InterfaceController mInterfaceCtrl; - - private InterfaceParams mInterfaceParams; - - /** - * Non-final member variables accessed only from within our StateMachine. - */ - private LinkProperties mLinkProperties; - private android.net.shared.ProvisioningConfiguration mConfiguration; - private IpReachabilityMonitor mIpReachabilityMonitor; - private DhcpClient mDhcpClient; - private DhcpResults mDhcpResults; - private String mTcpBufferSizes; - private ProxyInfo mHttpProxy; - private ApfFilter mApfFilter; - private String mL2Key; // The L2 key for this network, for writing into the memory store - private String mGroupHint; // The group hint for this network, for writing into the memory store - private boolean mMulticastFiltering; - private long mStartTimeMillis; - - /** - * Reading the snapshot is an asynchronous operation initiated by invoking - * Callback.startReadPacketFilter() and completed when the WiFi Service responds with an - * EVENT_READ_PACKET_FILTER_COMPLETE message. The mApfDataSnapshotComplete condition variable - * signals when a new snapshot is ready. - */ - private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable(); - - public static class Dependencies { - /** - * Get interface parameters for the specified interface. - */ - public InterfaceParams getInterfaceParams(String ifname) { - return InterfaceParams.getByName(ifname); - } - - /** - * Get a INetd connector. - */ - public INetd getNetd(Context context) { - return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)); - } - } - - public IpClient(Context context, String ifName, IIpClientCallbacks callback, - NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager) { - this(context, ifName, callback, observerRegistry, nssManager, new Dependencies()); - } - - @VisibleForTesting - IpClient(Context context, String ifName, IIpClientCallbacks callback, - NetworkObserverRegistry observerRegistry, NetworkStackServiceManager nssManager, - Dependencies deps) { - super(IpClient.class.getSimpleName() + "." + ifName); - Preconditions.checkNotNull(ifName); - Preconditions.checkNotNull(callback); - - mTag = getName(); - - mContext = context; - mInterfaceName = ifName; - mClatInterfaceName = CLAT_PREFIX + ifName; - mDependencies = deps; - mShutdownLatch = new CountDownLatch(1); - mCm = mContext.getSystemService(ConnectivityManager.class); - mObserverRegistry = observerRegistry; - mIpMemoryStore = - new NetworkStackIpMemoryStore(context, nssManager.getIpMemoryStoreService()); - - sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag)); - mLog = sSmLogs.get(mInterfaceName); - sPktLogs.putIfAbsent(mInterfaceName, new LocalLog(MAX_PACKET_RECORDS)); - mConnectivityPacketLog = sPktLogs.get(mInterfaceName); - mMsgStateLogger = new MessageHandlingLogger(); - mCallback = new IpClientCallbacksWrapper(callback, mLog); - - // TODO: Consider creating, constructing, and passing in some kind of - // InterfaceController.Dependencies class. - mNetd = deps.getNetd(mContext); - mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog); - - mLinkObserver = new IpClientLinkObserver( - mInterfaceName, - () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED)) { - @Override - public void onInterfaceAdded(String iface) { - super.onInterfaceAdded(iface); - if (mClatInterfaceName.equals(iface)) { - mCallback.setNeighborDiscoveryOffload(false); - } else if (!mInterfaceName.equals(iface)) { - return; - } - - final String msg = "interfaceAdded(" + iface + ")"; - logMsg(msg); - } - - @Override - public void onInterfaceRemoved(String iface) { - super.onInterfaceRemoved(iface); - // TODO: Also observe mInterfaceName going down and take some - // kind of appropriate action. - if (mClatInterfaceName.equals(iface)) { - // TODO: consider sending a message to the IpClient main - // StateMachine thread, in case "NDO enabled" state becomes - // tied to more things that 464xlat operation. - mCallback.setNeighborDiscoveryOffload(true); - } else if (!mInterfaceName.equals(iface)) { - return; - } - - final String msg = "interfaceRemoved(" + iface + ")"; - logMsg(msg); - } - - private void logMsg(String msg) { - Log.d(mTag, msg); - getHandler().post(() -> mLog.log("OBSERVED " + msg)); - } - }; - - mLinkProperties = new LinkProperties(); - mLinkProperties.setInterfaceName(mInterfaceName); - - mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(), - mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT); - mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(), - mTag + ".EVENT_DHCPACTION_TIMEOUT", EVENT_DHCPACTION_TIMEOUT); - - // Anything the StateMachine may access must have been instantiated - // before this point. - configureAndStartStateMachine(); - - // Anything that may send messages to the StateMachine must only be - // configured to do so after the StateMachine has started (above). - startStateMachineUpdaters(); - } - - /** - * Make a IIpClient connector to communicate with this IpClient. - */ - public IIpClient makeConnector() { - return new IpClientConnector(); - } - - class IpClientConnector extends IIpClient.Stub { - @Override - public void completedPreDhcpAction() { - checkNetworkStackCallingPermission(); - IpClient.this.completedPreDhcpAction(); - } - @Override - public void confirmConfiguration() { - checkNetworkStackCallingPermission(); - IpClient.this.confirmConfiguration(); - } - @Override - public void readPacketFilterComplete(byte[] data) { - checkNetworkStackCallingPermission(); - IpClient.this.readPacketFilterComplete(data); - } - @Override - public void shutdown() { - checkNetworkStackCallingPermission(); - IpClient.this.shutdown(); - } - @Override - public void startProvisioning(ProvisioningConfigurationParcelable req) { - checkNetworkStackCallingPermission(); - IpClient.this.startProvisioning(ProvisioningConfiguration.fromStableParcelable(req)); - } - @Override - public void stop() { - checkNetworkStackCallingPermission(); - IpClient.this.stop(); - } - @Override - public void setL2KeyAndGroupHint(String l2Key, String groupHint) { - checkNetworkStackCallingPermission(); - IpClient.this.setL2KeyAndGroupHint(l2Key, groupHint); - } - @Override - public void setTcpBufferSizes(String tcpBufferSizes) { - checkNetworkStackCallingPermission(); - IpClient.this.setTcpBufferSizes(tcpBufferSizes); - } - @Override - public void setHttpProxy(ProxyInfo proxyInfo) { - checkNetworkStackCallingPermission(); - IpClient.this.setHttpProxy(proxyInfo); - } - @Override - public void setMulticastFilter(boolean enabled) { - checkNetworkStackCallingPermission(); - IpClient.this.setMulticastFilter(enabled); - } - @Override - public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) { - checkNetworkStackCallingPermission(); - IpClient.this.addKeepalivePacketFilter(slot, pkt); - } - @Override - public void addNattKeepalivePacketFilter(int slot, NattKeepalivePacketDataParcelable pkt) { - checkNetworkStackCallingPermission(); - IpClient.this.addNattKeepalivePacketFilter(slot, pkt); - } - @Override - public void removeKeepalivePacketFilter(int slot) { - checkNetworkStackCallingPermission(); - IpClient.this.removeKeepalivePacketFilter(slot); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - } - - public String getInterfaceName() { - return mInterfaceName; - } - - private void configureAndStartStateMachine() { - // CHECKSTYLE:OFF IndentationCheck - addState(mStoppedState); - addState(mStartedState); - addState(mRunningState, mStartedState); - addState(mStoppingState); - // CHECKSTYLE:ON IndentationCheck - - setInitialState(mStoppedState); - - super.start(); - } - - private void startStateMachineUpdaters() { - mObserverRegistry.registerObserverForNonblockingCallback(mLinkObserver); - } - - private void stopStateMachineUpdaters() { - mObserverRegistry.unregisterObserver(mLinkObserver); - } - - @Override - protected void onQuitting() { - mCallback.onQuit(); - mShutdownLatch.countDown(); - } - - /** - * Shut down this IpClient instance altogether. - */ - public void shutdown() { - stop(); - sendMessage(CMD_TERMINATE_AFTER_STOP); - } - - /** - * Start provisioning with the provided parameters. - */ - public void startProvisioning(ProvisioningConfiguration req) { - if (!req.isValid()) { - doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING); - return; - } - - mInterfaceParams = mDependencies.getInterfaceParams(mInterfaceName); - if (mInterfaceParams == null) { - logError("Failed to find InterfaceParams for " + mInterfaceName); - doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND); - return; - } - - mCallback.setNeighborDiscoveryOffload(true); - sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req)); - } - - /** - * Stop this IpClient. - * - * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}. - */ - public void stop() { - sendMessage(CMD_STOP); - } - - /** - * Confirm the provisioning configuration. - */ - public void confirmConfiguration() { - sendMessage(CMD_CONFIRM); - } - - /** - * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be - * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to - * proceed. - */ - public void completedPreDhcpAction() { - sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); - } - - /** - * Indicate that packet filter read is complete. - */ - public void readPacketFilterComplete(byte[] data) { - sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data); - } - - /** - * Set the TCP buffer sizes to use. - * - * This may be called, repeatedly, at any time before or after a call to - * #startProvisioning(). The setting is cleared upon calling #stop(). - */ - public void setTcpBufferSizes(String tcpBufferSizes) { - sendMessage(CMD_UPDATE_TCP_BUFFER_SIZES, tcpBufferSizes); - } - - /** - * Set the L2 key and group hint for storing info into the memory store. - */ - public void setL2KeyAndGroupHint(String l2Key, String groupHint) { - sendMessage(CMD_UPDATE_L2KEY_GROUPHINT, new Pair<>(l2Key, groupHint)); - } - - /** - * Set the HTTP Proxy configuration to use. - * - * This may be called, repeatedly, at any time before or after a call to - * #startProvisioning(). The setting is cleared upon calling #stop(). - */ - public void setHttpProxy(ProxyInfo proxyInfo) { - sendMessage(CMD_UPDATE_HTTP_PROXY, proxyInfo); - } - - /** - * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering, - * if not, Callback.setFallbackMulticastFilter() is called. - */ - public void setMulticastFilter(boolean enabled) { - sendMessage(CMD_SET_MULTICAST_FILTER, enabled); - } - - /** - * Called by WifiStateMachine to add TCP keepalive packet filter before setting up - * keepalive offload. - */ - public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) { - sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt); - } - - /** - * Called by WifiStateMachine to add NATT keepalive packet filter before setting up - * keepalive offload. - */ - public void addNattKeepalivePacketFilter(int slot, - @NonNull NattKeepalivePacketDataParcelable pkt) { - sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */ , pkt); - } - - /** - * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive - * offload. - */ - public void removeKeepalivePacketFilter(int slot) { - sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */); - } - - /** - * Dump logs of this IpClient. - */ - public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) { - // Execute confirmConfiguration() and take no further action. - confirmConfiguration(); - return; - } - - // Thread-unsafe access to mApfFilter but just used for debugging. - final ApfFilter apfFilter = mApfFilter; - final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration; - final ApfCapabilities apfCapabilities = (provisioningConfig != null) - ? provisioningConfig.mApfCapabilities : null; - - IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); - pw.println(mTag + " APF dump:"); - pw.increaseIndent(); - if (apfFilter != null) { - if (apfCapabilities.hasDataAccess()) { - // Request a new snapshot, then wait for it. - mApfDataSnapshotComplete.close(); - mCallback.startReadPacketFilter(); - if (!mApfDataSnapshotComplete.block(1000)) { - pw.print("TIMEOUT: DUMPING STALE APF SNAPSHOT"); - } - } - apfFilter.dump(pw); - - } else { - pw.print("No active ApfFilter; "); - if (provisioningConfig == null) { - pw.println("IpClient not yet started."); - } else if (apfCapabilities == null || apfCapabilities.apfVersionSupported == 0) { - pw.println("Hardware does not support APF."); - } else { - pw.println("ApfFilter not yet started, APF capabilities: " + apfCapabilities); - } - } - pw.decreaseIndent(); - pw.println(); - pw.println(mTag + " current ProvisioningConfiguration:"); - pw.increaseIndent(); - pw.println(Objects.toString(provisioningConfig, "N/A")); - pw.decreaseIndent(); - - final IpReachabilityMonitor iprm = mIpReachabilityMonitor; - if (iprm != null) { - pw.println(); - pw.println(mTag + " current IpReachabilityMonitor state:"); - pw.increaseIndent(); - iprm.dump(pw); - pw.decreaseIndent(); - } - - pw.println(); - pw.println(mTag + " StateMachine dump:"); - pw.increaseIndent(); - mLog.dump(fd, pw, args); - pw.decreaseIndent(); - - pw.println(); - pw.println(mTag + " connectivity packet log:"); - pw.println(); - pw.println("Debug with python and scapy via:"); - pw.println("shell$ python"); - pw.println(">>> from scapy import all as scapy"); - pw.println(">>> scapy.Ether(\"<paste_hex_string>\".decode(\"hex\")).show2()"); - pw.println(); - - pw.increaseIndent(); - mConnectivityPacketLog.readOnlyLocalLog().dump(fd, pw, args); - pw.decreaseIndent(); - } - - - /** - * Internals. - */ - - @Override - protected String getWhatToString(int what) { - return sWhatToString.get(what, "UNKNOWN: " + Integer.toString(what)); - } - - @Override - protected String getLogRecString(Message msg) { - final String logLine = String.format( - "%s/%d %d %d %s [%s]", - mInterfaceName, (mInterfaceParams == null) ? -1 : mInterfaceParams.index, - msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger); - - final String richerLogLine = getWhatToString(msg.what) + " " + logLine; - mLog.log(richerLogLine); - if (DBG) { - Log.d(mTag, richerLogLine); - } - - mMsgStateLogger.reset(); - return logLine; - } - - @Override - protected boolean recordLogRec(Message msg) { - // Don't log EVENT_NETLINK_LINKPROPERTIES_CHANGED. They can be noisy, - // and we already log any LinkProperties change that results in an - // invocation of IpClient.Callback#onLinkPropertiesChange(). - final boolean shouldLog = (msg.what != EVENT_NETLINK_LINKPROPERTIES_CHANGED); - if (!shouldLog) { - mMsgStateLogger.reset(); - } - return shouldLog; - } - - private void logError(String fmt, Object... args) { - final String msg = "ERROR " + String.format(fmt, args); - Log.e(mTag, msg); - mLog.log(msg); - } - - // This needs to be called with care to ensure that our LinkProperties - // are in sync with the actual LinkProperties of the interface. For example, - // we should only call this if we know for sure that there are no IP addresses - // assigned to the interface, etc. - private void resetLinkProperties() { - mLinkObserver.clearLinkProperties(); - mConfiguration = null; - mDhcpResults = null; - mTcpBufferSizes = ""; - mHttpProxy = null; - - mLinkProperties = new LinkProperties(); - mLinkProperties.setInterfaceName(mInterfaceName); - } - - private void recordMetric(final int type) { - // We may record error metrics prior to starting. - // Map this to IMMEDIATE_FAILURE_DURATION. - final long duration = (mStartTimeMillis > 0) - ? (SystemClock.elapsedRealtime() - mStartTimeMillis) - : IMMEDIATE_FAILURE_DURATION; - mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration)); - } - - // For now: use WifiStateMachine's historical notion of provisioned. - @VisibleForTesting - static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) { - // For historical reasons, we should connect even if all we have is - // an IPv4 address and nothing else. - if (lp.hasIpv4Address() || lp.isProvisioned()) { - return true; - } - if (config == null) { - return false; - } - - // When an InitialConfiguration is specified, ignore any difference with previous - // properties and instead check if properties observed match the desired properties. - return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes()); - } - - // TODO: Investigate folding all this into the existing static function - // LinkProperties.compareProvisioning() or some other single function that - // takes two LinkProperties objects and returns a ProvisioningChange - // object that is a correct and complete assessment of what changed, taking - // account of the asymmetries described in the comments in this function. - // Then switch to using it everywhere (IpReachabilityMonitor, etc.). - private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) { - int delta; - InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null; - final boolean wasProvisioned = isProvisioned(oldLp, config); - final boolean isProvisioned = isProvisioned(newLp, config); - - if (!wasProvisioned && isProvisioned) { - delta = PROV_CHANGE_GAINED_PROVISIONING; - } else if (wasProvisioned && isProvisioned) { - delta = PROV_CHANGE_STILL_PROVISIONED; - } else if (!wasProvisioned && !isProvisioned) { - delta = PROV_CHANGE_STILL_NOT_PROVISIONED; - } else { - // (wasProvisioned && !isProvisioned) - // - // Note that this is true even if we lose a configuration element - // (e.g., a default gateway) that would not be required to advance - // into provisioned state. This is intended: if we have a default - // router and we lose it, that's a sure sign of a problem, but if - // we connect to a network with no IPv4 DNS servers, we consider - // that to be a network without DNS servers and connect anyway. - // - // See the comment below. - delta = PROV_CHANGE_LOST_PROVISIONING; - } - - final boolean lostIPv6 = oldLp.isIpv6Provisioned() && !newLp.isIpv6Provisioned(); - final boolean lostIPv4Address = oldLp.hasIpv4Address() && !newLp.hasIpv4Address(); - final boolean lostIPv6Router = oldLp.hasIpv6DefaultRoute() && !newLp.hasIpv6DefaultRoute(); - - // If bad wifi avoidance is disabled, then ignore IPv6 loss of - // provisioning. Otherwise, when a hotspot that loses Internet - // access sends out a 0-lifetime RA to its clients, the clients - // will disconnect and then reconnect, avoiding the bad hotspot, - // instead of getting stuck on the bad hotspot. http://b/31827713 . - // - // This is incorrect because if the hotspot then regains Internet - // access with a different prefix, TCP connections on the - // deprecated addresses will remain stuck. - // - // Note that we can still be disconnected by IpReachabilityMonitor - // if the IPv6 default gateway (but not the IPv6 DNS servers; see - // accompanying code in IpReachabilityMonitor) is unreachable. - final boolean ignoreIPv6ProvisioningLoss = - mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker - && mCm.shouldAvoidBadWifi(); - - // Additionally: - // - // Partial configurations (e.g., only an IPv4 address with no DNS - // servers and no default route) are accepted as long as DHCPv4 - // succeeds. On such a network, isProvisioned() will always return - // false, because the configuration is not complete, but we want to - // connect anyway. It might be a disconnected network such as a - // Chromecast or a wireless printer, for example. - // - // Because on such a network isProvisioned() will always return false, - // delta will never be LOST_PROVISIONING. So check for loss of - // provisioning here too. - if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) { - delta = PROV_CHANGE_LOST_PROVISIONING; - } - - // Additionally: - // - // If the previous link properties had a global IPv6 address and an - // IPv6 default route then also consider the loss of that default route - // to be a loss of provisioning. See b/27962810. - if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) { - delta = PROV_CHANGE_LOST_PROVISIONING; - } - - return delta; - } - - private void dispatchCallback(int delta, LinkProperties newLp) { - switch (delta) { - case PROV_CHANGE_GAINED_PROVISIONING: - if (DBG) { - Log.d(mTag, "onProvisioningSuccess()"); - } - recordMetric(IpManagerEvent.PROVISIONING_OK); - mCallback.onProvisioningSuccess(newLp); - break; - - case PROV_CHANGE_LOST_PROVISIONING: - if (DBG) { - Log.d(mTag, "onProvisioningFailure()"); - } - recordMetric(IpManagerEvent.PROVISIONING_FAIL); - mCallback.onProvisioningFailure(newLp); - break; - - default: - if (DBG) { - Log.d(mTag, "onLinkPropertiesChange()"); - } - mCallback.onLinkPropertiesChange(newLp); - break; - } - } - - // Updates all IpClient-related state concerned with LinkProperties. - // Returns a ProvisioningChange for possibly notifying other interested - // parties that are not fronted by IpClient. - private int setLinkProperties(LinkProperties newLp) { - if (mApfFilter != null) { - mApfFilter.setLinkProperties(newLp); - } - if (mIpReachabilityMonitor != null) { - mIpReachabilityMonitor.updateLinkProperties(newLp); - } - - int delta = compareProvisioning(mLinkProperties, newLp); - mLinkProperties = new LinkProperties(newLp); - - if (delta == PROV_CHANGE_GAINED_PROVISIONING) { - // TODO: Add a proper ProvisionedState and cancel the alarm in - // its enter() method. - mProvisioningTimeoutAlarm.cancel(); - } - - return delta; - } - - private LinkProperties assembleLinkProperties() { - // [1] Create a new LinkProperties object to populate. - LinkProperties newLp = new LinkProperties(); - newLp.setInterfaceName(mInterfaceName); - - // [2] Pull in data from netlink: - // - IPv4 addresses - // - IPv6 addresses - // - IPv6 routes - // - IPv6 DNS servers - // - // N.B.: this is fundamentally race-prone and should be fixed by - // changing IpClientLinkObserver from a hybrid edge/level model to an - // edge-only model, or by giving IpClient its own netlink socket(s) - // so as to track all required information directly. - LinkProperties netlinkLinkProperties = mLinkObserver.getLinkProperties(); - newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses()); - for (RouteInfo route : netlinkLinkProperties.getRoutes()) { - newLp.addRoute(route); - } - addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers()); - - // [3] Add in data from DHCPv4, if available. - // - // mDhcpResults is never shared with any other owner so we don't have - // to worry about concurrent modification. - if (mDhcpResults != null) { - final List<RouteInfo> routes = - mDhcpResults.toStaticIpConfiguration().getRoutes(mInterfaceName); - for (RouteInfo route : routes) { - newLp.addRoute(route); - } - addAllReachableDnsServers(newLp, mDhcpResults.dnsServers); - newLp.setDomains(mDhcpResults.domains); - - if (mDhcpResults.mtu != 0) { - newLp.setMtu(mDhcpResults.mtu); - } - } - - // [4] Add in TCP buffer sizes and HTTP Proxy config, if available. - if (!TextUtils.isEmpty(mTcpBufferSizes)) { - newLp.setTcpBufferSizes(mTcpBufferSizes); - } - if (mHttpProxy != null) { - newLp.setHttpProxy(mHttpProxy); - } - - // [5] Add data from InitialConfiguration - if (mConfiguration != null && mConfiguration.mInitialConfig != null) { - InitialConfiguration config = mConfiguration.mInitialConfig; - // Add InitialConfiguration routes and dns server addresses once all addresses - // specified in the InitialConfiguration have been observed with Netlink. - if (config.isProvisionedBy(newLp.getLinkAddresses(), null)) { - for (IpPrefix prefix : config.directlyConnectedRoutes) { - newLp.addRoute(new RouteInfo(prefix, null, mInterfaceName, RTN_UNICAST)); - } - } - addAllReachableDnsServers(newLp, config.dnsServers); - } - final LinkProperties oldLp = mLinkProperties; - if (DBG) { - Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s", - netlinkLinkProperties, newLp, oldLp)); - } - - // TODO: also learn via netlink routes specified by an InitialConfiguration and specified - // from a static IP v4 config instead of manually patching them in in steps [3] and [5]. - return newLp; - } - - private static void addAllReachableDnsServers( - LinkProperties lp, Iterable<InetAddress> dnses) { - // TODO: Investigate deleting this reachability check. We should be - // able to pass everything down to netd and let netd do evaluation - // and RFC6724-style sorting. - for (InetAddress dns : dnses) { - if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) { - lp.addDnsServer(dns); - } - } - } - - // Returns false if we have lost provisioning, true otherwise. - private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) { - final LinkProperties newLp = assembleLinkProperties(); - if (Objects.equals(newLp, mLinkProperties)) { - return true; - } - final int delta = setLinkProperties(newLp); - // Most of the attributes stored in the memory store are deduced from - // the link properties, therefore when the properties update the memory - // store record should be updated too. - maybeSaveNetworkToIpMemoryStore(); - if (sendCallbacks) { - dispatchCallback(delta, newLp); - } - return (delta != PROV_CHANGE_LOST_PROVISIONING); - } - - private void handleIPv4Success(DhcpResults dhcpResults) { - mDhcpResults = new DhcpResults(dhcpResults); - final LinkProperties newLp = assembleLinkProperties(); - final int delta = setLinkProperties(newLp); - - if (DBG) { - Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); - } - mCallback.onNewDhcpResults(dhcpResults); - maybeSaveNetworkToIpMemoryStore(); - dispatchCallback(delta, newLp); - } - - private void handleIPv4Failure() { - // TODO: Investigate deleting this clearIPv4Address() call. - // - // DhcpClient will send us CMD_CLEAR_LINKADDRESS in all circumstances - // that could trigger a call to this function. If we missed handling - // that message in StartedState for some reason we would still clear - // any addresses upon entry to StoppedState. - mInterfaceCtrl.clearIPv4Address(); - mDhcpResults = null; - if (DBG) { - Log.d(mTag, "onNewDhcpResults(null)"); - } - mCallback.onNewDhcpResults(null); - - handleProvisioningFailure(); - } - - private void handleProvisioningFailure() { - final LinkProperties newLp = assembleLinkProperties(); - int delta = setLinkProperties(newLp); - // If we've gotten here and we're still not provisioned treat that as - // a total loss of provisioning. - // - // Either (a) static IP configuration failed or (b) DHCPv4 failed AND - // there was no usable IPv6 obtained before a non-zero provisioning - // timeout expired. - // - // Regardless: GAME OVER. - if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) { - delta = PROV_CHANGE_LOST_PROVISIONING; - } - - dispatchCallback(delta, newLp); - if (delta == PROV_CHANGE_LOST_PROVISIONING) { - transitionTo(mStoppingState); - } - } - - private void doImmediateProvisioningFailure(int failureType) { - logError("onProvisioningFailure(): %s", failureType); - recordMetric(failureType); - mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties)); - } - - private boolean startIPv4() { - // If we have a StaticIpConfiguration attempt to apply it and - // handle the result accordingly. - if (mConfiguration.mStaticIpConfig != null) { - if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.getIpAddress())) { - handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig)); - } else { - return false; - } - } else { - // Start DHCPv4. - mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpClient.this, mInterfaceParams); - mDhcpClient.registerForPreDhcpNotification(); - mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); - } - - return true; - } - - private boolean startIPv6() { - return mInterfaceCtrl.setIPv6PrivacyExtensions(true) - && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode) - && mInterfaceCtrl.enableIPv6(); - } - - private boolean applyInitialConfig(InitialConfiguration config) { - // TODO: also support specifying a static IPv4 configuration in InitialConfiguration. - for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIpv6)) { - if (!mInterfaceCtrl.addAddress(addr)) return false; - } - - return true; - } - - private boolean startIpReachabilityMonitor() { - try { - // TODO: Fetch these parameters from settings, and install a - // settings observer to watch for update and re-program these - // parameters (Q: is this level of dynamic updatability really - // necessary or does reading from settings at startup suffice?). - final int numSolicits = 5; - final int interSolicitIntervalMs = 750; - setNeighborParameters(mNetd, mInterfaceName, numSolicits, interSolicitIntervalMs); - } catch (Exception e) { - mLog.e("Failed to adjust neighbor parameters", e); - // Carry on using the system defaults (currently: 3, 1000); - } - - try { - mIpReachabilityMonitor = new IpReachabilityMonitor( - mContext, - mInterfaceParams, - getHandler(), - mLog, - new IpReachabilityMonitor.Callback() { - @Override - public void notifyLost(InetAddress ip, String logMsg) { - mCallback.onReachabilityLost(logMsg); - } - }, - mConfiguration.mUsingMultinetworkPolicyTracker); - } catch (IllegalArgumentException iae) { - // Failed to start IpReachabilityMonitor. Log it and call - // onProvisioningFailure() immediately. - // - // See http://b/31038971. - logError("IpReachabilityMonitor failure: %s", iae); - mIpReachabilityMonitor = null; - } - - return (mIpReachabilityMonitor != null); - } - - private void stopAllIP() { - // We don't need to worry about routes, just addresses, because: - // - disableIpv6() will clear autoconf IPv6 routes as well, and - // - we don't get IPv4 routes from netlink - // so we neither react to nor need to wait for changes in either. - - mInterfaceCtrl.disableIPv6(); - mInterfaceCtrl.clearAllAddresses(); - } - - private void maybeSaveNetworkToIpMemoryStore() { - // TODO : implement this - } - - class StoppedState extends State { - @Override - public void enter() { - stopAllIP(); - - resetLinkProperties(); - if (mStartTimeMillis > 0) { - // Completed a life-cycle; send a final empty LinkProperties - // (cleared in resetLinkProperties() above) and record an event. - mCallback.onLinkPropertiesChange(new LinkProperties(mLinkProperties)); - recordMetric(IpManagerEvent.COMPLETE_LIFECYCLE); - mStartTimeMillis = 0; - } - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_TERMINATE_AFTER_STOP: - stopStateMachineUpdaters(); - quit(); - break; - - case CMD_STOP: - break; - - case CMD_START: - mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj; - transitionTo(mStartedState); - break; - - case EVENT_NETLINK_LINKPROPERTIES_CHANGED: - handleLinkPropertiesUpdate(NO_CALLBACKS); - break; - - case CMD_UPDATE_TCP_BUFFER_SIZES: - mTcpBufferSizes = (String) msg.obj; - handleLinkPropertiesUpdate(NO_CALLBACKS); - break; - - case CMD_UPDATE_HTTP_PROXY: - mHttpProxy = (ProxyInfo) msg.obj; - handleLinkPropertiesUpdate(NO_CALLBACKS); - break; - - case CMD_UPDATE_L2KEY_GROUPHINT: { - final Pair<String, String> args = (Pair<String, String>) msg.obj; - mL2Key = args.first; - mGroupHint = args.second; - break; - } - - case CMD_SET_MULTICAST_FILTER: - mMulticastFiltering = (boolean) msg.obj; - break; - - case DhcpClient.CMD_ON_QUIT: - // Everything is already stopped. - logError("Unexpected CMD_ON_QUIT (already stopped)."); - break; - - default: - return NOT_HANDLED; - } - - mMsgStateLogger.handled(this, getCurrentState()); - return HANDLED; - } - } - - class StoppingState extends State { - @Override - public void enter() { - if (mDhcpClient == null) { - // There's no DHCPv4 for which to wait; proceed to stopped. - deferMessage(obtainMessage(CMD_JUMP_STOPPING_TO_STOPPED)); - } - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_JUMP_STOPPING_TO_STOPPED: - transitionTo(mStoppedState); - break; - - case CMD_STOP: - break; - - case DhcpClient.CMD_CLEAR_LINKADDRESS: - mInterfaceCtrl.clearIPv4Address(); - break; - - case DhcpClient.CMD_ON_QUIT: - mDhcpClient = null; - transitionTo(mStoppedState); - break; - - default: - deferMessage(msg); - } - - mMsgStateLogger.handled(this, getCurrentState()); - return HANDLED; - } - } - - class StartedState extends State { - @Override - public void enter() { - mStartTimeMillis = SystemClock.elapsedRealtime(); - - if (mConfiguration.mProvisioningTimeoutMs > 0) { - final long alarmTime = SystemClock.elapsedRealtime() - + mConfiguration.mProvisioningTimeoutMs; - mProvisioningTimeoutAlarm.schedule(alarmTime); - } - - if (readyToProceed()) { - deferMessage(obtainMessage(CMD_JUMP_STARTED_TO_RUNNING)); - } else { - // Clear all IPv4 and IPv6 before proceeding to RunningState. - // Clean up any leftover state from an abnormal exit from - // tethering or during an IpClient restart. - stopAllIP(); - } - } - - @Override - public void exit() { - mProvisioningTimeoutAlarm.cancel(); - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_JUMP_STARTED_TO_RUNNING: - transitionTo(mRunningState); - break; - - case CMD_STOP: - transitionTo(mStoppingState); - break; - - case EVENT_NETLINK_LINKPROPERTIES_CHANGED: - handleLinkPropertiesUpdate(NO_CALLBACKS); - if (readyToProceed()) { - transitionTo(mRunningState); - } - break; - - case CMD_UPDATE_L2KEY_GROUPHINT: { - final Pair<String, String> args = (Pair<String, String>) msg.obj; - mL2Key = args.first; - mGroupHint = args.second; - // TODO : attributes should be saved to the memory store with - // these new values if they differ from the previous ones. - // If the state machine is in pure StartedState, then the values to input - // are not known yet and should be updated when the LinkProperties are updated. - // If the state machine is in RunningState (which is a child of StartedState) - // then the next NUD check should be used to store the new values to avoid - // inputting current values for what may be a different L3 network. - break; - } - - case EVENT_PROVISIONING_TIMEOUT: - handleProvisioningFailure(); - break; - - default: - // It's safe to process messages out of order because the - // only message that can both - // a) be received at this time and - // b) affect provisioning state - // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above). - deferMessage(msg); - } - - mMsgStateLogger.handled(this, getCurrentState()); - return HANDLED; - } - - private boolean readyToProceed() { - return (!mLinkProperties.hasIpv4Address() && !mLinkProperties.hasGlobalIpv6Address()); - } - } - - class RunningState extends State { - private ConnectivityPacketTracker mPacketTracker; - private boolean mDhcpActionInFlight; - - @Override - public void enter() { - ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration(); - apfConfig.apfCapabilities = mConfiguration.mApfCapabilities; - apfConfig.multicastFilter = mMulticastFiltering; - // Get the Configuration for ApfFilter from Context - apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames(); - apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList(); - mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback); - // TODO: investigate the effects of any multicast filtering racing/interfering with the - // rest of this IP configuration startup. - if (mApfFilter == null) { - mCallback.setFallbackMulticastFilter(mMulticastFiltering); - } - - mPacketTracker = createPacketTracker(); - if (mPacketTracker != null) mPacketTracker.start(mConfiguration.mDisplayName); - - if (mConfiguration.mEnableIPv6 && !startIPv6()) { - doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); - enqueueJumpToStoppingState(); - return; - } - - if (mConfiguration.mEnableIPv4 && !startIPv4()) { - doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4); - enqueueJumpToStoppingState(); - return; - } - - final InitialConfiguration config = mConfiguration.mInitialConfig; - if ((config != null) && !applyInitialConfig(config)) { - // TODO introduce a new IpManagerEvent constant to distinguish this error case. - doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING); - enqueueJumpToStoppingState(); - return; - } - - if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) { - doImmediateProvisioningFailure( - IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR); - enqueueJumpToStoppingState(); - return; - } - } - - @Override - public void exit() { - stopDhcpAction(); - - if (mIpReachabilityMonitor != null) { - mIpReachabilityMonitor.stop(); - mIpReachabilityMonitor = null; - } - - if (mDhcpClient != null) { - mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP); - mDhcpClient.doQuit(); - } - - if (mPacketTracker != null) { - mPacketTracker.stop(); - mPacketTracker = null; - } - - if (mApfFilter != null) { - mApfFilter.shutdown(); - mApfFilter = null; - } - - resetLinkProperties(); - } - - private void enqueueJumpToStoppingState() { - deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING)); - } - - private ConnectivityPacketTracker createPacketTracker() { - try { - return new ConnectivityPacketTracker( - getHandler(), mInterfaceParams, mConnectivityPacketLog); - } catch (IllegalArgumentException e) { - return null; - } - } - - private void ensureDhcpAction() { - if (!mDhcpActionInFlight) { - mCallback.onPreDhcpAction(); - mDhcpActionInFlight = true; - final long alarmTime = SystemClock.elapsedRealtime() - + mConfiguration.mRequestedPreDhcpActionMs; - mDhcpActionTimeoutAlarm.schedule(alarmTime); - } - } - - private void stopDhcpAction() { - mDhcpActionTimeoutAlarm.cancel(); - if (mDhcpActionInFlight) { - mCallback.onPostDhcpAction(); - mDhcpActionInFlight = false; - } - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_JUMP_RUNNING_TO_STOPPING: - case CMD_STOP: - transitionTo(mStoppingState); - break; - - case CMD_START: - logError("ALERT: START received in StartedState. Please fix caller."); - break; - - case CMD_CONFIRM: - // TODO: Possibly introduce a second type of confirmation - // that both probes (a) on-link neighbors and (b) does - // a DHCPv4 RENEW. We used to do this on Wi-Fi framework - // roams. - if (mIpReachabilityMonitor != null) { - mIpReachabilityMonitor.probeAll(); - } - break; - - case EVENT_PRE_DHCP_ACTION_COMPLETE: - // It's possible to reach here if, for example, someone - // calls completedPreDhcpAction() after provisioning with - // a static IP configuration. - if (mDhcpClient != null) { - mDhcpClient.sendMessage(DhcpClient.CMD_PRE_DHCP_ACTION_COMPLETE); - } - break; - - case EVENT_NETLINK_LINKPROPERTIES_CHANGED: - if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) { - transitionTo(mStoppingState); - } - break; - - case CMD_UPDATE_TCP_BUFFER_SIZES: - mTcpBufferSizes = (String) msg.obj; - // This cannot possibly change provisioning state. - handleLinkPropertiesUpdate(SEND_CALLBACKS); - break; - - case CMD_UPDATE_HTTP_PROXY: - mHttpProxy = (ProxyInfo) msg.obj; - // This cannot possibly change provisioning state. - handleLinkPropertiesUpdate(SEND_CALLBACKS); - break; - - case CMD_SET_MULTICAST_FILTER: { - mMulticastFiltering = (boolean) msg.obj; - if (mApfFilter != null) { - mApfFilter.setMulticastFilter(mMulticastFiltering); - } else { - mCallback.setFallbackMulticastFilter(mMulticastFiltering); - } - break; - } - - case EVENT_READ_PACKET_FILTER_COMPLETE: { - if (mApfFilter != null) { - mApfFilter.setDataSnapshot((byte[]) msg.obj); - } - mApfDataSnapshotComplete.open(); - break; - } - - case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: { - final int slot = msg.arg1; - - if (mApfFilter != null) { - if (msg.obj instanceof NattKeepalivePacketDataParcelable) { - mApfFilter.addNattKeepalivePacketFilter(slot, - (NattKeepalivePacketDataParcelable) msg.obj); - } else if (msg.obj instanceof TcpKeepalivePacketDataParcelable) { - mApfFilter.addTcpKeepalivePacketFilter(slot, - (TcpKeepalivePacketDataParcelable) msg.obj); - } - } - break; - } - - case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: { - final int slot = msg.arg1; - if (mApfFilter != null) { - mApfFilter.removeKeepalivePacketFilter(slot); - } - break; - } - - case EVENT_DHCPACTION_TIMEOUT: - stopDhcpAction(); - break; - - case DhcpClient.CMD_PRE_DHCP_ACTION: - if (mConfiguration.mRequestedPreDhcpActionMs > 0) { - ensureDhcpAction(); - } else { - sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE); - } - break; - - case DhcpClient.CMD_CLEAR_LINKADDRESS: - mInterfaceCtrl.clearIPv4Address(); - break; - - case DhcpClient.CMD_CONFIGURE_LINKADDRESS: { - final LinkAddress ipAddress = (LinkAddress) msg.obj; - if (mInterfaceCtrl.setIPv4Address(ipAddress)) { - mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED); - } else { - logError("Failed to set IPv4 address."); - dispatchCallback(PROV_CHANGE_LOST_PROVISIONING, - new LinkProperties(mLinkProperties)); - transitionTo(mStoppingState); - } - break; - } - - // This message is only received when: - // - // a) initial address acquisition succeeds, - // b) renew succeeds or is NAK'd, - // c) rebind succeeds or is NAK'd, or - // c) the lease expires, - // - // but never when initial address acquisition fails. The latter - // condition is now governed by the provisioning timeout. - case DhcpClient.CMD_POST_DHCP_ACTION: - stopDhcpAction(); - - switch (msg.arg1) { - case DhcpClient.DHCP_SUCCESS: - handleIPv4Success((DhcpResults) msg.obj); - break; - case DhcpClient.DHCP_FAILURE: - handleIPv4Failure(); - break; - default: - logError("Unknown CMD_POST_DHCP_ACTION status: %s", msg.arg1); - } - break; - - case DhcpClient.CMD_ON_QUIT: - // DHCPv4 quit early for some reason. - logError("Unexpected CMD_ON_QUIT."); - mDhcpClient = null; - break; - - default: - return NOT_HANDLED; - } - - mMsgStateLogger.handled(this, getCurrentState()); - return HANDLED; - } - } - - private static class MessageHandlingLogger { - public String processedInState; - public String receivedInState; - - public void reset() { - processedInState = null; - receivedInState = null; - } - - public void handled(State processedIn, IState receivedIn) { - processedInState = processedIn.getClass().getSimpleName(); - receivedInState = receivedIn.getName(); - } - - public String toString() { - return String.format("rcvd_in=%s, proc_in=%s", - receivedInState, processedInState); - } - } - - private static void setNeighborParameters( - INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs) - throws RemoteException, IllegalArgumentException { - Preconditions.checkNotNull(netd); - Preconditions.checkArgument(!TextUtils.isEmpty(ifName)); - Preconditions.checkArgument(numSolicits > 0); - Preconditions.checkArgument(interSolicitIntervalMs > 0); - - for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) { - netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms", - Integer.toString(interSolicitIntervalMs)); - netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit", - Integer.toString(numSolicits)); - } - } - - // TODO: extract out into CollectionUtils. - static <T> boolean any(Iterable<T> coll, Predicate<T> fn) { - for (T t : coll) { - if (fn.test(t)) { - return true; - } - } - return false; - } - - static <T> boolean all(Iterable<T> coll, Predicate<T> fn) { - return !any(coll, not(fn)); - } - - static <T> Predicate<T> not(Predicate<T> fn) { - return (t) -> !fn.test(t); - } - - static <T> String join(String delimiter, Collection<T> coll) { - return coll.stream().map(Object::toString).collect(Collectors.joining(delimiter)); - } - - static <T> T find(Iterable<T> coll, Predicate<T> fn) { - for (T t: coll) { - if (fn.test(t)) { - return t; - } - } - return null; - } - - static <T> List<T> findAll(Collection<T> coll, Predicate<T> fn) { - return coll.stream().filter(fn).collect(Collectors.toList()); - } -} diff --git a/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java b/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java deleted file mode 100644 index 8ad99aa0399a..000000000000 --- a/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java +++ /dev/null @@ -1,378 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.ip; - -import android.net.InetAddresses; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.RouteInfo; -import android.util.Log; - -import com.android.server.NetworkObserver; - -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -/** - * Keeps track of link configuration received from Netd. - * - * An instance of this class is constructed by passing in an interface name and a callback. The - * owner is then responsible for registering the tracker with NetworkObserverRegistry. When the - * class receives update notifications, it applies the update to its local LinkProperties, and if - * something has changed, notifies its owner of the update via the callback. - * - * The owner can then call {@code getLinkProperties()} in order to find out - * what changed. If in the meantime the LinkProperties stored here have changed, - * this class will return the current LinkProperties. Because each change - * triggers an update callback after the change is made, the owner may get more - * callbacks than strictly necessary (some of which may be no-ops), but will not - * be out of sync once all callbacks have been processed. - * - * Threading model: - * - * - The owner of this class is expected to create it, register it, and call - * getLinkProperties or clearLinkProperties on its thread. - * - Most of the methods in the class are implementing NetworkObserver and are called - * on the handler used to register the observer. - * - All accesses to mLinkProperties must be synchronized(this). All the other - * member variables are immutable once the object is constructed. - * - * @hide - */ -public class IpClientLinkObserver implements NetworkObserver { - private final String mTag; - - /** - * Callback used by {@link IpClientLinkObserver} to send update notifications. - */ - public interface Callback { - /** - * Called when some properties of the link were updated. - */ - void update(); - } - - private final String mInterfaceName; - private final Callback mCallback; - private final LinkProperties mLinkProperties; - private DnsServerRepository mDnsServerRepository; - - private static final boolean DBG = false; - - public IpClientLinkObserver(String iface, Callback callback) { - mTag = "NetlinkTracker/" + iface; - mInterfaceName = iface; - mCallback = callback; - mLinkProperties = new LinkProperties(); - mLinkProperties.setInterfaceName(mInterfaceName); - mDnsServerRepository = new DnsServerRepository(); - } - - private void maybeLog(String operation, String iface, LinkAddress address) { - if (DBG) { - Log.d(mTag, operation + ": " + address + " on " + iface - + " flags " + address.getFlags() + " scope " + address.getScope()); - } - } - - private void maybeLog(String operation, Object o) { - if (DBG) { - Log.d(mTag, operation + ": " + o.toString()); - } - } - - @Override - public void onInterfaceRemoved(String iface) { - maybeLog("interfaceRemoved", iface); - if (mInterfaceName.equals(iface)) { - // Our interface was removed. Clear our LinkProperties and tell our owner that they are - // now empty. Note that from the moment that the interface is removed, any further - // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd - // code that parses them will not be able to resolve the ifindex to an interface name. - clearLinkProperties(); - mCallback.update(); - } - } - - @Override - public void onInterfaceAddressUpdated(LinkAddress address, String iface) { - if (mInterfaceName.equals(iface)) { - maybeLog("addressUpdated", iface, address); - boolean changed; - synchronized (this) { - changed = mLinkProperties.addLinkAddress(address); - } - if (changed) { - mCallback.update(); - } - } - } - - @Override - public void onInterfaceAddressRemoved(LinkAddress address, String iface) { - if (mInterfaceName.equals(iface)) { - maybeLog("addressRemoved", iface, address); - boolean changed; - synchronized (this) { - changed = mLinkProperties.removeLinkAddress(address); - } - if (changed) { - mCallback.update(); - } - } - } - - @Override - public void onRouteUpdated(RouteInfo route) { - if (mInterfaceName.equals(route.getInterface())) { - maybeLog("routeUpdated", route); - boolean changed; - synchronized (this) { - changed = mLinkProperties.addRoute(route); - } - if (changed) { - mCallback.update(); - } - } - } - - @Override - public void onRouteRemoved(RouteInfo route) { - if (mInterfaceName.equals(route.getInterface())) { - maybeLog("routeRemoved", route); - boolean changed; - synchronized (this) { - changed = mLinkProperties.removeRoute(route); - } - if (changed) { - mCallback.update(); - } - } - } - - @Override - public void onInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) { - if (mInterfaceName.equals(iface)) { - maybeLog("interfaceDnsServerInfo", Arrays.toString(addresses)); - boolean changed = mDnsServerRepository.addServers(lifetime, addresses); - if (changed) { - synchronized (this) { - mDnsServerRepository.setDnsServersOn(mLinkProperties); - } - mCallback.update(); - } - } - } - - /** - * Returns a copy of this object's LinkProperties. - */ - public synchronized LinkProperties getLinkProperties() { - return new LinkProperties(mLinkProperties); - } - - /** - * Reset this object's LinkProperties. - */ - public synchronized void clearLinkProperties() { - // Clear the repository before clearing mLinkProperties. That way, if a clear() happens - // while interfaceDnsServerInfo() is being called, we'll end up with no DNS servers in - // mLinkProperties, as desired. - mDnsServerRepository = new DnsServerRepository(); - mLinkProperties.clear(); - mLinkProperties.setInterfaceName(mInterfaceName); - } - - /** - * Tracks DNS server updates received from Netlink. - * - * The network may announce an arbitrary number of DNS servers in Router Advertisements at any - * time. Each announcement has a lifetime; when the lifetime expires, the servers should not be - * used any more. In this way, the network can gracefully migrate clients from one set of DNS - * servers to another. Announcements can both raise and lower the lifetime, and an announcement - * can expire servers by announcing them with a lifetime of zero. - * - * Typically the system will only use a small number (2 or 3; {@code NUM_CURRENT_SERVERS}) of - * DNS servers at any given time. These are referred to as the current servers. In case all the - * current servers expire, the class also keeps track of a larger (but limited) number of - * servers that are promoted to current servers when the current ones expire. In order to - * minimize updates to the rest of the system (and potentially expensive cache flushes) this - * class attempts to keep the list of current servers constant where possible. More - * specifically, the list of current servers is only updated if a new server is learned and - * there are not yet {@code NUM_CURRENT_SERVERS} current servers, or if one or more of the - * current servers expires or is pushed out of the set. Therefore, the current servers will not - * necessarily be the ones with the highest lifetime, but the ones learned first. - * - * This is by design: if instead the class always preferred the servers with the highest - * lifetime, a (misconfigured?) network where two or more routers announce more than - * {@code NUM_CURRENT_SERVERS} unique servers would cause persistent oscillations. - * - * TODO: Currently servers are only expired when a new DNS update is received. - * Update them using timers, or possibly on every notification received by NetlinkTracker. - * - * Threading model: run by NetlinkTracker. Methods are synchronized(this) just in case netlink - * notifications are sent by multiple threads. If future threads use alarms to expire, those - * alarms must also be synchronized(this). - * - */ - private static class DnsServerRepository { - - /** How many DNS servers we will use. 3 is suggested by RFC 6106. */ - static final int NUM_CURRENT_SERVERS = 3; - - /** How many DNS servers we'll keep track of, in total. */ - static final int NUM_SERVERS = 12; - - /** Stores up to {@code NUM_CURRENT_SERVERS} DNS servers we're currently using. */ - private Set<InetAddress> mCurrentServers; - - public static final String TAG = "DnsServerRepository"; - - /** - * Stores all the DNS servers we know about, for use when the current servers expire. - * Always sorted in order of decreasing expiry. The elements in this list are also the - * values of mIndex, and may be elements in mCurrentServers. - */ - private ArrayList<DnsServerEntry> mAllServers; - - /** - * Indexes the servers so we can update their lifetimes more quickly in the common case - * where servers are not being added, but only being refreshed. - */ - private HashMap<InetAddress, DnsServerEntry> mIndex; - - DnsServerRepository() { - mCurrentServers = new HashSet<>(); - mAllServers = new ArrayList<>(NUM_SERVERS); - mIndex = new HashMap<>(NUM_SERVERS); - } - - /** Sets the DNS servers of the provided LinkProperties object to the current servers. */ - public synchronized void setDnsServersOn(LinkProperties lp) { - lp.setDnsServers(mCurrentServers); - } - - /** - * Notifies the class of new DNS server information. - * @param lifetime the time in seconds that the DNS servers are valid. - * @param addresses the string representations of the IP addresses of DNS servers to use. - */ - public synchronized boolean addServers(long lifetime, String[] addresses) { - // The lifetime is actually an unsigned 32-bit number, but Java doesn't have unsigned. - // Technically 0xffffffff (the maximum) is special and means "forever", but 2^32 seconds - // (136 years) is close enough. - long now = System.currentTimeMillis(); - long expiry = now + 1000 * lifetime; - - // Go through the list of servers. For each one, update the entry if one exists, and - // create one if it doesn't. - for (String addressString : addresses) { - InetAddress address; - try { - address = InetAddresses.parseNumericAddress(addressString); - } catch (IllegalArgumentException ex) { - continue; - } - - if (!updateExistingEntry(address, expiry)) { - // There was no entry for this server. Create one, unless it's already expired - // (i.e., if the lifetime is zero; it cannot be < 0 because it's unsigned). - if (expiry > now) { - DnsServerEntry entry = new DnsServerEntry(address, expiry); - mAllServers.add(entry); - mIndex.put(address, entry); - } - } - } - - // Sort the servers by expiry. - Collections.sort(mAllServers); - - // Prune excess entries and update the current server list. - return updateCurrentServers(); - } - - private synchronized boolean updateExistingEntry(InetAddress address, long expiry) { - DnsServerEntry existing = mIndex.get(address); - if (existing != null) { - existing.expiry = expiry; - return true; - } - return false; - } - - private synchronized boolean updateCurrentServers() { - long now = System.currentTimeMillis(); - boolean changed = false; - - // Prune excess or expired entries. - for (int i = mAllServers.size() - 1; i >= 0; i--) { - if (i >= NUM_SERVERS || mAllServers.get(i).expiry < now) { - DnsServerEntry removed = mAllServers.remove(i); - mIndex.remove(removed.address); - changed |= mCurrentServers.remove(removed.address); - } else { - break; - } - } - - // Add servers to the current set, in order of decreasing lifetime, until it has enough. - // Prefer existing servers over new servers in order to minimize updates to the rest of - // the system and avoid persistent oscillations. - for (DnsServerEntry entry : mAllServers) { - if (mCurrentServers.size() < NUM_CURRENT_SERVERS) { - changed |= mCurrentServers.add(entry.address); - } else { - break; - } - } - return changed; - } - } - - /** - * Represents a DNS server entry with an expiry time. - * - * Implements Comparable so DNS server entries can be sorted by lifetime, longest-lived first. - * The ordering of entries with the same lifetime is unspecified, because given two servers with - * identical lifetimes, we don't care which one we use, and only comparing the lifetime is much - * faster than comparing the IP address as well. - * - * Note: this class has a natural ordering that is inconsistent with equals. - */ - private static class DnsServerEntry implements Comparable<DnsServerEntry> { - /** The IP address of the DNS server. */ - public final InetAddress address; - /** The time until which the DNS server may be used. A Java millisecond time as might be - * returned by currentTimeMillis(). */ - public long expiry; - - DnsServerEntry(InetAddress address, long expiry) throws IllegalArgumentException { - this.address = address; - this.expiry = expiry; - } - - public int compareTo(DnsServerEntry other) { - return Long.compare(other.expiry, this.expiry); - } - } -} diff --git a/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java b/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java deleted file mode 100644 index 6ae9a2b7f19b..000000000000 --- a/packages/NetworkStack/src/android/net/ip/IpNeighborMonitor.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright (C) 2017 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.ip; - -import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH; -import static android.net.netlink.NetlinkConstants.hexify; -import static android.net.netlink.NetlinkConstants.stringForNlMsgType; -import static android.net.util.SocketUtils.makeNetlinkSocketAddress; -import static android.system.OsConstants.AF_NETLINK; -import static android.system.OsConstants.NETLINK_ROUTE; -import static android.system.OsConstants.SOCK_DGRAM; -import static android.system.OsConstants.SOCK_NONBLOCK; - -import android.net.MacAddress; -import android.net.netlink.NetlinkErrorMessage; -import android.net.netlink.NetlinkMessage; -import android.net.netlink.NetlinkSocket; -import android.net.netlink.RtNetlinkNeighborMessage; -import android.net.netlink.StructNdMsg; -import android.net.util.NetworkStackUtils; -import android.net.util.PacketReader; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.SystemClock; -import android.system.ErrnoException; -import android.system.Os; -import android.system.OsConstants; -import android.util.Log; - -import java.io.FileDescriptor; -import java.net.InetAddress; -import java.net.SocketAddress; -import java.net.SocketException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.StringJoiner; - - -/** - * IpNeighborMonitor. - * - * Monitors the kernel rtnetlink neighbor notifications and presents to callers - * NeighborEvents describing each event. Callers can provide a consumer instance - * to both filter (e.g. by interface index and IP address) and handle the - * generated NeighborEvents. - * - * @hide - */ -public class IpNeighborMonitor extends PacketReader { - private static final String TAG = IpNeighborMonitor.class.getSimpleName(); - private static final boolean DBG = false; - private static final boolean VDBG = false; - - /** - * Make the kernel perform neighbor reachability detection (IPv4 ARP or IPv6 ND) - * for the given IP address on the specified interface index. - * - * @return 0 if the request was successfully passed to the kernel; otherwise return - * a non-zero error code. - */ - public static int startKernelNeighborProbe(int ifIndex, InetAddress ip) { - final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex; - if (DBG) { Log.d(TAG, msgSnippet); } - - final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( - 1, ip, StructNdMsg.NUD_PROBE, ifIndex, null); - - try { - NetlinkSocket.sendOneShotKernelMessage(NETLINK_ROUTE, msg); - } catch (ErrnoException e) { - Log.e(TAG, "Error " + msgSnippet + ": " + e); - return -e.errno; - } - - return 0; - } - - public static class NeighborEvent { - final long elapsedMs; - final short msgType; - final int ifindex; - final InetAddress ip; - final short nudState; - final MacAddress macAddr; - - public NeighborEvent(long elapsedMs, short msgType, int ifindex, InetAddress ip, - short nudState, MacAddress macAddr) { - this.elapsedMs = elapsedMs; - this.msgType = msgType; - this.ifindex = ifindex; - this.ip = ip; - this.nudState = nudState; - this.macAddr = macAddr; - } - - boolean isConnected() { - return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateConnected(nudState); - } - - boolean isValid() { - return (msgType != RTM_DELNEIGH) && StructNdMsg.isNudStateValid(nudState); - } - - @Override - public String toString() { - final StringJoiner j = new StringJoiner(",", "NeighborEvent{", "}"); - return j.add("@" + elapsedMs) - .add(stringForNlMsgType(msgType)) - .add("if=" + ifindex) - .add(ip.getHostAddress()) - .add(StructNdMsg.stringForNudState(nudState)) - .add("[" + macAddr + "]") - .toString(); - } - } - - public interface NeighborEventConsumer { - // Every neighbor event received on the netlink socket is passed in - // here. Subclasses should filter for events of interest. - public void accept(NeighborEvent event); - } - - private final SharedLog mLog; - private final NeighborEventConsumer mConsumer; - - public IpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb) { - super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE); - mLog = log.forSubComponent(TAG); - mConsumer = (cb != null) ? cb : (event) -> { /* discard */ }; - } - - @Override - protected FileDescriptor createFd() { - FileDescriptor fd = null; - - try { - fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, NETLINK_ROUTE); - Os.bind(fd, makeNetlinkSocketAddress(0, OsConstants.RTMGRP_NEIGH)); - NetlinkSocket.connectToKernel(fd); - - if (VDBG) { - final SocketAddress nlAddr = Os.getsockname(fd); - Log.d(TAG, "bound to sockaddr_nl{" + nlAddr.toString() + "}"); - } - } catch (ErrnoException|SocketException e) { - logError("Failed to create rtnetlink socket", e); - NetworkStackUtils.closeSocketQuietly(fd); - return null; - } - - return fd; - } - - @Override - protected void handlePacket(byte[] recvbuf, int length) { - final long whenMs = SystemClock.elapsedRealtime(); - - final ByteBuffer byteBuffer = ByteBuffer.wrap(recvbuf, 0, length); - byteBuffer.order(ByteOrder.nativeOrder()); - - parseNetlinkMessageBuffer(byteBuffer, whenMs); - } - - private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { - while (byteBuffer.remaining() > 0) { - final int position = byteBuffer.position(); - final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); - if (nlMsg == null || nlMsg.getHeader() == null) { - byteBuffer.position(position); - mLog.e("unparsable netlink msg: " + hexify(byteBuffer)); - break; - } - - final int srcPortId = nlMsg.getHeader().nlmsg_pid; - if (srcPortId != 0) { - mLog.e("non-kernel source portId: " + Integer.toUnsignedLong(srcPortId)); - break; - } - - if (nlMsg instanceof NetlinkErrorMessage) { - mLog.e("netlink error: " + nlMsg); - continue; - } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { - mLog.i("non-rtnetlink neighbor msg: " + nlMsg); - continue; - } - - evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); - } - } - - private void evaluateRtNetlinkNeighborMessage( - RtNetlinkNeighborMessage neighMsg, long whenMs) { - final short msgType = neighMsg.getHeader().nlmsg_type; - final StructNdMsg ndMsg = neighMsg.getNdHeader(); - if (ndMsg == null) { - mLog.e("RtNetlinkNeighborMessage without ND message header!"); - return; - } - - final int ifindex = ndMsg.ndm_ifindex; - final InetAddress destination = neighMsg.getDestination(); - final short nudState = - (msgType == RTM_DELNEIGH) - ? StructNdMsg.NUD_NONE - : ndMsg.ndm_state; - - final NeighborEvent event = new NeighborEvent( - whenMs, msgType, ifindex, destination, nudState, - getMacAddress(neighMsg.getLinkLayerAddress())); - - if (VDBG) { - Log.d(TAG, neighMsg.toString()); - } - if (DBG) { - Log.d(TAG, event.toString()); - } - - mConsumer.accept(event); - } - - private static MacAddress getMacAddress(byte[] linkLayerAddress) { - if (linkLayerAddress != null) { - try { - return MacAddress.fromBytes(linkLayerAddress); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Failed to parse link-layer address: " + hexify(linkLayerAddress)); - } - } - - return null; - } -} diff --git a/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java b/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java deleted file mode 100644 index c19a24eb7fb6..000000000000 --- a/packages/NetworkStack/src/android/net/ip/IpReachabilityMonitor.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (C) 2015 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.ip; - -import static android.net.metrics.IpReachabilityEvent.NUD_FAILED; -import static android.net.metrics.IpReachabilityEvent.NUD_FAILED_ORGANIC; -import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST; -import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC; - -import android.content.Context; -import android.net.ConnectivityManager; -import android.net.LinkProperties; -import android.net.RouteInfo; -import android.net.ip.IpNeighborMonitor.NeighborEvent; -import android.net.metrics.IpConnectivityLog; -import android.net.metrics.IpReachabilityEvent; -import android.net.netlink.StructNdMsg; -import android.net.util.InterfaceParams; -import android.net.util.SharedLog; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Looper; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.os.SystemClock; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.PrintWriter; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - - -/** - * IpReachabilityMonitor. - * - * Monitors on-link IP reachability and notifies callers whenever any on-link - * addresses of interest appear to have become unresponsive. - * - * This code does not concern itself with "why" a neighbour might have become - * unreachable. Instead, it primarily reacts to the kernel's notion of IP - * reachability for each of the neighbours we know to be critically important - * to normal network connectivity. As such, it is often "just the messenger": - * the neighbours about which it warns are already deemed by the kernel to have - * become unreachable. - * - * - * How it works: - * - * 1. The "on-link neighbours of interest" found in a given LinkProperties - * instance are added to a "watch list" via #updateLinkProperties(). - * This usually means all default gateways and any on-link DNS servers. - * - * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH, - * RTM_DELNEIGH), watching only for neighbours in the watch list. - * - * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and - * even NUD_PROBE is perfectly normal; we merely record the new state. - * - * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due - * to garbage collection. This is not necessarily of immediate - * concern; we record the neighbour as moving to NUD_NONE. - * - * - A neighbour transitioning to NUD_FAILED (for any reason) is - * critically important and is handled as described below in #4. - * - * 3. All on-link neighbours in the watch list can be forcibly "probed" by - * calling #probeAll(). This should be called whenever it is important to - * verify that critical neighbours on the link are still reachable, e.g. - * when roaming between BSSIDs. - * - * - The kernel will send unicast ARP requests for IPv4 neighbours and - * unicast NS packets for IPv6 neighbours. The expected replies will - * likely be unicast. - * - * - The forced probing is done holding a wakelock. The kernel may, - * however, initiate probing of a neighbor on its own, i.e. whenever - * a neighbour has expired from NUD_DELAY. - * - * - The kernel sends: - * - * /proc/sys/net/ipv{4,6}/neigh/<ifname>/ucast_solicit - * - * number of probes (usually 3) every: - * - * /proc/sys/net/ipv{4,6}/neigh/<ifname>/retrans_time_ms - * - * number of milliseconds (usually 1000ms). This normally results in - * 3 unicast packets, 1 per second. - * - * - If no response is received to any of the probe packets, the kernel - * marks the neighbour as being in state NUD_FAILED, and the listening - * process in #2 will learn of it. - * - * 4. We call the supplied Callback#notifyLost() function if the loss of a - * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to - * become incomplete (a loss of provisioning). - * - * - For example, losing all our IPv4 on-link DNS servers (or losing - * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6) - * provisioning; Callback#notifyLost() would be called. - * - * - Since it can be non-trivial to reacquire certain IP provisioning - * state it may be best for the link to disconnect completely and - * reconnect afresh. - * - * Accessing an instance of this class from multiple threads is NOT safe. - * - * @hide - */ -public class IpReachabilityMonitor { - private static final String TAG = "IpReachabilityMonitor"; - private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); - private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); - - public interface Callback { - // This callback function must execute as quickly as possible as it is - // run on the same thread that listens to kernel neighbor updates. - // - // TODO: refactor to something like notifyProvisioningLost(String msg). - public void notifyLost(InetAddress ip, String logMsg); - } - - /** - * Encapsulates IpReachabilityMonitor depencencies on systems that hinder unit testing. - * TODO: consider also wrapping MultinetworkPolicyTracker in this interface. - */ - interface Dependencies { - void acquireWakeLock(long durationMs); - - static Dependencies makeDefault(Context context, String iface) { - final String lockName = TAG + "." + iface; - final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - final WakeLock lock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockName); - - return new Dependencies() { - public void acquireWakeLock(long durationMs) { - lock.acquire(durationMs); - } - }; - } - } - - private final InterfaceParams mInterfaceParams; - private final IpNeighborMonitor mIpNeighborMonitor; - private final SharedLog mLog; - private final Callback mCallback; - private final Dependencies mDependencies; - private final boolean mUsingMultinetworkPolicyTracker; - private final ConnectivityManager mCm; - private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); - private LinkProperties mLinkProperties = new LinkProperties(); - private Map<InetAddress, NeighborEvent> mNeighborWatchList = new HashMap<>(); - // Time in milliseconds of the last forced probe request. - private volatile long mLastProbeTimeMs; - - public IpReachabilityMonitor( - Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback, - boolean usingMultinetworkPolicyTracker) { - this(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker, - Dependencies.makeDefault(context, ifParams.name)); - } - - @VisibleForTesting - IpReachabilityMonitor(Context context, InterfaceParams ifParams, Handler h, SharedLog log, - Callback callback, boolean usingMultinetworkPolicyTracker, Dependencies dependencies) { - if (ifParams == null) throw new IllegalArgumentException("null InterfaceParams"); - - mInterfaceParams = ifParams; - mLog = log.forSubComponent(TAG); - mCallback = callback; - mUsingMultinetworkPolicyTracker = usingMultinetworkPolicyTracker; - mCm = context.getSystemService(ConnectivityManager.class); - mDependencies = dependencies; - - mIpNeighborMonitor = new IpNeighborMonitor(h, mLog, - (NeighborEvent event) -> { - if (mInterfaceParams.index != event.ifindex) return; - if (!mNeighborWatchList.containsKey(event.ip)) return; - - final NeighborEvent prev = mNeighborWatchList.put(event.ip, event); - - // TODO: Consider what to do with other states that are not within - // NeighborEvent#isValid() (i.e. NUD_NONE, NUD_INCOMPLETE). - if (event.nudState == StructNdMsg.NUD_FAILED) { - mLog.w("ALERT neighbor went from: " + prev + " to: " + event); - handleNeighborLost(event); - } - }); - mIpNeighborMonitor.start(); - } - - public void stop() { - mIpNeighborMonitor.stop(); - clearLinkProperties(); - } - - public void dump(PrintWriter pw) { - if (Looper.myLooper() == mIpNeighborMonitor.getHandler().getLooper()) { - pw.println(describeWatchList("\n")); - return; - } - - final ConditionVariable cv = new ConditionVariable(false); - mIpNeighborMonitor.getHandler().post(() -> { - pw.println(describeWatchList("\n")); - cv.open(); - }); - - if (!cv.block(1000)) { - pw.println("Timed out waiting for IpReachabilityMonitor dump"); - } - } - - private String describeWatchList() { return describeWatchList(" "); } - - private String describeWatchList(String sep) { - final StringBuilder sb = new StringBuilder(); - sb.append("iface{" + mInterfaceParams + "}," + sep); - sb.append("ntable=[" + sep); - String delimiter = ""; - for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { - sb.append(delimiter).append(entry.getKey().getHostAddress() + "/" + entry.getValue()); - delimiter = "," + sep; - } - sb.append("]"); - return sb.toString(); - } - - private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) { - for (RouteInfo route : routes) { - if (!route.hasGateway() && route.matches(ip)) { - return true; - } - } - return false; - } - - public void updateLinkProperties(LinkProperties lp) { - if (!mInterfaceParams.name.equals(lp.getInterfaceName())) { - // TODO: figure out whether / how to cope with interface changes. - Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + - "' does not match: " + mInterfaceParams.name); - return; - } - - mLinkProperties = new LinkProperties(lp); - Map<InetAddress, NeighborEvent> newNeighborWatchList = new HashMap<>(); - - final List<RouteInfo> routes = mLinkProperties.getRoutes(); - for (RouteInfo route : routes) { - if (route.hasGateway()) { - InetAddress gw = route.getGateway(); - if (isOnLink(routes, gw)) { - newNeighborWatchList.put(gw, mNeighborWatchList.getOrDefault(gw, null)); - } - } - } - - for (InetAddress dns : lp.getDnsServers()) { - if (isOnLink(routes, dns)) { - newNeighborWatchList.put(dns, mNeighborWatchList.getOrDefault(dns, null)); - } - } - - mNeighborWatchList = newNeighborWatchList; - if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } - } - - public void clearLinkProperties() { - mLinkProperties.clear(); - mNeighborWatchList.clear(); - if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } - } - - private void handleNeighborLost(NeighborEvent event) { - final LinkProperties whatIfLp = new LinkProperties(mLinkProperties); - - InetAddress ip = null; - for (Map.Entry<InetAddress, NeighborEvent> entry : mNeighborWatchList.entrySet()) { - // TODO: Consider using NeighborEvent#isValid() here; it's more - // strict but may interact badly if other entries are somehow in - // NUD_INCOMPLETE (say, during network attach). - if (entry.getValue().nudState != StructNdMsg.NUD_FAILED) continue; - - ip = entry.getKey(); - for (RouteInfo route : mLinkProperties.getRoutes()) { - if (ip.equals(route.getGateway())) { - whatIfLp.removeRoute(route); - } - } - - if (avoidingBadLinks() || !(ip instanceof Inet6Address)) { - // We should do this unconditionally, but alas we cannot: b/31827713. - whatIfLp.removeDnsServer(ip); - } - } - - final boolean lostProvisioning = - (mLinkProperties.isIpv4Provisioned() && !whatIfLp.isIpv4Provisioned()) - || (mLinkProperties.isIpv6Provisioned() && !whatIfLp.isIpv6Provisioned()); - - if (lostProvisioning) { - final String logMsg = "FAILURE: LOST_PROVISIONING, " + event; - Log.w(TAG, logMsg); - if (mCallback != null) { - // TODO: remove |ip| when the callback signature no longer has - // an InetAddress argument. - mCallback.notifyLost(ip, logMsg); - } - } - logNudFailed(lostProvisioning); - } - - private boolean avoidingBadLinks() { - return !mUsingMultinetworkPolicyTracker || mCm.shouldAvoidBadWifi(); - } - - public void probeAll() { - final List<InetAddress> ipProbeList = new ArrayList<>(mNeighborWatchList.keySet()); - - if (!ipProbeList.isEmpty()) { - // Keep the CPU awake long enough to allow all ARP/ND - // probes a reasonable chance at success. See b/23197666. - // - // The wakelock we use is (by default) refcounted, and this version - // of acquire(timeout) queues a release message to keep acquisitions - // and releases balanced. - mDependencies.acquireWakeLock(getProbeWakeLockDuration()); - } - - for (InetAddress ip : ipProbeList) { - final int rval = IpNeighborMonitor.startKernelNeighborProbe(mInterfaceParams.index, ip); - mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)", - ip.getHostAddress(), rval)); - logEvent(IpReachabilityEvent.PROBE, rval); - } - mLastProbeTimeMs = SystemClock.elapsedRealtime(); - } - - private static long getProbeWakeLockDuration() { - // Ideally, this would be computed by examining the values of: - // - // /proc/sys/net/ipv[46]/neigh/<ifname>/ucast_solicit - // - // and: - // - // /proc/sys/net/ipv[46]/neigh/<ifname>/retrans_time_ms - // - // For now, just make some assumptions. - final long numUnicastProbes = 3; - final long retransTimeMs = 1000; - final long gracePeriodMs = 500; - return (numUnicastProbes * retransTimeMs) + gracePeriodMs; - } - - private void logEvent(int probeType, int errorCode) { - int eventType = probeType | (errorCode & 0xff); - mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType)); - } - - private void logNudFailed(boolean lostProvisioning) { - long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs; - boolean isFromProbe = (duration < getProbeWakeLockDuration()); - int eventType = nudFailureEventType(isFromProbe, lostProvisioning); - mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType)); - } - - /** - * Returns the NUD failure event type code corresponding to the given conditions. - */ - private static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) { - if (isFromProbe) { - return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED; - } else { - return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC; - } - } -} diff --git a/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java b/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java deleted file mode 100644 index 08c3f60eff3d..000000000000 --- a/packages/NetworkStack/src/android/net/util/ConnectivityPacketSummary.java +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Copyright (C) 2016 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.util; - -import static android.system.OsConstants.IPPROTO_ICMPV6; -import static android.system.OsConstants.IPPROTO_UDP; - -import static com.android.server.util.NetworkStackConstants.ARP_HWTYPE_ETHER; -import static com.android.server.util.NetworkStackConstants.ARP_PAYLOAD_LEN; -import static com.android.server.util.NetworkStackConstants.ARP_REPLY; -import static com.android.server.util.NetworkStackConstants.ARP_REQUEST; -import static com.android.server.util.NetworkStackConstants.DHCP4_CLIENT_PORT; -import static com.android.server.util.NetworkStackConstants.ETHER_ADDR_LEN; -import static com.android.server.util.NetworkStackConstants.ETHER_DST_ADDR_OFFSET; -import static com.android.server.util.NetworkStackConstants.ETHER_HEADER_LEN; -import static com.android.server.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET; -import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_ARP; -import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV4; -import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_IPV6; -import static com.android.server.util.NetworkStackConstants.ETHER_TYPE_OFFSET; -import static com.android.server.util.NetworkStackConstants.ICMPV6_HEADER_MIN_LEN; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MIN_LENGTH; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_MTU; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA; -import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT; -import static com.android.server.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION; -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_LEN; -import static com.android.server.util.NetworkStackConstants.IPV4_DST_ADDR_OFFSET; -import static com.android.server.util.NetworkStackConstants.IPV4_FLAGS_OFFSET; -import static com.android.server.util.NetworkStackConstants.IPV4_FRAGMENT_MASK; -import static com.android.server.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN; -import static com.android.server.util.NetworkStackConstants.IPV4_IHL_MASK; -import static com.android.server.util.NetworkStackConstants.IPV4_PROTOCOL_OFFSET; -import static com.android.server.util.NetworkStackConstants.IPV4_SRC_ADDR_OFFSET; -import static com.android.server.util.NetworkStackConstants.IPV6_ADDR_LEN; -import static com.android.server.util.NetworkStackConstants.IPV6_HEADER_LEN; -import static com.android.server.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET; -import static com.android.server.util.NetworkStackConstants.IPV6_SRC_ADDR_OFFSET; -import static com.android.server.util.NetworkStackConstants.UDP_HEADER_LEN; - -import android.net.MacAddress; -import android.net.dhcp.DhcpPacket; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.StringJoiner; - - -/** - * Critical connectivity packet summarizing class. - * - * Outputs short descriptions of ARP, DHCPv4, and IPv6 RS/RA/NS/NA packets. - * - * @hide - */ -public class ConnectivityPacketSummary { - private static final String TAG = ConnectivityPacketSummary.class.getSimpleName(); - - private final byte[] mHwAddr; - private final byte[] mBytes; - private final int mLength; - private final ByteBuffer mPacket; - private final String mSummary; - - public static String summarize(MacAddress hwaddr, byte[] buffer) { - return summarize(hwaddr, buffer, buffer.length); - } - - // Methods called herein perform some but by no means all error checking. - // They may throw runtime exceptions on malformed packets. - public static String summarize(MacAddress macAddr, byte[] buffer, int length) { - if ((macAddr == null) || (buffer == null)) return null; - length = Math.min(length, buffer.length); - return (new ConnectivityPacketSummary(macAddr, buffer, length)).toString(); - } - - private ConnectivityPacketSummary(MacAddress macAddr, byte[] buffer, int length) { - mHwAddr = macAddr.toByteArray(); - mBytes = buffer; - mLength = Math.min(length, mBytes.length); - mPacket = ByteBuffer.wrap(mBytes, 0, mLength); - mPacket.order(ByteOrder.BIG_ENDIAN); - - final StringJoiner sj = new StringJoiner(" "); - // TODO: support other link-layers, or even no link-layer header. - parseEther(sj); - mSummary = sj.toString(); - } - - public String toString() { - return mSummary; - } - - private void parseEther(StringJoiner sj) { - if (mPacket.remaining() < ETHER_HEADER_LEN) { - sj.add("runt:").add(asString(mPacket.remaining())); - return; - } - - mPacket.position(ETHER_SRC_ADDR_OFFSET); - final ByteBuffer srcMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN); - sj.add(ByteBuffer.wrap(mHwAddr).equals(srcMac) ? "TX" : "RX"); - sj.add(getMacAddressString(srcMac)); - - mPacket.position(ETHER_DST_ADDR_OFFSET); - final ByteBuffer dstMac = (ByteBuffer) mPacket.slice().limit(ETHER_ADDR_LEN); - sj.add(">").add(getMacAddressString(dstMac)); - - mPacket.position(ETHER_TYPE_OFFSET); - final int etherType = asUint(mPacket.getShort()); - switch (etherType) { - case ETHER_TYPE_ARP: - sj.add("arp"); - parseARP(sj); - break; - case ETHER_TYPE_IPV4: - sj.add("ipv4"); - parseIPv4(sj); - break; - case ETHER_TYPE_IPV6: - sj.add("ipv6"); - parseIPv6(sj); - break; - default: - // Unknown ether type. - sj.add("ethtype").add(asString(etherType)); - break; - } - } - - private void parseARP(StringJoiner sj) { - if (mPacket.remaining() < ARP_PAYLOAD_LEN) { - sj.add("runt:").add(asString(mPacket.remaining())); - return; - } - - if (asUint(mPacket.getShort()) != ARP_HWTYPE_ETHER || - asUint(mPacket.getShort()) != ETHER_TYPE_IPV4 || - asUint(mPacket.get()) != ETHER_ADDR_LEN || - asUint(mPacket.get()) != IPV4_ADDR_LEN) { - sj.add("unexpected header"); - return; - } - - final int opCode = asUint(mPacket.getShort()); - - final String senderHwAddr = getMacAddressString(mPacket); - final String senderIPv4 = getIPv4AddressString(mPacket); - getMacAddressString(mPacket); // target hardware address, unused - final String targetIPv4 = getIPv4AddressString(mPacket); - - if (opCode == ARP_REQUEST) { - sj.add("who-has").add(targetIPv4); - } else if (opCode == ARP_REPLY) { - sj.add("reply").add(senderIPv4).add(senderHwAddr); - } else { - sj.add("unknown opcode").add(asString(opCode)); - } - } - - private void parseIPv4(StringJoiner sj) { - if (!mPacket.hasRemaining()) { - sj.add("runt"); - return; - } - - final int startOfIpLayer = mPacket.position(); - final int ipv4HeaderLength = (mPacket.get(startOfIpLayer) & IPV4_IHL_MASK) * 4; - if (mPacket.remaining() < ipv4HeaderLength || - mPacket.remaining() < IPV4_HEADER_MIN_LEN) { - sj.add("runt:").add(asString(mPacket.remaining())); - return; - } - final int startOfTransportLayer = startOfIpLayer + ipv4HeaderLength; - - mPacket.position(startOfIpLayer + IPV4_FLAGS_OFFSET); - final int flagsAndFragment = asUint(mPacket.getShort()); - final boolean isFragment = (flagsAndFragment & IPV4_FRAGMENT_MASK) != 0; - - mPacket.position(startOfIpLayer + IPV4_PROTOCOL_OFFSET); - final int protocol = asUint(mPacket.get()); - - mPacket.position(startOfIpLayer + IPV4_SRC_ADDR_OFFSET); - final String srcAddr = getIPv4AddressString(mPacket); - - mPacket.position(startOfIpLayer + IPV4_DST_ADDR_OFFSET); - final String dstAddr = getIPv4AddressString(mPacket); - - sj.add(srcAddr).add(">").add(dstAddr); - - mPacket.position(startOfTransportLayer); - if (protocol == IPPROTO_UDP) { - sj.add("udp"); - if (isFragment) sj.add("fragment"); - else parseUDP(sj); - } else { - sj.add("proto").add(asString(protocol)); - if (isFragment) sj.add("fragment"); - } - } - - private void parseIPv6(StringJoiner sj) { - if (mPacket.remaining() < IPV6_HEADER_LEN) { - sj.add("runt:").add(asString(mPacket.remaining())); - return; - } - - final int startOfIpLayer = mPacket.position(); - - mPacket.position(startOfIpLayer + IPV6_PROTOCOL_OFFSET); - final int protocol = asUint(mPacket.get()); - - mPacket.position(startOfIpLayer + IPV6_SRC_ADDR_OFFSET); - final String srcAddr = getIPv6AddressString(mPacket); - final String dstAddr = getIPv6AddressString(mPacket); - - sj.add(srcAddr).add(">").add(dstAddr); - - mPacket.position(startOfIpLayer + IPV6_HEADER_LEN); - if (protocol == IPPROTO_ICMPV6) { - sj.add("icmp6"); - parseICMPv6(sj); - } else { - sj.add("proto").add(asString(protocol)); - } - } - - private void parseICMPv6(StringJoiner sj) { - if (mPacket.remaining() < ICMPV6_HEADER_MIN_LEN) { - sj.add("runt:").add(asString(mPacket.remaining())); - return; - } - - final int icmp6Type = asUint(mPacket.get()); - final int icmp6Code = asUint(mPacket.get()); - mPacket.getShort(); // checksum, unused - - switch (icmp6Type) { - case ICMPV6_ROUTER_SOLICITATION: - sj.add("rs"); - parseICMPv6RouterSolicitation(sj); - break; - case ICMPV6_ROUTER_ADVERTISEMENT: - sj.add("ra"); - parseICMPv6RouterAdvertisement(sj); - break; - case ICMPV6_NEIGHBOR_SOLICITATION: - sj.add("ns"); - parseICMPv6NeighborMessage(sj); - break; - case ICMPV6_NEIGHBOR_ADVERTISEMENT: - sj.add("na"); - parseICMPv6NeighborMessage(sj); - break; - default: - sj.add("type").add(asString(icmp6Type)); - sj.add("code").add(asString(icmp6Code)); - break; - } - } - - private void parseICMPv6RouterSolicitation(StringJoiner sj) { - final int RESERVED = 4; - if (mPacket.remaining() < RESERVED) { - sj.add("runt:").add(asString(mPacket.remaining())); - return; - } - - mPacket.position(mPacket.position() + RESERVED); - parseICMPv6NeighborDiscoveryOptions(sj); - } - - private void parseICMPv6RouterAdvertisement(StringJoiner sj) { - final int FLAGS_AND_TIMERS = 3 * 4; - if (mPacket.remaining() < FLAGS_AND_TIMERS) { - sj.add("runt:").add(asString(mPacket.remaining())); - return; - } - - mPacket.position(mPacket.position() + FLAGS_AND_TIMERS); - parseICMPv6NeighborDiscoveryOptions(sj); - } - - private void parseICMPv6NeighborMessage(StringJoiner sj) { - final int RESERVED = 4; - final int minReq = RESERVED + IPV6_ADDR_LEN; - if (mPacket.remaining() < minReq) { - sj.add("runt:").add(asString(mPacket.remaining())); - return; - } - - mPacket.position(mPacket.position() + RESERVED); - sj.add(getIPv6AddressString(mPacket)); - parseICMPv6NeighborDiscoveryOptions(sj); - } - - private void parseICMPv6NeighborDiscoveryOptions(StringJoiner sj) { - // All ND options are TLV, where T is one byte and L is one byte equal - // to the length of T + L + V in units of 8 octets. - while (mPacket.remaining() >= ICMPV6_ND_OPTION_MIN_LENGTH) { - final int ndType = asUint(mPacket.get()); - final int ndLength = asUint(mPacket.get()); - final int ndBytes = ndLength * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR - 2; - if (ndBytes < 0 || ndBytes > mPacket.remaining()) { - sj.add("<malformed>"); - break; - } - final int position = mPacket.position(); - - switch (ndType) { - case ICMPV6_ND_OPTION_SLLA: - sj.add("slla"); - sj.add(getMacAddressString(mPacket)); - break; - case ICMPV6_ND_OPTION_TLLA: - sj.add("tlla"); - sj.add(getMacAddressString(mPacket)); - break; - case ICMPV6_ND_OPTION_MTU: - sj.add("mtu"); - final short reserved = mPacket.getShort(); - sj.add(asString(mPacket.getInt())); - break; - default: - // Skip. - break; - } - - mPacket.position(position + ndBytes); - } - } - - private void parseUDP(StringJoiner sj) { - if (mPacket.remaining() < UDP_HEADER_LEN) { - sj.add("runt:").add(asString(mPacket.remaining())); - return; - } - - final int previous = mPacket.position(); - final int srcPort = asUint(mPacket.getShort()); - final int dstPort = asUint(mPacket.getShort()); - sj.add(asString(srcPort)).add(">").add(asString(dstPort)); - - mPacket.position(previous + UDP_HEADER_LEN); - if (srcPort == DHCP4_CLIENT_PORT || dstPort == DHCP4_CLIENT_PORT) { - sj.add("dhcp4"); - parseDHCPv4(sj); - } - } - - private void parseDHCPv4(StringJoiner sj) { - final DhcpPacket dhcpPacket; - try { - dhcpPacket = DhcpPacket.decodeFullPacket(mBytes, mLength, DhcpPacket.ENCAP_L2); - sj.add(dhcpPacket.toString()); - } catch (DhcpPacket.ParseException e) { - sj.add("parse error: " + e); - } - } - - private static String getIPv4AddressString(ByteBuffer ipv4) { - return getIpAddressString(ipv4, IPV4_ADDR_LEN); - } - - private static String getIPv6AddressString(ByteBuffer ipv6) { - return getIpAddressString(ipv6, IPV6_ADDR_LEN); - } - - private static String getIpAddressString(ByteBuffer ip, int byteLength) { - if (ip == null || ip.remaining() < byteLength) return "invalid"; - - byte[] bytes = new byte[byteLength]; - ip.get(bytes, 0, byteLength); - try { - InetAddress addr = InetAddress.getByAddress(bytes); - return addr.getHostAddress(); - } catch (UnknownHostException uhe) { - return "unknown"; - } - } - - private static String getMacAddressString(ByteBuffer mac) { - if (mac == null || mac.remaining() < ETHER_ADDR_LEN) return "invalid"; - - byte[] bytes = new byte[ETHER_ADDR_LEN]; - mac.get(bytes, 0, bytes.length); - Object[] printableBytes = new Object[bytes.length]; - int i = 0; - for (byte b : bytes) printableBytes[i++] = new Byte(b); - - final String MAC48_FORMAT = "%02x:%02x:%02x:%02x:%02x:%02x"; - return String.format(MAC48_FORMAT, printableBytes); - } - - /** - * Convenience method to convert an int to a String. - */ - public static String asString(int i) { - return Integer.toString(i); - } - - /** - * Convenience method to read a byte as an unsigned int. - */ - public static int asUint(byte b) { - return (b & 0xff); - } - - /** - * Convenience method to read a short as an unsigned int. - */ - public static int asUint(short s) { - return (s & 0xffff); - } -} diff --git a/packages/NetworkStack/src/android/net/util/DataStallUtils.java b/packages/NetworkStack/src/android/net/util/DataStallUtils.java deleted file mode 100644 index b6dbeb19ad1d..000000000000 --- a/packages/NetworkStack/src/android/net/util/DataStallUtils.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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.util; - -/** - * Collection of utilities for data stall. - */ -public class DataStallUtils { - /** - * Detect data stall via using dns timeout counts. - */ - public static final int DATA_STALL_EVALUATION_TYPE_DNS = 1; - // Default configuration values for data stall detection. - public static final int DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = 5; - public static final int DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS = 60 * 1000; - public static final int DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS = 30 * 60 * 1000; - /** - * The threshold value for the number of consecutive dns timeout events received to be a - * signal of data stall. The number of consecutive timeouts needs to be {@code >=} this - * threshold to be considered a data stall. Set the value to {@code <= 0} to disable. Note - * that the value should be {@code > 0} if the DNS data stall detection is enabled. - * - */ - public static final String CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = - "data_stall_consecutive_dns_timeout_threshold"; - - /** - * The minimal time interval in milliseconds for data stall reevaluation. - * - */ - public static final String CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL = - "data_stall_min_evaluate_interval"; - - /** - * DNS timeouts older than this timeout (in milliseconds) are not considered for detecting - * a data stall. - * - */ - public static final String CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD = - "data_stall_valid_dns_time_threshold"; - - /** - * Which data stall detection signal to use. This is a bitmask constructed by bitwise-or-ing - * (i.e. {@code |}) the DATA_STALL_EVALUATION_TYPE_* values. - * - * Type: int - * Valid values: - * {@link #DATA_STALL_EVALUATION_TYPE_DNS} : Use dns as a signal. - */ - public static final String CONFIG_DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type"; - public static final int DEFAULT_DATA_STALL_EVALUATION_TYPES = DATA_STALL_EVALUATION_TYPE_DNS; - // The default number of DNS events kept of the log kept for dns signal evaluation. Each event - // is represented by a {@link com.android.server.connectivity.NetworkMonitor#DnsResult} objects. - // It's also the size of array of {@link com.android.server.connectivity.nano.DnsEvent} kept in - // metrics. Note that increasing the size may cause statsd log buffer bust. Need to check the - // design in statsd when you try to increase the size. - public static final int DEFAULT_DNS_LOG_SIZE = 20; -} diff --git a/packages/NetworkStack/src/android/net/util/FdEventsReader.java b/packages/NetworkStack/src/android/net/util/FdEventsReader.java deleted file mode 100644 index 1380ea720d40..000000000000 --- a/packages/NetworkStack/src/android/net/util/FdEventsReader.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright (C) 2016 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.util; - -import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; -import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.os.Handler; -import android.os.Looper; -import android.os.MessageQueue; -import android.system.ErrnoException; -import android.system.OsConstants; - -import java.io.FileDescriptor; -import java.io.IOException; - - -/** - * This class encapsulates the mechanics of registering a file descriptor - * with a thread's Looper and handling read events (and errors). - * - * Subclasses MUST implement createFd() and SHOULD override handlePacket(). They MAY override - * onStop() and onStart(). - * - * Subclasses can expect a call life-cycle like the following: - * - * [1] when a client calls start(), createFd() is called, followed by the onStart() hook if all - * goes well. Implementations may override onStart() for additional initialization. - * - * [2] yield, waiting for read event or error notification: - * - * [a] readPacket() && handlePacket() - * - * [b] if (no error): - * goto 2 - * else: - * goto 3 - * - * [3] when a client calls stop(), the onStop() hook is called (unless already stopped or never - * started). Implementations may override onStop() for additional cleanup. - * - * The packet receive buffer is recycled on every read call, so subclasses - * should make any copies they would like inside their handlePacket() - * implementation. - * - * All public methods MUST only be called from the same thread with which - * the Handler constructor argument is associated. - * - * @param <BufferType> the type of the buffer used to read data. - * @hide - */ -public abstract class FdEventsReader<BufferType> { - private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR; - private static final int UNREGISTER_THIS_FD = 0; - - @NonNull - private final Handler mHandler; - @NonNull - private final MessageQueue mQueue; - @NonNull - private final BufferType mBuffer; - @Nullable - private FileDescriptor mFd; - private long mPacketsReceived; - - protected static void closeFd(FileDescriptor fd) { - try { - SocketUtils.closeSocket(fd); - } catch (IOException ignored) { - } - } - - protected FdEventsReader(@NonNull Handler h, @NonNull BufferType buffer) { - mHandler = h; - mQueue = mHandler.getLooper().getQueue(); - mBuffer = buffer; - } - - /** Start this FdEventsReader. */ - public void start() { - if (onCorrectThread()) { - createAndRegisterFd(); - } else { - mHandler.post(() -> { - logError("start() called from off-thread", null); - createAndRegisterFd(); - }); - } - } - - /** Stop this FdEventsReader and destroy the file descriptor. */ - public void stop() { - if (onCorrectThread()) { - unregisterAndDestroyFd(); - } else { - mHandler.post(() -> { - logError("stop() called from off-thread", null); - unregisterAndDestroyFd(); - }); - } - } - - @NonNull - public Handler getHandler() { - return mHandler; - } - - protected abstract int recvBufSize(@NonNull BufferType buffer); - - /** Returns the size of the receive buffer. */ - public int recvBufSize() { - return recvBufSize(mBuffer); - } - - /** - * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}. - * - * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0. - */ - public final long numPacketsReceived() { - return mPacketsReceived; - } - - /** - * Subclasses MUST create the listening socket here, including setting all desired socket - * options, interface or address/port binding, etc. The socket MUST be created nonblocking. - */ - @Nullable - protected abstract FileDescriptor createFd(); - - /** - * Implementations MUST return the bytes read or throw an Exception. - * - * <p>The caller may throw a {@link ErrnoException} with {@link OsConstants#EAGAIN} or - * {@link OsConstants#EINTR}, in which case {@link FdEventsReader} will ignore the buffer - * contents and respectively wait for further input or retry the read immediately. For all other - * exceptions, the {@link FdEventsReader} will be stopped with no more interactions with this - * method. - */ - protected abstract int readPacket(@NonNull FileDescriptor fd, @NonNull BufferType buffer) - throws Exception; - - /** - * Called by the main loop for every packet. Any desired copies of - * |recvbuf| should be made in here, as the underlying byte array is - * reused across all reads. - */ - protected void handlePacket(@NonNull BufferType recvbuf, int length) {} - - /** - * Called by the main loop to log errors. In some cases |e| may be null. - */ - protected void logError(@NonNull String msg, @Nullable Exception e) {} - - /** - * Called by start(), if successful, just prior to returning. - */ - protected void onStart() {} - - /** - * Called by stop() just prior to returning. - */ - protected void onStop() {} - - private void createAndRegisterFd() { - if (mFd != null) return; - - try { - mFd = createFd(); - } catch (Exception e) { - logError("Failed to create socket: ", e); - closeFd(mFd); - mFd = null; - } - - if (mFd == null) return; - - mQueue.addOnFileDescriptorEventListener( - mFd, - FD_EVENTS, - (fd, events) -> { - // Always call handleInput() so read/recvfrom are given - // a proper chance to encounter a meaningful errno and - // perhaps log a useful error message. - if (!isRunning() || !handleInput()) { - unregisterAndDestroyFd(); - return UNREGISTER_THIS_FD; - } - return FD_EVENTS; - }); - onStart(); - } - - private boolean isRunning() { - return (mFd != null) && mFd.valid(); - } - - // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error. - private boolean handleInput() { - while (isRunning()) { - final int bytesRead; - - try { - bytesRead = readPacket(mFd, mBuffer); - if (bytesRead < 1) { - if (isRunning()) logError("Socket closed, exiting", null); - break; - } - mPacketsReceived++; - } catch (ErrnoException e) { - if (e.errno == OsConstants.EAGAIN) { - // We've read everything there is to read this time around. - return true; - } else if (e.errno == OsConstants.EINTR) { - continue; - } else { - if (isRunning()) logError("readPacket error: ", e); - break; - } - } catch (Exception e) { - if (isRunning()) logError("readPacket error: ", e); - break; - } - - try { - handlePacket(mBuffer, bytesRead); - } catch (Exception e) { - logError("handlePacket error: ", e); - break; - } - } - - return false; - } - - private void unregisterAndDestroyFd() { - if (mFd == null) return; - - mQueue.removeOnFileDescriptorEventListener(mFd); - closeFd(mFd); - mFd = null; - onStop(); - } - - private boolean onCorrectThread() { - return (mHandler.getLooper() == Looper.myLooper()); - } -} diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java deleted file mode 100644 index 541f9d8a3bf1..000000000000 --- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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.util; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.SparseArray; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.SocketException; -import java.util.List; -import java.util.function.Predicate; - -/** - * Collection of utilities for the network stack. - */ -public class NetworkStackUtils { - // TODO: Refer to DeviceConfig definition. - public static final String NAMESPACE_CONNECTIVITY = "connectivity"; - - /** - * A list of captive portal detection specifications used in addition to the fallback URLs. - * Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated - * by "@@,@@". - */ - public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = - "captive_portal_fallback_probe_specs"; - - /** - * A comma separated list of URLs used for captive portal detection in addition to the - * fallback HTTP url associated with the CAPTIVE_PORTAL_FALLBACK_URL settings. - */ - public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = - "captive_portal_other_fallback_urls"; - - /** - * Which User-Agent string to use in the header of the captive portal detection probes. - * The User-Agent field is unset when this setting has no value (HttpUrlConnection default). - */ - public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent"; - - /** - * Whether to use HTTPS for network validation. This is enabled by default and the setting - * needs to be set to 0 to disable it. This setting is a misnomer because captive portals - * don't actually use HTTPS, but it's consistent with the other settings. - */ - public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https"; - - /** - * The URL used for HTTPS captive portal detection upon a new connection. - * A 204 response code from the server is used for validation. - */ - public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; - - /** - * The URL used for HTTP captive portal detection upon a new connection. - * A 204 response code from the server is used for validation. - */ - public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; - - /** - * The URL used for fallback HTTP captive portal detection when previous HTTP - * and HTTPS captive portal detection attemps did not return a conclusive answer. - */ - public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; - - /** - * What to do when connecting a network that presents a captive portal. - * Must be one of the CAPTIVE_PORTAL_MODE_* constants above. - * - * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT. - */ - public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode"; - - /** - * Don't attempt to detect captive portals. - */ - public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; - - /** - * When detecting a captive portal, display a notification that - * prompts the user to sign in. - */ - public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; - - /** - * When detecting a captive portal, immediately disconnect from the - * network and do not reconnect to that network in the future. - */ - public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; - - static { - System.loadLibrary("networkstackutilsjni"); - } - - /** - * @return True if the array is null or 0-length. - */ - public static <T> boolean isEmpty(T[] array) { - return array == null || array.length == 0; - } - - /** - * Close a socket, ignoring any exception while closing. - */ - public static void closeSocketQuietly(FileDescriptor fd) { - try { - SocketUtils.closeSocket(fd); - } catch (IOException ignored) { - } - } - - /** - * Returns an int array from the given Integer list. - */ - public static int[] convertToIntArray(@NonNull List<Integer> list) { - int[] array = new int[list.size()]; - for (int i = 0; i < list.size(); i++) { - array[i] = list.get(i); - } - return array; - } - - /** - * Returns a long array from the given long list. - */ - public static long[] convertToLongArray(@NonNull List<Long> list) { - long[] array = new long[list.size()]; - for (int i = 0; i < list.size(); i++) { - array[i] = list.get(i); - } - return array; - } - - /** - * @return True if there exists at least one element in the sparse array for which - * condition {@code predicate} - */ - public static <T> boolean any(SparseArray<T> array, Predicate<T> predicate) { - for (int i = 0; i < array.size(); ++i) { - if (predicate.test(array.valueAt(i))) { - return true; - } - } - return false; - } - - /** - * Look up the value of a property for a particular namespace from {@link DeviceConfig}. - * @param namespace The namespace containing the property to look up. - * @param name The name of the property to look up. - * @param defaultValue The value to return if the property does not exist or has no valid value. - * @return the corresponding value, or defaultValue if none exists. - */ - @Nullable - public static String getDeviceConfigProperty(@NonNull String namespace, @NonNull String name, - @Nullable String defaultValue) { - // TODO: Link to DeviceConfig API once it is ready. - return defaultValue; - } - - /** - * Look up the value of a property for a particular namespace from {@link DeviceConfig}. - * @param namespace The namespace containing the property to look up. - * @param name The name of the property to look up. - * @param defaultValue The value to return if the property does not exist or has no non-null - * value. - * @return the corresponding value, or defaultValue if none exists. - */ - public static int getDeviceConfigPropertyInt(@NonNull String namespace, @NonNull String name, - int defaultValue) { - String value = getDeviceConfigProperty(namespace, name, null /* defaultValue */); - try { - return (value != null) ? Integer.parseInt(value) : defaultValue; - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * Attaches a socket filter that accepts DHCP packets to the given socket. - */ - public static native void attachDhcpFilter(FileDescriptor fd) throws SocketException; - - /** - * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket. - * @param fd the socket's {@link FileDescriptor}. - * @param packetType the hardware address type, one of ARPHRD_*. - */ - public static native void attachRaFilter(FileDescriptor fd, int packetType) - throws SocketException; - - /** - * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity. - * - * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges. - * - * @param fd the socket's {@link FileDescriptor}. - * @param packetType the hardware address type, one of ARPHRD_*. - */ - public static native void attachControlPacketFilter(FileDescriptor fd, int packetType) - throws SocketException; - - /** - * Add an entry into the ARP cache. - */ - public static void addArpEntry(Inet4Address ipv4Addr, android.net.MacAddress ethAddr, - String ifname, FileDescriptor fd) throws IOException { - addArpEntry(ethAddr.toByteArray(), ipv4Addr.getAddress(), ifname, fd); - } - - private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname, - FileDescriptor fd) throws IOException; - - /** - * Return IP address and port in a string format. - */ - public static String addressAndPortToString(InetAddress address, int port) { - return String.format( - (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d", - address.getHostAddress(), port); - } -} diff --git a/packages/NetworkStack/src/android/net/util/PacketReader.java b/packages/NetworkStack/src/android/net/util/PacketReader.java deleted file mode 100644 index 4aec6b6753a6..000000000000 --- a/packages/NetworkStack/src/android/net/util/PacketReader.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.util; - -import static java.lang.Math.max; - -import android.os.Handler; -import android.system.Os; - -import java.io.FileDescriptor; - -/** - * Specialization of {@link FdEventsReader} that reads packets into a byte array. - * - * TODO: rename this class to something more correctly descriptive (something - * like [or less horrible than] FdReadEventsHandler?). - * - * @hide - */ -public abstract class PacketReader extends FdEventsReader<byte[]> { - - public static final int DEFAULT_RECV_BUF_SIZE = 2 * 1024; - - protected PacketReader(Handler h) { - this(h, DEFAULT_RECV_BUF_SIZE); - } - - protected PacketReader(Handler h, int recvBufSize) { - super(h, new byte[max(recvBufSize, DEFAULT_RECV_BUF_SIZE)]); - } - - @Override - protected final int recvBufSize(byte[] buffer) { - return buffer.length; - } - - /** - * Subclasses MAY override this to change the default read() implementation - * in favour of, say, recvfrom(). - * - * Implementations MUST return the bytes read or throw an Exception. - */ - @Override - protected int readPacket(FileDescriptor fd, byte[] packetBuffer) throws Exception { - return Os.read(fd, packetBuffer, 0, packetBuffer.length); - } -} diff --git a/packages/NetworkStack/src/android/net/util/SharedLog.java b/packages/NetworkStack/src/android/net/util/SharedLog.java deleted file mode 100644 index 4fabf10bbd37..000000000000 --- a/packages/NetworkStack/src/android/net/util/SharedLog.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * Copyright (C) 2017 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.util; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.text.TextUtils; -import android.util.LocalLog; -import android.util.Log; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.StringJoiner; - - -/** - * Class to centralize logging functionality for tethering. - * - * All access to class methods other than dump() must be on the same thread. - * - * @hide - */ -public class SharedLog { - private static final int DEFAULT_MAX_RECORDS = 500; - private static final String COMPONENT_DELIMITER = "."; - - private enum Category { - NONE, - ERROR, - MARK, - WARN, - }; - - private final LocalLog mLocalLog; - // The tag to use for output to the system log. This is not output to the - // LocalLog because that would be redundant. - private final String mTag; - // The component (or subcomponent) of a system that is sharing this log. - // This can grow in depth if components call forSubComponent() to obtain - // their SharedLog instance. The tag is not included in the component for - // brevity. - private final String mComponent; - - public SharedLog(String tag) { - this(DEFAULT_MAX_RECORDS, tag); - } - - public SharedLog(int maxRecords, String tag) { - this(new LocalLog(maxRecords), tag, tag); - } - - private SharedLog(LocalLog localLog, String tag, String component) { - mLocalLog = localLog; - mTag = tag; - mComponent = component; - } - - public String getTag() { - return mTag; - } - - /** - * Create a SharedLog based on this log with an additional component prefix on each logged line. - */ - public SharedLog forSubComponent(String component) { - if (!isRootLogInstance()) { - component = mComponent + COMPONENT_DELIMITER + component; - } - return new SharedLog(mLocalLog, mTag, component); - } - - /** - * Dump the contents of this log. - * - * <p>This method may be called on any thread. - */ - public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { - mLocalLog.readOnlyLocalLog().dump(fd, writer, args); - } - - ////// - // Methods that both log an entry and emit it to the system log. - ////// - - /** - * Log an error due to an exception. This does not include the exception stacktrace. - * - * <p>The log entry will be also added to the system log. - * @see #e(String, Throwable) - */ - public void e(Exception e) { - Log.e(mTag, record(Category.ERROR, e.toString())); - } - - /** - * Log an error message. - * - * <p>The log entry will be also added to the system log. - */ - public void e(String msg) { - Log.e(mTag, record(Category.ERROR, msg)); - } - - /** - * Log an error due to an exception, with the exception stacktrace if provided. - * - * <p>The error and exception message appear in the shared log, but the stacktrace is only - * logged in general log output (logcat). The log entry will be also added to the system log. - */ - public void e(@NonNull String msg, @Nullable Throwable exception) { - if (exception == null) { - e(msg); - return; - } - Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception); - } - - /** - * Log an informational message. - * - * <p>The log entry will be also added to the system log. - */ - public void i(String msg) { - Log.i(mTag, record(Category.NONE, msg)); - } - - /** - * Log a warning message. - * - * <p>The log entry will be also added to the system log. - */ - public void w(String msg) { - Log.w(mTag, record(Category.WARN, msg)); - } - - ////// - // Methods that only log an entry (and do NOT emit to the system log). - ////// - - /** - * Log a general message to be only included in the in-memory log. - * - * <p>The log entry will *not* be added to the system log. - */ - public void log(String msg) { - record(Category.NONE, msg); - } - - /** - * Log a general, formatted message to be only included in the in-memory log. - * - * <p>The log entry will *not* be added to the system log. - * @see String#format(String, Object...) - */ - public void logf(String fmt, Object... args) { - log(String.format(fmt, args)); - } - - /** - * Log a message with MARK level. - * - * <p>The log entry will *not* be added to the system log. - */ - public void mark(String msg) { - record(Category.MARK, msg); - } - - private String record(Category category, String msg) { - final String entry = logLine(category, msg); - mLocalLog.log(entry); - return entry; - } - - private String logLine(Category category, String msg) { - final StringJoiner sj = new StringJoiner(" "); - if (!isRootLogInstance()) sj.add("[" + mComponent + "]"); - if (category != Category.NONE) sj.add(category.toString()); - return sj.add(msg).toString(); - } - - // Check whether this SharedLog instance is nominally the top level in - // a potential hierarchy of shared logs (the root of a tree), - // or is a subcomponent within the hierarchy. - private boolean isRootLogInstance() { - return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag); - } -} diff --git a/packages/NetworkStack/src/android/net/util/Stopwatch.java b/packages/NetworkStack/src/android/net/util/Stopwatch.java deleted file mode 100644 index c3166999cdca..000000000000 --- a/packages/NetworkStack/src/android/net/util/Stopwatch.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2016 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.util; - -import android.os.SystemClock; - - -/** - * @hide - */ -public class Stopwatch { - private long mStartTimeMs; - private long mStopTimeMs; - - public boolean isStarted() { - return (mStartTimeMs > 0); - } - - public boolean isStopped() { - return (mStopTimeMs > 0); - } - - public boolean isRunning() { - return (isStarted() && !isStopped()); - } - - /** - * Start the Stopwatch. - */ - public Stopwatch start() { - if (!isStarted()) { - mStartTimeMs = SystemClock.elapsedRealtime(); - } - return this; - } - - /** - * Stop the Stopwatch. - * @return the total time recorded, in milliseconds, or 0 if not started. - */ - public long stop() { - if (isRunning()) { - mStopTimeMs = SystemClock.elapsedRealtime(); - } - // Return either the delta after having stopped, or 0. - return (mStopTimeMs - mStartTimeMs); - } - - /** - * Return the total time recorded to date, in milliseconds. - * If the Stopwatch is not running, returns the same value as stop(), - * i.e. either the total time recorded before stopping or 0. - */ - public long lap() { - if (isRunning()) { - return (SystemClock.elapsedRealtime() - mStartTimeMs); - } else { - return stop(); - } - } - - /** - * Reset the Stopwatch. It will be stopped when this method returns. - */ - public void reset() { - mStartTimeMs = 0; - mStopTimeMs = 0; - } -} diff --git a/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallDetectionStats.java b/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallDetectionStats.java deleted file mode 100644 index 2523ecd4ea20..000000000000 --- a/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallDetectionStats.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * 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.networkstack.metrics; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.util.NetworkStackUtils; -import android.net.wifi.WifiInfo; - -import com.android.internal.util.HexDump; -import com.android.server.connectivity.nano.CellularData; -import com.android.server.connectivity.nano.DataStallEventProto; -import com.android.server.connectivity.nano.DnsEvent; -import com.android.server.connectivity.nano.WifiData; - -import com.google.protobuf.nano.MessageNano; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -/** - * Class to record the stats of detection level information for data stall. - * - * @hide - */ -public final class DataStallDetectionStats { - private static final int UNKNOWN_SIGNAL_STRENGTH = -1; - @NonNull - final byte[] mCellularInfo; - @NonNull - final byte[] mWifiInfo; - @NonNull - final byte[] mDns; - final int mEvaluationType; - final int mNetworkType; - - public DataStallDetectionStats(@Nullable byte[] cell, @Nullable byte[] wifi, - @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType) { - mCellularInfo = emptyCellDataIfNull(cell); - mWifiInfo = emptyWifiInfoIfNull(wifi); - - DnsEvent dns = new DnsEvent(); - dns.dnsReturnCode = returnCode; - dns.dnsTime = dnsTime; - mDns = MessageNano.toByteArray(dns); - mEvaluationType = evalType; - mNetworkType = netType; - } - - private byte[] emptyCellDataIfNull(@Nullable byte[] cell) { - if (cell != null) return cell; - - CellularData data = new CellularData(); - data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_UNKNOWN; - data.networkMccmnc = ""; - data.simMccmnc = ""; - data.signalStrength = UNKNOWN_SIGNAL_STRENGTH; - return MessageNano.toByteArray(data); - } - - private byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) { - if (wifi != null) return wifi; - - WifiData data = new WifiData(); - data.wifiBand = DataStallEventProto.AP_BAND_UNKNOWN; - data.signalStrength = UNKNOWN_SIGNAL_STRENGTH; - return MessageNano.toByteArray(data); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("type: ").append(mNetworkType) - .append(", evaluation type: ") - .append(mEvaluationType) - .append(", wifi info: ") - .append(HexDump.toHexString(mWifiInfo)) - .append(", cell info: ") - .append(HexDump.toHexString(mCellularInfo)) - .append(", dns: ") - .append(HexDump.toHexString(mDns)); - return sb.toString(); - } - - @Override - public boolean equals(@Nullable final Object o) { - if (!(o instanceof DataStallDetectionStats)) return false; - final DataStallDetectionStats other = (DataStallDetectionStats) o; - return (mNetworkType == other.mNetworkType) - && (mEvaluationType == other.mEvaluationType) - && Arrays.equals(mWifiInfo, other.mWifiInfo) - && Arrays.equals(mCellularInfo, other.mCellularInfo) - && Arrays.equals(mDns, other.mDns); - } - - @Override - public int hashCode() { - return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns); - } - - /** - * Utility to create an instance of {@Link DataStallDetectionStats} - * - * @hide - */ - public static class Builder { - @Nullable - private byte[] mCellularInfo; - @Nullable - private byte[] mWifiInfo; - @NonNull - private final List<Integer> mDnsReturnCode = new ArrayList<Integer>(); - @NonNull - private final List<Long> mDnsTimeStamp = new ArrayList<Long>(); - private int mEvaluationType; - private int mNetworkType; - - /** - * Add a dns event into Builder. - * - * @param code the return code of the dns event. - * @param timeMs the elapsedRealtime in ms that the the dns event was received from netd. - * @return {@code this} {@link Builder} instance. - */ - public Builder addDnsEvent(int code, long timeMs) { - mDnsReturnCode.add(code); - mDnsTimeStamp.add(timeMs); - return this; - } - - /** - * Set the dns evaluation type into Builder. - * - * @param type the return code of the dns event. - * @return {@code this} {@link Builder} instance. - */ - public Builder setEvaluationType(int type) { - mEvaluationType = type; - return this; - } - - /** - * Set the network type into Builder. - * - * @param type the network type of the logged network. - * @return {@code this} {@link Builder} instance. - */ - public Builder setNetworkType(int type) { - mNetworkType = type; - return this; - } - - /** - * Set the wifi data into Builder. - * - * @param info a {@link WifiInfo} of the connected wifi network. - * @return {@code this} {@link Builder} instance. - */ - public Builder setWiFiData(@Nullable final WifiInfo info) { - WifiData data = new WifiData(); - data.wifiBand = getWifiBand(info); - data.signalStrength = (info != null) ? info.getRssi() : UNKNOWN_SIGNAL_STRENGTH; - mWifiInfo = MessageNano.toByteArray(data); - return this; - } - - private static int getWifiBand(@Nullable final WifiInfo info) { - if (info == null) return DataStallEventProto.AP_BAND_UNKNOWN; - - int freq = info.getFrequency(); - // Refer to ScanResult.is5GHz() and ScanResult.is24GHz(). - if (freq > 4900 && freq < 5900) { - return DataStallEventProto.AP_BAND_5GHZ; - } else if (freq > 2400 && freq < 2500) { - return DataStallEventProto.AP_BAND_2GHZ; - } else { - return DataStallEventProto.AP_BAND_UNKNOWN; - } - } - - /** - * Set the cellular data into Builder. - * - * @param radioType the radio technology of the logged cellular network. - * @param roaming a boolean indicates if logged cellular network is roaming or not. - * @param networkMccmnc the mccmnc of the camped network. - * @param simMccmnc the mccmnc of the sim. - * @return {@code this} {@link Builder} instance. - */ - public Builder setCellData(int radioType, boolean roaming, - @NonNull String networkMccmnc, @NonNull String simMccmnc, int ss) { - CellularData data = new CellularData(); - data.ratType = radioType; - data.isRoaming = roaming; - data.networkMccmnc = networkMccmnc; - data.simMccmnc = simMccmnc; - data.signalStrength = ss; - mCellularInfo = MessageNano.toByteArray(data); - return this; - } - - /** - * Create a new {@Link DataStallDetectionStats}. - */ - public DataStallDetectionStats build() { - return new DataStallDetectionStats(mCellularInfo, mWifiInfo, - NetworkStackUtils.convertToIntArray(mDnsReturnCode), - NetworkStackUtils.convertToLongArray(mDnsTimeStamp), - mEvaluationType, mNetworkType); - } - } -} diff --git a/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallStatsUtils.java b/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallStatsUtils.java deleted file mode 100644 index 93089017fd47..000000000000 --- a/packages/NetworkStack/src/com/android/networkstack/metrics/DataStallStatsUtils.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * 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.networkstack.metrics; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.captiveportal.CaptivePortalProbeResult; -import android.util.Log; - -import com.android.internal.util.HexDump; -import com.android.server.connectivity.nano.DataStallEventProto; - -/** - * Collection of utilities for data stall metrics. - * - * To see if the logs are properly sent to statsd, execute following command. - * - * $ adb shell cmd stats print-logs - * $ adb logcat | grep statsd OR $ adb logcat -b stats - * - * @hide - */ -public class DataStallStatsUtils { - private static final String TAG = DataStallStatsUtils.class.getSimpleName(); - private static final boolean DBG = false; - - private static int probeResultToEnum(@Nullable final CaptivePortalProbeResult result) { - if (result == null) return DataStallEventProto.INVALID; - - if (result.isSuccessful()) { - return DataStallEventProto.VALID; - } else if (result.isPortal()) { - return DataStallEventProto.PORTAL; - } else if (result.isPartialConnectivity()) { - return DataStallEventProto.PARTIAL; - } else { - return DataStallEventProto.INVALID; - } - } - - /** - * Write the metric to {@link StatsLog}. - */ - public static void write(@NonNull final DataStallDetectionStats stats, - @NonNull final CaptivePortalProbeResult result) { - int validationResult = probeResultToEnum(result); - if (DBG) { - Log.d(TAG, "write: " + stats + " with result: " + validationResult - + ", dns: " + HexDump.toHexString(stats.mDns)); - } - NetworkStackStatsLog.write(NetworkStackStatsLog.DATA_STALL_EVENT, - stats.mEvaluationType, - validationResult, - stats.mNetworkType, - stats.mWifiInfo, - stats.mCellularInfo, - stats.mDns); - } -} diff --git a/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java b/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java deleted file mode 100644 index 4767d5574a00..000000000000 --- a/packages/NetworkStack/src/com/android/networkstack/util/DnsUtils.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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.networkstack.util; - -import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP; -import static android.net.DnsResolver.TYPE_A; -import static android.net.DnsResolver.TYPE_AAAA; - -import android.annotation.NonNull; -import android.net.DnsResolver; -import android.net.Network; -import android.net.TrafficStats; -import android.util.Log; - -import com.android.internal.util.TrafficStatsConstants; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -/** - * Collection of utilities for dns query. - */ -public class DnsUtils { - // Decide what queries to make depending on what IP addresses are on the system. - public static final int TYPE_ADDRCONFIG = -1; - private static final String TAG = DnsUtils.class.getSimpleName(); - - /** - * Return both A and AAAA query results regardless the ip address type of the giving network. - * Used for probing in NetworkMonitor. - */ - @NonNull - public static InetAddress[] getAllByName(@NonNull final DnsResolver dnsResolver, - @NonNull final Network network, @NonNull String host, int timeout) - throws UnknownHostException { - final List<InetAddress> result = new ArrayList<InetAddress>(); - - try { - result.addAll(Arrays.asList( - getAllByName(dnsResolver, network, host, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, - timeout))); - } catch (UnknownHostException e) { - // Might happen if the host is v4-only, still need to query TYPE_A - } - try { - result.addAll(Arrays.asList( - getAllByName(dnsResolver, network, host, TYPE_A, FLAG_NO_CACHE_LOOKUP, - timeout))); - } catch (UnknownHostException e) { - // Might happen if the host is v6-only, still need to return AAAA answers - } - if (result.size() == 0) { - throw new UnknownHostException(host); - } - return result.toArray(new InetAddress[0]); - } - - /** - * Return dns query result based on the given QueryType(TYPE_A, TYPE_AAAA) or TYPE_ADDRCONFIG. - * Used for probing in NetworkMonitor. - */ - @NonNull - public static InetAddress[] getAllByName(@NonNull final DnsResolver dnsResolver, - @NonNull final Network network, @NonNull final String host, int type, int flag, - int timeoutMs) throws UnknownHostException { - final CountDownLatch latch = new CountDownLatch(1); - final AtomicReference<List<InetAddress>> resultRef = new AtomicReference<>(); - - final DnsResolver.Callback<List<InetAddress>> callback = - new DnsResolver.Callback<List<InetAddress>>() { - @Override - public void onAnswer(List<InetAddress> answer, int rcode) { - if (rcode == 0) { - resultRef.set(answer); - } - latch.countDown(); - } - - @Override - public void onError(@NonNull DnsResolver.DnsException e) { - Log.d(TAG, "DNS error resolving " + host + ": " + e.getMessage()); - latch.countDown(); - } - }; - final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_PROBE); - - if (type == TYPE_ADDRCONFIG) { - dnsResolver.query(network, host, flag, r -> r.run(), null /* cancellationSignal */, - callback); - } else { - dnsResolver.query(network, host, type, flag, r -> r.run(), - null /* cancellationSignal */, callback); - } - - TrafficStats.setThreadStatsTag(oldTag); - - try { - latch.await(timeoutMs, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - } - - final List<InetAddress> result = resultRef.get(); - if (result == null || result.size() == 0) { - throw new UnknownHostException(host); - } - - return result.toArray(new InetAddress[0]); - } -} diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserver.java b/packages/NetworkStack/src/com/android/server/NetworkObserver.java deleted file mode 100644 index cccec0bb5d40..000000000000 --- a/packages/NetworkStack/src/com/android/server/NetworkObserver.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.server; - -import android.net.LinkAddress; -import android.net.RouteInfo; - -/** - * Observer for network events, to use with {@link NetworkObserverRegistry}. - */ -public interface NetworkObserver { - - /** - * @see android.net.INetdUnsolicitedEventListener#onInterfaceChanged(java.lang.String, boolean) - */ - default void onInterfaceChanged(String ifName, boolean up) {} - - /** - * @see android.net.INetdUnsolicitedEventListener#onInterfaceRemoved(String) - */ - default void onInterfaceRemoved(String ifName) {} - - /** - * @see android.net.INetdUnsolicitedEventListener - * #onInterfaceAddressUpdated(String, String, int, int) - */ - default void onInterfaceAddressUpdated(LinkAddress address, String ifName) {} - - /** - * @see android.net.INetdUnsolicitedEventListener - * #onInterfaceAddressRemoved(String, String, int, int) - */ - default void onInterfaceAddressRemoved(LinkAddress address, String ifName) {} - - /** - * @see android.net.INetdUnsolicitedEventListener#onInterfaceLinkStateChanged(String, boolean) - */ - default void onInterfaceLinkStateChanged(String ifName, boolean up) {} - - /** - * @see android.net.INetdUnsolicitedEventListener#onInterfaceAdded(String) - */ - default void onInterfaceAdded(String ifName) {} - - /** - * @see android.net.INetdUnsolicitedEventListener - * #onInterfaceClassActivityChanged(boolean, int, long, int) - */ - default void onInterfaceClassActivityChanged( - boolean isActive, int label, long timestamp, int uid) {} - - /** - * @see android.net.INetdUnsolicitedEventListener#onQuotaLimitReached(String, String) - */ - default void onQuotaLimitReached(String alertName, String ifName) {} - - /** - * @see android.net.INetdUnsolicitedEventListener - * #onInterfaceDnsServerInfo(String, long, String[]) - */ - default void onInterfaceDnsServerInfo(String ifName, long lifetime, String[] servers) {} - - /** - * @see android.net.INetdUnsolicitedEventListener - * #onRouteChanged(boolean, String, String, String) - */ - default void onRouteUpdated(RouteInfo route) {} - - /** - * @see android.net.INetdUnsolicitedEventListener - * #onRouteChanged(boolean, String, String, String) - */ - default void onRouteRemoved(RouteInfo route) {} -} diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java b/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java deleted file mode 100644 index afe166ba9246..000000000000 --- a/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * 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.server; - -import static android.net.RouteInfo.RTN_UNICAST; - -import android.annotation.NonNull; -import android.net.INetd; -import android.net.INetdUnsolicitedEventListener; -import android.net.InetAddresses; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.RouteInfo; -import android.os.Handler; -import android.os.RemoteException; -import android.util.Log; - -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -/** - * A class for reporting network events to clients. - * - * Implements INetdUnsolicitedEventListener and registers with netd, and relays those events to - * all INetworkManagementEventObserver objects that have registered with it. - */ -public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub { - private static final String TAG = NetworkObserverRegistry.class.getSimpleName(); - - /** - * Constructs a new NetworkObserverRegistry. - * - * <p>Only one registry should be used per process since netd will silently ignore multiple - * registrations from the same process. - */ - NetworkObserverRegistry() {} - - /** - * Start listening for Netd events. - * - * <p>This should be called before allowing any observer to be registered. - */ - void register(@NonNull INetd netd) throws RemoteException { - netd.registerUnsolicitedEventListener(this); - } - - private final ConcurrentHashMap<NetworkObserver, Optional<Handler>> mObservers = - new ConcurrentHashMap<>(); - - /** - * Registers the specified observer and start sending callbacks to it. - * This method may be called on any thread. - */ - public void registerObserver(@NonNull NetworkObserver observer, @NonNull Handler handler) { - if (handler == null) { - throw new IllegalArgumentException("handler must be non-null"); - } - mObservers.put(observer, Optional.of(handler)); - } - - /** - * Registers the specified observer, and start sending callbacks to it. - * - * <p>This method must only be called with callbacks that are nonblocking, such as callbacks - * that only send a message to a StateMachine. - */ - public void registerObserverForNonblockingCallback(@NonNull NetworkObserver observer) { - mObservers.put(observer, Optional.empty()); - } - - /** - * Unregisters the specified observer and stop sending callbacks to it. - * This method may be called on any thread. - */ - public void unregisterObserver(@NonNull NetworkObserver observer) { - mObservers.remove(observer); - } - - @FunctionalInterface - private interface NetworkObserverEventCallback { - void sendCallback(NetworkObserver o); - } - - private void invokeForAllObservers(@NonNull final NetworkObserverEventCallback callback) { - // ConcurrentHashMap#entrySet is weakly consistent: observers that were in the map before - // creation will be processed, those added during traversal may or may not. - for (Map.Entry<NetworkObserver, Optional<Handler>> entry : mObservers.entrySet()) { - final NetworkObserver observer = entry.getKey(); - final Optional<Handler> handler = entry.getValue(); - if (handler.isPresent()) { - handler.get().post(() -> callback.sendCallback(observer)); - return; - } - - try { - callback.sendCallback(observer); - } catch (RuntimeException e) { - Log.e(TAG, "Error sending callback to observer", e); - } - } - } - - @Override - public void onInterfaceClassActivityChanged(boolean isActive, - int label, long timestamp, int uid) { - invokeForAllObservers(o -> o.onInterfaceClassActivityChanged( - isActive, label, timestamp, uid)); - } - - /** - * Notify our observers of a limit reached. - */ - @Override - public void onQuotaLimitReached(String alertName, String ifName) { - invokeForAllObservers(o -> o.onQuotaLimitReached(alertName, ifName)); - } - - @Override - public void onInterfaceDnsServerInfo(String ifName, long lifetime, String[] servers) { - invokeForAllObservers(o -> o.onInterfaceDnsServerInfo(ifName, lifetime, servers)); - } - - @Override - public void onInterfaceAddressUpdated(String addr, String ifName, int flags, int scope) { - final LinkAddress address = new LinkAddress(addr, flags, scope); - invokeForAllObservers(o -> o.onInterfaceAddressUpdated(address, ifName)); - } - - @Override - public void onInterfaceAddressRemoved(String addr, - String ifName, int flags, int scope) { - final LinkAddress address = new LinkAddress(addr, flags, scope); - invokeForAllObservers(o -> o.onInterfaceAddressRemoved(address, ifName)); - } - - @Override - public void onInterfaceAdded(String ifName) { - invokeForAllObservers(o -> o.onInterfaceAdded(ifName)); - } - - @Override - public void onInterfaceRemoved(String ifName) { - invokeForAllObservers(o -> o.onInterfaceRemoved(ifName)); - } - - @Override - public void onInterfaceChanged(String ifName, boolean up) { - invokeForAllObservers(o -> o.onInterfaceChanged(ifName, up)); - } - - @Override - public void onInterfaceLinkStateChanged(String ifName, boolean up) { - invokeForAllObservers(o -> o.onInterfaceLinkStateChanged(ifName, up)); - } - - @Override - public void onRouteChanged(boolean updated, String route, String gateway, String ifName) { - final RouteInfo processRoute = new RouteInfo(new IpPrefix(route), - ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway), - ifName, RTN_UNICAST); - if (updated) { - invokeForAllObservers(o -> o.onRouteUpdated(processRoute)); - } else { - invokeForAllObservers(o -> o.onRouteRemoved(processRoute)); - } - } - - @Override - public void onStrictCleartextDetected(int uid, String hex) {} - - @Override - public int getInterfaceVersion() { - return INetdUnsolicitedEventListener.VERSION; - } -} diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java deleted file mode 100644 index c394d4c4cfd3..000000000000 --- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; -import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR; - -import static com.android.server.util.PermissionUtil.checkDumpPermission; -import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.net.ConnectivityManager; -import android.net.IIpMemoryStore; -import android.net.IIpMemoryStoreCallbacks; -import android.net.INetd; -import android.net.INetworkMonitor; -import android.net.INetworkMonitorCallbacks; -import android.net.INetworkStackConnector; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.PrivateDnsConfigParcel; -import android.net.dhcp.DhcpServer; -import android.net.dhcp.DhcpServingParams; -import android.net.dhcp.DhcpServingParamsParcel; -import android.net.dhcp.IDhcpServerCallbacks; -import android.net.ip.IIpClientCallbacks; -import android.net.ip.IpClient; -import android.net.shared.PrivateDnsConfig; -import android.net.util.SharedLog; -import android.os.IBinder; -import android.os.RemoteException; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.IndentingPrintWriter; -import com.android.server.connectivity.NetworkMonitor; -import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.lang.ref.WeakReference; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Android service used to start the network stack when bound to via an intent. - * - * <p>The service returns a binder for the system server to communicate with the network stack. - */ -public class NetworkStackService extends Service { - private static final String TAG = NetworkStackService.class.getSimpleName(); - private static NetworkStackConnector sConnector; - - /** - * Create a binder connector for the system server to communicate with the network stack. - * - * <p>On platforms where the network stack runs in the system server process, this method may - * be called directly instead of obtaining the connector by binding to the service. - */ - public static synchronized IBinder makeConnector(Context context) { - if (sConnector == null) { - sConnector = new NetworkStackConnector(context); - } - return sConnector; - } - - @NonNull - @Override - public IBinder onBind(Intent intent) { - return makeConnector(this); - } - - /** - * An interface for internal clients of the network stack service that can return - * or create inline instances of the service it manages. - */ - public interface NetworkStackServiceManager { - /** - * Get an instance of the IpMemoryStoreService. - */ - IIpMemoryStore getIpMemoryStoreService(); - } - - private static class NetworkStackConnector extends INetworkStackConnector.Stub - implements NetworkStackServiceManager { - private static final int NUM_VALIDATION_LOG_LINES = 20; - private final Context mContext; - private final INetd mNetd; - private final NetworkObserverRegistry mObserverRegistry; - private final ConnectivityManager mCm; - @GuardedBy("mIpClients") - private final ArrayList<WeakReference<IpClient>> mIpClients = new ArrayList<>(); - private final IpMemoryStoreService mIpMemoryStoreService; - - private static final int MAX_VALIDATION_LOGS = 10; - @GuardedBy("mValidationLogs") - private final ArrayDeque<SharedLog> mValidationLogs = new ArrayDeque<>(MAX_VALIDATION_LOGS); - - private static final int VERSION_UNKNOWN = 0; - private static final String DUMPSYS_ARG_VERSION = "version"; - - /** Version of the AIDL interfaces observed on the system */ - private final AtomicInteger mSystemAidlVersion = new AtomicInteger(VERSION_UNKNOWN); - - /** Whether different versions have been observed on interfaces provided by the system */ - private volatile boolean mConflictingSystemAidlVersions = false; - - private SharedLog addValidationLogs(Network network, String name) { - final SharedLog log = new SharedLog(NUM_VALIDATION_LOG_LINES, network + " - " + name); - synchronized (mValidationLogs) { - while (mValidationLogs.size() >= MAX_VALIDATION_LOGS) { - mValidationLogs.removeLast(); - } - mValidationLogs.addFirst(log); - } - return log; - } - - NetworkStackConnector(Context context) { - mContext = context; - mNetd = INetd.Stub.asInterface( - (IBinder) context.getSystemService(Context.NETD_SERVICE)); - mObserverRegistry = new NetworkObserverRegistry(); - mCm = context.getSystemService(ConnectivityManager.class); - mIpMemoryStoreService = new IpMemoryStoreService(context); - - try { - mObserverRegistry.register(mNetd); - } catch (RemoteException e) { - mLog.e("Error registering observer on Netd", e); - } - } - - private void updateSystemAidlVersion(final int version) { - final int previousVersion = mSystemAidlVersion.getAndSet(version); - if (previousVersion != VERSION_UNKNOWN && previousVersion != version) { - mConflictingSystemAidlVersions = true; - } - } - - @NonNull - private final SharedLog mLog = new SharedLog(TAG); - - @Override - public void makeDhcpServer(@NonNull String ifName, @NonNull DhcpServingParamsParcel params, - @NonNull IDhcpServerCallbacks cb) throws RemoteException { - checkNetworkStackCallingPermission(); - updateSystemAidlVersion(cb.getInterfaceVersion()); - final DhcpServer server; - try { - server = new DhcpServer( - ifName, - DhcpServingParams.fromParcelableObject(params), - mLog.forSubComponent(ifName + ".DHCP")); - } catch (DhcpServingParams.InvalidParameterException e) { - mLog.e("Invalid DhcpServingParams", e); - cb.onDhcpServerCreated(STATUS_INVALID_ARGUMENT, null); - return; - } catch (Exception e) { - mLog.e("Unknown error starting DhcpServer", e); - cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null); - return; - } - cb.onDhcpServerCreated(STATUS_SUCCESS, server); - } - - @Override - public void makeNetworkMonitor(Network network, String name, INetworkMonitorCallbacks cb) - throws RemoteException { - updateSystemAidlVersion(cb.getInterfaceVersion()); - final SharedLog log = addValidationLogs(network, name); - final NetworkMonitor nm = new NetworkMonitor(mContext, cb, network, log); - cb.onNetworkMonitorCreated(new NetworkMonitorImpl(nm)); - } - - @Override - public void makeIpClient(String ifName, IIpClientCallbacks cb) throws RemoteException { - updateSystemAidlVersion(cb.getInterfaceVersion()); - final IpClient ipClient = new IpClient(mContext, ifName, cb, mObserverRegistry, this); - - synchronized (mIpClients) { - final Iterator<WeakReference<IpClient>> it = mIpClients.iterator(); - while (it.hasNext()) { - final IpClient ipc = it.next().get(); - if (ipc == null) { - it.remove(); - } - } - mIpClients.add(new WeakReference<>(ipClient)); - } - - cb.onIpClientCreated(ipClient.makeConnector()); - } - - @Override - public IIpMemoryStore getIpMemoryStoreService() { - return mIpMemoryStoreService; - } - - @Override - public void fetchIpMemoryStore(@NonNull final IIpMemoryStoreCallbacks cb) - throws RemoteException { - updateSystemAidlVersion(cb.getInterfaceVersion()); - cb.onIpMemoryStoreFetched(mIpMemoryStoreService); - } - - @Override - protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout, - @Nullable String[] args) { - checkDumpPermission(); - if (args != null && args.length >= 1 && DUMPSYS_ARG_VERSION.equals(args[0])) { - dumpVersion(fout); - return; - } - - final IndentingPrintWriter pw = new IndentingPrintWriter(fout, " "); - pw.println("NetworkStack logs:"); - mLog.dump(fd, pw, args); - - // Dump full IpClient logs for non-GCed clients - pw.println(); - pw.println("Recently active IpClient logs:"); - final ArrayList<IpClient> ipClients = new ArrayList<>(); - final HashSet<String> dumpedIpClientIfaces = new HashSet<>(); - synchronized (mIpClients) { - for (WeakReference<IpClient> ipcRef : mIpClients) { - final IpClient ipc = ipcRef.get(); - if (ipc != null) { - ipClients.add(ipc); - } - } - } - - for (IpClient ipc : ipClients) { - pw.println(ipc.getName()); - pw.increaseIndent(); - ipc.dump(fd, pw, args); - pw.decreaseIndent(); - dumpedIpClientIfaces.add(ipc.getInterfaceName()); - } - - // State machine and connectivity metrics logs are kept for GCed IpClients - pw.println(); - pw.println("Other IpClient logs:"); - IpClient.dumpAllLogs(fout, dumpedIpClientIfaces); - - pw.println(); - pw.println("Validation logs (most recent first):"); - synchronized (mValidationLogs) { - for (SharedLog p : mValidationLogs) { - pw.println(p.getTag()); - pw.increaseIndent(); - p.dump(fd, pw, args); - pw.decreaseIndent(); - } - } - } - - /** - * Dump version information of the module and detected system version. - */ - private void dumpVersion(@NonNull PrintWriter fout) { - fout.println("NetworkStackConnector: " + this.VERSION); - fout.println("SystemServer: " + mSystemAidlVersion); - fout.println("SystemServerConflicts: " + mConflictingSystemAidlVersions); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - } - - private static class NetworkMonitorImpl extends INetworkMonitor.Stub { - private final NetworkMonitor mNm; - - NetworkMonitorImpl(NetworkMonitor nm) { - mNm = nm; - } - - @Override - public void start() { - checkNetworkStackCallingPermission(); - mNm.start(); - } - - @Override - public void launchCaptivePortalApp() { - checkNetworkStackCallingPermission(); - mNm.launchCaptivePortalApp(); - } - - @Override - public void notifyCaptivePortalAppFinished(int response) { - checkNetworkStackCallingPermission(); - mNm.notifyCaptivePortalAppFinished(response); - } - - @Override - public void setAcceptPartialConnectivity() { - checkNetworkStackCallingPermission(); - mNm.setAcceptPartialConnectivity(); - } - - @Override - public void forceReevaluation(int uid) { - checkNetworkStackCallingPermission(); - mNm.forceReevaluation(uid); - } - - @Override - public void notifyPrivateDnsChanged(PrivateDnsConfigParcel config) { - checkNetworkStackCallingPermission(); - mNm.notifyPrivateDnsSettingsChanged(PrivateDnsConfig.fromParcel(config)); - } - - @Override - public void notifyDnsResponse(int returnCode) { - checkNetworkStackCallingPermission(); - mNm.notifyDnsResponse(returnCode); - } - - @Override - public void notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) { - checkNetworkStackCallingPermission(); - mNm.notifyNetworkConnected(lp, nc); - } - - @Override - public void notifyNetworkDisconnected() { - checkNetworkStackCallingPermission(); - mNm.notifyNetworkDisconnected(); - } - - @Override - public void notifyLinkPropertiesChanged(LinkProperties lp) { - checkNetworkStackCallingPermission(); - mNm.notifyLinkPropertiesChanged(lp); - } - - @Override - public void notifyNetworkCapabilitiesChanged(NetworkCapabilities nc) { - checkNetworkStackCallingPermission(); - mNm.notifyNetworkCapabilitiesChanged(nc); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - } -} diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java deleted file mode 100644 index 8e9350d8cbbc..000000000000 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ /dev/null @@ -1,2027 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity; - -import static android.net.CaptivePortal.APP_RETURN_DISMISSED; -import static android.net.CaptivePortal.APP_RETURN_UNWANTED; -import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS; -import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC; -import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL; -import static android.net.ConnectivityManager.TYPE_MOBILE; -import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.DnsResolver.FLAG_EMPTY; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; -import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; -import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.NetworkCapabilities.TRANSPORT_WIFI; -import static android.net.captiveportal.CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs; -import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE; -import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS; -import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK; -import static android.net.metrics.ValidationProbeEvent.PROBE_PRIVDNS; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD; -import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS; -import static android.net.util.DataStallUtils.DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD; -import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_EVALUATION_TYPES; -import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS; -import static android.net.util.DataStallUtils.DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS; -import static android.net.util.DataStallUtils.DEFAULT_DNS_LOG_SIZE; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_URL; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_HTTPS_URL; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_HTTP_URL; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_IGNORE; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_MODE_PROMPT; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USER_AGENT; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; -import static android.net.util.NetworkStackUtils.NAMESPACE_CONNECTIVITY; -import static android.net.util.NetworkStackUtils.isEmpty; - -import static com.android.networkstack.util.DnsUtils.TYPE_ADDRCONFIG; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.PendingIntent; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.DnsResolver; -import android.net.INetworkMonitor; -import android.net.INetworkMonitorCallbacks; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.ProxyInfo; -import android.net.TrafficStats; -import android.net.Uri; -import android.net.captiveportal.CaptivePortalProbeResult; -import android.net.captiveportal.CaptivePortalProbeSpec; -import android.net.metrics.IpConnectivityLog; -import android.net.metrics.NetworkEvent; -import android.net.metrics.ValidationProbeEvent; -import android.net.shared.NetworkMonitorUtils; -import android.net.shared.PrivateDnsConfig; -import android.net.util.NetworkStackUtils; -import android.net.util.SharedLog; -import android.net.util.Stopwatch; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Bundle; -import android.os.Message; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.provider.Settings; -import android.telephony.AccessNetworkConstants; -import android.telephony.CellSignalStrength; -import android.telephony.NetworkRegistrationInfo; -import android.telephony.ServiceState; -import android.telephony.SignalStrength; -import android.telephony.TelephonyManager; -import android.text.TextUtils; -import android.util.Log; -import android.util.Pair; - -import androidx.annotation.ArrayRes; -import androidx.annotation.StringRes; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.RingBufferIndices; -import com.android.internal.util.State; -import com.android.internal.util.StateMachine; -import com.android.internal.util.TrafficStatsConstants; -import com.android.networkstack.R; -import com.android.networkstack.metrics.DataStallDetectionStats; -import com.android.networkstack.metrics.DataStallStatsUtils; -import com.android.networkstack.util.DnsUtils; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Random; -import java.util.UUID; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.Function; - -/** - * {@hide} - */ -public class NetworkMonitor extends StateMachine { - private static final String TAG = NetworkMonitor.class.getSimpleName(); - private static final boolean DBG = true; - private static final boolean VDBG = false; - private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG); - private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) " - + "AppleWebKit/537.36 (KHTML, like Gecko) " - + "Chrome/60.0.3112.32 Safari/537.36"; - - @VisibleForTesting - static final String CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT = - "captive_portal_dns_probe_timeout"; - - private static final int SOCKET_TIMEOUT_MS = 10000; - private static final int PROBE_TIMEOUT_MS = 3000; - - enum EvaluationResult { - VALIDATED(true), - CAPTIVE_PORTAL(false); - final boolean mIsValidated; - EvaluationResult(boolean isValidated) { - this.mIsValidated = isValidated; - } - } - - enum ValidationStage { - FIRST_VALIDATION(true), - REVALIDATION(false); - final boolean mIsFirstValidation; - ValidationStage(boolean isFirstValidation) { - this.mIsFirstValidation = isFirstValidation; - } - } - - /** - * ConnectivityService has sent a notification to indicate that network has connected. - * Initiates Network Validation. - */ - private static final int CMD_NETWORK_CONNECTED = 1; - - /** - * Message to self indicating it's time to evaluate a network's connectivity. - * arg1 = Token to ignore old messages. - */ - private static final int CMD_REEVALUATE = 6; - - /** - * ConnectivityService has sent a notification to indicate that network has disconnected. - */ - private static final int CMD_NETWORK_DISCONNECTED = 7; - - /** - * Force evaluation even if it has succeeded in the past. - * arg1 = UID responsible for requesting this reeval. Will be billed for data. - */ - private static final int CMD_FORCE_REEVALUATION = 8; - - /** - * Message to self indicating captive portal app finished. - * arg1 = one of: APP_RETURN_DISMISSED, - * APP_RETURN_UNWANTED, - * APP_RETURN_WANTED_AS_IS - * obj = mCaptivePortalLoggedInResponseToken as String - */ - private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = 9; - - /** - * Message indicating sign-in app should be launched. - * Sent by mLaunchCaptivePortalAppBroadcastReceiver when the - * user touches the sign in notification, or sent by - * ConnectivityService when the user touches the "sign into - * network" button in the wifi access point detail page. - */ - private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = 11; - - /** - * Retest network to see if captive portal is still in place. - * arg1 = UID responsible for requesting this reeval. Will be billed for data. - * 0 indicates self-initiated, so nobody to blame. - */ - private static final int CMD_CAPTIVE_PORTAL_RECHECK = 12; - - /** - * ConnectivityService notifies NetworkMonitor of settings changes to - * Private DNS. If a DNS resolution is required, e.g. for DNS-over-TLS in - * strict mode, then an event is sent back to ConnectivityService with the - * result of the resolution attempt. - * - * A separate message is used to trigger (re)evaluation of the Private DNS - * configuration, so that the message can be handled as needed in different - * states, including being ignored until after an ongoing captive portal - * validation phase is completed. - */ - private static final int CMD_PRIVATE_DNS_SETTINGS_CHANGED = 13; - private static final int CMD_EVALUATE_PRIVATE_DNS = 15; - - /** - * Message to self indicating captive portal detection is completed. - * obj = CaptivePortalProbeResult for detection result; - */ - private static final int CMD_PROBE_COMPLETE = 16; - - /** - * ConnectivityService notifies NetworkMonitor of DNS query responses event. - * arg1 = returncode in OnDnsEvent which indicates the response code for the DNS query. - */ - private static final int EVENT_DNS_NOTIFICATION = 17; - - /** - * ConnectivityService notifies NetworkMonitor that the user accepts partial connectivity and - * NetworkMonitor should ignore the https probe. - */ - private static final int EVENT_ACCEPT_PARTIAL_CONNECTIVITY = 18; - - /** - * ConnectivityService notifies NetworkMonitor of changed LinkProperties. - * obj = new LinkProperties. - */ - private static final int EVENT_LINK_PROPERTIES_CHANGED = 19; - - /** - * ConnectivityService notifies NetworkMonitor of changed NetworkCapabilities. - * obj = new NetworkCapabilities. - */ - private static final int EVENT_NETWORK_CAPABILITIES_CHANGED = 20; - - // Start mReevaluateDelayMs at this value and double. - private static final int INITIAL_REEVALUATE_DELAY_MS = 1000; - private static final int MAX_REEVALUATE_DELAY_MS = 10 * 60 * 1000; - // Before network has been evaluated this many times, ignore repeated reevaluate requests. - private static final int IGNORE_REEVALUATE_ATTEMPTS = 5; - private int mReevaluateToken = 0; - private static final int NO_UID = 0; - private static final int INVALID_UID = -1; - private int mUidResponsibleForReeval = INVALID_UID; - // Stop blaming UID that requested re-evaluation after this many attempts. - private static final int BLAME_FOR_EVALUATION_ATTEMPTS = 5; - // Delay between reevaluations once a captive portal has been found. - private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10 * 60 * 1000; - - private String mPrivateDnsProviderHostname = ""; - - private final Context mContext; - private final INetworkMonitorCallbacks mCallback; - private final Network mCleartextDnsNetwork; - private final Network mNetwork; - private final TelephonyManager mTelephonyManager; - private final WifiManager mWifiManager; - private final ConnectivityManager mCm; - private final IpConnectivityLog mMetricsLog; - private final Dependencies mDependencies; - private final DataStallStatsUtils mDetectionStatsUtils; - - // Configuration values for captive portal detection probes. - private final String mCaptivePortalUserAgent; - private final URL mCaptivePortalHttpsUrl; - private final URL mCaptivePortalHttpUrl; - private final URL[] mCaptivePortalFallbackUrls; - @Nullable - private final CaptivePortalProbeSpec[] mCaptivePortalFallbackSpecs; - - private NetworkCapabilities mNetworkCapabilities; - private LinkProperties mLinkProperties; - - @VisibleForTesting - protected boolean mIsCaptivePortalCheckEnabled; - - private boolean mUseHttps; - // The total number of captive portal detection attempts for this NetworkMonitor instance. - private int mValidations = 0; - - // Set if the user explicitly selected "Do not use this network" in captive portal sign-in app. - private boolean mUserDoesNotWant = false; - // Avoids surfacing "Sign in to network" notification. - private boolean mDontDisplaySigninNotification = false; - - private final State mDefaultState = new DefaultState(); - private final State mValidatedState = new ValidatedState(); - private final State mMaybeNotifyState = new MaybeNotifyState(); - private final State mEvaluatingState = new EvaluatingState(); - private final State mCaptivePortalState = new CaptivePortalState(); - private final State mEvaluatingPrivateDnsState = new EvaluatingPrivateDnsState(); - private final State mProbingState = new ProbingState(); - private final State mWaitingForNextProbeState = new WaitingForNextProbeState(); - - private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null; - - private final SharedLog mValidationLogs; - - private final Stopwatch mEvaluationTimer = new Stopwatch(); - - // This variable is set before transitioning to the mCaptivePortalState. - private CaptivePortalProbeResult mLastPortalProbeResult = CaptivePortalProbeResult.FAILED; - - // Random generator to select fallback URL index - private final Random mRandom; - private int mNextFallbackUrlIndex = 0; - - - private int mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS; - private int mEvaluateAttempts = 0; - private volatile int mProbeToken = 0; - private final int mConsecutiveDnsTimeoutThreshold; - private final int mDataStallMinEvaluateTime; - private final int mDataStallValidDnsTimeThreshold; - private final int mDataStallEvaluationType; - private final DnsStallDetector mDnsStallDetector; - private long mLastProbeTime; - // Set to true if data stall is suspected and reset to false after metrics are sent to statsd. - private boolean mCollectDataStallMetrics; - private boolean mAcceptPartialConnectivity; - - public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, - SharedLog validationLog) { - this(context, cb, network, new IpConnectivityLog(), validationLog, - Dependencies.DEFAULT, new DataStallStatsUtils()); - } - - @VisibleForTesting - protected NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, - IpConnectivityLog logger, SharedLog validationLogs, - Dependencies deps, DataStallStatsUtils detectionStatsUtils) { - // Add suffix indicating which NetworkMonitor we're talking about. - super(TAG + "/" + network.toString()); - - // Logs with a tag of the form given just above, e.g. - // <timestamp> 862 2402 D NetworkMonitor/NetworkAgentInfo [WIFI () - 100]: ... - setDbg(VDBG); - - mContext = context; - mMetricsLog = logger; - mValidationLogs = validationLogs; - mCallback = cb; - mDependencies = deps; - mDetectionStatsUtils = detectionStatsUtils; - mNetwork = network; - mCleartextDnsNetwork = deps.getPrivateDnsBypassNetwork(network); - mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); - mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - - // CHECKSTYLE:OFF IndentationCheck - addState(mDefaultState); - addState(mMaybeNotifyState, mDefaultState); - addState(mEvaluatingState, mMaybeNotifyState); - addState(mProbingState, mEvaluatingState); - addState(mWaitingForNextProbeState, mEvaluatingState); - addState(mCaptivePortalState, mMaybeNotifyState); - addState(mEvaluatingPrivateDnsState, mDefaultState); - addState(mValidatedState, mDefaultState); - setInitialState(mDefaultState); - // CHECKSTYLE:ON IndentationCheck - - mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled(); - mUseHttps = getUseHttpsValidation(); - mCaptivePortalUserAgent = getCaptivePortalUserAgent(); - mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl()); - mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl()); - mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls(); - mCaptivePortalFallbackSpecs = makeCaptivePortalFallbackProbeSpecs(); - mRandom = deps.getRandom(); - // TODO: Evaluate to move data stall configuration to a specific class. - mConsecutiveDnsTimeoutThreshold = getConsecutiveDnsTimeoutThreshold(); - mDnsStallDetector = new DnsStallDetector(mConsecutiveDnsTimeoutThreshold); - mDataStallMinEvaluateTime = getDataStallMinEvaluateTime(); - mDataStallValidDnsTimeThreshold = getDataStallValidDnsTimeThreshold(); - mDataStallEvaluationType = getDataStallEvaluationType(); - - // Provide empty LinkProperties and NetworkCapabilities to make sure they are never null, - // even before notifyNetworkConnected. - mLinkProperties = new LinkProperties(); - mNetworkCapabilities = new NetworkCapabilities(null); - } - - /** - * ConnectivityService notifies NetworkMonitor that the user already accepted partial - * connectivity previously, so NetworkMonitor can validate the network even if it has partial - * connectivity. - */ - public void setAcceptPartialConnectivity() { - sendMessage(EVENT_ACCEPT_PARTIAL_CONNECTIVITY); - } - - /** - * Request the NetworkMonitor to reevaluate the network. - */ - public void forceReevaluation(int responsibleUid) { - sendMessage(CMD_FORCE_REEVALUATION, responsibleUid, 0); - } - - /** - * Send a notification to NetworkMonitor indicating that there was a DNS query response event. - * @param returnCode the DNS return code of the response. - */ - public void notifyDnsResponse(int returnCode) { - sendMessage(EVENT_DNS_NOTIFICATION, returnCode); - } - - /** - * Send a notification to NetworkMonitor indicating that private DNS settings have changed. - * @param newCfg The new private DNS configuration. - */ - public void notifyPrivateDnsSettingsChanged(PrivateDnsConfig newCfg) { - // Cancel any outstanding resolutions. - removeMessages(CMD_PRIVATE_DNS_SETTINGS_CHANGED); - // Send the update to the proper thread. - sendMessage(CMD_PRIVATE_DNS_SETTINGS_CHANGED, newCfg); - } - - /** - * Send a notification to NetworkMonitor indicating that the network is now connected. - */ - public void notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) { - sendMessage(CMD_NETWORK_CONNECTED, new Pair<>( - new LinkProperties(lp), new NetworkCapabilities(nc))); - } - - private void updateConnectedNetworkAttributes(Message connectedMsg) { - final Pair<LinkProperties, NetworkCapabilities> attrs = - (Pair<LinkProperties, NetworkCapabilities>) connectedMsg.obj; - mLinkProperties = attrs.first; - mNetworkCapabilities = attrs.second; - } - - /** - * Send a notification to NetworkMonitor indicating that the network is now disconnected. - */ - public void notifyNetworkDisconnected() { - sendMessage(CMD_NETWORK_DISCONNECTED); - } - - /** - * Send a notification to NetworkMonitor indicating that link properties have changed. - */ - public void notifyLinkPropertiesChanged(final LinkProperties lp) { - sendMessage(EVENT_LINK_PROPERTIES_CHANGED, new LinkProperties(lp)); - } - - /** - * Send a notification to NetworkMonitor indicating that network capabilities have changed. - */ - public void notifyNetworkCapabilitiesChanged(final NetworkCapabilities nc) { - sendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED, new NetworkCapabilities(nc)); - } - - /** - * Request the captive portal application to be launched. - */ - public void launchCaptivePortalApp() { - sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP); - } - - /** - * Notify that the captive portal app was closed with the provided response code. - */ - public void notifyCaptivePortalAppFinished(int response) { - sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response); - } - - @Override - protected void log(String s) { - if (DBG) Log.d(TAG + "/" + mCleartextDnsNetwork.toString(), s); - } - - private void validationLog(int probeType, Object url, String msg) { - String probeName = ValidationProbeEvent.getProbeName(probeType); - validationLog(String.format("%s %s %s", probeName, url, msg)); - } - - private void validationLog(String s) { - if (DBG) log(s); - mValidationLogs.log(s); - } - - private ValidationStage validationStage() { - return 0 == mValidations ? ValidationStage.FIRST_VALIDATION : ValidationStage.REVALIDATION; - } - - private boolean isValidationRequired() { - return NetworkMonitorUtils.isValidationRequired(mNetworkCapabilities); - } - - private boolean isPrivateDnsValidationRequired() { - return NetworkMonitorUtils.isPrivateDnsValidationRequired(mNetworkCapabilities); - } - - private void notifyNetworkTested(int result, @Nullable String redirectUrl) { - try { - mCallback.notifyNetworkTested(result, redirectUrl); - } catch (RemoteException e) { - Log.e(TAG, "Error sending network test result", e); - } - } - - private void showProvisioningNotification(String action) { - try { - mCallback.showProvisioningNotification(action, mContext.getPackageName()); - } catch (RemoteException e) { - Log.e(TAG, "Error showing provisioning notification", e); - } - } - - private void hideProvisioningNotification() { - try { - mCallback.hideProvisioningNotification(); - } catch (RemoteException e) { - Log.e(TAG, "Error hiding provisioning notification", e); - } - } - - // DefaultState is the parent of all States. It exists only to handle CMD_* messages but - // does not entail any real state (hence no enter() or exit() routines). - private class DefaultState extends State { - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_NETWORK_CONNECTED: - updateConnectedNetworkAttributes(message); - logNetworkEvent(NetworkEvent.NETWORK_CONNECTED); - transitionTo(mEvaluatingState); - return HANDLED; - case CMD_NETWORK_DISCONNECTED: - logNetworkEvent(NetworkEvent.NETWORK_DISCONNECTED); - quit(); - return HANDLED; - case CMD_FORCE_REEVALUATION: - case CMD_CAPTIVE_PORTAL_RECHECK: - final int dnsCount = mDnsStallDetector.getConsecutiveTimeoutCount(); - validationLog("Forcing reevaluation for UID " + message.arg1 - + ". Dns signal count: " + dnsCount); - mUidResponsibleForReeval = message.arg1; - transitionTo(mEvaluatingState); - return HANDLED; - case CMD_CAPTIVE_PORTAL_APP_FINISHED: - log("CaptivePortal App responded with " + message.arg1); - - // If the user has seen and acted on a captive portal notification, and the - // captive portal app is now closed, disable HTTPS probes. This avoids the - // following pathological situation: - // - // 1. HTTP probe returns a captive portal, HTTPS probe fails or times out. - // 2. User opens the app and logs into the captive portal. - // 3. HTTP starts working, but HTTPS still doesn't work for some other reason - - // perhaps due to the network blocking HTTPS? - // - // In this case, we'll fail to validate the network even after the app is - // dismissed. There is now no way to use this network, because the app is now - // gone, so the user cannot select "Use this network as is". - mUseHttps = false; - - switch (message.arg1) { - case APP_RETURN_DISMISSED: - sendMessage(CMD_FORCE_REEVALUATION, NO_UID, 0); - break; - case APP_RETURN_WANTED_AS_IS: - mDontDisplaySigninNotification = true; - // TODO: Distinguish this from a network that actually validates. - // Displaying the "x" on the system UI icon may still be a good idea. - transitionTo(mEvaluatingPrivateDnsState); - break; - case APP_RETURN_UNWANTED: - mDontDisplaySigninNotification = true; - mUserDoesNotWant = true; - notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null); - // TODO: Should teardown network. - mUidResponsibleForReeval = 0; - transitionTo(mEvaluatingState); - break; - } - return HANDLED; - case CMD_PRIVATE_DNS_SETTINGS_CHANGED: { - final PrivateDnsConfig cfg = (PrivateDnsConfig) message.obj; - if (!isPrivateDnsValidationRequired() || cfg == null || !cfg.inStrictMode()) { - // No DNS resolution required. - // - // We don't force any validation in opportunistic mode - // here. Opportunistic mode nameservers are validated - // separately within netd. - // - // Reset Private DNS settings state. - mPrivateDnsProviderHostname = ""; - break; - } - - mPrivateDnsProviderHostname = cfg.hostname; - - // DNS resolutions via Private DNS strict mode block for a - // few seconds (~4.2) checking for any IP addresses to - // arrive and validate. Initiating a (re)evaluation now - // should not significantly alter the validation outcome. - // - // No matter what: enqueue a validation request; one of - // three things can happen with this request: - // [1] ignored (EvaluatingState or CaptivePortalState) - // [2] transition to EvaluatingPrivateDnsState - // (DefaultState and ValidatedState) - // [3] handled (EvaluatingPrivateDnsState) - // - // The Private DNS configuration to be evaluated will: - // [1] be skipped (not in strict mode), or - // [2] validate (huzzah), or - // [3] encounter some problem (invalid hostname, - // no resolved IP addresses, IPs unreachable, - // port 853 unreachable, port 853 is not running a - // DNS-over-TLS server, et cetera). - sendMessage(CMD_EVALUATE_PRIVATE_DNS); - break; - } - case EVENT_DNS_NOTIFICATION: - mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1); - break; - // Set mAcceptPartialConnectivity to true and if network start evaluating or - // re-evaluating and get the result of partial connectivity, ProbingState will - // disable HTTPS probe and transition to EvaluatingPrivateDnsState. - case EVENT_ACCEPT_PARTIAL_CONNECTIVITY: - mAcceptPartialConnectivity = true; - break; - case EVENT_LINK_PROPERTIES_CHANGED: - mLinkProperties = (LinkProperties) message.obj; - break; - case EVENT_NETWORK_CAPABILITIES_CHANGED: - mNetworkCapabilities = (NetworkCapabilities) message.obj; - break; - default: - break; - } - return HANDLED; - } - } - - // Being in the ValidatedState State indicates a Network is: - // - Successfully validated, or - // - Wanted "as is" by the user, or - // - Does not satisfy the default NetworkRequest and so validation has been skipped. - private class ValidatedState extends State { - @Override - public void enter() { - maybeLogEvaluationResult( - networkEventType(validationStage(), EvaluationResult.VALIDATED)); - notifyNetworkTested(INetworkMonitor.NETWORK_TEST_RESULT_VALID, null); - mValidations++; - } - - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_NETWORK_CONNECTED: - updateConnectedNetworkAttributes(message); - transitionTo(mValidatedState); - break; - case CMD_EVALUATE_PRIVATE_DNS: - transitionTo(mEvaluatingPrivateDnsState); - break; - case EVENT_DNS_NOTIFICATION: - mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1); - if (isDataStall()) { - mCollectDataStallMetrics = true; - validationLog("Suspecting data stall, reevaluate"); - transitionTo(mEvaluatingState); - } - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - } - - private void writeDataStallStats(@NonNull final CaptivePortalProbeResult result) { - /* - * Collect data stall detection level information for each transport type. Collect type - * specific information for cellular and wifi only currently. Generate - * DataStallDetectionStats for each transport type. E.g., if a network supports both - * TRANSPORT_WIFI and TRANSPORT_VPN, two DataStallDetectionStats will be generated. - */ - final int[] transports = mNetworkCapabilities.getTransportTypes(); - - for (int i = 0; i < transports.length; i++) { - DataStallStatsUtils.write(buildDataStallDetectionStats(transports[i]), result); - } - mCollectDataStallMetrics = false; - } - - @VisibleForTesting - protected DataStallDetectionStats buildDataStallDetectionStats(int transport) { - final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder(); - if (VDBG_STALL) log("collectDataStallMetrics: type=" + transport); - stats.setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); - stats.setNetworkType(transport); - switch (transport) { - case NetworkCapabilities.TRANSPORT_WIFI: - // TODO: Update it if status query in dual wifi is supported. - final WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); - stats.setWiFiData(wifiInfo); - break; - case NetworkCapabilities.TRANSPORT_CELLULAR: - final boolean isRoaming = !mNetworkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); - final SignalStrength ss = mTelephonyManager.getSignalStrength(); - // TODO(b/120452078): Support multi-sim. - stats.setCellData( - mTelephonyManager.getDataNetworkType(), - isRoaming, - mTelephonyManager.getNetworkOperator(), - mTelephonyManager.getSimOperator(), - (ss != null) - ? ss.getLevel() : CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); - break; - default: - // No transport type specific information for the other types. - break; - } - addDnsEvents(stats); - - return stats.build(); - } - - @VisibleForTesting - protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) { - final int size = mDnsStallDetector.mResultIndices.size(); - for (int i = 1; i <= DEFAULT_DNS_LOG_SIZE && i <= size; i++) { - final int index = mDnsStallDetector.mResultIndices.indexOf(size - i); - stats.addDnsEvent(mDnsStallDetector.mDnsEvents[index].mReturnCode, - mDnsStallDetector.mDnsEvents[index].mTimeStamp); - } - } - - - // Being in the MaybeNotifyState State indicates the user may have been notified that sign-in - // is required. This State takes care to clear the notification upon exit from the State. - private class MaybeNotifyState extends State { - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_LAUNCH_CAPTIVE_PORTAL_APP: - final Bundle appExtras = new Bundle(); - // OneAddressPerFamilyNetwork is not parcelable across processes. - final Network network = new Network(mCleartextDnsNetwork); - appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network); - final CaptivePortalProbeResult probeRes = mLastPortalProbeResult; - appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl); - if (probeRes.probeSpec != null) { - final String encodedSpec = probeRes.probeSpec.getEncodedSpec(); - appExtras.putString(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec); - } - appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT, - mCaptivePortalUserAgent); - mCm.startCaptivePortalApp(network, appExtras); - return HANDLED; - default: - return NOT_HANDLED; - } - } - - @Override - public void exit() { - if (mLaunchCaptivePortalAppBroadcastReceiver != null) { - mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver); - mLaunchCaptivePortalAppBroadcastReceiver = null; - } - hideProvisioningNotification(); - } - } - - // Being in the EvaluatingState State indicates the Network is being evaluated for internet - // connectivity, or that the user has indicated that this network is unwanted. - private class EvaluatingState extends State { - @Override - public void enter() { - // If we have already started to track time spent in EvaluatingState - // don't reset the timer due simply to, say, commands or events that - // cause us to exit and re-enter EvaluatingState. - if (!mEvaluationTimer.isStarted()) { - mEvaluationTimer.start(); - } - sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); - if (mUidResponsibleForReeval != INVALID_UID) { - TrafficStats.setThreadStatsUid(mUidResponsibleForReeval); - mUidResponsibleForReeval = INVALID_UID; - } - mReevaluateDelayMs = INITIAL_REEVALUATE_DELAY_MS; - mEvaluateAttempts = 0; - } - - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_REEVALUATE: - if (message.arg1 != mReevaluateToken || mUserDoesNotWant) { - return HANDLED; - } - // Don't bother validating networks that don't satisfy the default request. - // This includes: - // - VPNs which can be considered explicitly desired by the user and the - // user's desire trumps whether the network validates. - // - Networks that don't provide Internet access. It's unclear how to - // validate such networks. - // - Untrusted networks. It's unsafe to prompt the user to sign-in to - // such networks and the user didn't express interest in connecting to - // such networks (an app did) so the user may be unhappily surprised when - // asked to sign-in to a network they didn't want to connect to in the - // first place. Validation could be done to adjust the network scores - // however these networks are app-requested and may not be intended for - // general usage, in which case general validation may not be an accurate - // measure of the network's quality. Only the app knows how to evaluate - // the network so don't bother validating here. Furthermore sending HTTP - // packets over the network may be undesirable, for example an extremely - // expensive metered network, or unwanted leaking of the User Agent string. - // - // On networks that need to support private DNS in strict mode (e.g., VPNs, but - // not networks that don't provide Internet access), we still need to perform - // private DNS server resolution. - if (!isValidationRequired()) { - if (isPrivateDnsValidationRequired()) { - validationLog("Network would not satisfy default request, " - + "resolving private DNS"); - transitionTo(mEvaluatingPrivateDnsState); - } else { - validationLog("Network would not satisfy default request, " - + "not validating"); - transitionTo(mValidatedState); - } - return HANDLED; - } - mEvaluateAttempts++; - - transitionTo(mProbingState); - return HANDLED; - case CMD_FORCE_REEVALUATION: - // Before IGNORE_REEVALUATE_ATTEMPTS attempts are made, - // ignore any re-evaluation requests. After, restart the - // evaluation process via EvaluatingState#enter. - return (mEvaluateAttempts < IGNORE_REEVALUATE_ATTEMPTS) ? HANDLED : NOT_HANDLED; - // Disable HTTPS probe and transition to EvaluatingPrivateDnsState because: - // 1. Network is connected and finish the network validation. - // 2. NetworkMonitor detects network is partial connectivity and user accepts it. - case EVENT_ACCEPT_PARTIAL_CONNECTIVITY: - mAcceptPartialConnectivity = true; - mUseHttps = false; - transitionTo(mEvaluatingPrivateDnsState); - return HANDLED; - default: - return NOT_HANDLED; - } - } - - @Override - public void exit() { - TrafficStats.clearThreadStatsUid(); - } - } - - // BroadcastReceiver that waits for a particular Intent and then posts a message. - private class CustomIntentReceiver extends BroadcastReceiver { - private final int mToken; - private final int mWhat; - private final String mAction; - CustomIntentReceiver(String action, int token, int what) { - mToken = token; - mWhat = what; - mAction = action + "_" + mCleartextDnsNetwork.getNetworkHandle() + "_" + token; - mContext.registerReceiver(this, new IntentFilter(mAction)); - } - public PendingIntent getPendingIntent() { - final Intent intent = new Intent(mAction); - intent.setPackage(mContext.getPackageName()); - return PendingIntent.getBroadcast(mContext, 0, intent, 0); - } - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(mAction)) sendMessage(obtainMessage(mWhat, mToken)); - } - } - - // Being in the CaptivePortalState State indicates a captive portal was detected and the user - // has been shown a notification to sign-in. - private class CaptivePortalState extends State { - private static final String ACTION_LAUNCH_CAPTIVE_PORTAL_APP = - "android.net.netmon.launchCaptivePortalApp"; - - @Override - public void enter() { - maybeLogEvaluationResult( - networkEventType(validationStage(), EvaluationResult.CAPTIVE_PORTAL)); - // Don't annoy user with sign-in notifications. - if (mDontDisplaySigninNotification) return; - // Create a CustomIntentReceiver that sends us a - // CMD_LAUNCH_CAPTIVE_PORTAL_APP message when the user - // touches the notification. - if (mLaunchCaptivePortalAppBroadcastReceiver == null) { - // Wait for result. - mLaunchCaptivePortalAppBroadcastReceiver = new CustomIntentReceiver( - ACTION_LAUNCH_CAPTIVE_PORTAL_APP, new Random().nextInt(), - CMD_LAUNCH_CAPTIVE_PORTAL_APP); - // Display the sign in notification. - // Only do this once for every time we enter MaybeNotifyState. b/122164725 - showProvisioningNotification(mLaunchCaptivePortalAppBroadcastReceiver.mAction); - } - // Retest for captive portal occasionally. - sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */, - CAPTIVE_PORTAL_REEVALUATE_DELAY_MS); - mValidations++; - } - - @Override - public void exit() { - removeMessages(CMD_CAPTIVE_PORTAL_RECHECK); - } - } - - private class EvaluatingPrivateDnsState extends State { - private int mPrivateDnsReevalDelayMs; - private PrivateDnsConfig mPrivateDnsConfig; - - @Override - public void enter() { - mPrivateDnsReevalDelayMs = INITIAL_REEVALUATE_DELAY_MS; - mPrivateDnsConfig = null; - sendMessage(CMD_EVALUATE_PRIVATE_DNS); - } - - @Override - public boolean processMessage(Message msg) { - switch (msg.what) { - case CMD_EVALUATE_PRIVATE_DNS: - if (inStrictMode()) { - if (!isStrictModeHostnameResolved()) { - resolveStrictModeHostname(); - - if (isStrictModeHostnameResolved()) { - notifyPrivateDnsConfigResolved(); - } else { - handlePrivateDnsEvaluationFailure(); - break; - } - } - - // Look up a one-time hostname, to bypass caching. - // - // Note that this will race with ConnectivityService - // code programming the DNS-over-TLS server IP addresses - // into netd (if invoked, above). If netd doesn't know - // the IP addresses yet, or if the connections to the IP - // addresses haven't yet been validated, netd will block - // for up to a few seconds before failing the lookup. - if (!sendPrivateDnsProbe()) { - handlePrivateDnsEvaluationFailure(); - break; - } - } - - // All good! - transitionTo(mValidatedState); - break; - default: - return NOT_HANDLED; - } - return HANDLED; - } - - private boolean inStrictMode() { - return !TextUtils.isEmpty(mPrivateDnsProviderHostname); - } - - private boolean isStrictModeHostnameResolved() { - return (mPrivateDnsConfig != null) - && mPrivateDnsConfig.hostname.equals(mPrivateDnsProviderHostname) - && (mPrivateDnsConfig.ips.length > 0); - } - - private void resolveStrictModeHostname() { - try { - // Do a blocking DNS resolution using the network-assigned nameservers. - final InetAddress[] ips = DnsUtils.getAllByName(mDependencies.getDnsResolver(), - mCleartextDnsNetwork, mPrivateDnsProviderHostname, getDnsProbeTimeout()); - mPrivateDnsConfig = new PrivateDnsConfig(mPrivateDnsProviderHostname, ips); - validationLog("Strict mode hostname resolved: " + mPrivateDnsConfig); - } catch (UnknownHostException uhe) { - mPrivateDnsConfig = null; - validationLog("Strict mode hostname resolution failed: " + uhe.getMessage()); - } - } - - private void notifyPrivateDnsConfigResolved() { - try { - mCallback.notifyPrivateDnsConfigResolved(mPrivateDnsConfig.toParcel()); - } catch (RemoteException e) { - Log.e(TAG, "Error sending private DNS config resolved notification", e); - } - } - - private void handlePrivateDnsEvaluationFailure() { - notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, null); - - // Queue up a re-evaluation with backoff. - // - // TODO: Consider abandoning this state after a few attempts and - // transitioning back to EvaluatingState, to perhaps give ourselves - // the opportunity to (re)detect a captive portal or something. - sendMessageDelayed(CMD_EVALUATE_PRIVATE_DNS, mPrivateDnsReevalDelayMs); - mPrivateDnsReevalDelayMs *= 2; - if (mPrivateDnsReevalDelayMs > MAX_REEVALUATE_DELAY_MS) { - mPrivateDnsReevalDelayMs = MAX_REEVALUATE_DELAY_MS; - } - } - - private boolean sendPrivateDnsProbe() { - // q.v. system/netd/server/dns/DnsTlsTransport.cpp - final String oneTimeHostnameSuffix = "-dnsotls-ds.metric.gstatic.com"; - final String host = UUID.randomUUID().toString().substring(0, 8) - + oneTimeHostnameSuffix; - final Stopwatch watch = new Stopwatch().start(); - try { - final InetAddress[] ips = mNetwork.getAllByName(host); - final long time = watch.stop(); - final String strIps = Arrays.toString(ips); - final boolean success = (ips != null && ips.length > 0); - validationLog(PROBE_PRIVDNS, host, String.format("%dms: %s", time, strIps)); - logValidationProbe(time, PROBE_PRIVDNS, success ? DNS_SUCCESS : DNS_FAILURE); - return success; - } catch (UnknownHostException uhe) { - final long time = watch.stop(); - validationLog(PROBE_PRIVDNS, host, - String.format("%dms - Error: %s", time, uhe.getMessage())); - logValidationProbe(time, PROBE_PRIVDNS, DNS_FAILURE); - } - return false; - } - } - - private class ProbingState extends State { - private Thread mThread; - - @Override - public void enter() { - if (mEvaluateAttempts >= BLAME_FOR_EVALUATION_ATTEMPTS) { - //Don't continue to blame UID forever. - TrafficStats.clearThreadStatsUid(); - } - - final int token = ++mProbeToken; - mThread = new Thread(() -> sendMessage(obtainMessage(CMD_PROBE_COMPLETE, token, 0, - isCaptivePortal()))); - mThread.start(); - } - - @Override - public boolean processMessage(Message message) { - switch (message.what) { - case CMD_PROBE_COMPLETE: - // Ensure that CMD_PROBE_COMPLETE from stale threads are ignored. - if (message.arg1 != mProbeToken) { - return HANDLED; - } - - final CaptivePortalProbeResult probeResult = - (CaptivePortalProbeResult) message.obj; - mLastProbeTime = SystemClock.elapsedRealtime(); - - if (mCollectDataStallMetrics) { - writeDataStallStats(probeResult); - } - - if (probeResult.isSuccessful()) { - // Transit EvaluatingPrivateDnsState to get to Validated - // state (even if no Private DNS validation required). - transitionTo(mEvaluatingPrivateDnsState); - } else if (probeResult.isPortal()) { - notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl); - mLastPortalProbeResult = probeResult; - transitionTo(mCaptivePortalState); - } else if (probeResult.isPartialConnectivity()) { - logNetworkEvent(NetworkEvent.NETWORK_PARTIAL_CONNECTIVITY); - notifyNetworkTested(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY, - probeResult.redirectUrl); - if (mAcceptPartialConnectivity) { - mUseHttps = false; - transitionTo(mEvaluatingPrivateDnsState); - } else { - transitionTo(mWaitingForNextProbeState); - } - } else { - logNetworkEvent(NetworkEvent.NETWORK_VALIDATION_FAILED); - notifyNetworkTested(NETWORK_TEST_RESULT_INVALID, probeResult.redirectUrl); - transitionTo(mWaitingForNextProbeState); - } - return HANDLED; - case EVENT_DNS_NOTIFICATION: - case EVENT_ACCEPT_PARTIAL_CONNECTIVITY: - // Leave the event to DefaultState. - return NOT_HANDLED; - default: - // Wait for probe result and defer events to next state by default. - deferMessage(message); - return HANDLED; - } - } - - @Override - public void exit() { - if (mThread.isAlive()) { - mThread.interrupt(); - } - mThread = null; - } - } - - // Being in the WaitingForNextProbeState indicates that evaluating probes failed and state is - // transited from ProbingState. This ensures that the state machine is only in ProbingState - // while a probe is in progress, not while waiting to perform the next probe. That allows - // ProbingState to defer most messages until the probe is complete, which keeps the code simple - // and matches the pre-Q behaviour where probes were a blocking operation performed on the state - // machine thread. - private class WaitingForNextProbeState extends State { - @Override - public void enter() { - scheduleNextProbe(); - } - - private void scheduleNextProbe() { - final Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); - sendMessageDelayed(msg, mReevaluateDelayMs); - mReevaluateDelayMs *= 2; - if (mReevaluateDelayMs > MAX_REEVALUATE_DELAY_MS) { - mReevaluateDelayMs = MAX_REEVALUATE_DELAY_MS; - } - } - - @Override - public boolean processMessage(Message message) { - return NOT_HANDLED; - } - } - - // Limits the list of IP addresses returned by getAllByName or tried by openConnection to at - // most one per address family. This ensures we only wait up to 20 seconds for TCP connections - // to complete, regardless of how many IP addresses a host has. - private static class OneAddressPerFamilyNetwork extends Network { - OneAddressPerFamilyNetwork(Network network) { - // Always bypass Private DNS. - super(network.getPrivateDnsBypassingCopy()); - } - - @Override - public InetAddress[] getAllByName(String host) throws UnknownHostException { - final List<InetAddress> addrs = Arrays.asList(super.getAllByName(host)); - - // Ensure the address family of the first address is tried first. - LinkedHashMap<Class, InetAddress> addressByFamily = new LinkedHashMap<>(); - addressByFamily.put(addrs.get(0).getClass(), addrs.get(0)); - Collections.shuffle(addrs); - - for (InetAddress addr : addrs) { - addressByFamily.put(addr.getClass(), addr); - } - - return addressByFamily.values().toArray(new InetAddress[addressByFamily.size()]); - } - } - - private boolean getIsCaptivePortalCheckEnabled() { - String symbol = CAPTIVE_PORTAL_MODE; - int defaultValue = CAPTIVE_PORTAL_MODE_PROMPT; - int mode = mDependencies.getSetting(mContext, symbol, defaultValue); - return mode != CAPTIVE_PORTAL_MODE_IGNORE; - } - - private boolean getUseHttpsValidation() { - return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, - CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; - } - - private String getCaptivePortalServerHttpsUrl() { - return getSettingFromResource(mContext, R.string.config_captive_portal_https_url, - R.string.default_captive_portal_https_url, CAPTIVE_PORTAL_HTTPS_URL); - } - - private int getDnsProbeTimeout() { - return getIntSetting(mContext, R.integer.config_captive_portal_dns_probe_timeout, - CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, - R.integer.default_captive_portal_dns_probe_timeout); - } - - /** - * Gets an integer setting from resources or device config - * - * configResource is used if set, followed by device config if set, followed by defaultResource. - * If none of these are set then an exception is thrown. - * - * TODO: move to a common location such as a ConfigUtils class. - * TODO(b/130324939): test that the resources can be overlayed by an RRO package. - */ - @VisibleForTesting - int getIntSetting(@NonNull final Context context, @StringRes int configResource, - @NonNull String symbol, @StringRes int defaultResource) { - final Resources res = context.getResources(); - try { - return res.getInteger(configResource); - } catch (Resources.NotFoundException e) { - return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, - symbol, res.getInteger(defaultResource)); - } - } - - /** - * Get the captive portal server HTTP URL that is configured on the device. - * - * NetworkMonitor does not use {@link ConnectivityManager#getCaptivePortalServerUrl()} as - * it has its own updatable strategies to detect captive portals. The framework only advises - * on one URL that can be used, while NetworkMonitor may implement more complex logic. - */ - public String getCaptivePortalServerHttpUrl() { - return getSettingFromResource(mContext, R.string.config_captive_portal_http_url, - R.string.default_captive_portal_http_url, CAPTIVE_PORTAL_HTTP_URL); - } - - private int getConsecutiveDnsTimeoutThreshold() { - return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, - CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD, - DEFAULT_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD); - } - - private int getDataStallMinEvaluateTime() { - return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, - CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL, - DEFAULT_DATA_STALL_MIN_EVALUATE_TIME_MS); - } - - private int getDataStallValidDnsTimeThreshold() { - return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, - CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD, - DEFAULT_DATA_STALL_VALID_DNS_TIME_THRESHOLD_MS); - } - - private int getDataStallEvaluationType() { - return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, - CONFIG_DATA_STALL_EVALUATION_TYPE, - DEFAULT_DATA_STALL_EVALUATION_TYPES); - } - - private URL[] makeCaptivePortalFallbackUrls() { - try { - final String firstUrl = mDependencies.getSetting(mContext, CAPTIVE_PORTAL_FALLBACK_URL, - null); - - final URL[] settingProviderUrls; - if (!TextUtils.isEmpty(firstUrl)) { - final String otherUrls = mDependencies.getDeviceConfigProperty( - NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, ""); - // otherUrls may be empty, but .split() ignores trailing empty strings - final String separator = ","; - final String[] urls = (firstUrl + separator + otherUrls).split(separator); - settingProviderUrls = convertStrings(urls, this::makeURL, new URL[0]); - } else { - settingProviderUrls = new URL[0]; - } - - return getArrayConfig(settingProviderUrls, R.array.config_captive_portal_fallback_urls, - R.array.default_captive_portal_fallback_urls, this::makeURL); - } catch (Exception e) { - // Don't let a misconfiguration bootloop the system. - Log.e(TAG, "Error parsing configured fallback URLs", e); - return new URL[0]; - } - } - - private CaptivePortalProbeSpec[] makeCaptivePortalFallbackProbeSpecs() { - try { - final String settingsValue = mDependencies.getDeviceConfigProperty( - NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS, null); - - final CaptivePortalProbeSpec[] emptySpecs = new CaptivePortalProbeSpec[0]; - final CaptivePortalProbeSpec[] providerValue = TextUtils.isEmpty(settingsValue) - ? emptySpecs - : parseCaptivePortalProbeSpecs(settingsValue).toArray(emptySpecs); - - return getArrayConfig(providerValue, R.array.config_captive_portal_fallback_probe_specs, - R.array.default_captive_portal_fallback_probe_specs, - CaptivePortalProbeSpec::parseSpecOrNull); - } catch (Exception e) { - // Don't let a misconfiguration bootloop the system. - Log.e(TAG, "Error parsing configured fallback probe specs", e); - return null; - } - } - - /** - * Read a setting from a resource or the settings provider. - * - * <p>The configuration resource is prioritized, then the provider value, then the default - * resource value. - * @param context The context - * @param configResource The resource id for the configuration parameter - * @param defaultResource The resource id for the default value - * @param symbol The symbol in the settings provider - * @return The best available value - */ - @NonNull - private String getSettingFromResource(@NonNull final Context context, - @StringRes int configResource, @StringRes int defaultResource, - @NonNull String symbol) { - final Resources res = context.getResources(); - String setting = res.getString(configResource); - - if (!TextUtils.isEmpty(setting)) return setting; - - setting = mDependencies.getSetting(context, symbol, null); - if (!TextUtils.isEmpty(setting)) return setting; - - return res.getString(defaultResource); - } - - /** - * Get an array configuration from resources or the settings provider. - * - * <p>The configuration resource is prioritized, then the provider values, then the default - * resource values. - * @param providerValue Values obtained from the setting provider. - * @param configResId ID of the configuration resource. - * @param defaultResId ID of the default resource. - * @param resourceConverter Converter from the resource strings to stored setting class. Null - * return values are ignored. - */ - private <T> T[] getArrayConfig(@NonNull T[] providerValue, @ArrayRes int configResId, - @ArrayRes int defaultResId, @NonNull Function<String, T> resourceConverter) { - final Resources res = mContext.getResources(); - String[] configValue = res.getStringArray(configResId); - - if (configValue.length == 0) { - if (providerValue.length > 0) { - return providerValue; - } - - configValue = res.getStringArray(defaultResId); - } - - return convertStrings(configValue, resourceConverter, Arrays.copyOf(providerValue, 0)); - } - - /** - * Convert a String array to an array of some other type using the specified converter. - * - * <p>Any null value, or value for which the converter throws a {@link RuntimeException}, will - * not be added to the output array, so the output array may be smaller than the input. - */ - private <T> T[] convertStrings( - @NonNull String[] strings, Function<String, T> converter, T[] emptyArray) { - final ArrayList<T> convertedValues = new ArrayList<>(strings.length); - for (String configString : strings) { - T convertedValue = null; - try { - convertedValue = converter.apply(configString); - } catch (Exception e) { - Log.e(TAG, "Error parsing configuration", e); - // Fall through - } - if (convertedValue != null) { - convertedValues.add(convertedValue); - } - } - return convertedValues.toArray(emptyArray); - } - - private String getCaptivePortalUserAgent() { - return mDependencies.getDeviceConfigProperty(NAMESPACE_CONNECTIVITY, - CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT); - } - - private URL nextFallbackUrl() { - if (mCaptivePortalFallbackUrls.length == 0) { - return null; - } - int idx = Math.abs(mNextFallbackUrlIndex) % mCaptivePortalFallbackUrls.length; - mNextFallbackUrlIndex += mRandom.nextInt(); // randomly change url without memory. - return mCaptivePortalFallbackUrls[idx]; - } - - private CaptivePortalProbeSpec nextFallbackSpec() { - if (isEmpty(mCaptivePortalFallbackSpecs)) { - return null; - } - // Randomly change spec without memory. Also randomize the first attempt. - final int idx = Math.abs(mRandom.nextInt()) % mCaptivePortalFallbackSpecs.length; - return mCaptivePortalFallbackSpecs[idx]; - } - - @VisibleForTesting - protected CaptivePortalProbeResult isCaptivePortal() { - if (!mIsCaptivePortalCheckEnabled) { - validationLog("Validation disabled."); - return CaptivePortalProbeResult.SUCCESS; - } - - URL pacUrl = null; - URL httpsUrl = mCaptivePortalHttpsUrl; - URL httpUrl = mCaptivePortalHttpUrl; - - // On networks with a PAC instead of fetching a URL that should result in a 204 - // response, we instead simply fetch the PAC script. This is done for a few reasons: - // 1. At present our PAC code does not yet handle multiple PACs on multiple networks - // until something like https://android-review.googlesource.com/#/c/115180/ lands. - // Network.openConnection() will ignore network-specific PACs and instead fetch - // using NO_PROXY. If a PAC is in place, the only fetch we know will succeed with - // NO_PROXY is the fetch of the PAC itself. - // 2. To proxy the generate_204 fetch through a PAC would require a number of things - // happen before the fetch can commence, namely: - // a) the PAC script be fetched - // b) a PAC script resolver service be fired up and resolve the captive portal - // server. - // Network validation could be delayed until these prerequisities are satisifed or - // could simply be left to race them. Neither is an optimal solution. - // 3. PAC scripts are sometimes used to block or restrict Internet access and may in - // fact block fetching of the generate_204 URL which would lead to false negative - // results for network validation. - final ProxyInfo proxyInfo = mLinkProperties.getHttpProxy(); - if (proxyInfo != null && !Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) { - pacUrl = makeURL(proxyInfo.getPacFileUrl().toString()); - if (pacUrl == null) { - return CaptivePortalProbeResult.FAILED; - } - } - - if ((pacUrl == null) && (httpUrl == null || httpsUrl == null)) { - return CaptivePortalProbeResult.FAILED; - } - - long startTime = SystemClock.elapsedRealtime(); - - final CaptivePortalProbeResult result; - if (pacUrl != null) { - result = sendDnsAndHttpProbes(null, pacUrl, ValidationProbeEvent.PROBE_PAC); - } else if (mUseHttps) { - result = sendParallelHttpProbes(proxyInfo, httpsUrl, httpUrl); - } else { - result = sendDnsAndHttpProbes(proxyInfo, httpUrl, ValidationProbeEvent.PROBE_HTTP); - } - - long endTime = SystemClock.elapsedRealtime(); - - sendNetworkConditionsBroadcast(true /* response received */, - result.isPortal() /* isCaptivePortal */, - startTime, endTime); - - log("isCaptivePortal: isSuccessful()=" + result.isSuccessful() - + " isPortal()=" + result.isPortal() - + " RedirectUrl=" + result.redirectUrl - + " Time=" + (endTime - startTime) + "ms"); - - return result; - } - - /** - * Do a DNS resolution and URL fetch on a known web server to see if we get the data we expect. - * @return a CaptivePortalProbeResult inferred from the HTTP response. - */ - private CaptivePortalProbeResult sendDnsAndHttpProbes(ProxyInfo proxy, URL url, int probeType) { - // Pre-resolve the captive portal server host so we can log it. - // Only do this if HttpURLConnection is about to, to avoid any potentially - // unnecessary resolution. - final String host = (proxy != null) ? proxy.getHost() : url.getHost(); - sendDnsProbe(host); - return sendHttpProbe(url, probeType, null); - } - - /** Do a DNS lookup for the given server, or throw UnknownHostException after timeoutMs */ - @VisibleForTesting - protected InetAddress[] sendDnsProbeWithTimeout(String host, int timeoutMs) - throws UnknownHostException { - return DnsUtils.getAllByName(mDependencies.getDnsResolver(), mCleartextDnsNetwork, host, - TYPE_ADDRCONFIG, FLAG_EMPTY, timeoutMs); - } - - /** Do a DNS resolution of the given server. */ - private void sendDnsProbe(String host) { - if (TextUtils.isEmpty(host)) { - return; - } - - final String name = ValidationProbeEvent.getProbeName(ValidationProbeEvent.PROBE_DNS); - final Stopwatch watch = new Stopwatch().start(); - int result; - String connectInfo; - try { - InetAddress[] addresses = sendDnsProbeWithTimeout(host, getDnsProbeTimeout()); - StringBuffer buffer = new StringBuffer(); - for (InetAddress address : addresses) { - buffer.append(',').append(address.getHostAddress()); - } - result = ValidationProbeEvent.DNS_SUCCESS; - connectInfo = "OK " + buffer.substring(1); - } catch (UnknownHostException e) { - result = ValidationProbeEvent.DNS_FAILURE; - connectInfo = "FAIL"; - } - final long latency = watch.stop(); - validationLog(ValidationProbeEvent.PROBE_DNS, host, - String.format("%dms %s", latency, connectInfo)); - logValidationProbe(latency, ValidationProbeEvent.PROBE_DNS, result); - } - - /** - * Do a URL fetch on a known web server to see if we get the data we expect. - * @return a CaptivePortalProbeResult inferred from the HTTP response. - */ - @VisibleForTesting - protected CaptivePortalProbeResult sendHttpProbe(URL url, int probeType, - @Nullable CaptivePortalProbeSpec probeSpec) { - HttpURLConnection urlConnection = null; - int httpResponseCode = CaptivePortalProbeResult.FAILED_CODE; - String redirectUrl = null; - final Stopwatch probeTimer = new Stopwatch().start(); - final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_PROBE); - try { - urlConnection = (HttpURLConnection) mCleartextDnsNetwork.openConnection(url); - urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC); - urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS); - urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS); - urlConnection.setRequestProperty("Connection", "close"); - urlConnection.setUseCaches(false); - if (mCaptivePortalUserAgent != null) { - urlConnection.setRequestProperty("User-Agent", mCaptivePortalUserAgent); - } - // cannot read request header after connection - String requestHeader = urlConnection.getRequestProperties().toString(); - - // Time how long it takes to get a response to our request - long requestTimestamp = SystemClock.elapsedRealtime(); - - httpResponseCode = urlConnection.getResponseCode(); - redirectUrl = urlConnection.getHeaderField("location"); - - // Time how long it takes to get a response to our request - long responseTimestamp = SystemClock.elapsedRealtime(); - - validationLog(probeType, url, "time=" + (responseTimestamp - requestTimestamp) + "ms" - + " ret=" + httpResponseCode - + " request=" + requestHeader - + " headers=" + urlConnection.getHeaderFields()); - // NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive - // portal. The only example of this seen so far was a captive portal. For - // the time being go with prior behavior of assuming it's not a captive - // portal. If it is considered a captive portal, a different sign-in URL - // is needed (i.e. can't browse a 204). This could be the result of an HTTP - // proxy server. - if (httpResponseCode == 200) { - long contentLength = urlConnection.getContentLengthLong(); - if (probeType == ValidationProbeEvent.PROBE_PAC) { - validationLog( - probeType, url, "PAC fetch 200 response interpreted as 204 response."); - httpResponseCode = CaptivePortalProbeResult.SUCCESS_CODE; - } else if (contentLength == -1) { - // When no Content-length (default value == -1), attempt to read a byte - // from the response. Do not use available() as it is unreliable. - // See http://b/33498325. - if (urlConnection.getInputStream().read() == -1) { - validationLog(probeType, url, - "Empty 200 response interpreted as failed response."); - httpResponseCode = CaptivePortalProbeResult.FAILED_CODE; - } - } else if (contentLength <= 4) { - // Consider 200 response with "Content-length <= 4" to not be a captive - // portal. There's no point in considering this a captive portal as the - // user cannot sign-in to an empty page. Probably the result of a broken - // transparent proxy. See http://b/9972012 and http://b/122999481. - validationLog(probeType, url, "200 response with Content-length <= 4" - + " interpreted as failed response."); - httpResponseCode = CaptivePortalProbeResult.FAILED_CODE; - } - } - } catch (IOException e) { - validationLog(probeType, url, "Probe failed with exception " + e); - if (httpResponseCode == CaptivePortalProbeResult.FAILED_CODE) { - // TODO: Ping gateway and DNS server and log results. - } - } finally { - if (urlConnection != null) { - urlConnection.disconnect(); - } - TrafficStats.setThreadStatsTag(oldTag); - } - logValidationProbe(probeTimer.stop(), probeType, httpResponseCode); - - if (probeSpec == null) { - return new CaptivePortalProbeResult(httpResponseCode, redirectUrl, url.toString()); - } else { - return probeSpec.getResult(httpResponseCode, redirectUrl); - } - } - - private CaptivePortalProbeResult sendParallelHttpProbes( - ProxyInfo proxy, URL httpsUrl, URL httpUrl) { - // Number of probes to wait for. If a probe completes with a conclusive answer - // it shortcuts the latch immediately by forcing the count to 0. - final CountDownLatch latch = new CountDownLatch(2); - - final class ProbeThread extends Thread { - private final boolean mIsHttps; - private volatile CaptivePortalProbeResult mResult = CaptivePortalProbeResult.FAILED; - - ProbeThread(boolean isHttps) { - mIsHttps = isHttps; - } - - public CaptivePortalProbeResult result() { - return mResult; - } - - @Override - public void run() { - if (mIsHttps) { - mResult = - sendDnsAndHttpProbes(proxy, httpsUrl, ValidationProbeEvent.PROBE_HTTPS); - } else { - mResult = sendDnsAndHttpProbes(proxy, httpUrl, ValidationProbeEvent.PROBE_HTTP); - } - if ((mIsHttps && mResult.isSuccessful()) || (!mIsHttps && mResult.isPortal())) { - // Stop waiting immediately if https succeeds or if http finds a portal. - while (latch.getCount() > 0) { - latch.countDown(); - } - } - // Signal this probe has completed. - latch.countDown(); - } - } - - final ProbeThread httpsProbe = new ProbeThread(true); - final ProbeThread httpProbe = new ProbeThread(false); - - try { - httpsProbe.start(); - httpProbe.start(); - latch.await(PROBE_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - validationLog("Error: probes wait interrupted!"); - return CaptivePortalProbeResult.FAILED; - } - - final CaptivePortalProbeResult httpsResult = httpsProbe.result(); - final CaptivePortalProbeResult httpResult = httpProbe.result(); - - // Look for a conclusive probe result first. - if (httpResult.isPortal()) { - return httpResult; - } - // httpsResult.isPortal() is not expected, but check it nonetheless. - if (httpsResult.isPortal() || httpsResult.isSuccessful()) { - return httpsResult; - } - // If a fallback method exists, use it to retry portal detection. - // If we have new-style probe specs, use those. Otherwise, use the fallback URLs. - final CaptivePortalProbeSpec probeSpec = nextFallbackSpec(); - final URL fallbackUrl = (probeSpec != null) ? probeSpec.getUrl() : nextFallbackUrl(); - CaptivePortalProbeResult fallbackProbeResult = null; - if (fallbackUrl != null) { - fallbackProbeResult = sendHttpProbe(fallbackUrl, PROBE_FALLBACK, probeSpec); - if (fallbackProbeResult.isPortal()) { - return fallbackProbeResult; - } - } - // Otherwise wait until http and https probes completes and use their results. - try { - httpProbe.join(); - if (httpProbe.result().isPortal()) { - return httpProbe.result(); - } - httpsProbe.join(); - final boolean isHttpSuccessful = - (httpProbe.result().isSuccessful() - || (fallbackProbeResult != null && fallbackProbeResult.isSuccessful())); - if (httpsProbe.result().isFailed() && isHttpSuccessful) { - return CaptivePortalProbeResult.PARTIAL; - } - return httpsProbe.result(); - } catch (InterruptedException e) { - validationLog("Error: http or https probe wait interrupted!"); - return CaptivePortalProbeResult.FAILED; - } - } - - private URL makeURL(String url) { - if (url != null) { - try { - return new URL(url); - } catch (MalformedURLException e) { - validationLog("Bad URL: " + url); - } - } - return null; - } - - /** - * @param responseReceived - whether or not we received a valid HTTP response to our request. - * If false, isCaptivePortal and responseTimestampMs are ignored - * TODO: This should be moved to the transports. The latency could be passed to the transports - * along with the captive portal result. Currently the TYPE_MOBILE broadcasts appear unused so - * perhaps this could just be added to the WiFi transport only. - */ - private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal, - long requestTimestampMs, long responseTimestampMs) { - Intent latencyBroadcast = - new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED); - if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) { - if (!mWifiManager.isScanAlwaysAvailable()) { - return; - } - - WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo(); - if (currentWifiInfo != null) { - // NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not - // surrounded by double quotation marks (thus violating the Javadoc), but this - // was changed to match the Javadoc in API 17. Since clients may have started - // sanitizing the output of this method since API 17 was released, we should - // not change it here as it would become impossible to tell whether the SSID is - // simply being surrounded by quotes due to the API, or whether those quotes - // are actually part of the SSID. - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_SSID, - currentWifiInfo.getSSID()); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_BSSID, - currentWifiInfo.getBSSID()); - } else { - if (VDBG) logw("network info is TYPE_WIFI but no ConnectionInfo found"); - return; - } - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI); - } else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { - // TODO(b/123893112): Support multi-sim. - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE, - mTelephonyManager.getNetworkType()); - final ServiceState dataSs = mTelephonyManager.getServiceState(); - if (dataSs == null) { - logw("failed to retrieve ServiceState"); - return; - } - // See if the data sub is registered for PS services on cell. - final NetworkRegistrationInfo nri = dataSs.getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, - AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - latencyBroadcast.putExtra( - NetworkMonitorUtils.EXTRA_CELL_ID, - nri == null ? null : nri.getCellIdentity()); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE); - } else { - return; - } - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_RECEIVED, - responseReceived); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_REQUEST_TIMESTAMP_MS, - requestTimestampMs); - - if (responseReceived) { - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_IS_CAPTIVE_PORTAL, - isCaptivePortal); - latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_RESPONSE_TIMESTAMP_MS, - responseTimestampMs); - } - mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT, - NetworkMonitorUtils.PERMISSION_ACCESS_NETWORK_CONDITIONS); - } - - private void logNetworkEvent(int evtype) { - int[] transports = mNetworkCapabilities.getTransportTypes(); - mMetricsLog.log(mCleartextDnsNetwork, transports, new NetworkEvent(evtype)); - } - - private int networkEventType(ValidationStage s, EvaluationResult r) { - if (s.mIsFirstValidation) { - if (r.mIsValidated) { - return NetworkEvent.NETWORK_FIRST_VALIDATION_SUCCESS; - } else { - return NetworkEvent.NETWORK_FIRST_VALIDATION_PORTAL_FOUND; - } - } else { - if (r.mIsValidated) { - return NetworkEvent.NETWORK_REVALIDATION_SUCCESS; - } else { - return NetworkEvent.NETWORK_REVALIDATION_PORTAL_FOUND; - } - } - } - - private void maybeLogEvaluationResult(int evtype) { - if (mEvaluationTimer.isRunning()) { - int[] transports = mNetworkCapabilities.getTransportTypes(); - mMetricsLog.log(mCleartextDnsNetwork, transports, - new NetworkEvent(evtype, mEvaluationTimer.stop())); - mEvaluationTimer.reset(); - } - } - - private void logValidationProbe(long durationMs, int probeType, int probeResult) { - int[] transports = mNetworkCapabilities.getTransportTypes(); - boolean isFirstValidation = validationStage().mIsFirstValidation; - ValidationProbeEvent ev = new ValidationProbeEvent.Builder() - .setProbeType(probeType, isFirstValidation) - .setReturnCode(probeResult) - .setDurationMs(durationMs) - .build(); - mMetricsLog.log(mCleartextDnsNetwork, transports, ev); - } - - @VisibleForTesting - static class Dependencies { - public Network getPrivateDnsBypassNetwork(Network network) { - return new OneAddressPerFamilyNetwork(network); - } - - public DnsResolver getDnsResolver() { - return DnsResolver.getInstance(); - } - - public Random getRandom() { - return new Random(); - } - - /** - * Get the value of a global integer setting. - * @param symbol Name of the setting - * @param defaultValue Value to return if the setting is not defined. - */ - public int getSetting(Context context, String symbol, int defaultValue) { - return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue); - } - - /** - * Get the value of a global String setting. - * @param symbol Name of the setting - * @param defaultValue Value to return if the setting is not defined. - */ - public String getSetting(Context context, String symbol, String defaultValue) { - final String value = Settings.Global.getString(context.getContentResolver(), symbol); - return value != null ? value : defaultValue; - } - - /** - * Look up the value of a property in DeviceConfig. - * @param namespace The namespace containing the property to look up. - * @param name The name of the property to look up. - * @param defaultValue The value to return if the property does not exist or has no non-null - * value. - * @return the corresponding value, or defaultValue if none exists. - */ - @Nullable - public String getDeviceConfigProperty(@NonNull String namespace, @NonNull String name, - @Nullable String defaultValue) { - return NetworkStackUtils.getDeviceConfigProperty(namespace, name, defaultValue); - } - - /** - * Look up the value of a property in DeviceConfig. - * @param namespace The namespace containing the property to look up. - * @param name The name of the property to look up. - * @param defaultValue The value to return if the property does not exist or has no non-null - * value. - * @return the corresponding value, or defaultValue if none exists. - */ - public int getDeviceConfigPropertyInt(@NonNull String namespace, @NonNull String name, - int defaultValue) { - return NetworkStackUtils.getDeviceConfigPropertyInt(namespace, name, defaultValue); - } - - public static final Dependencies DEFAULT = new Dependencies(); - } - - /** - * Methods in this class perform no locking because all accesses are performed on the state - * machine's thread. Need to consider the thread safety if it ever could be accessed outside the - * state machine. - */ - @VisibleForTesting - protected class DnsStallDetector { - private int mConsecutiveTimeoutCount = 0; - private int mSize; - final DnsResult[] mDnsEvents; - final RingBufferIndices mResultIndices; - - DnsStallDetector(int size) { - mSize = Math.max(DEFAULT_DNS_LOG_SIZE, size); - mDnsEvents = new DnsResult[mSize]; - mResultIndices = new RingBufferIndices(mSize); - } - - @VisibleForTesting - protected void accumulateConsecutiveDnsTimeoutCount(int code) { - final DnsResult result = new DnsResult(code); - mDnsEvents[mResultIndices.add()] = result; - if (result.isTimeout()) { - mConsecutiveTimeoutCount++; - } else { - // Keep the event in mDnsEvents without clearing it so that there are logs to do the - // simulation and analysis. - mConsecutiveTimeoutCount = 0; - } - } - - private boolean isDataStallSuspected(int timeoutCountThreshold, int validTime) { - if (timeoutCountThreshold <= 0) { - Log.wtf(TAG, "Timeout count threshold should be larger than 0."); - return false; - } - - // Check if the consecutive timeout count reach the threshold or not. - if (mConsecutiveTimeoutCount < timeoutCountThreshold) { - return false; - } - - // Check if the target dns event index is valid or not. - final int firstConsecutiveTimeoutIndex = - mResultIndices.indexOf(mResultIndices.size() - timeoutCountThreshold); - - // If the dns timeout events happened long time ago, the events are meaningless for - // data stall evaluation. Thus, check if the first consecutive timeout dns event - // considered in the evaluation happened in defined threshold time. - final long now = SystemClock.elapsedRealtime(); - final long firstTimeoutTime = now - mDnsEvents[firstConsecutiveTimeoutIndex].mTimeStamp; - return (firstTimeoutTime < validTime); - } - - int getConsecutiveTimeoutCount() { - return mConsecutiveTimeoutCount; - } - } - - private static class DnsResult { - // TODO: Need to move the DNS return code definition to a specific class once unify DNS - // response code is done. - private static final int RETURN_CODE_DNS_TIMEOUT = 255; - - private final long mTimeStamp; - private final int mReturnCode; - - DnsResult(int code) { - mTimeStamp = SystemClock.elapsedRealtime(); - mReturnCode = code; - } - - private boolean isTimeout() { - return mReturnCode == RETURN_CODE_DNS_TIMEOUT; - } - } - - - @VisibleForTesting - protected DnsStallDetector getDnsStallDetector() { - return mDnsStallDetector; - } - - private boolean dataStallEvaluateTypeEnabled(int type) { - return (mDataStallEvaluationType & type) != 0; - } - - @VisibleForTesting - protected long getLastProbeTime() { - return mLastProbeTime; - } - - @VisibleForTesting - protected boolean isDataStall() { - boolean result = false; - // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the - // possible traffic cost in metered network. - if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) - && (SystemClock.elapsedRealtime() - getLastProbeTime() - < mDataStallMinEvaluateTime)) { - return false; - } - - // Check dns signal. Suspect it may be a data stall if both : - // 1. The number of consecutive DNS query timeouts >= mConsecutiveDnsTimeoutThreshold. - // 2. Those consecutive DNS queries happened in the last mValidDataStallDnsTimeThreshold ms. - if (dataStallEvaluateTypeEnabled(DATA_STALL_EVALUATION_TYPE_DNS)) { - if (mDnsStallDetector.isDataStallSuspected(mConsecutiveDnsTimeoutThreshold, - mDataStallValidDnsTimeThreshold)) { - result = true; - logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND); - } - } - - if (VDBG_STALL) { - log("isDataStall: result=" + result + ", consecutive dns timeout count=" - + mDnsStallDetector.getConsecutiveTimeoutCount()); - } - - return result; - } -} diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java deleted file mode 100644 index 764e2d07ce3d..000000000000 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreDatabase.java +++ /dev/null @@ -1,651 +0,0 @@ -/* - * 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.server.connectivity.ipmemorystore; - -import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; -import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteCursor; -import android.database.sqlite.SQLiteCursorDriver; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteOpenHelper; -import android.database.sqlite.SQLiteQuery; -import android.net.ipmemorystore.NetworkAttributes; -import android.net.ipmemorystore.Status; -import android.util.Log; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; -import java.util.StringJoiner; - -/** - * Encapsulating class for using the SQLite database backing the memory store. - * - * This class groups together the contracts and the SQLite helper used to - * use the database. - * - * @hide - */ -public class IpMemoryStoreDatabase { - private static final String TAG = IpMemoryStoreDatabase.class.getSimpleName(); - // A pair of NetworkAttributes objects is group-close if the confidence that they are - // the same is above this cutoff. See NetworkAttributes and SameL3NetworkResponse. - private static final float GROUPCLOSE_CONFIDENCE = 0.5f; - - /** - * Contract class for the Network Attributes table. - */ - public static class NetworkAttributesContract { - public static final String TABLENAME = "NetworkAttributes"; - - public static final String COLNAME_L2KEY = "l2Key"; - public static final String COLTYPE_L2KEY = "TEXT NOT NULL"; - - public static final String COLNAME_EXPIRYDATE = "expiryDate"; - // Milliseconds since the Epoch, in true Java style - public static final String COLTYPE_EXPIRYDATE = "BIGINT"; - - public static final String COLNAME_ASSIGNEDV4ADDRESS = "assignedV4Address"; - public static final String COLTYPE_ASSIGNEDV4ADDRESS = "INTEGER"; - - public static final String COLNAME_ASSIGNEDV4ADDRESSEXPIRY = "assignedV4AddressExpiry"; - // The lease expiry timestamp in uint of milliseconds - public static final String COLTYPE_ASSIGNEDV4ADDRESSEXPIRY = "BIGINT"; - - // Please note that the group hint is only a *hint*, hence its name. The client can offer - // this information to nudge the grouping in the decision it thinks is right, but it can't - // decide for the memory store what is the same L3 network. - public static final String COLNAME_GROUPHINT = "groupHint"; - public static final String COLTYPE_GROUPHINT = "TEXT"; - - public static final String COLNAME_DNSADDRESSES = "dnsAddresses"; - // Stored in marshalled form as is - public static final String COLTYPE_DNSADDRESSES = "BLOB"; - - public static final String COLNAME_MTU = "mtu"; - public static final String COLTYPE_MTU = "INTEGER DEFAULT -1"; - - public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " - + TABLENAME + " (" - + COLNAME_L2KEY + " " + COLTYPE_L2KEY + " PRIMARY KEY NOT NULL, " - + COLNAME_EXPIRYDATE + " " + COLTYPE_EXPIRYDATE + ", " - + COLNAME_ASSIGNEDV4ADDRESS + " " + COLTYPE_ASSIGNEDV4ADDRESS + ", " - + COLNAME_ASSIGNEDV4ADDRESSEXPIRY + " " + COLTYPE_ASSIGNEDV4ADDRESSEXPIRY + ", " - + COLNAME_GROUPHINT + " " + COLTYPE_GROUPHINT + ", " - + COLNAME_DNSADDRESSES + " " + COLTYPE_DNSADDRESSES + ", " - + COLNAME_MTU + " " + COLTYPE_MTU + ")"; - public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME; - } - - /** - * Contract class for the Private Data table. - */ - public static class PrivateDataContract { - public static final String TABLENAME = "PrivateData"; - - public static final String COLNAME_L2KEY = "l2Key"; - public static final String COLTYPE_L2KEY = "TEXT NOT NULL"; - - public static final String COLNAME_CLIENT = "client"; - public static final String COLTYPE_CLIENT = "TEXT NOT NULL"; - - public static final String COLNAME_DATANAME = "dataName"; - public static final String COLTYPE_DATANAME = "TEXT NOT NULL"; - - public static final String COLNAME_DATA = "data"; - public static final String COLTYPE_DATA = "BLOB NOT NULL"; - - public static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS " - + TABLENAME + " (" - + COLNAME_L2KEY + " " + COLTYPE_L2KEY + ", " - + COLNAME_CLIENT + " " + COLTYPE_CLIENT + ", " - + COLNAME_DATANAME + " " + COLTYPE_DATANAME + ", " - + COLNAME_DATA + " " + COLTYPE_DATA + ", " - + "PRIMARY KEY (" - + COLNAME_L2KEY + ", " - + COLNAME_CLIENT + ", " - + COLNAME_DATANAME + "))"; - public static final String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLENAME; - } - - // To save memory when the DB is not used, close it after 30s of inactivity. This is - // determined manually based on what feels right. - private static final long IDLE_CONNECTION_TIMEOUT_MS = 30_000; - - /** The SQLite DB helper */ - public static class DbHelper extends SQLiteOpenHelper { - // Update this whenever changing the schema. - private static final int SCHEMA_VERSION = 4; - private static final String DATABASE_FILENAME = "IpMemoryStore.db"; - private static final String TRIGGER_NAME = "delete_cascade_to_private"; - - public DbHelper(@NonNull final Context context) { - super(context, DATABASE_FILENAME, null, SCHEMA_VERSION); - setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT_MS); - } - - /** Called when the database is created */ - @Override - public void onCreate(@NonNull final SQLiteDatabase db) { - db.execSQL(NetworkAttributesContract.CREATE_TABLE); - db.execSQL(PrivateDataContract.CREATE_TABLE); - createTrigger(db); - } - - /** Called when the database is upgraded */ - @Override - public void onUpgrade(@NonNull final SQLiteDatabase db, final int oldVersion, - final int newVersion) { - try { - if (oldVersion < 2) { - // upgrade from version 1 to version 2 - // since we starts from version 2, do nothing here - } - - if (oldVersion < 3) { - // upgrade from version 2 to version 3 - final String sqlUpgradeAddressExpiry = "alter table" - + " " + NetworkAttributesContract.TABLENAME + " ADD" - + " " + NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESSEXPIRY - + " " + NetworkAttributesContract.COLTYPE_ASSIGNEDV4ADDRESSEXPIRY; - db.execSQL(sqlUpgradeAddressExpiry); - } - - if (oldVersion < 4) { - createTrigger(db); - } - } catch (SQLiteException e) { - Log.e(TAG, "Could not upgrade to the new version", e); - // create database with new version - db.execSQL(NetworkAttributesContract.DROP_TABLE); - db.execSQL(PrivateDataContract.DROP_TABLE); - onCreate(db); - } - } - - /** Called when the database is downgraded */ - @Override - public void onDowngrade(@NonNull final SQLiteDatabase db, final int oldVersion, - final int newVersion) { - // Downgrades always nuke all data and recreate an empty table. - db.execSQL(NetworkAttributesContract.DROP_TABLE); - db.execSQL(PrivateDataContract.DROP_TABLE); - db.execSQL("DROP TRIGGER " + TRIGGER_NAME); - onCreate(db); - } - - private void createTrigger(@NonNull final SQLiteDatabase db) { - final String createTrigger = "CREATE TRIGGER " + TRIGGER_NAME - + " DELETE ON " + NetworkAttributesContract.TABLENAME - + " BEGIN" - + " DELETE FROM " + PrivateDataContract.TABLENAME + " WHERE OLD." - + NetworkAttributesContract.COLNAME_L2KEY - + "=" + PrivateDataContract.COLNAME_L2KEY - + "; END;"; - db.execSQL(createTrigger); - } - } - - @NonNull - private static byte[] encodeAddressList(@NonNull final List<InetAddress> addresses) { - final ByteArrayOutputStream os = new ByteArrayOutputStream(); - for (final InetAddress address : addresses) { - final byte[] b = address.getAddress(); - os.write(b.length); - os.write(b, 0, b.length); - } - return os.toByteArray(); - } - - @NonNull - private static ArrayList<InetAddress> decodeAddressList(@NonNull final byte[] encoded) { - final ByteArrayInputStream is = new ByteArrayInputStream(encoded); - final ArrayList<InetAddress> addresses = new ArrayList<>(); - int d = -1; - while ((d = is.read()) != -1) { - final byte[] bytes = new byte[d]; - is.read(bytes, 0, d); - try { - addresses.add(InetAddress.getByAddress(bytes)); - } catch (UnknownHostException e) { /* Hopefully impossible */ } - } - return addresses; - } - - @NonNull - private static ContentValues toContentValues(@Nullable final NetworkAttributes attributes) { - final ContentValues values = new ContentValues(); - if (null == attributes) return values; - if (null != attributes.assignedV4Address) { - values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, - inet4AddressToIntHTH(attributes.assignedV4Address)); - } - if (null != attributes.assignedV4AddressExpiry) { - values.put(NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESSEXPIRY, - attributes.assignedV4AddressExpiry); - } - if (null != attributes.groupHint) { - values.put(NetworkAttributesContract.COLNAME_GROUPHINT, attributes.groupHint); - } - if (null != attributes.dnsAddresses) { - values.put(NetworkAttributesContract.COLNAME_DNSADDRESSES, - encodeAddressList(attributes.dnsAddresses)); - } - if (null != attributes.mtu) { - values.put(NetworkAttributesContract.COLNAME_MTU, attributes.mtu); - } - return values; - } - - // Convert a NetworkAttributes object to content values to store them in a table compliant - // with the contract defined in NetworkAttributesContract. - @NonNull - private static ContentValues toContentValues(@NonNull final String key, - @Nullable final NetworkAttributes attributes, final long expiry) { - final ContentValues values = toContentValues(attributes); - values.put(NetworkAttributesContract.COLNAME_L2KEY, key); - values.put(NetworkAttributesContract.COLNAME_EXPIRYDATE, expiry); - return values; - } - - // Convert a byte array into content values to store it in a table compliant with the - // contract defined in PrivateDataContract. - @NonNull - private static ContentValues toContentValues(@NonNull final String key, - @NonNull final String clientId, @NonNull final String name, - @NonNull final byte[] data) { - final ContentValues values = new ContentValues(); - values.put(PrivateDataContract.COLNAME_L2KEY, key); - values.put(PrivateDataContract.COLNAME_CLIENT, clientId); - values.put(PrivateDataContract.COLNAME_DATANAME, name); - values.put(PrivateDataContract.COLNAME_DATA, data); - return values; - } - - @Nullable - private static NetworkAttributes readNetworkAttributesLine(@NonNull final Cursor cursor) { - // Make sure the data hasn't expired - final long expiry = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, -1L); - if (expiry < System.currentTimeMillis()) return null; - - final NetworkAttributes.Builder builder = new NetworkAttributes.Builder(); - final int assignedV4AddressInt = getInt(cursor, - NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESS, 0); - final long assignedV4AddressExpiry = getLong(cursor, - NetworkAttributesContract.COLNAME_ASSIGNEDV4ADDRESSEXPIRY, 0); - final String groupHint = getString(cursor, NetworkAttributesContract.COLNAME_GROUPHINT); - final byte[] dnsAddressesBlob = - getBlob(cursor, NetworkAttributesContract.COLNAME_DNSADDRESSES); - final int mtu = getInt(cursor, NetworkAttributesContract.COLNAME_MTU, -1); - if (0 != assignedV4AddressInt) { - builder.setAssignedV4Address(intToInet4AddressHTH(assignedV4AddressInt)); - } - if (0 != assignedV4AddressExpiry) { - builder.setAssignedV4AddressExpiry(assignedV4AddressExpiry); - } - builder.setGroupHint(groupHint); - if (null != dnsAddressesBlob) { - builder.setDnsAddresses(decodeAddressList(dnsAddressesBlob)); - } - if (mtu >= 0) { - builder.setMtu(mtu); - } - return builder.build(); - } - - private static final String[] EXPIRY_COLUMN = new String[] { - NetworkAttributesContract.COLNAME_EXPIRYDATE - }; - static final int EXPIRY_ERROR = -1; // Legal values for expiry are positive - - static final String SELECT_L2KEY = NetworkAttributesContract.COLNAME_L2KEY + " = ?"; - - // Returns the expiry date of the specified row, or one of the error codes above if the - // row is not found or some other error - static long getExpiry(@NonNull final SQLiteDatabase db, @NonNull final String key) { - final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, - EXPIRY_COLUMN, // columns - SELECT_L2KEY, // selection - new String[] { key }, // selectionArgs - null, // groupBy - null, // having - null // orderBy - ); - // L2KEY is the primary key ; it should not be possible to get more than one - // result here. 0 results means the key was not found. - if (cursor.getCount() != 1) return EXPIRY_ERROR; - cursor.moveToFirst(); - final long result = cursor.getLong(0); // index in the EXPIRY_COLUMN array - cursor.close(); - return result; - } - - static final int RELEVANCE_ERROR = -1; // Legal values for relevance are positive - - // Returns the relevance of the specified row, or one of the error codes above if the - // row is not found or some other error - static int getRelevance(@NonNull final SQLiteDatabase db, @NonNull final String key) { - final long expiry = getExpiry(db, key); - return expiry < 0 ? (int) expiry : RelevanceUtils.computeRelevanceForNow(expiry); - } - - // If the attributes are null, this will only write the expiry. - // Returns an int out of Status.{SUCCESS, ERROR_*} - static int storeNetworkAttributes(@NonNull final SQLiteDatabase db, @NonNull final String key, - final long expiry, @Nullable final NetworkAttributes attributes) { - final ContentValues cv = toContentValues(key, attributes, expiry); - db.beginTransaction(); - try { - // Unfortunately SQLite does not have any way to do INSERT OR UPDATE. Options are - // to either insert with on conflict ignore then update (like done here), or to - // construct a custom SQL INSERT statement with nested select. - final long resultId = db.insertWithOnConflict(NetworkAttributesContract.TABLENAME, - null, cv, SQLiteDatabase.CONFLICT_IGNORE); - if (resultId < 0) { - db.update(NetworkAttributesContract.TABLENAME, cv, SELECT_L2KEY, new String[]{key}); - } - db.setTransactionSuccessful(); - return Status.SUCCESS; - } catch (SQLiteException e) { - // No space left on disk or something - Log.e(TAG, "Could not write to the memory store", e); - } finally { - db.endTransaction(); - } - return Status.ERROR_STORAGE; - } - - // Returns an int out of Status.{SUCCESS, ERROR_*} - static int storeBlob(@NonNull final SQLiteDatabase db, @NonNull final String key, - @NonNull final String clientId, @NonNull final String name, - @NonNull final byte[] data) { - final long res = db.insertWithOnConflict(PrivateDataContract.TABLENAME, null, - toContentValues(key, clientId, name, data), SQLiteDatabase.CONFLICT_REPLACE); - return (res == -1) ? Status.ERROR_STORAGE : Status.SUCCESS; - } - - @Nullable - static NetworkAttributes retrieveNetworkAttributes(@NonNull final SQLiteDatabase db, - @NonNull final String key) { - final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, - null, // columns, null means everything - NetworkAttributesContract.COLNAME_L2KEY + " = ?", // selection - new String[] { key }, // selectionArgs - null, // groupBy - null, // having - null); // orderBy - // L2KEY is the primary key ; it should not be possible to get more than one - // result here. 0 results means the key was not found. - if (cursor.getCount() != 1) return null; - cursor.moveToFirst(); - final NetworkAttributes attributes = readNetworkAttributesLine(cursor); - cursor.close(); - return attributes; - } - - private static final String[] DATA_COLUMN = new String[] { - PrivateDataContract.COLNAME_DATA - }; - @Nullable - static byte[] retrieveBlob(@NonNull final SQLiteDatabase db, @NonNull final String key, - @NonNull final String clientId, @NonNull final String name) { - final Cursor cursor = db.query(PrivateDataContract.TABLENAME, - DATA_COLUMN, // columns - PrivateDataContract.COLNAME_L2KEY + " = ? AND " // selection - + PrivateDataContract.COLNAME_CLIENT + " = ? AND " - + PrivateDataContract.COLNAME_DATANAME + " = ?", - new String[] { key, clientId, name }, // selectionArgs - null, // groupBy - null, // having - null); // orderBy - // The query above is querying by (composite) primary key, so it should not be possible to - // get more than one result here. 0 results means the key was not found. - if (cursor.getCount() != 1) return null; - cursor.moveToFirst(); - final byte[] result = cursor.getBlob(0); // index in the DATA_COLUMN array - cursor.close(); - return result; - } - - /** - * The following is a horrible hack that is necessary because the Android SQLite API does not - * have a way to query a binary blob. This, almost certainly, is an overlook. - * - * The Android SQLite API has two family of methods : one for query that returns data, and - * one for more general SQL statements that can execute any statement but may not return - * anything. All the query methods, however, take only String[] for the arguments. - * - * In principle it is simple to write a function that will encode the binary blob in the - * way SQLite expects it. However, because the API forces the argument to be coerced into a - * String, the SQLiteQuery object generated by the default query methods will bind all - * arguments as Strings and SQL will *sanitize* them. This works okay for numeric types, - * but the format for blobs is x'<hex string>'. Note the presence of quotes, which will - * be sanitized, changing the contents of the field, and the query will fail to match the - * blob. - * - * As far as I can tell, there are two possible ways around this problem. The first one - * is to put the data in the query string and eschew it being an argument. This would - * require doing the sanitizing by hand. The other is to call bindBlob directly on the - * generated SQLiteQuery object, which not only is a lot less dangerous than rolling out - * sanitizing, but also will do the right thing if the underlying format ever changes. - * - * But none of the methods that take an SQLiteQuery object can return data ; this *must* - * be called with SQLiteDatabase#query. This object is not accessible from outside. - * However, there is a #query version that accepts a CursorFactory and this is pretty - * straightforward to implement as all the arguments are coming in and the SQLiteCursor - * class is public API. - * With this, it's possible to intercept the SQLiteQuery object, and assuming the args - * are available, to bind them directly and work around the API's oblivious coercion into - * Strings. - * - * This is really sad, but I don't see another way of having this work than this or the - * hand-rolled sanitizing, and this is the lesser evil. - */ - private static class CustomCursorFactory implements SQLiteDatabase.CursorFactory { - @NonNull - private final ArrayList<Object> mArgs; - CustomCursorFactory(@NonNull final ArrayList<Object> args) { - mArgs = args; - } - @Override - public Cursor newCursor(final SQLiteDatabase db, final SQLiteCursorDriver masterQuery, - final String editTable, - final SQLiteQuery query) { - int index = 1; // bind is 1-indexed - for (final Object arg : mArgs) { - if (arg instanceof String) { - query.bindString(index++, (String) arg); - } else if (arg instanceof Long) { - query.bindLong(index++, (Long) arg); - } else if (arg instanceof Integer) { - query.bindLong(index++, Long.valueOf((Integer) arg)); - } else if (arg instanceof byte[]) { - query.bindBlob(index++, (byte[]) arg); - } else { - throw new IllegalStateException("Unsupported type CustomCursorFactory " - + arg.getClass().toString()); - } - } - return new SQLiteCursor(masterQuery, editTable, query); - } - } - - // Returns the l2key of the closest match, if and only if it matches - // closely enough (as determined by group-closeness). - @Nullable - static String findClosestAttributes(@NonNull final SQLiteDatabase db, - @NonNull final NetworkAttributes attr) { - if (attr.isEmpty()) return null; - final ContentValues values = toContentValues(attr); - - // Build the selection and args. To cut down on the number of lines to search, limit - // the search to those with at least one argument equals to the requested attributes. - // This works only because null attributes match only will not result in group-closeness. - final StringJoiner sj = new StringJoiner(" OR "); - final ArrayList<Object> args = new ArrayList<>(); - args.add(System.currentTimeMillis()); - for (final String field : values.keySet()) { - sj.add(field + " = ?"); - args.add(values.get(field)); - } - - final String selection = NetworkAttributesContract.COLNAME_EXPIRYDATE + " > ? AND (" - + sj.toString() + ")"; - final Cursor cursor = db.queryWithFactory(new CustomCursorFactory(args), - false, // distinct - NetworkAttributesContract.TABLENAME, - null, // columns, null means everything - selection, // selection - null, // selectionArgs, horrendously passed to the cursor factory instead - null, // groupBy - null, // having - null, // orderBy - null); // limit - if (cursor.getCount() <= 0) return null; - cursor.moveToFirst(); - String bestKey = null; - float bestMatchConfidence = GROUPCLOSE_CONFIDENCE; // Never return a match worse than this. - while (!cursor.isAfterLast()) { - final NetworkAttributes read = readNetworkAttributesLine(cursor); - final float confidence = read.getNetworkGroupSamenessConfidence(attr); - if (confidence > bestMatchConfidence) { - bestKey = getString(cursor, NetworkAttributesContract.COLNAME_L2KEY); - bestMatchConfidence = confidence; - } - cursor.moveToNext(); - } - cursor.close(); - return bestKey; - } - - // Drops all records that are expired. Relevance has decayed to zero of these records. Returns - // an int out of Status.{SUCCESS, ERROR_*} - static int dropAllExpiredRecords(@NonNull final SQLiteDatabase db) { - db.beginTransaction(); - try { - // Deletes NetworkAttributes that have expired. - db.delete(NetworkAttributesContract.TABLENAME, - NetworkAttributesContract.COLNAME_EXPIRYDATE + " < ?", - new String[]{Long.toString(System.currentTimeMillis())}); - db.setTransactionSuccessful(); - } catch (SQLiteException e) { - Log.e(TAG, "Could not delete data from memory store", e); - return Status.ERROR_STORAGE; - } finally { - db.endTransaction(); - } - - // Execute vacuuming here if above operation has no exception. If above operation got - // exception, vacuuming can be ignored for reducing unnecessary consumption. - try { - db.execSQL("VACUUM"); - } catch (SQLiteException e) { - // Do nothing. - } - return Status.SUCCESS; - } - - // Drops number of records that start from the lowest expiryDate. Returns an int out of - // Status.{SUCCESS, ERROR_*} - static int dropNumberOfRecords(@NonNull final SQLiteDatabase db, int number) { - if (number <= 0) { - return Status.ERROR_ILLEGAL_ARGUMENT; - } - - // Queries number of NetworkAttributes that start from the lowest expiryDate. - final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, - new String[] {NetworkAttributesContract.COLNAME_EXPIRYDATE}, // columns - null, // selection - null, // selectionArgs - null, // groupBy - null, // having - NetworkAttributesContract.COLNAME_EXPIRYDATE, // orderBy - Integer.toString(number)); // limit - if (cursor == null || cursor.getCount() <= 0) return Status.ERROR_GENERIC; - cursor.moveToLast(); - - //Get the expiryDate from last record. - final long expiryDate = getLong(cursor, NetworkAttributesContract.COLNAME_EXPIRYDATE, 0); - cursor.close(); - - db.beginTransaction(); - try { - // Deletes NetworkAttributes that expiryDate are lower than given value. - db.delete(NetworkAttributesContract.TABLENAME, - NetworkAttributesContract.COLNAME_EXPIRYDATE + " <= ?", - new String[]{Long.toString(expiryDate)}); - db.setTransactionSuccessful(); - } catch (SQLiteException e) { - Log.e(TAG, "Could not delete data from memory store", e); - return Status.ERROR_STORAGE; - } finally { - db.endTransaction(); - } - - // Execute vacuuming here if above operation has no exception. If above operation got - // exception, vacuuming can be ignored for reducing unnecessary consumption. - try { - db.execSQL("VACUUM"); - } catch (SQLiteException e) { - // Do nothing. - } - return Status.SUCCESS; - } - - static int getTotalRecordNumber(@NonNull final SQLiteDatabase db) { - // Query the total number of NetworkAttributes - final Cursor cursor = db.query(NetworkAttributesContract.TABLENAME, - new String[] {"COUNT(*)"}, // columns - null, // selection - null, // selectionArgs - null, // groupBy - null, // having - null); // orderBy - cursor.moveToFirst(); - return cursor == null ? 0 : cursor.getInt(0); - } - - // Helper methods - private static String getString(final Cursor cursor, final String columnName) { - final int columnIndex = cursor.getColumnIndex(columnName); - return (columnIndex >= 0) ? cursor.getString(columnIndex) : null; - } - private static byte[] getBlob(final Cursor cursor, final String columnName) { - final int columnIndex = cursor.getColumnIndex(columnName); - return (columnIndex >= 0) ? cursor.getBlob(columnIndex) : null; - } - private static int getInt(final Cursor cursor, final String columnName, - final int defaultValue) { - final int columnIndex = cursor.getColumnIndex(columnName); - return (columnIndex >= 0) ? cursor.getInt(columnIndex) : defaultValue; - } - private static long getLong(final Cursor cursor, final String columnName, - final long defaultValue) { - final int columnIndex = cursor.getColumnIndex(columnName); - return (columnIndex >= 0) ? cursor.getLong(columnIndex) : defaultValue; - } -} diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java deleted file mode 100644 index 8312dfeb1a3b..000000000000 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreService.java +++ /dev/null @@ -1,506 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity.ipmemorystore; - -import static android.net.ipmemorystore.Status.ERROR_DATABASE_CANNOT_BE_OPENED; -import static android.net.ipmemorystore.Status.ERROR_GENERIC; -import static android.net.ipmemorystore.Status.ERROR_ILLEGAL_ARGUMENT; -import static android.net.ipmemorystore.Status.SUCCESS; - -import static com.android.server.connectivity.ipmemorystore.IpMemoryStoreDatabase.EXPIRY_ERROR; -import static com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService.InterruptMaintenance; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.net.IIpMemoryStore; -import android.net.ipmemorystore.Blob; -import android.net.ipmemorystore.IOnBlobRetrievedListener; -import android.net.ipmemorystore.IOnL2KeyResponseListener; -import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener; -import android.net.ipmemorystore.IOnSameL3NetworkResponseListener; -import android.net.ipmemorystore.IOnStatusListener; -import android.net.ipmemorystore.NetworkAttributes; -import android.net.ipmemorystore.NetworkAttributesParcelable; -import android.net.ipmemorystore.SameL3NetworkResponse; -import android.net.ipmemorystore.Status; -import android.net.ipmemorystore.StatusParcelable; -import android.os.RemoteException; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; - -import java.io.File; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Implementation for the IP memory store. - * This component offers specialized services for network components to store and retrieve - * knowledge about networks, and provides intelligence that groups level 2 networks together - * into level 3 networks. - * - * @hide - */ -public class IpMemoryStoreService extends IIpMemoryStore.Stub { - private static final String TAG = IpMemoryStoreService.class.getSimpleName(); - private static final int MAX_CONCURRENT_THREADS = 4; - private static final int DATABASE_SIZE_THRESHOLD = 10 * 1024 * 1024; //10MB - private static final int MAX_DROP_RECORD_TIMES = 500; - private static final int MIN_DELETE_NUM = 5; - private static final boolean DBG = true; - - // Error codes below are internal and used for notifying status beteween IpMemoryStore modules. - static final int ERROR_INTERNAL_BASE = -1_000_000_000; - // This error code is used for maintenance only to notify RegularMaintenanceJobService that - // full maintenance job has been interrupted. - static final int ERROR_INTERNAL_INTERRUPTED = ERROR_INTERNAL_BASE - 1; - - @NonNull - final Context mContext; - @Nullable - final SQLiteDatabase mDb; - @NonNull - final ExecutorService mExecutor; - - /** - * Construct an IpMemoryStoreService object. - * This constructor will block on disk access to open the database. - * @param context the context to access storage with. - */ - public IpMemoryStoreService(@NonNull final Context context) { - // Note that constructing the service will access the disk and block - // for some time, but it should make no difference to the clients. Because - // the interface is one-way, clients fire and forget requests, and the callback - // will get called eventually in any case, and the framework will wait for the - // service to be created to deliver subsequent requests. - // Avoiding this would mean the mDb member can't be final, which means the service would - // have to test for nullity, care for failure, and allow for a wait at every single access, - // which would make the code a lot more complex and require all methods to possibly block. - mContext = context; - SQLiteDatabase db; - final IpMemoryStoreDatabase.DbHelper helper = new IpMemoryStoreDatabase.DbHelper(context); - try { - db = helper.getWritableDatabase(); - if (null == db) Log.e(TAG, "Unexpected null return of getWriteableDatabase"); - } catch (final SQLException e) { - Log.e(TAG, "Can't open the Ip Memory Store database", e); - db = null; - } catch (final Exception e) { - Log.wtf(TAG, "Impossible exception Ip Memory Store database", e); - db = null; - } - mDb = db; - // The work-stealing thread pool executor will spawn threads as needed up to - // the max only when there is no free thread available. This generally behaves - // exactly like one would expect it intuitively : - // - When work arrives, it will spawn a new thread iff there are no available threads - // - When there is no work to do it will shutdown threads after a while (the while - // being equal to 2 seconds (not configurable) when max threads are spun up and - // twice as much for every one less thread) - // - When all threads are busy the work is enqueued and waits for any worker - // to become available. - // Because the stealing pool is made for very heavily parallel execution of - // small tasks that spawn others, it creates a queue per thread that in this - // case is overhead. However, the three behaviors above make it a superior - // choice to cached or fixedThreadPoolExecutor, neither of which can actually - // enqueue a task waiting for a thread to be free. This can probably be solved - // with judicious subclassing of ThreadPoolExecutor, but that's a lot of dangerous - // complexity for little benefit in this case. - mExecutor = Executors.newWorkStealingPool(MAX_CONCURRENT_THREADS); - RegularMaintenanceJobService.schedule(mContext, this); - } - - /** - * Shutdown the memory store service, cancelling running tasks and dropping queued tasks. - * - * This is provided to give a way to clean up, and is meant to be available in case of an - * emergency shutdown. - */ - public void shutdown() { - // By contrast with ExecutorService#shutdown, ExecutorService#shutdownNow tries - // to cancel the existing tasks, and does not wait for completion. It does not - // guarantee the threads can be terminated in any given amount of time. - mExecutor.shutdownNow(); - if (mDb != null) mDb.close(); - RegularMaintenanceJobService.unschedule(mContext); - } - - /** Helper function to make a status object */ - private StatusParcelable makeStatus(final int code) { - return new Status(code).toParcelable(); - } - - /** - * Store network attributes for a given L2 key. - * - * @param l2Key The L2 key for the L2 network. Clients that don't know or care about the L2 - * key and only care about grouping can pass a unique ID here like the ones - * generated by {@code java.util.UUID.randomUUID()}, but keep in mind the low - * relevance of such a network will lead to it being evicted soon if it's not - * refreshed. Use findL2Key to try and find a similar L2Key to these attributes. - * @param attributes The attributes for this network. - * @param listener A listener to inform of the completion of this call, or null if the client - * is not interested in learning about success/failure. - * Through the listener, returns the L2 key. This is useful if the L2 key was not specified. - * If the call failed, the L2 key will be null. - */ - // Note that while l2Key and attributes are non-null in spirit, they are received from - // another process. If the remote process decides to ignore everything and send null, this - // process should still not crash. - @Override - public void storeNetworkAttributes(@Nullable final String l2Key, - @Nullable final NetworkAttributesParcelable attributes, - @Nullable final IOnStatusListener listener) { - // Because the parcelable is 100% mutable, the thread may not see its members initialized. - // Therefore either an immutable object is created on this same thread before it's passed - // to the executor, or there need to be a write barrier here and a read barrier in the - // remote thread. - final NetworkAttributes na = null == attributes ? null : new NetworkAttributes(attributes); - mExecutor.execute(() -> { - try { - final int code = storeNetworkAttributesAndBlobSync(l2Key, na, - null /* clientId */, null /* name */, null /* data */); - if (null != listener) listener.onComplete(makeStatus(code)); - } catch (final RemoteException e) { - // Client at the other end died - } - }); - } - - /** - * Store a binary blob associated with an L2 key and a name. - * - * @param l2Key The L2 key for this network. - * @param clientId The ID of the client. - * @param name The name of this data. - * @param blob The data to store. - * @param listener The listener that will be invoked to return the answer, or null if the - * is not interested in learning about success/failure. - * Through the listener, returns a status to indicate success or failure. - */ - @Override - public void storeBlob(@Nullable final String l2Key, @Nullable final String clientId, - @Nullable final String name, @Nullable final Blob blob, - @Nullable final IOnStatusListener listener) { - final byte[] data = null == blob ? null : blob.data; - mExecutor.execute(() -> { - try { - final int code = storeNetworkAttributesAndBlobSync(l2Key, - null /* NetworkAttributes */, clientId, name, data); - if (null != listener) listener.onComplete(makeStatus(code)); - } catch (final RemoteException e) { - // Client at the other end died - } - }); - } - - /** - * Helper method for storeNetworkAttributes and storeBlob. - * - * Either attributes or none of clientId, name and data may be null. This will write the - * passed data if non-null, and will write attributes if non-null, but in any case it will - * bump the relevance up. - * Returns a success code from Status. - */ - private int storeNetworkAttributesAndBlobSync(@Nullable final String l2Key, - @Nullable final NetworkAttributes attributes, - @Nullable final String clientId, - @Nullable final String name, @Nullable final byte[] data) { - if (null == l2Key) return ERROR_ILLEGAL_ARGUMENT; - if (null == attributes && null == data) return ERROR_ILLEGAL_ARGUMENT; - if (null != data && (null == clientId || null == name)) return ERROR_ILLEGAL_ARGUMENT; - if (null == mDb) return ERROR_DATABASE_CANNOT_BE_OPENED; - try { - final long oldExpiry = IpMemoryStoreDatabase.getExpiry(mDb, l2Key); - final long newExpiry = RelevanceUtils.bumpExpiryDate( - oldExpiry == EXPIRY_ERROR ? System.currentTimeMillis() : oldExpiry); - final int errorCode = - IpMemoryStoreDatabase.storeNetworkAttributes(mDb, l2Key, newExpiry, attributes); - // If no blob to store, the client is interested in the result of storing the attributes - if (null == data) return errorCode; - // Otherwise it's interested in the result of storing the blob - return IpMemoryStoreDatabase.storeBlob(mDb, l2Key, clientId, name, data); - } catch (Exception e) { - if (DBG) { - Log.e(TAG, "Exception while storing for key {" + l2Key - + "} ; NetworkAttributes {" + (null == attributes ? "null" : attributes) - + "} ; clientId {" + (null == clientId ? "null" : clientId) - + "} ; name {" + (null == name ? "null" : name) - + "} ; data {" + Utils.byteArrayToString(data) + "}", e); - } - } - return ERROR_GENERIC; - } - - /** - * Returns the best L2 key associated with the attributes. - * - * This will find a record that would be in the same group as the passed attributes. This is - * useful to choose the key for storing a sample or private data when the L2 key is not known. - * If multiple records are group-close to these attributes, the closest match is returned. - * If multiple records have the same closeness, the one with the smaller (unicode codepoint - * order) L2 key is returned. - * If no record matches these attributes, null is returned. - * - * @param attributes The attributes of the network to find. - * @param listener The listener that will be invoked to return the answer. - * Through the listener, returns the L2 key if one matched, or null. - */ - @Override - public void findL2Key(@Nullable final NetworkAttributesParcelable attributes, - @Nullable final IOnL2KeyResponseListener listener) { - if (null == listener) return; - mExecutor.execute(() -> { - try { - if (null == attributes) { - listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null); - return; - } - if (null == mDb) { - listener.onL2KeyResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null); - return; - } - final String key = IpMemoryStoreDatabase.findClosestAttributes(mDb, - new NetworkAttributes(attributes)); - listener.onL2KeyResponse(makeStatus(SUCCESS), key); - } catch (final RemoteException e) { - // Client at the other end died - } - }); - } - - /** - * Returns whether, to the best of the store's ability to tell, the two specified L2 keys point - * to the same L3 network. Group-closeness is used to determine this. - * - * @param l2Key1 The key for the first network. - * @param l2Key2 The key for the second network. - * @param listener The listener that will be invoked to return the answer. - * Through the listener, a SameL3NetworkResponse containing the answer and confidence. - */ - @Override - public void isSameNetwork(@Nullable final String l2Key1, @Nullable final String l2Key2, - @Nullable final IOnSameL3NetworkResponseListener listener) { - if (null == listener) return; - mExecutor.execute(() -> { - try { - if (null == l2Key1 || null == l2Key2) { - listener.onSameL3NetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null); - return; - } - if (null == mDb) { - listener.onSameL3NetworkResponse(makeStatus(ERROR_ILLEGAL_ARGUMENT), null); - return; - } - try { - final NetworkAttributes attr1 = - IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key1); - final NetworkAttributes attr2 = - IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key2); - if (null == attr1 || null == attr2) { - listener.onSameL3NetworkResponse(makeStatus(SUCCESS), - new SameL3NetworkResponse(l2Key1, l2Key2, - -1f /* never connected */).toParcelable()); - return; - } - final float confidence = attr1.getNetworkGroupSamenessConfidence(attr2); - listener.onSameL3NetworkResponse(makeStatus(SUCCESS), - new SameL3NetworkResponse(l2Key1, l2Key2, confidence).toParcelable()); - } catch (Exception e) { - listener.onSameL3NetworkResponse(makeStatus(ERROR_GENERIC), null); - } - } catch (final RemoteException e) { - // Client at the other end died - } - }); - } - - /** - * Retrieve the network attributes for a key. - * If no record is present for this key, this will return null attributes. - * - * @param l2Key The key of the network to query. - * @param listener The listener that will be invoked to return the answer. - * Through the listener, returns the network attributes and the L2 key associated with - * the query. - */ - @Override - public void retrieveNetworkAttributes(@Nullable final String l2Key, - @Nullable final IOnNetworkAttributesRetrievedListener listener) { - if (null == listener) return; - mExecutor.execute(() -> { - try { - if (null == l2Key) { - listener.onNetworkAttributesRetrieved( - makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, null); - return; - } - if (null == mDb) { - listener.onNetworkAttributesRetrieved( - makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key, null); - return; - } - try { - final NetworkAttributes attributes = - IpMemoryStoreDatabase.retrieveNetworkAttributes(mDb, l2Key); - listener.onNetworkAttributesRetrieved(makeStatus(SUCCESS), l2Key, - null == attributes ? null : attributes.toParcelable()); - } catch (final Exception e) { - listener.onNetworkAttributesRetrieved(makeStatus(ERROR_GENERIC), l2Key, null); - } - } catch (final RemoteException e) { - // Client at the other end died - } - }); - } - - /** - * Retrieve previously stored private data. - * If no data was stored for this L2 key and name this will return null. - * - * @param l2Key The L2 key. - * @param clientId The id of the client that stored this data. - * @param name The name of the data. - * @param listener The listener that will be invoked to return the answer. - * Through the listener, returns the private data if any or null if none, with the L2 key - * and the name of the data associated with the query. - */ - @Override - public void retrieveBlob(@NonNull final String l2Key, @NonNull final String clientId, - @NonNull final String name, @NonNull final IOnBlobRetrievedListener listener) { - if (null == listener) return; - mExecutor.execute(() -> { - try { - if (null == l2Key) { - listener.onBlobRetrieved(makeStatus(ERROR_ILLEGAL_ARGUMENT), l2Key, name, null); - return; - } - if (null == mDb) { - listener.onBlobRetrieved(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED), l2Key, - name, null); - return; - } - try { - final Blob b = new Blob(); - b.data = IpMemoryStoreDatabase.retrieveBlob(mDb, l2Key, clientId, name); - listener.onBlobRetrieved(makeStatus(SUCCESS), l2Key, name, b); - } catch (final Exception e) { - listener.onBlobRetrieved(makeStatus(ERROR_GENERIC), l2Key, name, null); - } - } catch (final RemoteException e) { - // Client at the other end died - } - }); - } - - @Override - public void factoryReset() { - } - - /** Get db size threshold. */ - @VisibleForTesting - protected int getDbSizeThreshold() { - return DATABASE_SIZE_THRESHOLD; - } - - private long getDbSize() { - final File dbFile = new File(mDb.getPath()); - try { - return dbFile.length(); - } catch (final SecurityException e) { - if (DBG) Log.e(TAG, "Read db size access deny.", e); - // Return zero value if can't get disk usage exactly. - return 0; - } - } - - /** Check if db size is over the threshold. */ - @VisibleForTesting - boolean isDbSizeOverThreshold() { - return getDbSize() > getDbSizeThreshold(); - } - - /** - * Full maintenance. - * - * @param listener A listener to inform of the completion of this call. - */ - void fullMaintenance(@NonNull final IOnStatusListener listener, - @NonNull final InterruptMaintenance interrupt) { - mExecutor.execute(() -> { - try { - if (null == mDb) { - listener.onComplete(makeStatus(ERROR_DATABASE_CANNOT_BE_OPENED)); - return; - } - - // Interrupt maintenance because the scheduling job has been canceled. - if (checkForInterrupt(listener, interrupt)) return; - - int result = SUCCESS; - // Drop all records whose relevance has decayed to zero. - // This is the first step to decrease memory store size. - result = IpMemoryStoreDatabase.dropAllExpiredRecords(mDb); - - if (checkForInterrupt(listener, interrupt)) return; - - // Aggregate historical data in passes - // TODO : Waiting for historical data implement. - - // Check if db size meets the storage goal(10MB). If not, keep dropping records and - // aggregate historical data until the storage goal is met. Use for loop with 500 - // times restriction to prevent infinite loop (Deleting records always fail and db - // size is still over the threshold) - for (int i = 0; isDbSizeOverThreshold() && i < MAX_DROP_RECORD_TIMES; i++) { - if (checkForInterrupt(listener, interrupt)) return; - - final int totalNumber = IpMemoryStoreDatabase.getTotalRecordNumber(mDb); - final long dbSize = getDbSize(); - final float decreaseRate = (dbSize == 0) - ? 0 : (float) (dbSize - getDbSizeThreshold()) / (float) dbSize; - final int deleteNumber = Math.max( - (int) (totalNumber * decreaseRate), MIN_DELETE_NUM); - - result = IpMemoryStoreDatabase.dropNumberOfRecords(mDb, deleteNumber); - - if (checkForInterrupt(listener, interrupt)) return; - - // Aggregate historical data - // TODO : Waiting for historical data implement. - } - listener.onComplete(makeStatus(result)); - } catch (final RemoteException e) { - // Client at the other end died - } - }); - } - - private boolean checkForInterrupt(@NonNull final IOnStatusListener listener, - @NonNull final InterruptMaintenance interrupt) throws RemoteException { - if (!interrupt.isInterrupted()) return false; - listener.onComplete(makeStatus(ERROR_INTERNAL_INTERRUPTED)); - return true; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } -} diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java deleted file mode 100644 index bea7052d8af2..000000000000 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RegularMaintenanceJobService.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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.server.connectivity.ipmemorystore; - -import android.app.job.JobInfo; -import android.app.job.JobParameters; -import android.app.job.JobScheduler; -import android.app.job.JobService; -import android.content.ComponentName; -import android.content.Context; -import android.net.ipmemorystore.IOnStatusListener; -import android.net.ipmemorystore.Status; -import android.net.ipmemorystore.StatusParcelable; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.concurrent.TimeUnit; - -/** - * Regular maintenance job service. - * @hide - */ -public final class RegularMaintenanceJobService extends JobService { - // Must be unique within the system server uid. - public static final int REGULAR_MAINTENANCE_ID = 3345678; - - /** - * Class for interrupt check of maintenance job. - */ - public static final class InterruptMaintenance { - private volatile boolean mIsInterrupted; - private final int mJobId; - - public InterruptMaintenance(int jobId) { - mJobId = jobId; - mIsInterrupted = false; - } - - public int getJobId() { - return mJobId; - } - - public void setInterrupted(boolean interrupt) { - mIsInterrupted = interrupt; - } - - public boolean isInterrupted() { - return mIsInterrupted; - } - } - - private static final ArrayList<InterruptMaintenance> sInterruptList = new ArrayList<>(); - private static IpMemoryStoreService sIpMemoryStoreService; - - @Override - public boolean onStartJob(JobParameters params) { - if (sIpMemoryStoreService == null) { - Log.wtf("RegularMaintenanceJobService", - "Can not start job because sIpMemoryStoreService is null."); - return false; - } - final InterruptMaintenance im = new InterruptMaintenance(params.getJobId()); - sInterruptList.add(im); - - sIpMemoryStoreService.fullMaintenance(new IOnStatusListener() { - @Override - public void onComplete(final StatusParcelable statusParcelable) throws RemoteException { - final Status result = new Status(statusParcelable); - if (!result.isSuccess()) { - Log.e("RegularMaintenanceJobService", "Regular maintenance failed." - + " Error is " + result.resultCode); - } - sInterruptList.remove(im); - jobFinished(params, !result.isSuccess()); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - - @Override - public IBinder asBinder() { - return null; - } - }, im); - return true; - } - - @Override - public boolean onStopJob(JobParameters params) { - final int jobId = params.getJobId(); - for (InterruptMaintenance im : sInterruptList) { - if (im.getJobId() == jobId) { - im.setInterrupted(true); - } - } - return true; - } - - /** Schedule regular maintenance job */ - static void schedule(Context context, IpMemoryStoreService ipMemoryStoreService) { - final JobScheduler jobScheduler = - (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - - final ComponentName maintenanceJobName = - new ComponentName(context, RegularMaintenanceJobService.class); - - // Regular maintenance is scheduled for when the device is idle with access power and a - // minimum interval of one day. - final JobInfo regularMaintenanceJob = - new JobInfo.Builder(REGULAR_MAINTENANCE_ID, maintenanceJobName) - .setRequiresDeviceIdle(true) - .setRequiresCharging(true) - .setRequiresBatteryNotLow(true) - .setPeriodic(TimeUnit.HOURS.toMillis(24)).build(); - - jobScheduler.schedule(regularMaintenanceJob); - sIpMemoryStoreService = ipMemoryStoreService; - } - - /** Unschedule regular maintenance job */ - static void unschedule(Context context) { - final JobScheduler jobScheduler = - (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - jobScheduler.cancel(REGULAR_MAINTENANCE_ID); - sIpMemoryStoreService = null; - } -} diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RelevanceUtils.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RelevanceUtils.java deleted file mode 100644 index 38d55448aa2a..000000000000 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/RelevanceUtils.java +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity.ipmemorystore; - -import com.android.internal.annotations.VisibleForTesting; - -/** - * A class containing the logic around the relevance value for - * IP Memory Store. - * - * @hide - */ -public class RelevanceUtils { - /** - * The relevance is a decaying value that gets lower and lower until it - * reaches 0 after some time passes. It follows an exponential decay law, - * dropping slowly at first then faster and faster, because a network is - * likely to be visited again if it was visited not long ago, and the longer - * it hasn't been visited the more likely it is that it won't be visited - * again. For example, a network visited on holiday should stay fresh for - * the duration of the holiday and persist for a while, but after the venue - * hasn't been visited for a while it should quickly be discarded. What - * should accelerate forgetting the network is extended periods without - * visits, so that occasional venues get discarded but regular visits keep - * the network relevant, even if the visits are infrequent. - * - * This function must be stable by iteration, meaning that adjusting the same value - * for different dates iteratively multiple times should give the same result. - * Formally, if f is the decay function that associates a relevance x at a date d1 - * to the value at ulterior date d3, then for any date d2 between d1 and d3 : - * f(x, d3 - d1) = f(f(x, d3 - d2), d2 - d1). Intuitively, this property simply - * means it should be the same to compute and store back the value after two months, - * or to do it once after one month, store it back, and do it again after another - * months has passed. - * The pair of the relevance and date define the entire curve, so any pair - * of values on the curve will define the same curve. Setting one of them to a - * constant, so as not to have to store it, means the other one will always suffice - * to describe the curve. For example, only storing the date for a known, constant - * value of the relevance is an efficient way of remembering this information (and - * to compare relevances together, as f is monotonically decreasing). - * - *** Choosing the function : - * Functions of the kind described above are standard exponential decay functions - * like the ones that govern atomic decay where the value at any given date can be - * computed uniformly from the value at a previous date and the time elapsed since - * that date. It is simple to picture this kind of function as one where after a - * given period of time called the half-life, the relevance value will have been - * halved. Decay of this kind is expressed in function of the previous value by - * functions like - * f(x, t) = x * F ^ (t / L) - * ...where x is the value, t is the elapsed time, L is the half-life (or more - * generally the F-th-life) and F the decay factor (typically 0.5, hence why L is - * usually called the half-life). The ^ symbol here is used for exponentiation. - * Or, starting at a given M for t = 0 : - * f(t) = M * F ^ (t / L) - * - * Because a line in the store needs to become irrelevant at some point but - * this class of functions never go to 0, a minimum cutoff has to be chosen to - * represent irrelevance. The simpler way of doing this is to simply add this - * minimum cutoff to the computation before and removing it after. - * Thus the function becomes : - * f(x, t) = ((x + K) * F ^ (t / L)) - K - * ...where K is the minimum cutoff, L the half-life, and F the factor between - * the original x and x after its half-life. Strictly speaking using the word - * "half-life" implies that F = 0.5, but the relation works for any value of F. - * - * It is easy enough to check that this function satisfies the stability - * relation that was given above for any value of F, L and K, which become - * parameters that can be defined at will. - * - * relevance - * 1.0 | - * |\ - * | \ - * | \ (this graph rendered with L = 75 days and K = 1/40) - * 0.75| ', - * | \ - * | '. - * | \. - * | \ - * 0.5 | '\ - * | ''. - * | ''. - * | ''. - * 0.25| '''.. - * | '''.. - * | ''''.... - * | '''''.......... - * 0 +-------------------------------------------------------''''''''''---- - * 0 50 100 150 200 250 300 350 400 days - * - *** Choosing the parameters - * The maximum M is an arbitrary parameter that simply scales the curve. - * The tradeoff for M is pretty simple : if the relevance is going to be an - * integer, the bigger M is the more precision there is in the relevance. - * However, values of M that are easy for humans to read are preferable to - * help debugging, and a suitably low value may be enough to ensure there - * won't be integer overflows in intermediate computations. - * A value of 1_000_000 probably is plenty for precision, while still in the - * low range of what ints can represent. - * - * F and L are parameters to be chosen arbitrarily and have an impact on how - * fast the relevance will be decaying at first, keeping in mind that - * the 400 days value and the cap stay the same. In simpler words, F and L - * define the steepness of the curve. - * To keep things simple (and familiar) F is arbitrarily chosen to be 0.5, and - * L is set to 200 days visually to achieve the desired effect. Refer to the - * illustration above to get a feel of how that feels. - * - * Moreover, the memory store works on an assumption that the relevance should - * be capped, and that an entry with capped relevance should decay in 400 days. - * This is on premises that the networks a device will need to remember the - * longest should be networks visited about once a year. - * For this reason, the relevance is at the maximum M 400 days before expiry : - * f(M, 400 days) = 0 - * From replacing this with the value of the function, K can then be derived - * from the values of M, F and L : - * (M + K) * F ^ (t / L) - K = 0 - * K = M * F ^ (400 days / L) / (1 - F ^ (400 days / L)) - * Replacing with actual values this gives : - * K = 1_000_000 * 0.5 ^ (400 / 200) / (1 - 0.5 ^ (400 / 200)) - * = 1_000_000 / 3 ≈ 333_333.3 - * This ensures the function has the desired profile, the desired value at - * cap, and the desired value at expiry. - * - *** Useful relations - * Let's define the expiry time for any given relevance x as the interval of - * time such as : - * f(x, expiry) = 0 - * which can be rewritten - * ((x + K) * F ^ (expiry / L)) = K - * ...giving an expression of the expiry in function of the relevance x as - * expiry = L * logF(K / (x + K)) - * Conversely the relevance x can be expressed in function of the expiry as - * x = K / F ^ (expiry / L) - K - * These relations are useful in utility functions. - * - *** Bumping things up - * The last issue therefore is to decide how to bump up the relevance. The - * simple approach is to simply lift up the curve a little bit by a constant - * normalized amount, delaying the time of expiry. For example increasing - * the relevance by an amount I gives : - * x2 = x1 + I - * x2 and x1 correspond to two different expiry times expiry2 and expiry1, - * and replacing x1 and x2 in the relation above with their expression in - * function of the expiry comes : - * K / F ^ (expiry2 / L) - K = K / F ^ (expiry1 / L) - K + I - * which resolves to : - * expiry2 = L * logF(K / (I + K / F ^ (expiry1 / L))) - * - * In this implementation, the bump is defined as 1/25th of the cap for - * the relevance. This means a network will be remembered for the maximum - * period of 400 days if connected 25 times in succession not accounting - * for decay. Of course decay actually happens so it will take more than 25 - * connections for any given network to actually reach the cap, but because - * decay is slow at first, it is a good estimate of how fast cap happens. - * - * Specifically, it gives the following four results : - * - A network that a device connects to once hits irrelevance about 32.7 days after - * it was first registered if never connected again. - * - A network that a device connects to once a day at a fixed hour will hit the cap - * on the 27th connection. - * - A network that a device connects to once a week at a fixed hour will hit the cap - * on the 57th connection. - * - A network that a device connects to every day for 7 straight days then never again - * expires 144 days after the last connection. - * These metrics tend to match pretty well the requirements. - */ - - // TODO : make these constants configurable at runtime. Don't forget to build it so that - // changes will wipe the database, migrate the values, or otherwise make sure the relevance - // values are still meaningful. - - // How long, in milliseconds, is a capped relevance valid for, or in other - // words how many milliseconds after its relevance was set to RELEVANCE_CAP does - // any given line expire. 400 days. - @VisibleForTesting - public static final long CAPPED_RELEVANCE_LIFETIME_MS = 400L * 24 * 60 * 60 * 1000; - - // The constant that represents a normalized 1.0 value for the relevance. In other words, - // the cap for the relevance. This is referred to as M in the explanation above. - @VisibleForTesting - public static final int CAPPED_RELEVANCE = 1_000_000; - - // The decay factor. After a half-life, the relevance will have decayed by this value. - // This is referred to as F in the explanation above. - private static final double DECAY_FACTOR = 0.5; - - // The half-life. After this time, the relevance will have decayed by a factor DECAY_FACTOR. - // This is referred to as L in the explanation above. - private static final long HALF_LIFE_MS = 200L * 24 * 60 * 60 * 1000; - - // The value of the frame change. This is referred to as K in the explanation above. - private static final double IRRELEVANCE_FLOOR = - CAPPED_RELEVANCE * powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS) - / (1 - powF((double) CAPPED_RELEVANCE_LIFETIME_MS / HALF_LIFE_MS)); - - // How much to bump the relevance by every time a line is written to. - @VisibleForTesting - public static final int RELEVANCE_BUMP = CAPPED_RELEVANCE / 25; - - // Java doesn't include a function for the logarithm in an arbitrary base, so implement it - private static final double LOG_DECAY_FACTOR = Math.log(DECAY_FACTOR); - private static double logF(final double value) { - return Math.log(value) / LOG_DECAY_FACTOR; - } - - // Utility function to get a power of the decay factor, to simplify the code. - private static double powF(final double value) { - return Math.pow(DECAY_FACTOR, value); - } - - /** - * Compute the value of the relevance now given an expiry date. - * - * @param expiry the date at which the column in the database expires. - * @return the adjusted value of the relevance for this moment in time. - */ - public static int computeRelevanceForNow(final long expiry) { - return computeRelevanceForTargetDate(expiry, System.currentTimeMillis()); - } - - /** - * Compute the value of the relevance at a given date from an expiry date. - * - * Because relevance decays with time, a relevance in the past corresponds to - * a different relevance later. - * - * Relevance is always a positive value. 0 means not relevant at all. - * - * See the explanation at the top of this file to get the justification for this - * computation. - * - * @param expiry the date at which the column in the database expires. - * @param target the target date to adjust the relevance to. - * @return the adjusted value of the relevance for the target moment. - */ - public static int computeRelevanceForTargetDate(final long expiry, final long target) { - final long delay = expiry - target; - if (delay >= CAPPED_RELEVANCE_LIFETIME_MS) return CAPPED_RELEVANCE; - if (delay <= 0) return 0; - return (int) (IRRELEVANCE_FLOOR / powF((float) delay / HALF_LIFE_MS) - IRRELEVANCE_FLOOR); - } - - /** - * Compute the expiry duration adjusted up for a new fresh write. - * - * Every time data is written to the memory store for a given line, the - * relevance is bumped up by a certain amount, which will boost the priority - * of this line for computation of group attributes, and delay (possibly - * indefinitely, if the line is accessed regularly) forgetting the data stored - * in that line. - * As opposed to bumpExpiryDate, this function uses a duration from now to expiry. - * - * See the explanation at the top of this file for a justification of this computation. - * - * @param oldExpiryDuration the old expiry duration in milliseconds from now. - * @return the expiry duration representing a bumped up relevance value. - */ - public static long bumpExpiryDuration(final long oldExpiryDuration) { - // L * logF(K / (I + K / F ^ (expiry1 / L))), as documented above - final double divisionFactor = powF(((double) oldExpiryDuration) / HALF_LIFE_MS); - final double oldRelevance = IRRELEVANCE_FLOOR / divisionFactor; - final long newDuration = - (long) (HALF_LIFE_MS * logF(IRRELEVANCE_FLOOR / (RELEVANCE_BUMP + oldRelevance))); - return Math.min(newDuration, CAPPED_RELEVANCE_LIFETIME_MS); - } - - /** - * Compute the new expiry date adjusted up for a new fresh write. - * - * Every time data is written to the memory store for a given line, the - * relevance is bumped up by a certain amount, which will boost the priority - * of this line for computation of group attributes, and delay (possibly - * indefinitely, if the line is accessed regularly) forgetting the data stored - * in that line. - * As opposed to bumpExpiryDuration, this function takes the old timestamp and returns the - * new timestamp. - * - * {@see bumpExpiryDuration}, and keep in mind that the bump depends on when this is called, - * because the relevance decays exponentially, therefore bumping up a high relevance (for a - * date far in the future) is less potent than bumping up a low relevance (for a date in - * a close future). - * - * @param oldExpiryDate the old date of expiration. - * @return the new expiration date after the relevance bump. - */ - public static long bumpExpiryDate(final long oldExpiryDate) { - final long now = System.currentTimeMillis(); - final long newDuration = bumpExpiryDuration(oldExpiryDate - now); - return now + newDuration; - } -} diff --git a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/Utils.java b/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/Utils.java deleted file mode 100644 index 9cbf490505f4..000000000000 --- a/packages/NetworkStack/src/com/android/server/connectivity/ipmemorystore/Utils.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity.ipmemorystore; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.ipmemorystore.Blob; - -/** {@hide} */ -public class Utils { - /** Pretty print */ - public static String blobToString(@Nullable final Blob blob) { - return "Blob : " + byteArrayToString(null == blob ? null : blob.data); - } - - /** Pretty print */ - public static String byteArrayToString(@Nullable final byte[] data) { - if (null == data) return "null"; - final StringBuilder sb = new StringBuilder("["); - if (data.length <= 24) { - appendByteArray(sb, data, 0, data.length); - } else { - appendByteArray(sb, data, 0, 16); - sb.append("..."); - appendByteArray(sb, data, data.length - 8, data.length); - } - sb.append("]"); - return sb.toString(); - } - - // Adds the hex representation of the array between the specified indices (inclusive, exclusive) - private static void appendByteArray(@NonNull final StringBuilder sb, @NonNull final byte[] ar, - final int from, final int to) { - for (int i = from; i < to; ++i) { - sb.append(String.format("%02X", ar[i])); - } - } -} diff --git a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java b/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java deleted file mode 100644 index 804765e33a87..000000000000 --- a/packages/NetworkStack/src/com/android/server/util/NetworkStackConstants.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.util; - -import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; - -import java.net.Inet4Address; - -/** - * Network constants used by the network stack. - */ -public final class NetworkStackConstants { - - /** - * IPv4 constants. - * - * See also: - * - https://tools.ietf.org/html/rfc791 - */ - public static final int IPV4_ADDR_BITS = 32; - public static final int IPV4_MIN_MTU = 68; - public static final int IPV4_MAX_MTU = 65_535; - - /** - * Ethernet constants. - * - * See also: - * - https://tools.ietf.org/html/rfc894 - * - https://tools.ietf.org/html/rfc2464 - * - https://tools.ietf.org/html/rfc7042 - * - http://www.iana.org/assignments/ethernet-numbers/ethernet-numbers.xhtml - * - http://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml - */ - public static final int ETHER_DST_ADDR_OFFSET = 0; - public static final int ETHER_SRC_ADDR_OFFSET = 6; - public static final int ETHER_ADDR_LEN = 6; - public static final int ETHER_TYPE_OFFSET = 12; - public static final int ETHER_TYPE_LENGTH = 2; - public static final int ETHER_TYPE_ARP = 0x0806; - public static final int ETHER_TYPE_IPV4 = 0x0800; - public static final int ETHER_TYPE_IPV6 = 0x86dd; - public static final int ETHER_HEADER_LEN = 14; - - /** - * ARP constants. - * - * See also: - * - https://tools.ietf.org/html/rfc826 - * - http://www.iana.org/assignments/arp-parameters/arp-parameters.xhtml - */ - public static final int ARP_PAYLOAD_LEN = 28; // For Ethernet+IPv4. - public static final int ARP_REQUEST = 1; - public static final int ARP_REPLY = 2; - public static final int ARP_HWTYPE_RESERVED_LO = 0; - public static final int ARP_HWTYPE_ETHER = 1; - public static final int ARP_HWTYPE_RESERVED_HI = 0xffff; - - /** - * IPv4 constants. - * - * See also: - * - https://tools.ietf.org/html/rfc791 - */ - public static final int IPV4_HEADER_MIN_LEN = 20; - public static final int IPV4_IHL_MASK = 0xf; - public static final int IPV4_FLAGS_OFFSET = 6; - public static final int IPV4_FRAGMENT_MASK = 0x1fff; - public static final int IPV4_PROTOCOL_OFFSET = 9; - public static final int IPV4_SRC_ADDR_OFFSET = 12; - public static final int IPV4_DST_ADDR_OFFSET = 16; - public static final int IPV4_ADDR_LEN = 4; - public static final Inet4Address IPV4_ADDR_ALL = intToInet4AddressHTH(0xffffffff); - public static final Inet4Address IPV4_ADDR_ANY = intToInet4AddressHTH(0x0); - - /** - * IPv6 constants. - * - * See also: - * - https://tools.ietf.org/html/rfc2460 - */ - public static final int IPV6_ADDR_LEN = 16; - public static final int IPV6_HEADER_LEN = 40; - public static final int IPV6_PROTOCOL_OFFSET = 6; - public static final int IPV6_SRC_ADDR_OFFSET = 8; - public static final int IPV6_DST_ADDR_OFFSET = 24; - - /** - * ICMPv6 constants. - * - * See also: - * - https://tools.ietf.org/html/rfc4443 - * - https://tools.ietf.org/html/rfc4861 - */ - public static final int ICMPV6_HEADER_MIN_LEN = 4; - public static final int ICMPV6_ECHO_REPLY_TYPE = 129; - public static final int ICMPV6_ECHO_REQUEST_TYPE = 128; - public static final int ICMPV6_ROUTER_SOLICITATION = 133; - public static final int ICMPV6_ROUTER_ADVERTISEMENT = 134; - public static final int ICMPV6_NEIGHBOR_SOLICITATION = 135; - public static final int ICMPV6_NEIGHBOR_ADVERTISEMENT = 136; - public static final int ICMPV6_ND_OPTION_MIN_LENGTH = 8; - public static final int ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR = 8; - public static final int ICMPV6_ND_OPTION_SLLA = 1; - public static final int ICMPV6_ND_OPTION_TLLA = 2; - public static final int ICMPV6_ND_OPTION_MTU = 5; - - /** - * UDP constants. - * - * See also: - * - https://tools.ietf.org/html/rfc768 - */ - public static final int UDP_HEADER_LEN = 8; - - - /** - * DHCP constants. - * - * See also: - * - https://tools.ietf.org/html/rfc2131 - */ - public static final int INFINITE_LEASE = 0xffffffff; - public static final int DHCP4_CLIENT_PORT = 68; - - private NetworkStackConstants() { - throw new UnsupportedOperationException("This class is not to be instantiated"); - } -} diff --git a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java deleted file mode 100644 index 6fbeeadb7e72..000000000000 --- a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.util; - -import static android.os.Binder.getCallingUid; - -import android.os.Process; -import android.os.UserHandle; - -/** - * Utility class to check calling permissions on the network stack. - */ -public final class PermissionUtil { - - /** - * Check that the caller is allowed to communicate with the network stack. - * @throws SecurityException The caller is not allowed to communicate with the network stack. - */ - public static void checkNetworkStackCallingPermission() { - // TODO: check that the calling PID is the system server. - final int caller = getCallingUid(); - if (caller != Process.SYSTEM_UID - && UserHandle.getAppId(caller) != Process.BLUETOOTH_UID - && UserHandle.getAppId(caller) != Process.PHONE_UID) { - throw new SecurityException("Invalid caller: " + caller); - } - } - - /** - * Check that the caller is allowed to dump the network stack, e.g. dumpsys. - * @throws SecurityException The caller is not allowed to dump the network stack. - */ - public static void checkDumpPermission() { - final int caller = getCallingUid(); - if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID - && caller != Process.SHELL_UID) { - throw new SecurityException("No dump permissions for caller: " + caller); - } - } - - private PermissionUtil() { - throw new UnsupportedOperationException("This class is not to be instantiated"); - } -} diff --git a/packages/NetworkStack/tests/unit/Android.bp b/packages/NetworkStack/tests/unit/Android.bp deleted file mode 100644 index 6cc80543fc74..000000000000 --- a/packages/NetworkStack/tests/unit/Android.bp +++ /dev/null @@ -1,103 +0,0 @@ -// -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -android_test { - name: "NetworkStackTests", - certificate: "platform", - srcs: ["src/**/*.java"], - test_suites: ["device-tests"], - resource_dirs: ["res"], - static_libs: [ - "androidx.test.rules", - "mockito-target-extended-minus-junit4", - "NetworkStackBase", - "testables", - ], - libs: [ - "android.test.runner", - "android.test.base", - "android.test.mock", - ], - jni_libs: [ - // For mockito extended - "libdexmakerjvmtiagent", - "libstaticjvmtiagent", - // For ApfTest - "libartbase", - "libbacktrace", - "libbase", - "libbinder", - "libbinderthreadstate", - "libc++", - "libcgrouprc", - "libcrypto", - "libcutils", - "libdexfile", - "ld-android", - "libdl_android", - "libhidl-gen-utils", - "libhidlbase", - "libhidltransport", - "libhwbinder", - "libjsoncpp", - "liblog", - "liblzma", - "libnativehelper", - "libnativehelper_compat_libc++", - "libnetworkstacktestsjni", - "libnetworkstackutilsjni", - "libpackagelistparser", - "libpcre2", - "libprocessgroup", - "libselinux", - "libui", - "libutils", - "libvintf", - "libvndksupport", - "libtinyxml2", - "libunwindstack", - "libutilscallstack", - "libziparchive", - "libz", - "netd_aidl_interface-V2-cpp", - ], -} - -cc_library_shared { - name: "libnetworkstacktestsjni", - srcs: [ - "jni/**/*.cpp" - ], - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - ], - include_dirs: [ - "hardware/google/apf", - ], - shared_libs: [ - "libbinder", - "liblog", - "libcutils", - "libnativehelper", - "netd_aidl_interface-V2-cpp", - ], - static_libs: [ - "libapf", - "libpcap", - ], -} diff --git a/packages/NetworkStack/tests/unit/AndroidManifest.xml b/packages/NetworkStack/tests/unit/AndroidManifest.xml deleted file mode 100644 index 5dcf6ff1b514..000000000000 --- a/packages/NetworkStack/tests/unit/AndroidManifest.xml +++ /dev/null @@ -1,54 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.server.networkstack.tests"> - - <uses-permission android:name="android.permission.READ_LOGS" /> - <uses-permission android:name="android.permission.WRITE_SETTINGS" /> - <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> - <uses-permission android:name="android.permission.READ_PHONE_STATE" /> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> - <uses-permission android:name="android.permission.BROADCAST_STICKY" /> - <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" /> - <uses-permission android:name="android.permission.MANAGE_APP_TOKENS" /> - <uses-permission android:name="android.permission.WAKE_LOCK" /> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> - <uses-permission android:name="android.permission.REAL_GET_TASKS" /> - <uses-permission android:name="android.permission.GET_DETAILED_TASKS" /> - <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> - <uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" /> - <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> - <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> - <uses-permission android:name="android.permission.MANAGE_USERS" /> - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> - <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" /> - <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" /> - <uses-permission android:name="android.permission.INTERNET" /> - <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> - <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" /> - <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" /> - <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" /> - <uses-permission android:name="android.permission.INSTALL_PACKAGES" /> - <uses-permission android:name="android.permission.NETWORK_STACK" /> - - <application android:debuggable="true"> - <uses-library android:name="android.test.runner" /> - </application> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.server.networkstack.tests" - android:label="Networking service tests"> - </instrumentation> -</manifest>
\ No newline at end of file diff --git a/packages/NetworkStack/tests/unit/AndroidTest.xml b/packages/NetworkStack/tests/unit/AndroidTest.xml deleted file mode 100644 index 047bc2e67808..000000000000 --- a/packages/NetworkStack/tests/unit/AndroidTest.xml +++ /dev/null @@ -1,29 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<configuration description="Runs Tests for NetworkStack"> - <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> - <option name="test-file-name" value="NetworkStackTests.apk" /> - </target_preparer> - - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="framework-base-presubmit" /> - <option name="test-tag" value="NetworkStackTests" /> - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > - <option name="package" value="com.android.server.networkstack.tests" /> - <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> - <option name="hidden-api-checks" value="false"/> - </test> -</configuration>
\ No newline at end of file diff --git a/packages/NetworkStack/tests/unit/jni/apf_jni.cpp b/packages/NetworkStack/tests/unit/jni/apf_jni.cpp deleted file mode 100644 index 4222adf9e06b..000000000000 --- a/packages/NetworkStack/tests/unit/jni/apf_jni.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2018, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <nativehelper/JNIHelp.h> -#include <nativehelper/ScopedUtfChars.h> -#include <jni.h> -#include <pcap.h> -#include <stdlib.h> -#include <string> -#include <utils/Log.h> -#include <vector> - -#include "apf_interpreter.h" -#include "nativehelper/scoped_primitive_array.h" - -#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) - -// JNI function acting as simply call-through to native APF interpreter. -static jint com_android_server_ApfTest_apfSimulate( - JNIEnv* env, jclass, jbyteArray jprogram, jbyteArray jpacket, - jbyteArray jdata, jint filter_age) { - - ScopedByteArrayRO packet(env, jpacket); - uint32_t packet_len = (uint32_t)packet.size(); - uint32_t program_len = env->GetArrayLength(jprogram); - uint32_t data_len = jdata ? env->GetArrayLength(jdata) : 0; - std::vector<uint8_t> buf(program_len + data_len, 0); - - env->GetByteArrayRegion(jprogram, 0, program_len, reinterpret_cast<jbyte*>(buf.data())); - if (jdata) { - // Merge program and data into a single buffer. - env->GetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + program_len)); - } - - jint result = - accept_packet(buf.data(), program_len, program_len + data_len, - reinterpret_cast<const uint8_t*>(packet.get()), packet_len, filter_age); - - if (jdata) { - env->SetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + program_len)); - } - - return result; -} - -class ScopedPcap { - public: - explicit ScopedPcap(pcap_t* pcap) : pcap_ptr(pcap) {} - ~ScopedPcap() { - pcap_close(pcap_ptr); - } - - pcap_t* get() const { return pcap_ptr; }; - private: - pcap_t* const pcap_ptr; -}; - -class ScopedFILE { - public: - explicit ScopedFILE(FILE* fp) : file(fp) {} - ~ScopedFILE() { - fclose(file); - } - - FILE* get() const { return file; }; - private: - FILE* const file; -}; - -static void throwException(JNIEnv* env, const std::string& error) { - jclass newExcCls = env->FindClass("java/lang/IllegalStateException"); - if (newExcCls == 0) { - abort(); - return; - } - env->ThrowNew(newExcCls, error.c_str()); -} - -static jstring com_android_server_ApfTest_compileToBpf(JNIEnv* env, jclass, jstring jfilter) { - ScopedUtfChars filter(env, jfilter); - std::string bpf_string; - ScopedPcap pcap(pcap_open_dead(DLT_EN10MB, 65535)); - if (pcap.get() == NULL) { - throwException(env, "pcap_open_dead failed"); - return NULL; - } - - // Compile "filter" to a BPF program - bpf_program bpf; - if (pcap_compile(pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) { - throwException(env, "pcap_compile failed"); - return NULL; - } - - // Translate BPF program to human-readable format - const struct bpf_insn* insn = bpf.bf_insns; - for (uint32_t i = 0; i < bpf.bf_len; i++) { - bpf_string += bpf_image(insn++, i); - bpf_string += "\n"; - } - - return env->NewStringUTF(bpf_string.c_str()); -} - -static jboolean com_android_server_ApfTest_compareBpfApf(JNIEnv* env, jclass, jstring jfilter, - jstring jpcap_filename, jbyteArray japf_program) { - ScopedUtfChars filter(env, jfilter); - ScopedUtfChars pcap_filename(env, jpcap_filename); - ScopedByteArrayRO apf_program(env, japf_program); - - // Open pcap file for BPF filtering - ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb")); - char pcap_error[PCAP_ERRBUF_SIZE]; - ScopedPcap bpf_pcap(pcap_fopen_offline(bpf_fp.get(), pcap_error)); - if (bpf_pcap.get() == NULL) { - throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); - return false; - } - - // Open pcap file for APF filtering - ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb")); - ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error)); - if (apf_pcap.get() == NULL) { - throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); - return false; - } - - // Compile "filter" to a BPF program - bpf_program bpf; - if (pcap_compile(bpf_pcap.get(), &bpf, filter.c_str(), 0, PCAP_NETMASK_UNKNOWN)) { - throwException(env, "pcap_compile failed"); - return false; - } - - // Install BPF filter on bpf_pcap - if (pcap_setfilter(bpf_pcap.get(), &bpf)) { - throwException(env, "pcap_setfilter failed"); - return false; - } - - while (1) { - pcap_pkthdr bpf_header, apf_header; - // Run BPF filter to the next matching packet. - const uint8_t* bpf_packet = pcap_next(bpf_pcap.get(), &bpf_header); - - // Run APF filter to the next matching packet. - const uint8_t* apf_packet; - do { - apf_packet = pcap_next(apf_pcap.get(), &apf_header); - } while (apf_packet != NULL && !accept_packet( - reinterpret_cast<uint8_t*>(const_cast<int8_t*>(apf_program.get())), - apf_program.size(), 0 /* data_len */, - apf_packet, apf_header.len, 0 /* filter_age */)); - - // Make sure both filters matched the same packet. - if (apf_packet == NULL && bpf_packet == NULL) - break; - if (apf_packet == NULL || bpf_packet == NULL) - return false; - if (apf_header.len != bpf_header.len || - apf_header.ts.tv_sec != bpf_header.ts.tv_sec || - apf_header.ts.tv_usec != bpf_header.ts.tv_usec || - memcmp(apf_packet, bpf_packet, apf_header.len)) - return false; - } - return true; -} - -static jboolean com_android_server_ApfTest_dropsAllPackets(JNIEnv* env, jclass, jbyteArray jprogram, - jbyteArray jdata, jstring jpcap_filename) { - ScopedUtfChars pcap_filename(env, jpcap_filename); - ScopedByteArrayRO apf_program(env, jprogram); - uint32_t apf_program_len = (uint32_t)apf_program.size(); - uint32_t data_len = env->GetArrayLength(jdata); - pcap_pkthdr apf_header; - const uint8_t* apf_packet; - char pcap_error[PCAP_ERRBUF_SIZE]; - std::vector<uint8_t> buf(apf_program_len + data_len, 0); - - // Merge program and data into a single buffer. - env->GetByteArrayRegion(jprogram, 0, apf_program_len, reinterpret_cast<jbyte*>(buf.data())); - env->GetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + apf_program_len)); - - // Open pcap file - ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb")); - ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error)); - - if (apf_pcap.get() == NULL) { - throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error)); - return false; - } - - while ((apf_packet = pcap_next(apf_pcap.get(), &apf_header)) != NULL) { - int result = accept_packet(buf.data(), apf_program_len, - apf_program_len + data_len, apf_packet, apf_header.len, 0); - - // Return false once packet passes the filter - if (result) { - env->SetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + apf_program_len)); - return false; - } - } - - env->SetByteArrayRegion(jdata, 0, data_len, - reinterpret_cast<jbyte*>(buf.data() + apf_program_len)); - return true; -} - -extern "C" jint JNI_OnLoad(JavaVM* vm, void*) { - JNIEnv *env; - if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - ALOGE("ERROR: GetEnv failed"); - return -1; - } - - static JNINativeMethod gMethods[] = { - { "apfSimulate", "([B[B[BI)I", - (void*)com_android_server_ApfTest_apfSimulate }, - { "compileToBpf", "(Ljava/lang/String;)Ljava/lang/String;", - (void*)com_android_server_ApfTest_compileToBpf }, - { "compareBpfApf", "(Ljava/lang/String;Ljava/lang/String;[B)Z", - (void*)com_android_server_ApfTest_compareBpfApf }, - { "dropsAllPackets", "([B[BLjava/lang/String;)Z", - (void*)com_android_server_ApfTest_dropsAllPackets }, - }; - - jniRegisterNativeMethods(env, "android/net/apf/ApfTest", - gMethods, ARRAY_SIZE(gMethods)); - - return JNI_VERSION_1_6; -} diff --git a/packages/NetworkStack/tests/unit/res/raw/apf.pcap b/packages/NetworkStack/tests/unit/res/raw/apf.pcap Binary files differdeleted file mode 100644 index 963165f19f73..000000000000 --- a/packages/NetworkStack/tests/unit/res/raw/apf.pcap +++ /dev/null diff --git a/packages/NetworkStack/tests/unit/res/raw/apfPcap.pcap b/packages/NetworkStack/tests/unit/res/raw/apfPcap.pcap Binary files differdeleted file mode 100644 index 6f69c4add0f8..000000000000 --- a/packages/NetworkStack/tests/unit/res/raw/apfPcap.pcap +++ /dev/null diff --git a/packages/NetworkStack/tests/unit/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/unit/src/android/net/apf/ApfTest.java deleted file mode 100644 index 8f2b96807860..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/apf/ApfTest.java +++ /dev/null @@ -1,2110 +0,0 @@ -/* - * Copyright (C) 2012 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.apf; - -import static android.system.OsConstants.AF_UNIX; -import static android.system.OsConstants.ARPHRD_ETHER; -import static android.system.OsConstants.ETH_P_ARP; -import static android.system.OsConstants.ETH_P_IP; -import static android.system.OsConstants.ETH_P_IPV6; -import static android.system.OsConstants.IPPROTO_ICMPV6; -import static android.system.OsConstants.IPPROTO_TCP; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_STREAM; - -import static com.android.internal.util.BitUtils.bytesToBEInt; -import static com.android.server.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.NattKeepalivePacketDataParcelable; -import android.net.TcpKeepalivePacketDataParcelable; -import android.net.apf.ApfFilter.ApfConfiguration; -import android.net.apf.ApfGenerator.IllegalInstructionException; -import android.net.apf.ApfGenerator.Register; -import android.net.ip.IIpClientCallbacks; -import android.net.ip.IpClient.IpClientCallbacksWrapper; -import android.net.metrics.IpConnectivityLog; -import android.net.metrics.RaEvent; -import android.net.util.InterfaceParams; -import android.net.util.SharedLog; -import android.os.ConditionVariable; -import android.os.Parcelable; -import android.os.SystemClock; -import android.system.ErrnoException; -import android.system.Os; -import android.text.format.DateUtils; -import android.util.Log; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.HexDump; -import com.android.server.networkstack.tests.R; -import com.android.server.util.NetworkStackConstants; - -import libcore.io.IoUtils; -import libcore.io.Streams; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.nio.ByteBuffer; -import java.util.List; -import java.util.Random; - -/** - * Tests for APF program generator and interpreter. - * - * Build, install and run with: - * runtest frameworks-net -c android.net.apf.ApfTest - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ApfTest { - private static final int TIMEOUT_MS = 500; - private static final int MIN_APF_VERSION = 2; - - @Mock IpConnectivityLog mLog; - @Mock Context mContext; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - // Load up native shared library containing APF interpreter exposed via JNI. - System.loadLibrary("networkstacktestsjni"); - } - - private static final String TAG = "ApfTest"; - // Expected return codes from APF interpreter. - private static final int PASS = 1; - private static final int DROP = 0; - // Interpreter will just accept packets without link layer headers, so pad fake packet to at - // least the minimum packet size. - private static final int MIN_PKT_SIZE = 15; - - private static final ApfCapabilities MOCK_APF_CAPABILITIES = - new ApfCapabilities(2, 1700, ARPHRD_ETHER); - - private static final boolean DROP_MULTICAST = true; - private static final boolean ALLOW_MULTICAST = false; - - private static final boolean DROP_802_3_FRAMES = true; - private static final boolean ALLOW_802_3_FRAMES = false; - - // Constants for opcode encoding - private static final byte LI_OP = (byte)(13 << 3); - private static final byte LDDW_OP = (byte)(22 << 3); - private static final byte STDW_OP = (byte)(23 << 3); - private static final byte SIZE0 = (byte)(0 << 1); - private static final byte SIZE8 = (byte)(1 << 1); - private static final byte SIZE16 = (byte)(2 << 1); - private static final byte SIZE32 = (byte)(3 << 1); - private static final byte R1 = 1; - - private static ApfConfiguration getDefaultConfig() { - ApfFilter.ApfConfiguration config = new ApfConfiguration(); - config.apfCapabilities = MOCK_APF_CAPABILITIES; - config.multicastFilter = ALLOW_MULTICAST; - config.ieee802_3Filter = ALLOW_802_3_FRAMES; - config.ethTypeBlackList = new int[0]; - return config; - } - - private static String label(int code) { - switch (code) { - case PASS: return "PASS"; - case DROP: return "DROP"; - default: return "UNKNOWN"; - } - } - - private static void assertReturnCodesEqual(int expected, int got) { - assertEquals(label(expected), label(got)); - } - - private void assertVerdict(int expected, byte[] program, byte[] packet, int filterAge) { - assertReturnCodesEqual(expected, apfSimulate(program, packet, null, filterAge)); - } - - private void assertVerdict(int expected, byte[] program, byte[] packet) { - assertReturnCodesEqual(expected, apfSimulate(program, packet, null, 0)); - } - - private void assertPass(byte[] program, byte[] packet, int filterAge) { - assertVerdict(PASS, program, packet, filterAge); - } - - private void assertPass(byte[] program, byte[] packet) { - assertVerdict(PASS, program, packet); - } - - private void assertDrop(byte[] program, byte[] packet, int filterAge) { - assertVerdict(DROP, program, packet, filterAge); - } - - private void assertDrop(byte[] program, byte[] packet) { - assertVerdict(DROP, program, packet); - } - - private void assertProgramEquals(byte[] expected, byte[] program) throws AssertionError { - // assertArrayEquals() would only print one byte, making debugging difficult. - if (!java.util.Arrays.equals(expected, program)) { - throw new AssertionError( - "\nexpected: " + HexDump.toHexString(expected) + - "\nactual: " + HexDump.toHexString(program)); - } - } - - private void assertDataMemoryContents( - int expected, byte[] program, byte[] packet, byte[] data, byte[] expected_data) - throws IllegalInstructionException, Exception { - assertReturnCodesEqual(expected, apfSimulate(program, packet, data, 0 /* filterAge */)); - - // assertArrayEquals() would only print one byte, making debugging difficult. - if (!java.util.Arrays.equals(expected_data, data)) { - throw new Exception( - "\nprogram: " + HexDump.toHexString(program) + - "\ndata memory: " + HexDump.toHexString(data) + - "\nexpected: " + HexDump.toHexString(expected_data)); - } - } - - private void assertVerdict(int expected, ApfGenerator gen, byte[] packet, int filterAge) - throws IllegalInstructionException { - assertReturnCodesEqual(expected, apfSimulate(gen.generate(), packet, null, - filterAge)); - } - - private void assertPass(ApfGenerator gen, byte[] packet, int filterAge) - throws IllegalInstructionException { - assertVerdict(PASS, gen, packet, filterAge); - } - - private void assertDrop(ApfGenerator gen, byte[] packet, int filterAge) - throws IllegalInstructionException { - assertVerdict(DROP, gen, packet, filterAge); - } - - private void assertPass(ApfGenerator gen) - throws IllegalInstructionException { - assertVerdict(PASS, gen, new byte[MIN_PKT_SIZE], 0); - } - - private void assertDrop(ApfGenerator gen) - throws IllegalInstructionException { - assertVerdict(DROP, gen, new byte[MIN_PKT_SIZE], 0); - } - - /** - * Test each instruction by generating a program containing the instruction, - * generating bytecode for that program and running it through the - * interpreter to verify it functions correctly. - */ - @Test - public void testApfInstructions() throws IllegalInstructionException { - // Empty program should pass because having the program counter reach the - // location immediately after the program indicates the packet should be - // passed to the AP. - ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION); - assertPass(gen); - - // Test jumping to pass label. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJump(gen.PASS_LABEL); - byte[] program = gen.generate(); - assertEquals(1, program.length); - assertEquals((14 << 3) | (0 << 1) | 0, program[0]); - assertPass(program, new byte[MIN_PKT_SIZE], 0); - - // Test jumping to drop label. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJump(gen.DROP_LABEL); - program = gen.generate(); - assertEquals(2, program.length); - assertEquals((14 << 3) | (1 << 1) | 0, program[0]); - assertEquals(1, program[1]); - assertDrop(program, new byte[15], 15); - - // Test jumping if equal to 0. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0Equals(0, gen.DROP_LABEL); - assertDrop(gen); - - // Test jumping if not equal to 0. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL); - assertPass(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfR0NotEquals(0, gen.DROP_LABEL); - assertDrop(gen); - - // Test jumping if registers equal. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0EqualsR1(gen.DROP_LABEL); - assertDrop(gen); - - // Test jumping if registers not equal. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL); - assertPass(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfR0NotEqualsR1(gen.DROP_LABEL); - assertDrop(gen); - - // Test load immediate. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test add. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addAdd(1234567890); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test subtract. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addAdd(-1234567890); - gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test or. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addOr(1234567890); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test and. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addAnd(123456789); - gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL); - assertDrop(gen); - - // Test left shift. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addLeftShift(1); - gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL); - assertDrop(gen); - - // Test right shift. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addRightShift(1); - gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL); - assertDrop(gen); - - // Test multiply. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 123456789); - gen.addMul(2); - gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL); - assertDrop(gen); - - // Test divide. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addDiv(2); - gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL); - assertDrop(gen); - - // Test divide by zero. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addDiv(0); - gen.addJump(gen.DROP_LABEL); - assertPass(gen); - - // Test add. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); - gen.addAddR1(); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test subtract. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, -1234567890); - gen.addAddR1(); - gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test or. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); - gen.addOrR1(); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test and. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addLoadImmediate(Register.R1, 123456789); - gen.addAndR1(); - gen.addJumpIfR0Equals(1234567890 & 123456789, gen.DROP_LABEL); - assertDrop(gen); - - // Test left shift. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addLoadImmediate(Register.R1, 1); - gen.addLeftShiftR1(); - gen.addJumpIfR0Equals(1234567890 << 1, gen.DROP_LABEL); - assertDrop(gen); - - // Test right shift. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addLoadImmediate(Register.R1, -1); - gen.addLeftShiftR1(); - gen.addJumpIfR0Equals(1234567890 >> 1, gen.DROP_LABEL); - assertDrop(gen); - - // Test multiply. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 123456789); - gen.addLoadImmediate(Register.R1, 2); - gen.addMulR1(); - gen.addJumpIfR0Equals(123456789 * 2, gen.DROP_LABEL); - assertDrop(gen); - - // Test divide. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addLoadImmediate(Register.R1, 2); - gen.addDivR1(); - gen.addJumpIfR0Equals(1234567890 / 2, gen.DROP_LABEL); - assertDrop(gen); - - // Test divide by zero. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addDivR1(); - gen.addJump(gen.DROP_LABEL); - assertPass(gen); - - // Test byte load. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoad8(Register.R0, 1); - gen.addJumpIfR0Equals(45, gen.DROP_LABEL); - assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); - - // Test out of bounds load. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoad8(Register.R0, 16); - gen.addJumpIfR0Equals(0, gen.DROP_LABEL); - assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); - - // Test half-word load. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoad16(Register.R0, 1); - gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL); - assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0); - - // Test word load. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoad32(Register.R0, 1); - gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL); - assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0); - - // Test byte indexed load. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1); - gen.addLoad8Indexed(Register.R0, 0); - gen.addJumpIfR0Equals(45, gen.DROP_LABEL); - assertDrop(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); - - // Test out of bounds indexed load. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 8); - gen.addLoad8Indexed(Register.R0, 8); - gen.addJumpIfR0Equals(0, gen.DROP_LABEL); - assertPass(gen, new byte[]{123,45,0,0,0,0,0,0,0,0,0,0,0,0,0}, 0); - - // Test half-word indexed load. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1); - gen.addLoad16Indexed(Register.R0, 0); - gen.addJumpIfR0Equals((45 << 8) | 67, gen.DROP_LABEL); - assertDrop(gen, new byte[]{123,45,67,0,0,0,0,0,0,0,0,0,0,0,0}, 0); - - // Test word indexed load. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1); - gen.addLoad32Indexed(Register.R0, 0); - gen.addJumpIfR0Equals((45 << 24) | (67 << 16) | (89 << 8) | 12, gen.DROP_LABEL); - assertDrop(gen, new byte[]{123,45,67,89,12,0,0,0,0,0,0,0,0,0,0}, 0); - - // Test jumping if greater than. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL); - assertPass(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfR0GreaterThan(0, gen.DROP_LABEL); - assertDrop(gen); - - // Test jumping if less than. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0LessThan(0, gen.DROP_LABEL); - assertPass(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0LessThan(1, gen.DROP_LABEL); - assertDrop(gen); - - // Test jumping if any bits set. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL); - assertPass(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL); - assertDrop(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 3); - gen.addJumpIfR0AnyBitsSet(3, gen.DROP_LABEL); - assertDrop(gen); - - // Test jumping if register greater than. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL); - assertPass(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 2); - gen.addLoadImmediate(Register.R1, 1); - gen.addJumpIfR0GreaterThanR1(gen.DROP_LABEL); - assertDrop(gen); - - // Test jumping if register less than. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfR0LessThanR1(gen.DROP_LABEL); - assertPass(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1); - gen.addJumpIfR0LessThanR1(gen.DROP_LABEL); - assertDrop(gen); - - // Test jumping if any bits set in register. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 3); - gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL); - assertPass(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 3); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL); - assertDrop(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 3); - gen.addLoadImmediate(Register.R0, 3); - gen.addJumpIfR0AnyBitsSetR1(gen.DROP_LABEL); - assertDrop(gen); - - // Test load from memory. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadFromMemory(Register.R0, 0); - gen.addJumpIfR0Equals(0, gen.DROP_LABEL); - assertDrop(gen); - - // Test store to memory. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); - gen.addStoreToMemory(Register.R1, 12); - gen.addLoadFromMemory(Register.R0, 12); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test filter age pre-filled memory. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadFromMemory(Register.R0, gen.FILTER_AGE_MEMORY_SLOT); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen, new byte[MIN_PKT_SIZE], 1234567890); - - // Test packet size pre-filled memory. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadFromMemory(Register.R0, gen.PACKET_SIZE_MEMORY_SLOT); - gen.addJumpIfR0Equals(MIN_PKT_SIZE, gen.DROP_LABEL); - assertDrop(gen); - - // Test IPv4 header size pre-filled memory. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadFromMemory(Register.R0, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - gen.addJumpIfR0Equals(20, gen.DROP_LABEL); - assertDrop(gen, new byte[]{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0x45}, 0); - - // Test not. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addNot(Register.R0); - gen.addJumpIfR0Equals(~1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test negate. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addNeg(Register.R0); - gen.addJumpIfR0Equals(-1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test move. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); - gen.addMove(Register.R0); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addMove(Register.R1); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen); - - // Test swap. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R1, 1234567890); - gen.addSwap(); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1234567890); - gen.addSwap(); - gen.addJumpIfR0Equals(0, gen.DROP_LABEL); - assertDrop(gen); - - // Test jump if bytes not equal. - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL); - program = gen.generate(); - assertEquals(6, program.length); - assertEquals((13 << 3) | (1 << 1) | 0, program[0]); - assertEquals(1, program[1]); - assertEquals(((20 << 3) | (1 << 1) | 0) - 256, program[2]); - assertEquals(1, program[3]); - assertEquals(1, program[4]); - assertEquals(123, program[5]); - assertDrop(program, new byte[MIN_PKT_SIZE], 0); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL); - byte[] packet123 = {0,123,0,0,0,0,0,0,0,0,0,0,0,0,0}; - assertPass(gen, packet123, 0); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{123}, gen.DROP_LABEL); - assertDrop(gen, packet123, 0); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,30,4,5}, gen.DROP_LABEL); - byte[] packet12345 = {0,1,2,3,4,5,0,0,0,0,0,0,0,0,0}; - assertDrop(gen, packet12345, 0); - gen = new ApfGenerator(MIN_APF_VERSION); - gen.addLoadImmediate(Register.R0, 1); - gen.addJumpIfBytesNotEqual(Register.R0, new byte[]{1,2,3,4,5}, gen.DROP_LABEL); - assertPass(gen, packet12345, 0); - } - - @Test(expected = ApfGenerator.IllegalInstructionException.class) - public void testApfGeneratorWantsV2OrGreater() throws Exception { - // The minimum supported APF version is 2. - new ApfGenerator(1); - } - - @Test - public void testApfDataOpcodesWantApfV3() throws IllegalInstructionException, Exception { - ApfGenerator gen = new ApfGenerator(MIN_APF_VERSION); - try { - gen.addStoreData(Register.R0, 0); - fail(); - } catch (IllegalInstructionException expected) { - /* pass */ - } - try { - gen.addLoadData(Register.R0, 0); - fail(); - } catch (IllegalInstructionException expected) { - /* pass */ - } - } - - /** - * Test that the generator emits immediates using the shortest possible encoding. - */ - @Test - public void testImmediateEncoding() throws IllegalInstructionException { - ApfGenerator gen; - - // 0-byte immediate: li R0, 0 - gen = new ApfGenerator(4); - gen.addLoadImmediate(Register.R0, 0); - assertProgramEquals(new byte[]{LI_OP | SIZE0}, gen.generate()); - - // 1-byte immediate: li R0, 42 - gen = new ApfGenerator(4); - gen.addLoadImmediate(Register.R0, 42); - assertProgramEquals(new byte[]{LI_OP | SIZE8, 42}, gen.generate()); - - // 2-byte immediate: li R1, 0x1234 - gen = new ApfGenerator(4); - gen.addLoadImmediate(Register.R1, 0x1234); - assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1, 0x12, 0x34}, gen.generate()); - - // 4-byte immediate: li R0, 0x12345678 - gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 0x12345678); - assertProgramEquals( - new byte[]{LI_OP | SIZE32, 0x12, 0x34, 0x56, 0x78}, - gen.generate()); - } - - /** - * Test that the generator emits negative immediates using the shortest possible encoding. - */ - @Test - public void testNegativeImmediateEncoding() throws IllegalInstructionException { - ApfGenerator gen; - - // 1-byte negative immediate: li R0, -42 - gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, -42); - assertProgramEquals(new byte[]{LI_OP | SIZE8, -42}, gen.generate()); - - // 2-byte negative immediate: li R1, -0x1122 - gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R1, -0x1122); - assertProgramEquals(new byte[]{LI_OP | SIZE16 | R1, (byte)0xEE, (byte)0xDE}, - gen.generate()); - - // 4-byte negative immediate: li R0, -0x11223344 - gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, -0x11223344); - assertProgramEquals( - new byte[]{LI_OP | SIZE32, (byte)0xEE, (byte)0xDD, (byte)0xCC, (byte)0xBC}, - gen.generate()); - } - - /** - * Test that the generator correctly emits positive and negative immediates for LDDW/STDW. - */ - @Test - public void testLoadStoreDataEncoding() throws IllegalInstructionException { - ApfGenerator gen; - - // Load data with no offset: lddw R0, [0 + r1] - gen = new ApfGenerator(3); - gen.addLoadData(Register.R0, 0); - assertProgramEquals(new byte[]{LDDW_OP | SIZE0}, gen.generate()); - - // Store data with 8bit negative offset: lddw r0, [-42 + r1] - gen = new ApfGenerator(3); - gen.addStoreData(Register.R0, -42); - assertProgramEquals(new byte[]{STDW_OP | SIZE8, -42}, gen.generate()); - - // Store data to R1 with 16bit negative offset: stdw r1, [-0x1122 + r0] - gen = new ApfGenerator(3); - gen.addStoreData(Register.R1, -0x1122); - assertProgramEquals(new byte[]{STDW_OP | SIZE16 | R1, (byte)0xEE, (byte)0xDE}, - gen.generate()); - - // Load data to R1 with 32bit negative offset: lddw r1, [0xDEADBEEF + r0] - gen = new ApfGenerator(3); - gen.addLoadData(Register.R1, 0xDEADBEEF); - assertProgramEquals( - new byte[]{LDDW_OP | SIZE32 | R1, (byte)0xDE, (byte)0xAD, (byte)0xBE, (byte)0xEF}, - gen.generate()); - } - - /** - * Test that the interpreter correctly executes STDW with a negative 8bit offset - */ - @Test - public void testApfDataWrite() throws IllegalInstructionException, Exception { - byte[] packet = new byte[MIN_PKT_SIZE]; - byte[] data = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; - byte[] expected_data = data.clone(); - - // No memory access instructions: should leave the data segment untouched. - ApfGenerator gen = new ApfGenerator(3); - assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data); - - // Expect value 0x87654321 to be stored starting from address -11 from the end of the - // data buffer, in big-endian order. - gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 0x87654321); - gen.addLoadImmediate(Register.R1, -5); - gen.addStoreData(Register.R0, -6); // -5 + -6 = -11 (offset +5 with data_len=16) - expected_data[5] = (byte)0x87; - expected_data[6] = (byte)0x65; - expected_data[7] = (byte)0x43; - expected_data[8] = (byte)0x21; - assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data); - } - - /** - * Test that the interpreter correctly executes LDDW with a negative 16bit offset - */ - @Test - public void testApfDataRead() throws IllegalInstructionException, Exception { - // Program that DROPs if address 10 (-6) contains 0x87654321. - ApfGenerator gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R1, 1000); - gen.addLoadData(Register.R0, -1006); // 1000 + -1006 = -6 (offset +10 with data_len=16) - gen.addJumpIfR0Equals(0x87654321, gen.DROP_LABEL); - byte[] program = gen.generate(); - byte[] packet = new byte[MIN_PKT_SIZE]; - - // Content is incorrect (last byte does not match) -> PASS - byte[] data = new byte[16]; - data[10] = (byte)0x87; - data[11] = (byte)0x65; - data[12] = (byte)0x43; - data[13] = (byte)0x00; // != 0x21 - byte[] expected_data = data.clone(); - assertDataMemoryContents(PASS, program, packet, data, expected_data); - - // Fix the last byte -> conditional jump taken -> DROP - data[13] = (byte)0x21; - expected_data = data; - assertDataMemoryContents(DROP, program, packet, data, expected_data); - } - - /** - * Test that the interpreter correctly executes LDDW followed by a STDW. - * To cover a few more edge cases, LDDW has a 0bit offset, while STDW has a positive 8bit - * offset. - */ - @Test - public void testApfDataReadModifyWrite() throws IllegalInstructionException, Exception { - ApfGenerator gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R1, -22); - gen.addLoadData(Register.R0, 0); // Load from address 32 -22 + 0 = 10 - gen.addAdd(0x78453412); // 87654321 + 78453412 = FFAA7733 - gen.addStoreData(Register.R0, 4); // Write back to address 32 -22 + 4 = 14 - - byte[] packet = new byte[MIN_PKT_SIZE]; - byte[] data = new byte[32]; - data[10] = (byte)0x87; - data[11] = (byte)0x65; - data[12] = (byte)0x43; - data[13] = (byte)0x21; - byte[] expected_data = data.clone(); - expected_data[14] = (byte)0xFF; - expected_data[15] = (byte)0xAA; - expected_data[16] = (byte)0x77; - expected_data[17] = (byte)0x33; - assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data); - } - - @Test - public void testApfDataBoundChecking() throws IllegalInstructionException, Exception { - byte[] packet = new byte[MIN_PKT_SIZE]; - byte[] data = new byte[32]; - byte[] expected_data = data; - - // Program that DROPs unconditionally. This is our the baseline. - ApfGenerator gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 3); - gen.addLoadData(Register.R1, 7); - gen.addJump(gen.DROP_LABEL); - assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data); - - // Same program as before, but this time we're trying to load past the end of the data. - gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 20); - gen.addLoadData(Register.R1, 15); // 20 + 15 > 32 - gen.addJump(gen.DROP_LABEL); // Not reached. - assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data); - - // Subtracting an immediate should work... - gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 20); - gen.addLoadData(Register.R1, -4); - gen.addJump(gen.DROP_LABEL); - assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data); - - // ...and underflowing simply wraps around to the end of the buffer... - gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 20); - gen.addLoadData(Register.R1, -30); - gen.addJump(gen.DROP_LABEL); - assertDataMemoryContents(DROP, gen.generate(), packet, data, expected_data); - - // ...but doesn't allow accesses before the start of the buffer - gen = new ApfGenerator(3); - gen.addLoadImmediate(Register.R0, 20); - gen.addLoadData(Register.R1, -1000); - gen.addJump(gen.DROP_LABEL); // Not reached. - assertDataMemoryContents(PASS, gen.generate(), packet, data, expected_data); - } - - /** - * Generate some BPF programs, translate them to APF, then run APF and BPF programs - * over packet traces and verify both programs filter out the same packets. - */ - @Test - public void testApfAgainstBpf() throws Exception { - String[] tcpdump_filters = new String[]{ "udp", "tcp", "icmp", "icmp6", "udp port 53", - "arp", "dst 239.255.255.250", "arp or tcp or udp port 53", "net 192.168.1.0/24", - "arp or icmp6 or portrange 53-54", "portrange 53-54 or portrange 100-50000", - "tcp[tcpflags] & (tcp-ack|tcp-fin) != 0 and (ip[2:2] > 57 or icmp)" }; - String pcap_filename = stageFile(R.raw.apf); - for (String tcpdump_filter : tcpdump_filters) { - byte[] apf_program = Bpf2Apf.convert(compileToBpf(tcpdump_filter)); - assertTrue("Failed to match for filter: " + tcpdump_filter, - compareBpfApf(tcpdump_filter, pcap_filename, apf_program)); - } - } - - /** - * Generate APF program, run pcap file though APF filter, then check all the packets in the file - * should be dropped. - */ - @Test - public void testApfFilterPcapFile() throws Exception { - final byte[] MOCK_PCAP_IPV4_ADDR = {(byte) 172, 16, 7, (byte) 151}; - String pcapFilename = stageFile(R.raw.apfPcap); - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_PCAP_IPV4_ADDR), 16); - LinkProperties lp = new LinkProperties(); - lp.addLinkAddress(link); - - ApfConfiguration config = getDefaultConfig(); - ApfCapabilities MOCK_APF_PCAP_CAPABILITIES = new ApfCapabilities(4, 1700, ARPHRD_ETHER); - config.apfCapabilities = MOCK_APF_PCAP_CAPABILITIES; - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog); - apfFilter.setLinkProperties(lp); - byte[] program = ipClientCallback.getApfProgram(); - byte[] data = new byte[ApfFilter.Counter.totalSize()]; - final boolean result; - - result = dropsAllPackets(program, data, pcapFilename); - Log.i(TAG, "testApfFilterPcapFile(): Data counters: " + HexDump.toHexString(data, false)); - - assertTrue("Failed to drop all packets by filter. \nAPF counters:" + - HexDump.toHexString(data, false), result); - } - - private class MockIpClientCallback extends IpClientCallbacksWrapper { - private final ConditionVariable mGotApfProgram = new ConditionVariable(); - private byte[] mLastApfProgram; - - MockIpClientCallback() { - super(mock(IIpClientCallbacks.class), mock(SharedLog.class)); - } - - @Override - public void installPacketFilter(byte[] filter) { - mLastApfProgram = filter; - mGotApfProgram.open(); - } - - public void resetApfProgramWait() { - mGotApfProgram.close(); - } - - public byte[] getApfProgram() { - assertTrue(mGotApfProgram.block(TIMEOUT_MS)); - return mLastApfProgram; - } - - public void assertNoProgramUpdate() { - assertFalse(mGotApfProgram.block(TIMEOUT_MS)); - } - } - - private static class TestApfFilter extends ApfFilter { - public static final byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6}; - - private FileDescriptor mWriteSocket; - private final long mFixedTimeMs = SystemClock.elapsedRealtime(); - - public TestApfFilter(Context context, ApfConfiguration config, - IpClientCallbacksWrapper ipClientCallback, IpConnectivityLog log) throws Exception { - super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, log); - } - - // Pretend an RA packet has been received and show it to ApfFilter. - public void pretendPacketReceived(byte[] packet) throws IOException, ErrnoException { - // ApfFilter's ReceiveThread will be waiting to read this. - Os.write(mWriteSocket, packet, 0, packet.length); - } - - @Override - protected long currentTimeSeconds() { - return mFixedTimeMs / DateUtils.SECOND_IN_MILLIS; - } - - @Override - void maybeStartFilter() { - mHardwareAddress = MOCK_MAC_ADDR; - installNewProgramLocked(); - - // Create two sockets, "readSocket" and "mWriteSocket" and connect them together. - FileDescriptor readSocket = new FileDescriptor(); - mWriteSocket = new FileDescriptor(); - try { - Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mWriteSocket, readSocket); - } catch (ErrnoException e) { - fail(); - return; - } - // Now pass readSocket to ReceiveThread as if it was setup to read raw RAs. - // This allows us to pretend RA packets have been recieved via pretendPacketReceived(). - mReceiveThread = new ReceiveThread(readSocket); - mReceiveThread.start(); - } - - @Override - public void shutdown() { - super.shutdown(); - IoUtils.closeQuietly(mWriteSocket); - } - } - - private static final int ETH_HEADER_LEN = 14; - private static final int ETH_DEST_ADDR_OFFSET = 0; - private static final int ETH_ETHERTYPE_OFFSET = 12; - private static final byte[] ETH_BROADCAST_MAC_ADDRESS = - {(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; - - private static final int IPV4_HEADER_LEN = 20; - private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0; - private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2; - private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9; - private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12; - private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16; - - private static final int IPV4_TCP_HEADER_LEN = 20; - private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN; - private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0; - private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2; - private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4; - private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8; - private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12; - private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13; - - private static final int IPV4_UDP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;; - private static final int IPV4_UDP_SRC_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 0; - private static final int IPV4_UDP_DEST_PORT_OFFSET = IPV4_UDP_HEADER_OFFSET + 2; - private static final int IPV4_UDP_LENGTH_OFFSET = IPV4_UDP_HEADER_OFFSET + 4; - private static final int IPV4_UDP_PAYLOAD_OFFSET = IPV4_UDP_HEADER_OFFSET + 8; - private static final byte[] IPV4_BROADCAST_ADDRESS = - {(byte) 255, (byte) 255, (byte) 255, (byte) 255}; - - private static final int IPV6_HEADER_LEN = 40; - private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6; - private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8; - private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24; - private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; - private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0; - private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2; - private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4; - private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8; - // The IPv6 all nodes address ff02::1 - private static final byte[] IPV6_ALL_NODES_ADDRESS = - { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; - private static final byte[] IPV6_ALL_ROUTERS_ADDRESS = - { (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2 }; - - private static final int ICMP6_TYPE_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN; - private static final int ICMP6_ROUTER_SOLICITATION = 133; - private static final int ICMP6_ROUTER_ADVERTISEMENT = 134; - private static final int ICMP6_NEIGHBOR_SOLICITATION = 135; - private static final int ICMP6_NEIGHBOR_ANNOUNCEMENT = 136; - - private static final int ICMP6_RA_HEADER_LEN = 16; - private static final int ICMP6_RA_ROUTER_LIFETIME_OFFSET = - ETH_HEADER_LEN + IPV6_HEADER_LEN + 6; - private static final int ICMP6_RA_CHECKSUM_OFFSET = - ETH_HEADER_LEN + IPV6_HEADER_LEN + 2; - private static final int ICMP6_RA_OPTION_OFFSET = - ETH_HEADER_LEN + IPV6_HEADER_LEN + ICMP6_RA_HEADER_LEN; - - private static final int ICMP6_PREFIX_OPTION_TYPE = 3; - private static final int ICMP6_PREFIX_OPTION_LEN = 32; - private static final int ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET = 4; - private static final int ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET = 8; - - // From RFC6106: Recursive DNS Server option - private static final int ICMP6_RDNSS_OPTION_TYPE = 25; - // From RFC6106: DNS Search List option - private static final int ICMP6_DNSSL_OPTION_TYPE = 31; - - // From RFC4191: Route Information option - private static final int ICMP6_ROUTE_INFO_OPTION_TYPE = 24; - // Above three options all have the same format: - private static final int ICMP6_4_BYTE_OPTION_LEN = 8; - private static final int ICMP6_4_BYTE_LIFETIME_OFFSET = 4; - private static final int ICMP6_4_BYTE_LIFETIME_LEN = 4; - - private static final int UDP_HEADER_LEN = 8; - private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 22; - - private static final int DHCP_CLIENT_PORT = 68; - private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 48; - - private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN; - private static final byte[] ARP_IPV4_REQUEST_HEADER = { - 0, 1, // Hardware type: Ethernet (1) - 8, 0, // Protocol type: IP (0x0800) - 6, // Hardware size: 6 - 4, // Protocol size: 4 - 0, 1 // Opcode: request (1) - }; - private static final byte[] ARP_IPV4_REPLY_HEADER = { - 0, 1, // Hardware type: Ethernet (1) - 8, 0, // Protocol type: IP (0x0800) - 6, // Hardware size: 6 - 4, // Protocol size: 4 - 0, 2 // Opcode: reply (2) - }; - private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14; - private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24; - - private static final byte[] MOCK_IPV4_ADDR = {10, 0, 0, 1}; - private static final byte[] MOCK_BROADCAST_IPV4_ADDR = {10, 0, 31, (byte) 255}; // prefix = 19 - private static final byte[] MOCK_MULTICAST_IPV4_ADDR = {(byte) 224, 0, 0, 1}; - private static final byte[] ANOTHER_IPV4_ADDR = {10, 0, 0, 2}; - private static final byte[] IPV4_SOURCE_ADDR = {10, 0, 0, 3}; - private static final byte[] ANOTHER_IPV4_SOURCE_ADDR = {(byte) 192, 0, 2, 1}; - private static final byte[] BUG_PROBE_SOURCE_ADDR1 = {0, 0, 1, 2}; - private static final byte[] BUG_PROBE_SOURCE_ADDR2 = {3, 4, 0, 0}; - private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0}; - - // Helper to initialize a default apfFilter. - private ApfFilter setupApfFilter( - IpClientCallbacksWrapper ipClientCallback, ApfConfiguration config) throws Exception { - LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); - LinkProperties lp = new LinkProperties(); - lp.addLinkAddress(link); - TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog); - apfFilter.setLinkProperties(lp); - return apfFilter; - } - - @Test - public void testApfFilterIPv4() throws Exception { - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); - LinkProperties lp = new LinkProperties(); - lp.addLinkAddress(link); - - ApfConfiguration config = getDefaultConfig(); - config.multicastFilter = DROP_MULTICAST; - TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog); - apfFilter.setLinkProperties(lp); - - byte[] program = ipClientCallback.getApfProgram(); - - // Verify empty packet of 100 zero bytes is passed - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - assertPass(program, packet.array()); - - // Verify unicast IPv4 packet is passed - put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR); - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_IPV4_ADDR); - assertPass(program, packet.array()); - - // Verify L2 unicast to IPv4 broadcast addresses is dropped (b/30231088) - put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS); - assertDrop(program, packet.array()); - put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR); - assertDrop(program, packet.array()); - - // Verify multicast/broadcast IPv4, not DHCP to us, is dropped - put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS); - assertDrop(program, packet.array()); - packet.put(IPV4_VERSION_IHL_OFFSET, (byte)0x45); - assertDrop(program, packet.array()); - packet.put(IPV4_PROTOCOL_OFFSET, (byte)IPPROTO_UDP); - assertDrop(program, packet.array()); - packet.putShort(UDP_DESTINATION_PORT_OFFSET, (short)DHCP_CLIENT_PORT); - assertDrop(program, packet.array()); - put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_MULTICAST_IPV4_ADDR); - assertDrop(program, packet.array()); - put(packet, IPV4_DEST_ADDR_OFFSET, MOCK_BROADCAST_IPV4_ADDR); - assertDrop(program, packet.array()); - put(packet, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS); - assertDrop(program, packet.array()); - - // Verify broadcast IPv4 DHCP to us is passed - put(packet, DHCP_CLIENT_MAC_OFFSET, TestApfFilter.MOCK_MAC_ADDR); - assertPass(program, packet.array()); - - // Verify unicast IPv4 DHCP to us is passed - put(packet, ETH_DEST_ADDR_OFFSET, TestApfFilter.MOCK_MAC_ADDR); - assertPass(program, packet.array()); - - apfFilter.shutdown(); - } - - @Test - public void testApfFilterIPv6() throws Exception { - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - ApfConfiguration config = getDefaultConfig(); - TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog); - byte[] program = ipClientCallback.getApfProgram(); - - // Verify empty IPv6 packet is passed - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); - assertPass(program, packet.array()); - - // Verify empty ICMPv6 packet is passed - packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6); - assertPass(program, packet.array()); - - // Verify empty ICMPv6 NA packet is passed - packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_NEIGHBOR_ANNOUNCEMENT); - assertPass(program, packet.array()); - - // Verify ICMPv6 NA to ff02::1 is dropped - put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_NODES_ADDRESS); - assertDrop(program, packet.array()); - - // Verify ICMPv6 RS to any is dropped - packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_SOLICITATION); - assertDrop(program, packet.array()); - put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_ROUTERS_ADDRESS); - assertDrop(program, packet.array()); - - apfFilter.shutdown(); - } - - @Test - public void testApfFilterMulticast() throws Exception { - final byte[] unicastIpv4Addr = {(byte)192,0,2,63}; - final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255}; - final byte[] multicastIpv4Addr = {(byte)224,0,0,1}; - final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb}; - - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24); - LinkProperties lp = new LinkProperties(); - lp.addLinkAddress(link); - - ApfConfiguration config = getDefaultConfig(); - config.ieee802_3Filter = DROP_802_3_FRAMES; - TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog); - apfFilter.setLinkProperties(lp); - - byte[] program = ipClientCallback.getApfProgram(); - - // Construct IPv4 and IPv6 multicast packets. - ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]); - mcastv4packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - put(mcastv4packet, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr); - - ByteBuffer mcastv6packet = ByteBuffer.wrap(new byte[100]); - mcastv6packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); - mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_UDP); - put(mcastv6packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr); - - // Construct IPv4 broadcast packet. - ByteBuffer bcastv4packet1 = ByteBuffer.wrap(new byte[100]); - bcastv4packet1.put(ETH_BROADCAST_MAC_ADDRESS); - bcastv4packet1.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - put(bcastv4packet1, IPV4_DEST_ADDR_OFFSET, multicastIpv4Addr); - - ByteBuffer bcastv4packet2 = ByteBuffer.wrap(new byte[100]); - bcastv4packet2.put(ETH_BROADCAST_MAC_ADDRESS); - bcastv4packet2.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - put(bcastv4packet2, IPV4_DEST_ADDR_OFFSET, IPV4_BROADCAST_ADDRESS); - - // Construct IPv4 broadcast with L2 unicast address packet (b/30231088). - ByteBuffer bcastv4unicastl2packet = ByteBuffer.wrap(new byte[100]); - bcastv4unicastl2packet.put(TestApfFilter.MOCK_MAC_ADDR); - bcastv4unicastl2packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - put(bcastv4unicastl2packet, IPV4_DEST_ADDR_OFFSET, broadcastIpv4Addr); - - // Verify initially disabled multicast filter is off - assertPass(program, mcastv4packet.array()); - assertPass(program, mcastv6packet.array()); - assertPass(program, bcastv4packet1.array()); - assertPass(program, bcastv4packet2.array()); - assertPass(program, bcastv4unicastl2packet.array()); - - // Turn on multicast filter and verify it works - ipClientCallback.resetApfProgramWait(); - apfFilter.setMulticastFilter(true); - program = ipClientCallback.getApfProgram(); - assertDrop(program, mcastv4packet.array()); - assertDrop(program, mcastv6packet.array()); - assertDrop(program, bcastv4packet1.array()); - assertDrop(program, bcastv4packet2.array()); - assertDrop(program, bcastv4unicastl2packet.array()); - - // Turn off multicast filter and verify it's off - ipClientCallback.resetApfProgramWait(); - apfFilter.setMulticastFilter(false); - program = ipClientCallback.getApfProgram(); - assertPass(program, mcastv4packet.array()); - assertPass(program, mcastv6packet.array()); - assertPass(program, bcastv4packet1.array()); - assertPass(program, bcastv4packet2.array()); - assertPass(program, bcastv4unicastl2packet.array()); - - // Verify it can be initialized to on - ipClientCallback.resetApfProgramWait(); - apfFilter.shutdown(); - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog); - apfFilter.setLinkProperties(lp); - program = ipClientCallback.getApfProgram(); - assertDrop(program, mcastv4packet.array()); - assertDrop(program, mcastv6packet.array()); - assertDrop(program, bcastv4packet1.array()); - assertDrop(program, bcastv4unicastl2packet.array()); - - // Verify that ICMPv6 multicast is not dropped. - mcastv6packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6); - assertPass(program, mcastv6packet.array()); - - apfFilter.shutdown(); - } - - @Test - public void testApfFilterMulticastPingWhileDozing() throws Exception { - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - ApfFilter apfFilter = setupApfFilter(ipClientCallback, getDefaultConfig()); - - // Construct a multicast ICMPv6 ECHO request. - final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb}; - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); - packet.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6); - packet.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ECHO_REQUEST_TYPE); - put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr); - - // Normally, we let multicast pings alone... - assertPass(ipClientCallback.getApfProgram(), packet.array()); - - // ...and even while dozing... - apfFilter.setDozeMode(true); - assertPass(ipClientCallback.getApfProgram(), packet.array()); - - // ...but when the multicast filter is also enabled, drop the multicast pings to save power. - apfFilter.setMulticastFilter(true); - assertDrop(ipClientCallback.getApfProgram(), packet.array()); - - // However, we should still let through all other ICMPv6 types. - ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone()); - raPacket.put(ICMP6_TYPE_OFFSET, (byte) NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT); - assertPass(ipClientCallback.getApfProgram(), raPacket.array()); - - // Now wake up from doze mode to ensure that we no longer drop the packets. - // (The multicast filter is still enabled at this point). - apfFilter.setDozeMode(false); - assertPass(ipClientCallback.getApfProgram(), packet.array()); - - apfFilter.shutdown(); - } - - @Test - public void testApfFilter802_3() throws Exception { - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - ApfConfiguration config = getDefaultConfig(); - ApfFilter apfFilter = setupApfFilter(ipClientCallback, config); - byte[] program = ipClientCallback.getApfProgram(); - - // Verify empty packet of 100 zero bytes is passed - // Note that eth-type = 0 makes it an IEEE802.3 frame - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - assertPass(program, packet.array()); - - // Verify empty packet with IPv4 is passed - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - assertPass(program, packet.array()); - - // Verify empty IPv6 packet is passed - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); - assertPass(program, packet.array()); - - // Now turn on the filter - ipClientCallback.resetApfProgramWait(); - apfFilter.shutdown(); - config.ieee802_3Filter = DROP_802_3_FRAMES; - apfFilter = setupApfFilter(ipClientCallback, config); - program = ipClientCallback.getApfProgram(); - - // Verify that IEEE802.3 frame is dropped - // In this case ethtype is used for payload length - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)(100 - 14)); - assertDrop(program, packet.array()); - - // Verify that IPv4 (as example of Ethernet II) frame will pass - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - assertPass(program, packet.array()); - - // Verify that IPv6 (as example of Ethernet II) frame will pass - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); - assertPass(program, packet.array()); - - apfFilter.shutdown(); - } - - @Test - public void testApfFilterEthTypeBL() throws Exception { - final int[] emptyBlackList = {}; - final int[] ipv4BlackList = {ETH_P_IP}; - final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6}; - - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - ApfConfiguration config = getDefaultConfig(); - ApfFilter apfFilter = setupApfFilter(ipClientCallback, config); - byte[] program = ipClientCallback.getApfProgram(); - - // Verify empty packet of 100 zero bytes is passed - // Note that eth-type = 0 makes it an IEEE802.3 frame - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - assertPass(program, packet.array()); - - // Verify empty packet with IPv4 is passed - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - assertPass(program, packet.array()); - - // Verify empty IPv6 packet is passed - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); - assertPass(program, packet.array()); - - // Now add IPv4 to the black list - ipClientCallback.resetApfProgramWait(); - apfFilter.shutdown(); - config.ethTypeBlackList = ipv4BlackList; - apfFilter = setupApfFilter(ipClientCallback, config); - program = ipClientCallback.getApfProgram(); - - // Verify that IPv4 frame will be dropped - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - assertDrop(program, packet.array()); - - // Verify that IPv6 frame will pass - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); - assertPass(program, packet.array()); - - // Now let us have both IPv4 and IPv6 in the black list - ipClientCallback.resetApfProgramWait(); - apfFilter.shutdown(); - config.ethTypeBlackList = ipv4Ipv6BlackList; - apfFilter = setupApfFilter(ipClientCallback, config); - program = ipClientCallback.getApfProgram(); - - // Verify that IPv4 frame will be dropped - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP); - assertDrop(program, packet.array()); - - // Verify that IPv6 frame will be dropped - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); - assertDrop(program, packet.array()); - - apfFilter.shutdown(); - } - - private byte[] getProgram(MockIpClientCallback cb, ApfFilter filter, LinkProperties lp) { - cb.resetApfProgramWait(); - filter.setLinkProperties(lp); - return cb.getApfProgram(); - } - - private void verifyArpFilter(byte[] program, int filterResult) { - // Verify ARP request packet - assertPass(program, arpRequestBroadcast(MOCK_IPV4_ADDR)); - assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR)); - assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR)); - - // Verify ARP reply packets from different source ip - assertDrop(program, arpReply(IPV4_ANY_HOST_ADDR, IPV4_ANY_HOST_ADDR)); - assertPass(program, arpReply(ANOTHER_IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR)); - assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR1, IPV4_ANY_HOST_ADDR)); - assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR2, IPV4_ANY_HOST_ADDR)); - - // Verify unicast ARP reply packet is always accepted. - assertPass(program, arpReply(IPV4_SOURCE_ADDR, MOCK_IPV4_ADDR)); - assertPass(program, arpReply(IPV4_SOURCE_ADDR, ANOTHER_IPV4_ADDR)); - assertPass(program, arpReply(IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR)); - - // Verify GARP reply packets are always filtered - assertDrop(program, garpReply()); - } - - @Test - public void testApfFilterArp() throws Exception { - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - ApfConfiguration config = getDefaultConfig(); - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog); - - // Verify initially ARP request filter is off, and GARP filter is on. - verifyArpFilter(ipClientCallback.getApfProgram(), PASS); - - // Inform ApfFilter of our address and verify ARP filtering is on - LinkAddress linkAddress = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24); - LinkProperties lp = new LinkProperties(); - assertTrue(lp.addLinkAddress(linkAddress)); - verifyArpFilter(getProgram(ipClientCallback, apfFilter, lp), DROP); - - // Inform ApfFilter of loss of IP and verify ARP filtering is off - verifyArpFilter(getProgram(ipClientCallback, apfFilter, new LinkProperties()), PASS); - - apfFilter.shutdown(); - } - - private static byte[] arpReply(byte[] sip, byte[] tip) { - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); - put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER); - put(packet, ARP_SOURCE_IP_ADDRESS_OFFSET, sip); - put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip); - return packet.array(); - } - - private static byte[] arpRequestBroadcast(byte[] tip) { - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); - put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS); - put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REQUEST_HEADER); - put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip); - return packet.array(); - } - - private static byte[] garpReply() { - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP); - put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS); - put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER); - put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, IPV4_ANY_HOST_ADDR); - return packet.array(); - } - - private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 5}; - private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 6}; - private static final byte[] IPV4_ANOTHER_ADDR = {10, 0 , 0, 7}; - private static final byte[] IPV6_KEEPALIVE_SRC_ADDR = - {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf1}; - private static final byte[] IPV6_KEEPALIVE_DST_ADDR = - {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf2}; - private static final byte[] IPV6_ANOTHER_ADDR = - {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf5}; - - @Test - public void testApfFilterKeepaliveAck() throws Exception { - final MockIpClientCallback cb = new MockIpClientCallback(); - final ApfConfiguration config = getDefaultConfig(); - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); - byte[] program; - final int srcPort = 12345; - final int dstPort = 54321; - final int seqNum = 2123456789; - final int ackNum = 1234567890; - final int anotherSrcPort = 23456; - final int anotherDstPort = 65432; - final int anotherSeqNum = 2123456780; - final int anotherAckNum = 1123456789; - final int slot1 = 1; - final int slot2 = 2; - final int window = 14480; - final int windowScale = 4; - - // src: 10.0.0.5, port: 12345 - // dst: 10.0.0.6, port: 54321 - InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR); - InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR); - - final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable(); - parcel.srcAddress = srcAddr.getAddress(); - parcel.srcPort = srcPort; - parcel.dstAddress = dstAddr.getAddress(); - parcel.dstPort = dstPort; - parcel.seq = seqNum; - parcel.ack = ackNum; - - apfFilter.addTcpKeepalivePacketFilter(slot1, parcel); - program = cb.getApfProgram(); - - // Verify IPv4 keepalive ack packet is dropped - // src: 10.0.0.6, port: 54321 - // dst: 10.0.0.5, port: 12345 - assertDrop(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); - // Verify IPv4 non-keepalive ack packet from the same source address is passed - assertPass(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */)); - assertPass(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1, 10 /* dataLength */)); - // Verify IPv4 packet from another address is passed - assertPass(program, - ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */)); - - // Remove IPv4 keepalive filter - apfFilter.removeKeepalivePacketFilter(slot1); - - try { - // src: 2404:0:0:0:0:0:faf1, port: 12345 - // dst: 2404:0:0:0:0:0:faf2, port: 54321 - srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR); - dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR); - - final TcpKeepalivePacketDataParcelable ipv6Parcel = - new TcpKeepalivePacketDataParcelable(); - ipv6Parcel.srcAddress = srcAddr.getAddress(); - ipv6Parcel.srcPort = srcPort; - ipv6Parcel.dstAddress = dstAddr.getAddress(); - ipv6Parcel.dstPort = dstPort; - ipv6Parcel.seq = seqNum; - ipv6Parcel.ack = ackNum; - - apfFilter.addTcpKeepalivePacketFilter(slot1, ipv6Parcel); - program = cb.getApfProgram(); - - // Verify IPv6 keepalive ack packet is dropped - // src: 2404:0:0:0:0:0:faf2, port: 54321 - // dst: 2404:0:0:0:0:0:faf1, port: 12345 - assertDrop(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1)); - // Verify IPv6 non-keepalive ack packet from the same source address is passed - assertPass(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum + 100, seqNum)); - // Verify IPv6 packet from another address is passed - assertPass(program, - ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum)); - - // Remove IPv6 keepalive filter - apfFilter.removeKeepalivePacketFilter(slot1); - - // Verify multiple filters - apfFilter.addTcpKeepalivePacketFilter(slot1, parcel); - apfFilter.addTcpKeepalivePacketFilter(slot2, ipv6Parcel); - program = cb.getApfProgram(); - - // Verify IPv4 keepalive ack packet is dropped - // src: 10.0.0.6, port: 54321 - // dst: 10.0.0.5, port: 12345 - assertDrop(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); - // Verify IPv4 non-keepalive ack packet from the same source address is passed - assertPass(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */)); - // Verify IPv4 packet from another address is passed - assertPass(program, - ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */)); - - // Verify IPv6 keepalive ack packet is dropped - // src: 2404:0:0:0:0:0:faf2, port: 54321 - // dst: 2404:0:0:0:0:0:faf1, port: 12345 - assertDrop(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1)); - // Verify IPv6 non-keepalive ack packet from the same source address is passed - assertPass(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum + 100, seqNum)); - // Verify IPv6 packet from another address is passed - assertPass(program, - ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort, - anotherDstPort, anotherSeqNum, anotherAckNum)); - - // Remove keepalive filters - apfFilter.removeKeepalivePacketFilter(slot1); - apfFilter.removeKeepalivePacketFilter(slot2); - } catch (UnsupportedOperationException e) { - // TODO: support V6 packets - } - - program = cb.getApfProgram(); - - // Verify IPv4, IPv6 packets are passed - assertPass(program, - ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */)); - assertPass(program, - ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, ackNum, seqNum + 1)); - assertPass(program, - ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort, - dstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */)); - assertPass(program, - ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort, - dstPort, anotherSeqNum, anotherAckNum)); - - apfFilter.shutdown(); - } - - private static byte[] ipv4Packet(byte[] sip, byte[] dip, int sport, - int dport, int seq, int ack, int dataLength) { - final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN; - - ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]); - - // ether type - packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP); - - // IPv4 header - packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45); - packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength); - packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_TCP); - put(packet, IPV4_SRC_ADDR_OFFSET, sip); - put(packet, IPV4_DEST_ADDR_OFFSET, dip); - packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport); - packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport); - packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq); - packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack); - - // TCP header length 5(20 bytes), reserved 3 bits, NS=0 - packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50); - // TCP flags: ACK set - packet.put(IPV4_TCP_HEADER_FLAG_OFFSET, (byte) 0x10); - return packet.array(); - } - - private static byte[] ipv6Packet(byte[] sip, byte[] tip, int sport, - int dport, int seq, int ack) { - ByteBuffer packet = ByteBuffer.wrap(new byte[100]); - packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6); - put(packet, IPV6_SRC_ADDR_OFFSET, sip); - put(packet, IPV6_DEST_ADDR_OFFSET, tip); - packet.putShort(IPV6_TCP_SRC_PORT_OFFSET, (short) sport); - packet.putShort(IPV6_TCP_DEST_PORT_OFFSET, (short) dport); - packet.putInt(IPV6_TCP_SEQ_NUM_OFFSET, seq); - packet.putInt(IPV6_TCP_ACK_NUM_OFFSET, ack); - return packet.array(); - } - - @Test - public void testApfFilterNattKeepalivePacket() throws Exception { - final MockIpClientCallback cb = new MockIpClientCallback(); - final ApfConfiguration config = getDefaultConfig(); - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); - byte[] program; - final int srcPort = 1024; - final int dstPort = 4500; - final int slot1 = 1; - // NAT-T keepalive - final byte[] kaPayload = {(byte) 0xff}; - final byte[] nonKaPayload = {(byte) 0xfe}; - - // src: 10.0.0.5, port: 1024 - // dst: 10.0.0.6, port: 4500 - InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR); - InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR); - - final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable(); - parcel.srcAddress = srcAddr.getAddress(); - parcel.srcPort = srcPort; - parcel.dstAddress = dstAddr.getAddress(); - parcel.dstPort = dstPort; - - apfFilter.addNattKeepalivePacketFilter(slot1, parcel); - program = cb.getApfProgram(); - - // Verify IPv4 keepalive packet is dropped - // src: 10.0.0.6, port: 4500 - // dst: 10.0.0.5, port: 1024 - byte[] pkt = ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR, - IPV4_KEEPALIVE_SRC_ADDR, dstPort, srcPort, 1 /* dataLength */); - System.arraycopy(kaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, kaPayload.length); - assertDrop(program, pkt); - - // Verify a packet with payload length 1 byte but it is not 0xff will pass the filter. - System.arraycopy(nonKaPayload, 0, pkt, IPV4_UDP_PAYLOAD_OFFSET, nonKaPayload.length); - assertPass(program, pkt); - - // Verify IPv4 non-keepalive response packet from the same source address is passed - assertPass(program, - ipv4UdpPacket(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, 10 /* dataLength */)); - - // Verify IPv4 non-keepalive response packet from other source address is passed - assertPass(program, - ipv4UdpPacket(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, - dstPort, srcPort, 10 /* dataLength */)); - - apfFilter.removeKeepalivePacketFilter(slot1); - apfFilter.shutdown(); - } - - private static byte[] ipv4UdpPacket(byte[] sip, byte[] dip, int sport, - int dport, int dataLength) { - final int totalLength = dataLength + IPV4_HEADER_LEN + UDP_HEADER_LEN; - final int udpLength = UDP_HEADER_LEN + dataLength; - ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]); - - // ether type - packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP); - - // IPv4 header - packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45); - packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength); - packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_UDP); - put(packet, IPV4_SRC_ADDR_OFFSET, sip); - put(packet, IPV4_DEST_ADDR_OFFSET, dip); - packet.putShort(IPV4_UDP_SRC_PORT_OFFSET, (short) sport); - packet.putShort(IPV4_UDP_DEST_PORT_OFFSET, (short) dport); - packet.putShort(IPV4_UDP_LENGTH_OFFSET, (short) udpLength); - - return packet.array(); - } - - // Verify that the last program pushed to the IpClient.Callback properly filters the - // given packet for the given lifetime. - private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) { - final int FRACTION_OF_LIFETIME = 6; - final int ageLimit = lifetime / FRACTION_OF_LIFETIME; - - // Verify new program should drop RA for 1/6th its lifetime and pass afterwards. - assertDrop(program, packet.array()); - assertDrop(program, packet.array(), ageLimit); - assertPass(program, packet.array(), ageLimit + 1); - assertPass(program, packet.array(), lifetime); - // Verify RA checksum is ignored - final short originalChecksum = packet.getShort(ICMP6_RA_CHECKSUM_OFFSET); - packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)12345); - assertDrop(program, packet.array()); - packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)-12345); - assertDrop(program, packet.array()); - packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, originalChecksum); - - // Verify other changes to RA make it not match filter - final byte originalFirstByte = packet.get(0); - packet.put(0, (byte)-1); - assertPass(program, packet.array()); - packet.put(0, (byte)0); - assertDrop(program, packet.array()); - packet.put(0, originalFirstByte); - } - - // Test that when ApfFilter is shown the given packet, it generates a program to filter it - // for the given lifetime. - private void verifyRaLifetime(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback, - ByteBuffer packet, int lifetime) throws IOException, ErrnoException { - // Verify new program generated if ApfFilter witnesses RA - ipClientCallback.resetApfProgramWait(); - apfFilter.pretendPacketReceived(packet.array()); - byte[] program = ipClientCallback.getApfProgram(); - verifyRaLifetime(program, packet, lifetime); - } - - private void verifyRaEvent(RaEvent expected) { - ArgumentCaptor<IpConnectivityLog.Event> captor = - ArgumentCaptor.forClass(IpConnectivityLog.Event.class); - verify(mLog, atLeastOnce()).log(captor.capture()); - RaEvent got = lastRaEvent(captor.getAllValues()); - if (!raEventEquals(expected, got)) { - assertEquals(expected, got); // fail for printing an assertion error message. - } - } - - private RaEvent lastRaEvent(List<IpConnectivityLog.Event> events) { - RaEvent got = null; - for (Parcelable ev : events) { - if (ev instanceof RaEvent) { - got = (RaEvent) ev; - } - } - return got; - } - - private boolean raEventEquals(RaEvent ev1, RaEvent ev2) { - return (ev1 != null) && (ev2 != null) - && (ev1.routerLifetime == ev2.routerLifetime) - && (ev1.prefixValidLifetime == ev2.prefixValidLifetime) - && (ev1.prefixPreferredLifetime == ev2.prefixPreferredLifetime) - && (ev1.routeInfoLifetime == ev2.routeInfoLifetime) - && (ev1.rdnssLifetime == ev2.rdnssLifetime) - && (ev1.dnsslLifetime == ev2.dnsslLifetime); - } - - private void assertInvalidRa(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback, - ByteBuffer packet) throws IOException, ErrnoException { - ipClientCallback.resetApfProgramWait(); - apfFilter.pretendPacketReceived(packet.array()); - ipClientCallback.assertNoProgramUpdate(); - } - - @Test - public void testApfFilterRa() throws Exception { - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - ApfConfiguration config = getDefaultConfig(); - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog); - byte[] program = ipClientCallback.getApfProgram(); - - final int ROUTER_LIFETIME = 1000; - final int PREFIX_VALID_LIFETIME = 200; - final int PREFIX_PREFERRED_LIFETIME = 100; - final int RDNSS_LIFETIME = 300; - final int ROUTE_LIFETIME = 400; - // Note that lifetime of 2000 will be ignored in favor of shorter route lifetime of 1000. - final int DNSSL_LIFETIME = 2000; - final int VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET = ETH_HEADER_LEN; - // IPv6, traffic class = 0, flow label = 0x12345 - final int VERSION_TRAFFIC_CLASS_FLOW_LABEL = 0x60012345; - - // Verify RA is passed the first time - ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]); - basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); - basePacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET, - VERSION_TRAFFIC_CLASS_FLOW_LABEL); - basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6); - basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT); - basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)ROUTER_LIFETIME); - basePacket.position(IPV6_DEST_ADDR_OFFSET); - basePacket.put(IPV6_ALL_NODES_ADDRESS); - assertPass(program, basePacket.array()); - - verifyRaLifetime(apfFilter, ipClientCallback, basePacket, ROUTER_LIFETIME); - verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1)); - - ByteBuffer newFlowLabelPacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]); - basePacket.clear(); - newFlowLabelPacket.put(basePacket); - // Check that changes are ignored in every byte of the flow label. - newFlowLabelPacket.putInt(VERSION_TRAFFIC_CLASS_FLOW_LABEL_OFFSET, - VERSION_TRAFFIC_CLASS_FLOW_LABEL + 0x11111); - - // Ensure zero-length options cause the packet to be silently skipped. - // Do this before we test other packets. http://b/29586253 - ByteBuffer zeroLengthOptionPacket = ByteBuffer.wrap( - new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]); - basePacket.clear(); - zeroLengthOptionPacket.put(basePacket); - zeroLengthOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE); - zeroLengthOptionPacket.put((byte)0); - assertInvalidRa(apfFilter, ipClientCallback, zeroLengthOptionPacket); - - // Generate several RAs with different options and lifetimes, and verify when - // ApfFilter is shown these packets, it generates programs to filter them for the - // appropriate lifetime. - ByteBuffer prefixOptionPacket = ByteBuffer.wrap( - new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_LEN]); - basePacket.clear(); - prefixOptionPacket.put(basePacket); - prefixOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE); - prefixOptionPacket.put((byte)(ICMP6_PREFIX_OPTION_LEN / 8)); - prefixOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET, - PREFIX_PREFERRED_LIFETIME); - prefixOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, - PREFIX_VALID_LIFETIME); - verifyRaLifetime( - apfFilter, ipClientCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME); - verifyRaEvent(new RaEvent( - ROUTER_LIFETIME, PREFIX_VALID_LIFETIME, PREFIX_PREFERRED_LIFETIME, -1, -1, -1)); - - ByteBuffer rdnssOptionPacket = ByteBuffer.wrap( - new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]); - basePacket.clear(); - rdnssOptionPacket.put(basePacket); - rdnssOptionPacket.put((byte)ICMP6_RDNSS_OPTION_TYPE); - rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8)); - rdnssOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, RDNSS_LIFETIME); - verifyRaLifetime(apfFilter, ipClientCallback, rdnssOptionPacket, RDNSS_LIFETIME); - verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, RDNSS_LIFETIME, -1)); - - ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap( - new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]); - basePacket.clear(); - routeInfoOptionPacket.put(basePacket); - routeInfoOptionPacket.put((byte)ICMP6_ROUTE_INFO_OPTION_TYPE); - routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8)); - routeInfoOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, ROUTE_LIFETIME); - verifyRaLifetime(apfFilter, ipClientCallback, routeInfoOptionPacket, ROUTE_LIFETIME); - verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1)); - - ByteBuffer dnsslOptionPacket = ByteBuffer.wrap( - new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]); - basePacket.clear(); - dnsslOptionPacket.put(basePacket); - dnsslOptionPacket.put((byte)ICMP6_DNSSL_OPTION_TYPE); - dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8)); - dnsslOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, DNSSL_LIFETIME); - verifyRaLifetime(apfFilter, ipClientCallback, dnsslOptionPacket, ROUTER_LIFETIME); - verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME)); - - // Verify that current program filters all five RAs: - program = ipClientCallback.getApfProgram(); - verifyRaLifetime(program, basePacket, ROUTER_LIFETIME); - verifyRaLifetime(program, newFlowLabelPacket, ROUTER_LIFETIME); - verifyRaLifetime(program, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME); - verifyRaLifetime(program, rdnssOptionPacket, RDNSS_LIFETIME); - verifyRaLifetime(program, routeInfoOptionPacket, ROUTE_LIFETIME); - verifyRaLifetime(program, dnsslOptionPacket, ROUTER_LIFETIME); - - apfFilter.shutdown(); - } - - /** - * Stage a file for testing, i.e. make it native accessible. Given a resource ID, - * copy that resource into the app's data directory and return the path to it. - */ - private String stageFile(int rawId) throws Exception { - File file = new File(InstrumentationRegistry.getContext().getFilesDir(), "staged_file"); - new File(file.getParent()).mkdirs(); - InputStream in = null; - OutputStream out = null; - try { - in = InstrumentationRegistry.getContext().getResources().openRawResource(rawId); - out = new FileOutputStream(file); - Streams.copy(in, out); - } finally { - if (in != null) in.close(); - if (out != null) out.close(); - } - return file.getAbsolutePath(); - } - - private static void put(ByteBuffer buffer, int position, byte[] bytes) { - final int original = buffer.position(); - buffer.position(position); - buffer.put(bytes); - buffer.position(original); - } - - @Test - public void testRaParsing() throws Exception { - final int maxRandomPacketSize = 512; - final Random r = new Random(); - MockIpClientCallback cb = new MockIpClientCallback(); - ApfConfiguration config = getDefaultConfig(); - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); - for (int i = 0; i < 1000; i++) { - byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)]; - r.nextBytes(packet); - try { - apfFilter.new Ra(packet, packet.length); - } catch (ApfFilter.InvalidRaException e) { - } catch (Exception e) { - throw new Exception("bad packet: " + HexDump.toHexString(packet), e); - } - } - } - - @Test - public void testRaProcessing() throws Exception { - final int maxRandomPacketSize = 512; - final Random r = new Random(); - MockIpClientCallback cb = new MockIpClientCallback(); - ApfConfiguration config = getDefaultConfig(); - config.multicastFilter = DROP_MULTICAST; - config.ieee802_3Filter = DROP_802_3_FRAMES; - TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog); - for (int i = 0; i < 1000; i++) { - byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)]; - r.nextBytes(packet); - try { - apfFilter.processRa(packet, packet.length); - } catch (Exception e) { - throw new Exception("bad packet: " + HexDump.toHexString(packet), e); - } - } - } - - /** - * Call the APF interpreter to run {@code program} on {@code packet} with persistent memory - * segment {@data} pretending the filter was installed {@code filter_age} seconds ago. - */ - private native static int apfSimulate(byte[] program, byte[] packet, byte[] data, - int filter_age); - - /** - * Compile a tcpdump human-readable filter (e.g. "icmp" or "tcp port 54") into a BPF - * prorgam and return a human-readable dump of the BPF program identical to "tcpdump -d". - */ - private native static String compileToBpf(String filter); - - /** - * Open packet capture file {@code pcap_filename} and filter the packets using tcpdump - * human-readable filter (e.g. "icmp" or "tcp port 54") compiled to a BPF program and - * at the same time using APF program {@code apf_program}. Return {@code true} if - * both APF and BPF programs filter out exactly the same packets. - */ - private native static boolean compareBpfApf(String filter, String pcap_filename, - byte[] apf_program); - - - /** - * Open packet capture file {@code pcapFilename} and run it through APF filter. Then - * checks whether all the packets are dropped and populates data[] {@code data} with - * the APF counters. - */ - private native static boolean dropsAllPackets(byte[] program, byte[] data, String pcapFilename); - - @Test - public void testBroadcastAddress() throws Exception { - assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 0)); - assertEqualsIp("0.0.0.0", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 32)); - assertEqualsIp("0.0.3.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 22)); - assertEqualsIp("0.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 8)); - - assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 0)); - assertEqualsIp("10.0.0.1", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 32)); - assertEqualsIp("10.0.0.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 24)); - assertEqualsIp("10.0.255.255", ApfFilter.ipv4BroadcastAddress(MOCK_IPV4_ADDR, 16)); - } - - public void assertEqualsIp(String expected, int got) throws Exception { - int want = bytesToBEInt(InetAddress.getByName(expected).getAddress()); - assertEquals(want, got); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/apf/Bpf2Apf.java b/packages/NetworkStack/tests/unit/src/android/net/apf/Bpf2Apf.java deleted file mode 100644 index 5d57cde22fb1..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/apf/Bpf2Apf.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2015 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.apf; - -import android.net.apf.ApfGenerator; -import android.net.apf.ApfGenerator.IllegalInstructionException; -import android.net.apf.ApfGenerator.Register; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -/** - * BPF to APF translator. - * - * Note: This is for testing purposes only and is not guaranteed to support - * translation of all BPF programs. - * - * Example usage: - * javac net/java/android/net/apf/ApfGenerator.java \ - * tests/servicestests/src/android/net/apf/Bpf2Apf.java - * sudo tcpdump -i em1 -d icmp | java -classpath tests/servicestests/src:net/java \ - * android.net.apf.Bpf2Apf - */ -public class Bpf2Apf { - private static int parseImm(String line, String arg) { - if (!arg.startsWith("#0x")) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - final long val_long = Long.parseLong(arg.substring(3), 16); - if (val_long < 0 || val_long > Long.parseLong("ffffffff", 16)) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - return new Long((val_long << 32) >> 32).intValue(); - } - - /** - * Convert a single line of "tcpdump -d" (human readable BPF program dump) {@code line} into - * APF instruction(s) and append them to {@code gen}. Here's an example line: - * (001) jeq #0x86dd jt 2 jf 7 - */ - private static void convertLine(String line, ApfGenerator gen) - throws IllegalInstructionException { - if (line.indexOf("(") != 0 || line.indexOf(")") != 4 || line.indexOf(" ") != 5) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - int label = Integer.parseInt(line.substring(1, 4)); - gen.defineLabel(Integer.toString(label)); - String opcode = line.substring(6, 10).trim(); - String arg = line.substring(15, Math.min(32, line.length())).trim(); - switch (opcode) { - case "ld": - case "ldh": - case "ldb": - case "ldx": - case "ldxb": - case "ldxh": - Register dest = opcode.contains("x") ? Register.R1 : Register.R0; - if (arg.equals("4*([14]&0xf)")) { - if (!opcode.equals("ldxb")) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - gen.addLoadFromMemory(dest, gen.IPV4_HEADER_SIZE_MEMORY_SLOT); - break; - } - if (arg.equals("#pktlen")) { - if (!opcode.equals("ld")) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - gen.addLoadFromMemory(dest, gen.PACKET_SIZE_MEMORY_SLOT); - break; - } - if (arg.startsWith("#0x")) { - if (!opcode.equals("ld")) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - gen.addLoadImmediate(dest, parseImm(line, arg)); - break; - } - if (arg.startsWith("M[")) { - if (!opcode.startsWith("ld")) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - int memory_slot = Integer.parseInt(arg.substring(2, arg.length() - 1)); - if (memory_slot < 0 || memory_slot >= gen.MEMORY_SLOTS || - // Disallow use of pre-filled slots as BPF programs might - // wrongfully assume they're initialized to 0. - (memory_slot >= gen.FIRST_PREFILLED_MEMORY_SLOT && - memory_slot <= gen.LAST_PREFILLED_MEMORY_SLOT)) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - gen.addLoadFromMemory(dest, memory_slot); - break; - } - if (arg.startsWith("[x + ")) { - int offset = Integer.parseInt(arg.substring(5, arg.length() - 1)); - switch (opcode) { - case "ld": - case "ldx": - gen.addLoad32Indexed(dest, offset); - break; - case "ldh": - case "ldxh": - gen.addLoad16Indexed(dest, offset); - break; - case "ldb": - case "ldxb": - gen.addLoad8Indexed(dest, offset); - break; - } - } else { - int offset = Integer.parseInt(arg.substring(1, arg.length() - 1)); - switch (opcode) { - case "ld": - case "ldx": - gen.addLoad32(dest, offset); - break; - case "ldh": - case "ldxh": - gen.addLoad16(dest, offset); - break; - case "ldb": - case "ldxb": - gen.addLoad8(dest, offset); - break; - } - } - break; - case "st": - case "stx": - Register src = opcode.contains("x") ? Register.R1 : Register.R0; - if (!arg.startsWith("M[")) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - int memory_slot = Integer.parseInt(arg.substring(2, arg.length() - 1)); - if (memory_slot < 0 || memory_slot >= gen.MEMORY_SLOTS || - // Disallow overwriting pre-filled slots - (memory_slot >= gen.FIRST_PREFILLED_MEMORY_SLOT && - memory_slot <= gen.LAST_PREFILLED_MEMORY_SLOT)) { - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - gen.addStoreToMemory(src, memory_slot); - break; - case "add": - case "and": - case "or": - case "sub": - if (arg.equals("x")) { - switch(opcode) { - case "add": - gen.addAddR1(); - break; - case "and": - gen.addAndR1(); - break; - case "or": - gen.addOrR1(); - break; - case "sub": - gen.addNeg(Register.R1); - gen.addAddR1(); - gen.addNeg(Register.R1); - break; - } - } else { - int imm = parseImm(line, arg); - switch(opcode) { - case "add": - gen.addAdd(imm); - break; - case "and": - gen.addAnd(imm); - break; - case "or": - gen.addOr(imm); - break; - case "sub": - gen.addAdd(-imm); - break; - } - } - break; - case "jeq": - case "jset": - case "jgt": - case "jge": - int val = 0; - boolean reg_compare; - if (arg.startsWith("x")) { - reg_compare = true; - } else { - reg_compare = false; - val = parseImm(line, arg); - } - int jt_offset = line.indexOf("jt"); - int jf_offset = line.indexOf("jf"); - String true_label = line.substring(jt_offset + 2, jf_offset).trim(); - String false_label = line.substring(jf_offset + 2).trim(); - boolean true_label_is_fallthrough = Integer.parseInt(true_label) == label + 1; - boolean false_label_is_fallthrough = Integer.parseInt(false_label) == label + 1; - if (true_label_is_fallthrough && false_label_is_fallthrough) - break; - switch (opcode) { - case "jeq": - if (!true_label_is_fallthrough) { - if (reg_compare) { - gen.addJumpIfR0EqualsR1(true_label); - } else { - gen.addJumpIfR0Equals(val, true_label); - } - } - if (!false_label_is_fallthrough) { - if (!true_label_is_fallthrough) { - gen.addJump(false_label); - } else if (reg_compare) { - gen.addJumpIfR0NotEqualsR1(false_label); - } else { - gen.addJumpIfR0NotEquals(val, false_label); - } - } - break; - case "jset": - if (reg_compare) { - gen.addJumpIfR0AnyBitsSetR1(true_label); - } else { - gen.addJumpIfR0AnyBitsSet(val, true_label); - } - if (!false_label_is_fallthrough) { - gen.addJump(false_label); - } - break; - case "jgt": - if (!true_label_is_fallthrough || - // We have no less-than-or-equal-to register to register - // comparison instruction, so in this case we'll jump - // around an unconditional jump. - (!false_label_is_fallthrough && reg_compare)) { - if (reg_compare) { - gen.addJumpIfR0GreaterThanR1(true_label); - } else { - gen.addJumpIfR0GreaterThan(val, true_label); - } - } - if (!false_label_is_fallthrough) { - if (!true_label_is_fallthrough || reg_compare) { - gen.addJump(false_label); - } else { - gen.addJumpIfR0LessThan(val + 1, false_label); - } - } - break; - case "jge": - if (!false_label_is_fallthrough || - // We have no greater-than-or-equal-to register to register - // comparison instruction, so in this case we'll jump - // around an unconditional jump. - (!true_label_is_fallthrough && reg_compare)) { - if (reg_compare) { - gen.addJumpIfR0LessThanR1(false_label); - } else { - gen.addJumpIfR0LessThan(val, false_label); - } - } - if (!true_label_is_fallthrough) { - if (!false_label_is_fallthrough || reg_compare) { - gen.addJump(true_label); - } else { - gen.addJumpIfR0GreaterThan(val - 1, true_label); - } - } - break; - } - break; - case "ret": - if (arg.equals("#0")) { - gen.addJump(gen.DROP_LABEL); - } else { - gen.addJump(gen.PASS_LABEL); - } - break; - case "tax": - gen.addMove(Register.R1); - break; - case "txa": - gen.addMove(Register.R0); - break; - default: - throw new IllegalArgumentException("Unhandled instruction: " + line); - } - } - - /** - * Convert the output of "tcpdump -d" (human readable BPF program dump) {@code bpf} into an APF - * program and return it. - */ - public static byte[] convert(String bpf) throws IllegalInstructionException { - ApfGenerator gen = new ApfGenerator(3); - for (String line : bpf.split("\\n")) convertLine(line, gen); - return gen.generate(); - } - - /** - * Convert the output of "tcpdump -d" (human readable BPF program dump) piped in stdin into an - * APF program and output it via stdout. - */ - public static void main(String[] args) throws Exception { - BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); - String line = null; - StringBuilder responseData = new StringBuilder(); - ApfGenerator gen = new ApfGenerator(3); - while ((line = in.readLine()) != null) convertLine(line, gen); - System.out.write(gen.generate()); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java b/packages/NetworkStack/tests/unit/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java deleted file mode 100644 index f948086ac79b..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/captiveportal/CaptivePortalProbeSpecTest.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.captiveportal; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.net.MalformedURLException; -import java.text.ParseException; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class CaptivePortalProbeSpecTest { - - @Test - public void testGetResult_Regex() throws MalformedURLException, ParseException { - // 2xx status or 404, with an empty (match everything) location regex - CaptivePortalProbeSpec statusRegexSpec = CaptivePortalProbeSpec.parseSpec( - "http://www.google.com@@/@@2[0-9]{2}|404@@/@@"); - - // 404, or 301/302 redirect to some HTTPS page under google.com - CaptivePortalProbeSpec redirectSpec = CaptivePortalProbeSpec.parseSpec( - "http://google.com@@/@@404|30[12]@@/@@https://([0-9a-z]+\\.)*google\\.com.*"); - - assertSuccess(statusRegexSpec.getResult(200, null)); - assertSuccess(statusRegexSpec.getResult(299, "qwer")); - assertSuccess(statusRegexSpec.getResult(404, null)); - assertSuccess(statusRegexSpec.getResult(404, "")); - - assertPortal(statusRegexSpec.getResult(300, null)); - assertPortal(statusRegexSpec.getResult(399, "qwer")); - assertPortal(statusRegexSpec.getResult(500, null)); - - assertSuccess(redirectSpec.getResult(404, null)); - assertSuccess(redirectSpec.getResult(404, "")); - assertSuccess(redirectSpec.getResult(301, "https://www.google.com")); - assertSuccess(redirectSpec.getResult(301, "https://www.google.com/test?q=3")); - assertSuccess(redirectSpec.getResult(302, "https://google.com/test?q=3")); - - assertPortal(redirectSpec.getResult(299, "https://google.com/test?q=3")); - assertPortal(redirectSpec.getResult(299, "")); - assertPortal(redirectSpec.getResult(499, null)); - assertPortal(redirectSpec.getResult(301, "http://login.portal.example.com/loginpage")); - assertPortal(redirectSpec.getResult(302, "http://www.google.com/test?q=3")); - } - - @Test(expected = ParseException.class) - public void testParseSpec_Empty() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec(""); - } - - @Test(expected = ParseException.class) - public void testParseSpec_Null() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec(null); - } - - @Test(expected = ParseException.class) - public void testParseSpec_MissingParts() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123"); - } - - @Test(expected = ParseException.class) - public void testParseSpec_TooManyParts() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@456@@/@@extra"); - } - - @Test(expected = ParseException.class) - public void testParseSpec_InvalidStatusRegex() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@unmatched(parenthesis@@/@@456"); - } - - @Test(expected = ParseException.class) - public void testParseSpec_InvalidLocationRegex() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("http://google.com/@@/@@123@@/@@unmatched[[]bracket"); - } - - @Test(expected = MalformedURLException.class) - public void testParseSpec_EmptyURL() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("@@/@@123@@/@@123"); - } - - @Test(expected = ParseException.class) - public void testParseSpec_NoParts() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("invalid"); - } - - @Test(expected = MalformedURLException.class) - public void testParseSpec_RegexInvalidUrl() throws MalformedURLException, ParseException { - CaptivePortalProbeSpec.parseSpec("notaurl@@/@@123@@/@@123"); - } - - @Test - public void testParseSpecOrNull_UsesSpec() { - final String specUrl = "http://google.com/probe"; - final String redirectUrl = "https://google.com/probe"; - CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull( - specUrl + "@@/@@302@@/@@" + redirectUrl); - assertEquals(specUrl, spec.getUrl().toString()); - - assertPortal(spec.getResult(302, "http://portal.example.com")); - assertSuccess(spec.getResult(302, redirectUrl)); - } - - @Test - public void testParseSpecOrNull_UsesFallback() throws MalformedURLException { - CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(null); - assertNull(spec); - - spec = CaptivePortalProbeSpec.parseSpecOrNull(""); - assertNull(spec); - - spec = CaptivePortalProbeSpec.parseSpecOrNull("@@/@@ @@/@@ @@/@@"); - assertNull(spec); - - spec = CaptivePortalProbeSpec.parseSpecOrNull("invalid@@/@@123@@/@@456"); - assertNull(spec); - } - - @Test - public void testParseSpecOrUseStatusCodeFallback_EmptySpec() throws MalformedURLException { - CaptivePortalProbeSpec spec = CaptivePortalProbeSpec.parseSpecOrNull(""); - assertNull(spec); - } - - private void assertIsStatusSpec(CaptivePortalProbeSpec spec) { - assertSuccess(spec.getResult(204, null)); - assertSuccess(spec.getResult(204, "1234")); - - assertPortal(spec.getResult(200, null)); - assertPortal(spec.getResult(301, null)); - assertPortal(spec.getResult(302, "1234")); - assertPortal(spec.getResult(399, "")); - - assertFailed(spec.getResult(404, null)); - assertFailed(spec.getResult(500, "1234")); - } - - private void assertPortal(CaptivePortalProbeResult result) { - assertTrue(result.isPortal()); - } - - private void assertSuccess(CaptivePortalProbeResult result) { - assertTrue(result.isSuccessful()); - } - - private void assertFailed(CaptivePortalProbeResult result) { - assertTrue(result.isFailed()); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java deleted file mode 100644 index 27d725540d34..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java +++ /dev/null @@ -1,544 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import static android.net.InetAddresses.parseNumericAddress; -import static android.net.dhcp.DhcpLease.HOSTNAME_NONE; -import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC; -import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC; - -import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.IpPrefix; -import android.net.MacAddress; -import android.net.dhcp.DhcpServer.Clock; -import android.net.util.SharedLog; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static java.lang.String.format; - -import java.net.Inet4Address; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DhcpLeaseRepositoryTest { - private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247"); - private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241"); - private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243"); - private static final MacAddress TEST_MAC_1 = MacAddress.fromBytes( - new byte[] { 5, 4, 3, 2, 1, 0 }); - private static final MacAddress TEST_MAC_2 = MacAddress.fromBytes( - new byte[] { 0, 1, 2, 3, 4, 5 }); - private static final MacAddress TEST_MAC_3 = MacAddress.fromBytes( - new byte[] { 0, 1, 2, 3, 4, 6 }); - private static final Inet4Address TEST_INETADDR_1 = parseAddr4("192.168.42.248"); - private static final Inet4Address TEST_INETADDR_2 = parseAddr4("192.168.42.249"); - private static final String TEST_HOSTNAME_1 = "hostname1"; - private static final String TEST_HOSTNAME_2 = "hostname2"; - private static final IpPrefix TEST_IP_PREFIX = new IpPrefix(TEST_SERVER_ADDR, 22); - private static final long TEST_TIME = 100L; - private static final int TEST_LEASE_TIME_MS = 3_600_000; - private static final Set<Inet4Address> TEST_EXCL_SET = - Collections.unmodifiableSet(new HashSet<>(Arrays.asList( - TEST_SERVER_ADDR, TEST_DEF_ROUTER, TEST_RESERVED_ADDR))); - - @NonNull - private SharedLog mLog; - @NonNull @Mock - private Clock mClock; - @NonNull - private DhcpLeaseRepository mRepo; - - private static Inet4Address parseAddr4(String inet4Addr) { - return (Inet4Address) parseNumericAddress(inet4Addr); - } - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mLog = new SharedLog("DhcpLeaseRepositoryTest"); - when(mClock.elapsedRealtime()).thenReturn(TEST_TIME); - mRepo = new DhcpLeaseRepository( - TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, mLog, mClock); - } - - /** - * Request a number of addresses through offer/request. Useful to test address exhaustion. - * @param nAddr Number of addresses to request. - */ - private void requestAddresses(byte nAddr) throws Exception { - final HashSet<Inet4Address> addrs = new HashSet<>(); - byte[] hwAddrBytes = new byte[] { 8, 4, 3, 2, 1, 0 }; - for (byte i = 0; i < nAddr; i++) { - hwAddrBytes[5] = i; - MacAddress newMac = MacAddress.fromBytes(hwAddrBytes); - final String hostname = "host_" + i; - final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, newMac, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname); - - assertNotNull(lease); - assertEquals(newMac, lease.getHwAddr()); - assertEquals(hostname, lease.getHostname()); - assertTrue(format("Duplicate address allocated: %s in %s", lease.getNetAddr(), addrs), - addrs.add(lease.getNetAddr())); - - requestLeaseSelecting(newMac, lease.getNetAddr(), hostname); - } - } - - @Test - public void testAddressExhaustion() throws Exception { - // Use a /28 to quickly run out of addresses - mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS); - - // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses - requestAddresses((byte) 11); - - try { - mRepo.getOffer(null, TEST_MAC_2, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - fail("Should be out of addresses"); - } catch (DhcpLeaseRepository.OutOfAddressesException e) { - // Expected - } - } - - @Test - public void testUpdateParams_LeaseCleanup() throws Exception { - // Inside /28: - final Inet4Address reqAddrIn28 = parseAddr4("192.168.42.242"); - final Inet4Address declinedAddrIn28 = parseAddr4("192.168.42.245"); - - // Inside /28, but not available there (first address of the range) - final Inet4Address declinedFirstAddrIn28 = parseAddr4("192.168.42.240"); - - final DhcpLease reqAddrIn28Lease = requestLeaseSelecting(TEST_MAC_1, reqAddrIn28); - mRepo.markLeaseDeclined(declinedAddrIn28); - mRepo.markLeaseDeclined(declinedFirstAddrIn28); - - // Inside /22, but outside /28: - final Inet4Address reqAddrIn22 = parseAddr4("192.168.42.3"); - final Inet4Address declinedAddrIn22 = parseAddr4("192.168.42.4"); - - final DhcpLease reqAddrIn22Lease = requestLeaseSelecting(TEST_MAC_3, reqAddrIn22); - mRepo.markLeaseDeclined(declinedAddrIn22); - - // Address that will be reserved in the updateParams call below - final Inet4Address reservedAddr = parseAddr4("192.168.42.244"); - final DhcpLease reservedAddrLease = requestLeaseSelecting(TEST_MAC_2, reservedAddr); - - // Update from /22 to /28 and add another reserved address - Set<Inet4Address> newReserved = new HashSet<>(TEST_EXCL_SET); - newReserved.add(reservedAddr); - mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS); - - assertHasLease(reqAddrIn28Lease); - assertDeclined(declinedAddrIn28); - - assertNotDeclined(declinedFirstAddrIn28); - - assertNoLease(reqAddrIn22Lease); - assertNotDeclined(declinedAddrIn22); - - assertNoLease(reservedAddrLease); - } - - @Test - public void testGetOffer_StableAddress() throws Exception { - for (final MacAddress macAddr : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) { - final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - - // Same lease is offered twice - final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertEquals(lease, newLease); - } - } - - @Test - public void testUpdateParams_UsesNewPrefix() throws Exception { - final IpPrefix newPrefix = new IpPrefix(parseAddr4("192.168.123.0"), 24); - mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS); - - DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertTrue(newPrefix.contains(lease.getNetAddr())); - } - - @Test - public void testGetOffer_ExistingLease() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, TEST_HOSTNAME_1); - - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertEquals(TEST_INETADDR_1, offer.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, offer.getHostname()); - } - - @Test - public void testGetOffer_ClientIdHasExistingLease() throws Exception { - final byte[] clientId = new byte[] { 1, 2 }; - mRepo.requestLease(clientId, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */, - IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, - TEST_HOSTNAME_1); - - // Different MAC, but same clientId - DhcpLease offer = mRepo.getOffer(clientId, TEST_MAC_2, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertEquals(TEST_INETADDR_1, offer.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, offer.getHostname()); - } - - @Test - public void testGetOffer_DifferentClientId() throws Exception { - final byte[] clientId1 = new byte[] { 1, 2 }; - final byte[] clientId2 = new byte[] { 3, 4 }; - mRepo.requestLease(clientId1, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */, - IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, - TEST_HOSTNAME_1); - - // Same MAC, different client ID - DhcpLease offer = mRepo.getOffer(clientId2, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - // Obtains a different address - assertNotEquals(TEST_INETADDR_1, offer.getNetAddr()); - assertEquals(HOSTNAME_NONE, offer.getHostname()); - assertEquals(TEST_MAC_1, offer.getHwAddr()); - } - - @Test - public void testGetOffer_RequestedAddress() throws Exception { - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */, - TEST_INETADDR_1 /* reqAddr */, TEST_HOSTNAME_1); - assertEquals(TEST_INETADDR_1, offer.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, offer.getHostname()); - } - - @Test - public void testGetOffer_RequestedAddressInUse() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, IPV4_ADDR_ANY /* relayAddr */, - TEST_INETADDR_1 /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(TEST_INETADDR_1, offer.getNetAddr()); - } - - @Test - public void testGetOffer_RequestedAddressReserved() throws Exception { - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */, - TEST_RESERVED_ADDR /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(TEST_RESERVED_ADDR, offer.getNetAddr()); - } - - @Test - public void testGetOffer_RequestedAddressInvalid() throws Exception { - final Inet4Address invalidAddr = parseAddr4("192.168.42.0"); - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */, - invalidAddr /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(invalidAddr, offer.getNetAddr()); - } - - @Test - public void testGetOffer_RequestedAddressOutsideSubnet() throws Exception { - final Inet4Address invalidAddr = parseAddr4("192.168.254.2"); - DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */, - invalidAddr /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(invalidAddr, offer.getNetAddr()); - } - - @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class) - public void testGetOffer_RelayInInvalidSubnet() throws Exception { - mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, parseAddr4("192.168.254.2") /* relayAddr */, - INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - } - - @Test - public void testRequestLease_SelectingTwice() throws Exception { - final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, - TEST_HOSTNAME_1); - - // Second request from same client for a different address - final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_2, - TEST_HOSTNAME_2); - - assertEquals(TEST_INETADDR_1, lease1.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, lease1.getHostname()); - - assertEquals(TEST_INETADDR_2, lease2.getNetAddr()); - assertEquals(TEST_HOSTNAME_2, lease2.getHostname()); - - // First address freed when client requested a different one: another client can request it - final DhcpLease lease3 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1, HOSTNAME_NONE); - assertEquals(TEST_INETADDR_1, lease3.getNetAddr()); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_SelectingInvalid() throws Exception { - requestLeaseSelecting(TEST_MAC_1, parseAddr4("192.168.254.5")); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_SelectingInUse() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_SelectingReserved() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_RESERVED_ADDR); - } - - @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class) - public void testRequestLease_SelectingRelayInInvalidSubnet() throws Exception { - mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */, - parseAddr4("192.168.128.1") /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, - true /* sidSet */, HOSTNAME_NONE); - } - - @Test - public void testRequestLease_InitReboot() throws Exception { - // Request address once - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - - final long newTime = TEST_TIME + 100; - when(mClock.elapsedRealtime()).thenReturn(newTime); - - // init-reboot (sidSet == false): verify configuration - final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_1); - assertEquals(TEST_INETADDR_1, lease.getNetAddr()); - assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime()); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_InitRebootWrongAddr() throws Exception { - // Request address once - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - // init-reboot with different requested address - requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2); - } - - @Test - public void testRequestLease_InitRebootUnknownAddr() throws Exception { - // init-reboot with unknown requested address - final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2); - // RFC2131 says we should not reply to accommodate other servers, but since we are - // authoritative we allow creating the lease to avoid issues with lost lease DB (same as - // dnsmasq behavior) - assertEquals(TEST_INETADDR_2, lease.getNetAddr()); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_InitRebootWrongSubnet() throws Exception { - requestLeaseInitReboot(TEST_MAC_1, parseAddr4("192.168.254.2")); - } - - @Test - public void testRequestLease_Renewing() throws Exception { - requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - - final long newTime = TEST_TIME + 100; - when(mClock.elapsedRealtime()).thenReturn(newTime); - - final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1); - - assertEquals(TEST_INETADDR_1, lease.getNetAddr()); - assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime()); - } - - @Test - public void testRequestLease_RenewingUnknownAddr() throws Exception { - final long newTime = TEST_TIME + 100; - when(mClock.elapsedRealtime()).thenReturn(newTime); - final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1); - // Allows renewing an unknown address if available - assertEquals(TEST_INETADDR_1, lease.getNetAddr()); - assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime()); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_RenewingAddrInUse() throws Exception { - requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1); - requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1); - } - - @Test(expected = DhcpLeaseRepository.InvalidAddressException.class) - public void testRequestLease_RenewingInvalidAddr() throws Exception { - requestLeaseRenewing(TEST_MAC_1, parseAddr4("192.168.254.2")); - } - - @Test - public void testReleaseLease() throws Exception { - final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1); - - assertHasLease(lease1); - assertTrue(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1)); - assertNoLease(lease1); - - final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1); - assertEquals(TEST_INETADDR_1, lease2.getNetAddr()); - } - - @Test - public void testReleaseLease_UnknownLease() { - assertFalse(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1)); - } - - @Test - public void testReleaseLease_StableOffer() throws Exception { - for (MacAddress mac : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) { - final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, mac, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - - requestLeaseSelecting(mac, lease.getNetAddr()); - mRepo.releaseLease(CLIENTID_UNSPEC, mac, lease.getNetAddr()); - - // Same lease is offered after it was released - final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, mac, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertEquals(lease.getNetAddr(), newLease.getNetAddr()); - } - } - - @Test - public void testMarkLeaseDeclined() throws Exception { - final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - - mRepo.markLeaseDeclined(lease.getNetAddr()); - - // Same lease is not offered again - final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - assertNotEquals(lease.getNetAddr(), newLease.getNetAddr()); - } - - @Test - public void testMarkLeaseDeclined_UsedIfOutOfAddresses() throws Exception { - // Use a /28 to quickly run out of addresses - mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS); - - mRepo.markLeaseDeclined(TEST_INETADDR_1); - mRepo.markLeaseDeclined(TEST_INETADDR_2); - - // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses - requestAddresses((byte) 9); - - // Last 2 addresses: addresses marked declined should be used - final DhcpLease firstLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1); - requestLeaseSelecting(TEST_MAC_1, firstLease.getNetAddr()); - - final DhcpLease secondLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, - IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2); - requestLeaseSelecting(TEST_MAC_2, secondLease.getNetAddr()); - - // Now out of addresses - try { - mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, IPV4_ADDR_ANY /* relayAddr */, - INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE); - fail("Repository should be out of addresses and throw"); - } catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ } - - assertEquals(TEST_INETADDR_1, firstLease.getNetAddr()); - assertEquals(TEST_HOSTNAME_1, firstLease.getHostname()); - assertEquals(TEST_INETADDR_2, secondLease.getNetAddr()); - assertEquals(TEST_HOSTNAME_2, secondLease.getHostname()); - } - - private DhcpLease requestLease(@NonNull MacAddress macAddr, @NonNull Inet4Address clientAddr, - @Nullable Inet4Address reqAddr, @Nullable String hostname, boolean sidSet) - throws DhcpLeaseRepository.DhcpLeaseException { - return mRepo.requestLease(CLIENTID_UNSPEC, macAddr, clientAddr, - IPV4_ADDR_ANY /* relayAddr */, - reqAddr, sidSet, hostname); - } - - /** - * Request a lease simulating a client in the SELECTING state. - */ - private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr, - @NonNull Inet4Address reqAddr, @Nullable String hostname) - throws DhcpLeaseRepository.DhcpLeaseException { - return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, hostname, - true /* sidSet */); - } - - /** - * Request a lease simulating a client in the SELECTING state. - */ - private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr, - @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException { - return requestLeaseSelecting(macAddr, reqAddr, HOSTNAME_NONE); - } - - /** - * Request a lease simulating a client in the INIT-REBOOT state. - */ - private DhcpLease requestLeaseInitReboot(@NonNull MacAddress macAddr, - @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException { - return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, HOSTNAME_NONE, - false /* sidSet */); - } - - /** - * Request a lease simulating a client in the RENEWING state. - */ - private DhcpLease requestLeaseRenewing(@NonNull MacAddress macAddr, - @NonNull Inet4Address clientAddr) throws DhcpLeaseRepository.DhcpLeaseException { - // Renewing: clientAddr filled in, no reqAddr - return requestLease(macAddr, clientAddr, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE, - true /* sidSet */); - } - - private void assertNoLease(DhcpLease lease) { - assertFalse("Leases contain " + lease, mRepo.getCommittedLeases().contains(lease)); - } - - private void assertHasLease(DhcpLease lease) { - assertTrue("Leases do not contain " + lease, mRepo.getCommittedLeases().contains(lease)); - } - - private void assertNotDeclined(Inet4Address addr) { - assertFalse("Address is declined: " + addr, mRepo.getDeclinedAddresses().contains(addr)); - } - - private void assertDeclined(Inet4Address addr) { - assertTrue("Address is not declined: " + addr, mRepo.getDeclinedAddresses().contains(addr)); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpPacketTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpPacketTest.java deleted file mode 100644 index a30d3e492406..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpPacketTest.java +++ /dev/null @@ -1,1117 +0,0 @@ -/* - * Copyright (C) 2015 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.dhcp; - -import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS; -import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER; -import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME; -import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME; -import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_ACK; -import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_OFFER; -import static android.net.dhcp.DhcpPacket.DHCP_MTU; -import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME; -import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME; -import static android.net.dhcp.DhcpPacket.DHCP_ROUTER; -import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK; -import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO; -import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP; -import static android.net.dhcp.DhcpPacket.ENCAP_L2; -import static android.net.dhcp.DhcpPacket.ENCAP_L3; -import static android.net.dhcp.DhcpPacket.INADDR_ANY; -import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; -import static android.net.dhcp.DhcpPacket.ParseException; -import static android.net.shared.Inet4AddressUtils.getBroadcastAddress; -import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address; - -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 android.annotation.Nullable; -import android.net.DhcpResults; -import android.net.LinkAddress; -import android.net.NetworkUtils; -import android.net.metrics.DhcpErrorEvent; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.HexDump; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.ByteArrayOutputStream; -import java.net.Inet4Address; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Random; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DhcpPacketTest { - - private static final Inet4Address SERVER_ADDR = v4Address("192.0.2.1"); - private static final Inet4Address CLIENT_ADDR = v4Address("192.0.2.234"); - private static final int PREFIX_LENGTH = 22; - private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH); - private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress( - SERVER_ADDR, PREFIX_LENGTH); - private static final String HOSTNAME = "testhostname"; - private static final short MTU = 1500; - // Use our own empty address instead of IPV4_ADDR_ANY or INADDR_ANY to ensure that the code - // doesn't use == instead of equals when comparing addresses. - private static final Inet4Address ANY = v4Address("0.0.0.0"); - - private static final byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; - - private static final Inet4Address v4Address(String addrString) throws IllegalArgumentException { - return (Inet4Address) NetworkUtils.numericToInetAddress(addrString); - } - - @Before - public void setUp() { - DhcpPacket.testOverrideVendorId = "android-dhcp-???"; - DhcpPacket.testOverrideHostname = "android-01234567890abcde"; - } - - class TestDhcpPacket extends DhcpPacket { - private byte mType; - // TODO: Make this a map of option numbers to bytes instead. - private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes; - - public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) { - super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY, - CLIENT_MAC, true); - mType = type; - } - - public TestDhcpPacket(byte type) { - this(type, INADDR_ANY, CLIENT_ADDR); - } - - public TestDhcpPacket setDomainBytes(byte[] domainBytes) { - mDomainBytes = domainBytes; - return this; - } - - public TestDhcpPacket setVendorInfoBytes(byte[] vendorInfoBytes) { - mVendorInfoBytes = vendorInfoBytes; - return this; - } - - public TestDhcpPacket setLeaseTimeBytes(byte[] leaseTimeBytes) { - mLeaseTimeBytes = leaseTimeBytes; - return this; - } - - public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) { - mNetmaskBytes = netmaskBytes; - return this; - } - - public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) { - ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH); - fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR, - DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false); - return result; - } - - public void finishPacket(ByteBuffer buffer) { - addTlv(buffer, DHCP_MESSAGE_TYPE, mType); - if (mDomainBytes != null) { - addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes); - } - if (mVendorInfoBytes != null) { - addTlv(buffer, DHCP_VENDOR_INFO, mVendorInfoBytes); - } - if (mLeaseTimeBytes != null) { - addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes); - } - if (mNetmaskBytes != null) { - addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes); - } - addTlvEnd(buffer); - } - - // Convenience method. - public ByteBuffer build() { - // ENCAP_BOOTP packets don't contain ports, so just pass in 0. - ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0); - pkt.flip(); - return pkt; - } - } - - private void assertDomainAndVendorInfoParses( - String expectedDomain, byte[] domainBytes, - String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception { - ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER) - .setDomainBytes(domainBytes) - .setVendorInfoBytes(vendorInfoBytes) - .build(); - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); - assertEquals(expectedDomain, offerPacket.mDomainName); - assertEquals(expectedVendorInfo, offerPacket.mVendorInfo); - } - - @Test - public void testDomainName() throws Exception { - byte[] nullByte = new byte[] { 0x00 }; - byte[] twoNullBytes = new byte[] { 0x00, 0x00 }; - byte[] nonNullDomain = new byte[] { - (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l' - }; - byte[] trailingNullDomain = new byte[] { - (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00 - }; - byte[] embeddedNullsDomain = new byte[] { - (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l' - }; - byte[] metered = "ANDROID_METERED".getBytes("US-ASCII"); - - byte[] meteredEmbeddedNull = metered.clone(); - meteredEmbeddedNull[7] = (char) 0; - - byte[] meteredTrailingNull = metered.clone(); - meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0; - - assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte); - assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes); - assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered); - assertDomainAndVendorInfoParses("goo", embeddedNullsDomain, - "ANDROID\u0000METERED", meteredEmbeddedNull); - assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain, - "ANDROID_METERE\u0000", meteredTrailingNull); - } - - private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime, - long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception { - TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER); - if (leaseTimeBytes != null) { - testPacket.setLeaseTimeBytes(leaseTimeBytes); - } - ByteBuffer packet = testPacket.build(); - DhcpPacket offerPacket = null; - - if (!expectValid) { - try { - offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); - fail("Invalid packet parsed successfully: " + offerPacket); - } catch (ParseException expected) { - } - return; - } - - offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); - assertNotNull(offerPacket); - assertEquals(rawLeaseTime, offerPacket.mLeaseTime); - DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash. - assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis()); - } - - @Test - public void testLeaseTime() throws Exception { - byte[] noLease = null; - byte[] tooShortLease = new byte[] { 0x00, 0x00 }; - byte[] tooLongLease = new byte[] { 0x00, 0x00, 0x00, 60, 0x01 }; - byte[] zeroLease = new byte[] { 0x00, 0x00, 0x00, 0x00 }; - byte[] tenSecondLease = new byte[] { 0x00, 0x00, 0x00, 10 }; - byte[] oneMinuteLease = new byte[] { 0x00, 0x00, 0x00, 60 }; - byte[] fiveMinuteLease = new byte[] { 0x00, 0x00, 0x01, 0x2c }; - byte[] oneDayLease = new byte[] { 0x00, 0x01, 0x51, (byte) 0x80 }; - byte[] maxIntPlusOneLease = new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 }; - byte[] infiniteLease = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; - - assertLeaseTimeParses(true, null, 0, noLease); - assertLeaseTimeParses(false, null, 0, tooShortLease); - assertLeaseTimeParses(false, null, 0, tooLongLease); - assertLeaseTimeParses(true, 0, 60 * 1000, zeroLease); - assertLeaseTimeParses(true, 10, 60 * 1000, tenSecondLease); - assertLeaseTimeParses(true, 60, 60 * 1000, oneMinuteLease); - assertLeaseTimeParses(true, 300, 300 * 1000, fiveMinuteLease); - assertLeaseTimeParses(true, 86400, 86400 * 1000, oneDayLease); - assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease); - assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease); - } - - private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp, - byte[] netmaskBytes) throws Exception { - checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes); - checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes); - } - - private void checkIpAddress(String expected, byte type, - Inet4Address clientIp, Inet4Address yourIp, - byte[] netmaskBytes) throws Exception { - ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp) - .setNetmaskBytes(netmaskBytes) - .build(); - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); - DhcpResults results = offerPacket.toDhcpResults(); - - if (expected != null) { - LinkAddress expectedAddress = new LinkAddress(expected); - assertEquals(expectedAddress, results.ipAddress); - } else { - assertNull(results); - } - } - - @Test - public void testIpAddress() throws Exception { - byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 }; - byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 }; - byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 }; - Inet4Address example1 = v4Address("192.0.2.1"); - Inet4Address example2 = v4Address("192.0.2.43"); - - // A packet without any addresses is not valid. - checkIpAddress(null, ANY, ANY, slash24Netmask); - - // ClientIP is used iff YourIP is not present. - checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask); - checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask); - checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask); - - // Invalid netmasks are ignored. - checkIpAddress(null, example2, ANY, invalidNetmask); - - // If there is no netmask, implicit netmasks are used. - checkIpAddress("192.0.2.43/24", ANY, example2, null); - } - - private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString, - String domains, String serverAddress, String serverHostName, String vendorInfo, - int leaseDuration, boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) - throws Exception { - assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress); - assertEquals(v4Address(gateway), dhcpResults.gateway); - - String[] dnsServerStrings = dnsServersString.split(","); - ArrayList dnsServers = new ArrayList(); - for (String dnsServerString : dnsServerStrings) { - dnsServers.add(v4Address(dnsServerString)); - } - assertEquals(dnsServers, dhcpResults.dnsServers); - - assertEquals(domains, dhcpResults.domains); - assertEquals(v4Address(serverAddress), dhcpResults.serverAddress); - assertEquals(serverHostName, dhcpResults.serverHostName); - assertEquals(vendorInfo, dhcpResults.vendorInfo); - assertEquals(leaseDuration, dhcpResults.leaseDuration); - assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint()); - assertEquals(mtu, dhcpResults.mtu); - } - - @Test - public void testOffer1() throws Exception { - // TODO: Turn all of these into golden files. This will probably require using - // androidx.test.InstrumentationRegistry for obtaining a Context object - // to read such golden files, along with an appropriate Android.mk. - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // IP header. - "451001480000000080118849c0a89003c0a89ff7" + - // UDP header. - "004300440134dcfa" + - // BOOTP header. - "02010600c997a63b0000000000000000c0a89ff70000000000000000" + - // MAC address. - "30766ff2a90c00000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options - "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" + - "3a0400000e103b040000189cff00000000000000000000")); - // CHECKSTYLE:ON Generated code - - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); - assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null. - DhcpResults dhcpResults = offerPacket.toDhcpResults(); - assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4", - null, "192.168.144.3", "", null, 7200, false, 0, dhcpResults); - } - - @Test - public void testOffer2() throws Exception { - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // IP header. - "450001518d0600004011144dc0a82b01c0a82bf7" + - // UDP header. - "00430044013d9ac7" + - // BOOTP header. - "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" + - // MAC address. - "30766ff2a90c00000000000000000000" + - // Server name ("dhcp.android.com" plus invalid "AAAA" after null terminator). - "646863702e616e64726f69642e636f6d00000000000000000000000000000000" + - "0000000000004141414100000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options - "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" + - "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff")); - // CHECKSTYLE:ON Generated code - - assertEquals(337, packet.limit()); - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); - assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null. - DhcpResults dhcpResults = offerPacket.toDhcpResults(); - assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1", - null, "192.168.43.1", "dhcp.android.com", "ANDROID_METERED", 3600, true, 0, - dhcpResults); - assertTrue(dhcpResults.hasMeteredHint()); - } - - @Test - public void testBadIpPacket() throws Exception { - final byte[] packet = HexDump.hexStringToByteArray( - // IP header. - "450001518d0600004011144dc0a82b01c0a82bf7"); - - try { - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3); - } catch (DhcpPacket.ParseException expected) { - assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode); - return; - } - fail("Dhcp packet parsing should have failed"); - } - - @Test - public void testBadDhcpPacket() throws Exception { - final byte[] packet = HexDump.hexStringToByteArray( - // IP header. - "450001518d0600004011144dc0a82b01c0a82bf7" + - // UDP header. - "00430044013d9ac7" + - // BOOTP header. - "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000"); - - try { - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3); - } catch (DhcpPacket.ParseException expected) { - assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode); - return; - } - fail("Dhcp packet parsing should have failed"); - } - - @Test - public void testBadTruncatedOffer() throws Exception { - final byte[] packet = HexDump.hexStringToByteArray( - // IP header. - "450001518d0600004011144dc0a82b01c0a82bf7" + - // UDP header. - "00430044013d9ac7" + - // BOOTP header. - "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" + - // MAC address. - "30766ff2a90c00000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File, missing one byte - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "00000000000000000000000000000000000000000000000000000000000000"); - - try { - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3); - } catch (DhcpPacket.ParseException expected) { - assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode); - return; - } - fail("Dhcp packet parsing should have failed"); - } - - @Test - public void testBadOfferWithoutACookie() throws Exception { - final byte[] packet = HexDump.hexStringToByteArray( - // IP header. - "450001518d0600004011144dc0a82b01c0a82bf7" + - // UDP header. - "00430044013d9ac7" + - // BOOTP header. - "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" + - // MAC address. - "30766ff2a90c00000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" - // No options - ); - - try { - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3); - } catch (DhcpPacket.ParseException expected) { - assertDhcpErrorCodes(DhcpErrorEvent.DHCP_NO_COOKIE, expected.errorCode); - return; - } - fail("Dhcp packet parsing should have failed"); - } - - @Test - public void testOfferWithBadCookie() throws Exception { - final byte[] packet = HexDump.hexStringToByteArray( - // IP header. - "450001518d0600004011144dc0a82b01c0a82bf7" + - // UDP header. - "00430044013d9ac7" + - // BOOTP header. - "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" + - // MAC address. - "30766ff2a90c00000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Bad cookie - "DEADBEEF3501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" + - "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"); - - try { - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3); - } catch (DhcpPacket.ParseException expected) { - assertDhcpErrorCodes(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE, expected.errorCode); - return; - } - fail("Dhcp packet parsing should have failed"); - } - - private void assertDhcpErrorCodes(int expected, int got) { - assertEquals(Integer.toHexString(expected), Integer.toHexString(got)); - } - - @Test - public void testTruncatedOfferPackets() throws Exception { - final byte[] packet = HexDump.hexStringToByteArray( - // IP header. - "450001518d0600004011144dc0a82b01c0a82bf7" + - // UDP header. - "00430044013d9ac7" + - // BOOTP header. - "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" + - // MAC address. - "30766ff2a90c00000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options - "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" + - "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"); - - for (int len = 0; len < packet.length; len++) { - try { - DhcpPacket.decodeFullPacket(packet, len, ENCAP_L3); - } catch (ParseException e) { - if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) { - fail(String.format("bad truncated packet of length %d", len)); - } - } - } - } - - @Test - public void testRandomPackets() throws Exception { - final int maxRandomPacketSize = 512; - final Random r = new Random(); - for (int i = 0; i < 10000; i++) { - byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)]; - r.nextBytes(packet); - try { - DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3); - } catch (ParseException e) { - if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) { - fail("bad packet: " + HexDump.toHexString(packet)); - } - } - } - } - - private byte[] mtuBytes(int mtu) { - // 0x1a02: option 26, length 2. 0xff: no more options. - if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) { - throw new IllegalArgumentException( - String.format("Invalid MTU %d, must be 16-bit unsigned", mtu)); - } - String hexString = String.format("1a02%04xff", mtu); - return HexDump.hexStringToByteArray(hexString); - } - - private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception { - if (mtuBytes != null) { - packet.position(packet.capacity() - mtuBytes.length); - packet.put(mtuBytes); - packet.clear(); - } - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); - assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null. - DhcpResults dhcpResults = offerPacket.toDhcpResults(); - assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4", - null, "192.168.144.3", "", null, 7200, false, expectedMtu, dhcpResults); - } - - @Test - public void testMtu() throws Exception { - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // IP header. - "451001480000000080118849c0a89003c0a89ff7" + - // UDP header. - "004300440134dcfa" + - // BOOTP header. - "02010600c997a63b0000000000000000c0a89ff70000000000000000" + - // MAC address. - "30766ff2a90c00000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options - "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" + - "3a0400000e103b040000189cff00000000")); - // CHECKSTYLE:ON Generated code - - checkMtu(packet, 0, null); - checkMtu(packet, 0, mtuBytes(1501)); - checkMtu(packet, 1500, mtuBytes(1500)); - checkMtu(packet, 1499, mtuBytes(1499)); - checkMtu(packet, 1280, mtuBytes(1280)); - checkMtu(packet, 0, mtuBytes(1279)); - checkMtu(packet, 0, mtuBytes(576)); - checkMtu(packet, 0, mtuBytes(68)); - checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE)); - checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3)); - checkMtu(packet, 0, mtuBytes(-1)); - } - - @Test - public void testBadHwaddrLength() throws Exception { - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // IP header. - "450001518d0600004011144dc0a82b01c0a82bf7" + - // UDP header. - "00430044013d9ac7" + - // BOOTP header. - "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" + - // MAC address. - "30766ff2a90c00000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options - "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" + - "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff")); - // CHECKSTYLE:ON Generated code - String expectedClientMac = "30766FF2A90C"; - - final int hwAddrLenOffset = 20 + 8 + 2; - assertEquals(6, packet.get(hwAddrLenOffset)); - - // Expect the expected. - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); - assertNotNull(offerPacket); - assertEquals(6, offerPacket.getClientMac().length); - assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac())); - - // Reduce the hardware address length and verify that it shortens the client MAC. - packet.flip(); - packet.put(hwAddrLenOffset, (byte) 5); - offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); - assertNotNull(offerPacket); - assertEquals(5, offerPacket.getClientMac().length); - assertEquals(expectedClientMac.substring(0, 10), - HexDump.toHexString(offerPacket.getClientMac())); - - packet.flip(); - packet.put(hwAddrLenOffset, (byte) 3); - offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); - assertNotNull(offerPacket); - assertEquals(3, offerPacket.getClientMac().length); - assertEquals(expectedClientMac.substring(0, 6), - HexDump.toHexString(offerPacket.getClientMac())); - - // Set the the hardware address length to 0xff and verify that we a) don't treat it as -1 - // and crash, and b) hardcode it to 6. - packet.flip(); - packet.put(hwAddrLenOffset, (byte) -1); - offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); - assertNotNull(offerPacket); - assertEquals(6, offerPacket.getClientMac().length); - assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac())); - - // Set the the hardware address length to a positive invalid value (> 16) and verify that we - // hardcode it to 6. - packet.flip(); - packet.put(hwAddrLenOffset, (byte) 17); - offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); - assertNotNull(offerPacket); - assertEquals(6, offerPacket.getClientMac().length); - assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac())); - } - - @Test - public void testPadAndOverloadedOptionsOffer() throws Exception { - // A packet observed in the real world that is interesting for two reasons: - // - // 1. It uses pad bytes, which we previously didn't support correctly. - // 2. It uses DHCP option overloading, which we don't currently support (but it doesn't - // store any information in the overloaded fields). - // - // For now, we just check that it parses correctly. - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // Ethernet header. - "b4cef6000000e80462236e300800" + - // IP header. - "4500014c00000000ff11741701010101ac119876" + - // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). - "004300440138ae5a" + - // BOOTP header. - "020106000fa0059f0000000000000000ac1198760000000000000000" + - // MAC address. - "b4cef600000000000000000000000000" + - // Server name. - "ff00000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "ff00000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options - "638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" + - "0000000000000000000000000000000000000000000000ff000000")); - // CHECKSTYLE:ON Generated code - - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); - assertTrue(offerPacket instanceof DhcpOfferPacket); - DhcpResults dhcpResults = offerPacket.toDhcpResults(); - assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1", - null, "1.1.1.1", "", null, 43200, false, 0, dhcpResults); - } - - @Test - public void testBug2111() throws Exception { - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // IP header. - "4500014c00000000ff119beac3eaf3880a3f5d04" + - // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). - "0043004401387464" + - // BOOTP header. - "0201060002554812000a0000000000000a3f5d040000000000000000" + - // MAC address. - "00904c00000000000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options. - "638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" + - "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000")); - // CHECKSTYLE:ON Generated code - - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3); - assertTrue(offerPacket instanceof DhcpOfferPacket); - DhcpResults dhcpResults = offerPacket.toDhcpResults(); - assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2", - "domain123.co.uk", "192.0.2.254", "", null, 49094, false, 0, dhcpResults); - } - - @Test - public void testBug2136() throws Exception { - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // Ethernet header. - "bcf5ac000000d0c7890000000800" + - // IP header. - "4500014c00000000ff119beac3eaf3880a3f5d04" + - // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). - "0043004401387574" + - // BOOTP header. - "0201060163339a3000050000000000000a209ecd0000000000000000" + - // MAC address. - "bcf5ac00000000000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options. - "6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" + - "0f0b6c616e63732e61632e756b000000000000000000ff00000000")); - // CHECKSTYLE:ON Generated code - - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); - assertTrue(offerPacket instanceof DhcpOfferPacket); - assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac())); - DhcpResults dhcpResults = offerPacket.toDhcpResults(); - assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53", - "lancs.ac.uk", "10.32.255.128", "", null, 7200, false, 0, dhcpResults); - } - - @Test - public void testUdpServerAnySourcePort() throws Exception { - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // Ethernet header. - "9cd917000000001c2e0000000800" + - // IP header. - "45a00148000040003d115087d18194fb0a0f7af2" + - // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). - // NOTE: The server source port is not the canonical port 67. - "C29F004401341268" + - // BOOTP header. - "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" + - // MAC address. - "9cd91700000000000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options. - "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" + - "d18180060f0777766d2e6564751c040a0fffffff000000")); - // CHECKSTYLE:ON Generated code - - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); - assertTrue(offerPacket instanceof DhcpOfferPacket); - assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac())); - DhcpResults dhcpResults = offerPacket.toDhcpResults(); - assertDhcpResults("10.15.122.242/16", "10.15.200.23", - "209.129.128.3,209.129.148.3,209.129.128.6", - "wvm.edu", "10.1.105.252", "", null, 86400, false, 0, dhcpResults); - } - - @Test - public void testUdpInvalidDstPort() throws Exception { - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // Ethernet header. - "9cd917000000001c2e0000000800" + - // IP header. - "45a00148000040003d115087d18194fb0a0f7af2" + - // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). - // NOTE: The destination port is a non-DHCP port. - "0043aaaa01341268" + - // BOOTP header. - "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" + - // MAC address. - "9cd91700000000000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options. - "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" + - "d18180060f0777766d2e6564751c040a0fffffff000000")); - // CHECKSTYLE:ON Generated code - - try { - DhcpPacket.decodeFullPacket(packet, ENCAP_L2); - fail("Packet with invalid dst port did not throw ParseException"); - } catch (ParseException expected) {} - } - - @Test - public void testMultipleRouters() throws Exception { - // CHECKSTYLE:OFF Generated code - final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray( - // Ethernet header. - "fc3d93000000" + "081735000000" + "0800" + - // IP header. - "45000148c2370000ff117ac2c0a8bd02ffffffff" + - // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). - "0043004401343beb" + - // BOOTP header. - "0201060027f518e20000800000000000c0a8bd310000000000000000" + - // MAC address. - "fc3d9300000000000000000000000000" + - // Server name. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // File. - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - // Options. - "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" + - "0308c0a8bd01ffffff0006080808080808080404ff000000000000")); - // CHECKSTYLE:ON Generated code - - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); - assertTrue(offerPacket instanceof DhcpOfferPacket); - assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac())); - DhcpResults dhcpResults = offerPacket.toDhcpResults(); - assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4", - null, "192.171.189.2", "", null, 28800, false, 0, dhcpResults); - } - - @Test - public void testDiscoverPacket() throws Exception { - short secs = 7; - int transactionId = 0xdeadbeef; - byte[] hwaddr = { - (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a - }; - - ByteBuffer packet = DhcpPacket.buildDiscoverPacket( - DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr, - false /* do unicast */, DhcpClient.REQUESTED_PARAMS); - - byte[] headers = new byte[] { - // Ethernet header. - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a, - (byte) 0x08, (byte) 0x00, - // IP header. - (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56, - (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00, - (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, - // UDP header. - (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43, - (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a, - // BOOTP. - (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00, - (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, - (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, - (byte) 0xb1, (byte) 0x7a - }; - byte[] options = new byte[] { - // Magic cookie 0x63825363. - (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63, - // Message type DISCOVER. - (byte) 0x35, (byte) 0x01, (byte) 0x01, - // Client identifier Ethernet, da:01:19:5b:b1:7a. - (byte) 0x3d, (byte) 0x07, - (byte) 0x01, - (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a, - // Max message size 1500. - (byte) 0x39, (byte) 0x02, (byte) 0x05, (byte) 0xdc, - // Version "android-dhcp-???". - (byte) 0x3c, (byte) 0x10, - 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 'h', 'c', 'p', '-', '?', '?', '?', - // Hostname "android-01234567890abcde" - (byte) 0x0c, (byte) 0x18, - 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', - // Requested parameter list. - (byte) 0x37, (byte) 0x0a, - DHCP_SUBNET_MASK, - DHCP_ROUTER, - DHCP_DNS_SERVER, - DHCP_DOMAIN_NAME, - DHCP_MTU, - DHCP_BROADCAST_ADDRESS, - DHCP_LEASE_TIME, - DHCP_RENEWAL_TIME, - DHCP_REBINDING_TIME, - DHCP_VENDOR_INFO, - // End options. - (byte) 0xff, - // Our packets are always of even length. TODO: find out why and possibly fix it. - (byte) 0x00 - }; - byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length]; - assertTrue((expected.length & 1) == 0); - System.arraycopy(headers, 0, expected, 0, headers.length); - System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length); - - byte[] actual = new byte[packet.limit()]; - packet.get(actual); - String msg = - "Expected:\n " + Arrays.toString(expected) + - "\nActual:\n " + Arrays.toString(actual); - assertTrue(msg, Arrays.equals(expected, actual)); - } - - public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname) - throws Exception { - final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2); - final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000); - final int transactionId = 0xdeadbeef; - - final ByteBuffer packet = DhcpPacket.buildOfferPacket( - DhcpPacket.ENCAP_BOOTP, transactionId, false /* broadcast */, - SERVER_ADDR, INADDR_ANY /* relayIp */, CLIENT_ADDR /* yourIp */, - CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */, - BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */, - Collections.singletonList(SERVER_ADDR) /* dnsServers */, - SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname, - false /* metered */, MTU); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - // BOOTP headers - bos.write(new byte[] { - (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x00, - (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - // ciaddr - (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, - }); - // yiaddr - bos.write(CLIENT_ADDR.getAddress()); - // siaddr - bos.write(SERVER_ADDR.getAddress()); - // giaddr - bos.write(INADDR_ANY.getAddress()); - // chaddr - bos.write(CLIENT_MAC); - - // Padding - bos.write(new byte[202]); - - // Options - bos.write(new byte[]{ - // Magic cookie 0x63825363. - (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63, - // Message type OFFER. - (byte) 0x35, (byte) 0x01, (byte) 0x02, - }); - // Server ID - bos.write(new byte[] { (byte) 0x36, (byte) 0x04 }); - bos.write(SERVER_ADDR.getAddress()); - // Lease time - bos.write(new byte[] { (byte) 0x33, (byte) 0x04 }); - bos.write(intToByteArray(leaseTimeSecs)); - if (leaseTimeSecs != INFINITE_LEASE) { - // Renewal time - bos.write(new byte[]{(byte) 0x3a, (byte) 0x04}); - bos.write(intToByteArray(renewalTime)); - // Rebinding time - bos.write(new byte[]{(byte) 0x3b, (byte) 0x04}); - bos.write(intToByteArray(rebindingTime)); - } - // Subnet mask - bos.write(new byte[] { (byte) 0x01, (byte) 0x04 }); - bos.write(NETMASK.getAddress()); - // Broadcast address - bos.write(new byte[] { (byte) 0x1c, (byte) 0x04 }); - bos.write(BROADCAST_ADDR.getAddress()); - // Router - bos.write(new byte[] { (byte) 0x03, (byte) 0x04 }); - bos.write(SERVER_ADDR.getAddress()); - // Nameserver - bos.write(new byte[] { (byte) 0x06, (byte) 0x04 }); - bos.write(SERVER_ADDR.getAddress()); - // Hostname - if (hostname != null) { - bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()}); - bos.write(hostname.getBytes(Charset.forName("US-ASCII"))); - } - // MTU - bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 }); - bos.write(shortToByteArray(MTU)); - // End options. - bos.write(0xff); - - if ((bos.size() & 1) != 0) { - bos.write(0x00); - } - - final byte[] expected = bos.toByteArray(); - final byte[] actual = new byte[packet.limit()]; - packet.get(actual); - final String msg = "Expected:\n " + HexDump.dumpHexString(expected) + - "\nActual:\n " + HexDump.dumpHexString(actual); - assertTrue(msg, Arrays.equals(expected, actual)); - } - - @Test - public void testOfferPacket() throws Exception { - checkBuildOfferPacket(3600, HOSTNAME); - checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME); - checkBuildOfferPacket(0x80000000, HOSTNAME); - checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME); - checkBuildOfferPacket(3600, null); - } - - private static byte[] intToByteArray(int val) { - return ByteBuffer.allocate(4).putInt(val).array(); - } - - private static byte[] shortToByteArray(short val) { - return ByteBuffer.allocate(2).putShort(val).array(); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServerTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServerTest.java deleted file mode 100644 index f0e2f1b8d459..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServerTest.java +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import static android.net.InetAddresses.parseNumericAddress; -import static android.net.dhcp.DhcpPacket.DHCP_CLIENT; -import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME; -import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP; -import static android.net.dhcp.DhcpPacket.INADDR_ANY; -import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; -import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.INetworkStackStatusCallback; -import android.net.LinkAddress; -import android.net.MacAddress; -import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException; -import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException; -import android.net.dhcp.DhcpServer.Clock; -import android.net.dhcp.DhcpServer.Dependencies; -import android.net.util.SharedLog; -import android.os.HandlerThread; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.testing.TestableLooper.RunWithLooper; - -import androidx.test.filters.SmallTest; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.Inet4Address; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - -@RunWith(AndroidTestingRunner.class) -@SmallTest -@RunWithLooper -public class DhcpServerTest { - private static final String TEST_IFACE = "testiface"; - - private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2"); - private static final LinkAddress TEST_SERVER_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20); - private static final Set<Inet4Address> TEST_DEFAULT_ROUTERS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.123"), parseAddr("192.168.0.124"))); - private static final Set<Inet4Address> TEST_DNS_SERVERS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.126"), parseAddr("192.168.0.127"))); - private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201"))); - private static final long TEST_LEASE_TIME_SECS = 3600L; - private static final int TEST_MTU = 1500; - private static final String TEST_HOSTNAME = "testhostname"; - - private static final int TEST_TRANSACTION_ID = 123; - private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 }; - private static final MacAddress TEST_CLIENT_MAC = MacAddress.fromBytes(TEST_CLIENT_MAC_BYTES); - private static final Inet4Address TEST_CLIENT_ADDR = parseAddr("192.168.0.42"); - - private static final long TEST_CLOCK_TIME = 1234L; - private static final int TEST_LEASE_EXPTIME_SECS = 3600; - private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC, - TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, - null /* hostname */); - private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC, - TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, TEST_HOSTNAME); - - @NonNull @Mock - private Dependencies mDeps; - @NonNull @Mock - private DhcpLeaseRepository mRepository; - @NonNull @Mock - private Clock mClock; - @NonNull @Mock - private DhcpPacketListener mPacketListener; - - @NonNull @Captor - private ArgumentCaptor<ByteBuffer> mSentPacketCaptor; - @NonNull @Captor - private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor; - - @NonNull - private HandlerThread mHandlerThread; - @NonNull - private TestableLooper mLooper; - @NonNull - private DhcpServer mServer; - - @Nullable - private String mPrevShareClassloaderProp; - - private final INetworkStackStatusCallback mAssertSuccessCallback = - new INetworkStackStatusCallback.Stub() { - @Override - public void onStatusAvailable(int statusCode) { - assertEquals(STATUS_SUCCESS, statusCode); - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository); - when(mDeps.makeClock()).thenReturn(mClock); - when(mDeps.makePacketListener()).thenReturn(mPacketListener); - doNothing().when(mDeps) - .sendPacket(any(), mSentPacketCaptor.capture(), mResponseDstAddrCaptor.capture()); - when(mClock.elapsedRealtime()).thenReturn(TEST_CLOCK_TIME); - - final DhcpServingParams servingParams = new DhcpServingParams.Builder() - .setDefaultRouters(TEST_DEFAULT_ROUTERS) - .setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS) - .setDnsServers(TEST_DNS_SERVERS) - .setServerAddr(TEST_SERVER_LINKADDR) - .setLinkMtu(TEST_MTU) - .setExcludedAddrs(TEST_EXCLUDED_ADDRS) - .build(); - - mLooper = TestableLooper.get(this); - mHandlerThread = spy(new HandlerThread("TestDhcpServer")); - when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper()); - mServer = new DhcpServer(mHandlerThread, TEST_IFACE, servingParams, - new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps); - - mServer.start(mAssertSuccessCallback); - mLooper.processAllMessages(); - } - - @After - public void tearDown() throws Exception { - mServer.stop(mAssertSuccessCallback); - mLooper.processMessages(1); - verify(mPacketListener, times(1)).stop(); - verify(mHandlerThread, times(1)).quitSafely(); - } - - @Test - public void testStart() throws Exception { - verify(mPacketListener, times(1)).start(); - } - - @Test - public void testDiscover() throws Exception { - // TODO: refactor packet construction to eliminate unnecessary/confusing/duplicate fields - when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC), - eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */)) - .thenReturn(TEST_LEASE); - - final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID, - (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES, - false /* broadcast */, INADDR_ANY /* srcIp */); - mServer.processPacket(discover, DHCP_CLIENT); - - assertResponseSentTo(TEST_CLIENT_ADDR); - final DhcpOfferPacket packet = assertOffer(getPacket()); - assertMatchesTestLease(packet); - } - - @Test - public void testDiscover_OutOfAddresses() throws Exception { - when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC), - eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */)) - .thenThrow(new OutOfAddressesException("Test exception")); - - final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID, - (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES, - false /* broadcast */, INADDR_ANY /* srcIp */); - mServer.processPacket(discover, DHCP_CLIENT); - - assertResponseSentTo(INADDR_BROADCAST); - final DhcpNakPacket packet = assertNak(getPacket()); - assertMatchesClient(packet); - } - - private DhcpRequestPacket makeRequestSelectingPacket() { - final DhcpRequestPacket request = new DhcpRequestPacket(TEST_TRANSACTION_ID, - (short) 0 /* secs */, INADDR_ANY /* clientIp */, INADDR_ANY /* relayIp */, - TEST_CLIENT_MAC_BYTES, false /* broadcast */); - request.mServerIdentifier = TEST_SERVER_ADDR; - request.mRequestedIp = TEST_CLIENT_ADDR; - return request; - } - - @Test - public void testRequest_Selecting_Ack() throws Exception { - when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC), - eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */, - eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME))) - .thenReturn(TEST_LEASE_WITH_HOSTNAME); - - final DhcpRequestPacket request = makeRequestSelectingPacket(); - request.mHostName = TEST_HOSTNAME; - request.mRequestedParams = new byte[] { DHCP_HOST_NAME }; - mServer.processPacket(request, DHCP_CLIENT); - - assertResponseSentTo(TEST_CLIENT_ADDR); - final DhcpAckPacket packet = assertAck(getPacket()); - assertMatchesTestLease(packet, TEST_HOSTNAME); - } - - @Test - public void testRequest_Selecting_Nak() throws Exception { - when(mRepository.requestLease(isNull(), eq(TEST_CLIENT_MAC), - eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */, - eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */)) - .thenThrow(new InvalidAddressException("Test error")); - - final DhcpRequestPacket request = makeRequestSelectingPacket(); - mServer.processPacket(request, DHCP_CLIENT); - - assertResponseSentTo(INADDR_BROADCAST); - final DhcpNakPacket packet = assertNak(getPacket()); - assertMatchesClient(packet); - } - - @Test - public void testRequest_Selecting_WrongClientPort() throws Exception { - final DhcpRequestPacket request = makeRequestSelectingPacket(); - mServer.processPacket(request, 50000); - - verify(mRepository, never()) - .requestLease(any(), any(), any(), any(), any(), anyBoolean(), any()); - verify(mDeps, never()).sendPacket(any(), any(), any()); - } - - @Test - public void testRelease() throws Exception { - final DhcpReleasePacket release = new DhcpReleasePacket(TEST_TRANSACTION_ID, - TEST_SERVER_ADDR, TEST_CLIENT_ADDR, - INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES); - mServer.processPacket(release, DHCP_CLIENT); - - verify(mRepository, times(1)) - .releaseLease(isNull(), eq(TEST_CLIENT_MAC), eq(TEST_CLIENT_ADDR)); - } - - /* TODO: add more tests once packet construction is refactored, including: - * - usage of giaddr - * - usage of broadcast bit - * - other request states (init-reboot/renewing/rebinding) - */ - - private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) { - assertMatchesClient(packet); - assertFalse(packet.hasExplicitClientId()); - assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier); - assertEquals(TEST_CLIENT_ADDR, packet.mYourIp); - assertNotNull(packet.mLeaseTime); - assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime); - assertEquals(hostname, packet.mHostName); - } - - private void assertMatchesTestLease(@NonNull DhcpPacket packet) { - assertMatchesTestLease(packet, null); - } - - private void assertMatchesClient(@NonNull DhcpPacket packet) { - assertEquals(TEST_TRANSACTION_ID, packet.mTransId); - assertEquals(TEST_CLIENT_MAC, MacAddress.fromBytes(packet.mClientMac)); - } - - private void assertResponseSentTo(@NonNull Inet4Address addr) { - assertEquals(addr, mResponseDstAddrCaptor.getValue()); - } - - private static DhcpNakPacket assertNak(@Nullable DhcpPacket packet) { - assertTrue(packet instanceof DhcpNakPacket); - return (DhcpNakPacket) packet; - } - - private static DhcpAckPacket assertAck(@Nullable DhcpPacket packet) { - assertTrue(packet instanceof DhcpAckPacket); - return (DhcpAckPacket) packet; - } - - private static DhcpOfferPacket assertOffer(@Nullable DhcpPacket packet) { - assertTrue(packet instanceof DhcpOfferPacket); - return (DhcpOfferPacket) packet; - } - - private DhcpPacket getPacket() throws Exception { - verify(mDeps, times(1)).sendPacket(any(), any(), any()); - return DhcpPacket.decodeFullPacket(mSentPacketCaptor.getValue(), ENCAP_BOOTP); - } - - private static Inet4Address parseAddr(@Nullable String inet4Addr) { - return (Inet4Address) parseNumericAddress(inet4Addr); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java b/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java deleted file mode 100644 index 57a87a4d3fb1..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.dhcp; - -import static android.net.InetAddresses.parseNumericAddress; -import static android.net.dhcp.DhcpServingParams.MTU_UNSET; -import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.net.LinkAddress; -import android.net.dhcp.DhcpServingParams.InvalidParameterException; -import android.net.shared.Inet4AddressUtils; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.lang.reflect.Modifier; -import java.net.Inet4Address; -import java.util.Arrays; -import java.util.Collection; -import java.util.HashSet; -import java.util.Set; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class DhcpServingParamsTest { - @NonNull - private DhcpServingParams.Builder mBuilder; - - private static final Set<Inet4Address> TEST_DEFAULT_ROUTERS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.123"), parseAddr("192.168.0.124"))); - private static final long TEST_LEASE_TIME_SECS = 3600L; - private static final Set<Inet4Address> TEST_DNS_SERVERS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.126"), parseAddr("192.168.0.127"))); - private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2"); - private static final LinkAddress TEST_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20); - private static final int TEST_MTU = 1500; - private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>( - Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201"))); - private static final boolean TEST_METERED = true; - - @Before - public void setUp() { - mBuilder = new DhcpServingParams.Builder() - .setDefaultRouters(TEST_DEFAULT_ROUTERS) - .setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS) - .setDnsServers(TEST_DNS_SERVERS) - .setServerAddr(TEST_LINKADDR) - .setLinkMtu(TEST_MTU) - .setExcludedAddrs(TEST_EXCLUDED_ADDRS) - .setMetered(TEST_METERED); - } - - @Test - public void testBuild_Immutable() throws InvalidParameterException { - final Set<Inet4Address> routers = new HashSet<>(TEST_DEFAULT_ROUTERS); - final Set<Inet4Address> dnsServers = new HashSet<>(TEST_DNS_SERVERS); - final Set<Inet4Address> excludedAddrs = new HashSet<>(TEST_EXCLUDED_ADDRS); - - final DhcpServingParams params = mBuilder - .setDefaultRouters(routers) - .setDnsServers(dnsServers) - .setExcludedAddrs(excludedAddrs) - .build(); - - // Modifications to source objects should not affect builder or final parameters - final Inet4Address addedAddr = parseAddr("192.168.0.223"); - routers.add(addedAddr); - dnsServers.add(addedAddr); - excludedAddrs.add(addedAddr); - - assertEquals(TEST_DEFAULT_ROUTERS, params.defaultRouters); - assertEquals(TEST_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs); - assertEquals(TEST_DNS_SERVERS, params.dnsServers); - assertEquals(TEST_LINKADDR, params.serverAddr); - assertEquals(TEST_MTU, params.linkMtu); - assertEquals(TEST_METERED, params.metered); - - assertContains(params.excludedAddrs, TEST_EXCLUDED_ADDRS); - assertContains(params.excludedAddrs, TEST_DEFAULT_ROUTERS); - assertContains(params.excludedAddrs, TEST_DNS_SERVERS); - assertContains(params.excludedAddrs, TEST_SERVER_ADDR); - - assertFalse("excludedAddrs should not contain " + addedAddr, - params.excludedAddrs.contains(addedAddr)); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_NegativeLeaseTime() throws InvalidParameterException { - mBuilder.setDhcpLeaseTimeSecs(-1).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_LeaseTimeTooLarge() throws InvalidParameterException { - // Set lease time larger than max value for uint32 - mBuilder.setDhcpLeaseTimeSecs(1L << 32).build(); - } - - @Test - public void testBuild_InfiniteLeaseTime() throws InvalidParameterException { - final long infiniteLeaseTime = 0xffffffffL; - final DhcpServingParams params = mBuilder - .setDhcpLeaseTimeSecs(infiniteLeaseTime).build(); - assertEquals(infiniteLeaseTime, params.dhcpLeaseTimeSecs); - assertTrue(params.dhcpLeaseTimeSecs > 0L); - } - - @Test - public void testBuild_UnsetMtu() throws InvalidParameterException { - final DhcpServingParams params = mBuilder.setLinkMtu(MTU_UNSET).build(); - assertEquals(MTU_UNSET, params.linkMtu); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_MtuTooSmall() throws InvalidParameterException { - mBuilder.setLinkMtu(20).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_MtuTooLarge() throws InvalidParameterException { - mBuilder.setLinkMtu(65_536).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_IPv6Addr() throws InvalidParameterException { - mBuilder.setServerAddr(new LinkAddress(parseNumericAddress("fe80::1111"), 120)).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_PrefixTooLarge() throws InvalidParameterException { - mBuilder.setServerAddr(new LinkAddress(TEST_SERVER_ADDR, 15)).build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_PrefixTooSmall() throws InvalidParameterException { - mBuilder.setDefaultRouters(parseAddr("192.168.0.254")) - .setServerAddr(new LinkAddress(TEST_SERVER_ADDR, 31)) - .build(); - } - - @Test(expected = InvalidParameterException.class) - public void testBuild_RouterNotInPrefix() throws InvalidParameterException { - mBuilder.setDefaultRouters(parseAddr("192.168.254.254")).build(); - } - - @Test - public void testFromParcelableObject() throws InvalidParameterException { - final DhcpServingParams params = mBuilder.build(); - final DhcpServingParamsParcel parcel = new DhcpServingParamsParcel(); - parcel.defaultRouters = toIntArray(TEST_DEFAULT_ROUTERS); - parcel.dhcpLeaseTimeSecs = TEST_LEASE_TIME_SECS; - parcel.dnsServers = toIntArray(TEST_DNS_SERVERS); - parcel.serverAddr = inet4AddressToIntHTH(TEST_SERVER_ADDR); - parcel.serverAddrPrefixLength = TEST_LINKADDR.getPrefixLength(); - parcel.linkMtu = TEST_MTU; - parcel.excludedAddrs = toIntArray(TEST_EXCLUDED_ADDRS); - parcel.metered = TEST_METERED; - final DhcpServingParams parceled = DhcpServingParams.fromParcelableObject(parcel); - - assertEquals(params.defaultRouters, parceled.defaultRouters); - assertEquals(params.dhcpLeaseTimeSecs, parceled.dhcpLeaseTimeSecs); - assertEquals(params.dnsServers, parceled.dnsServers); - assertEquals(params.serverAddr, parceled.serverAddr); - assertEquals(params.linkMtu, parceled.linkMtu); - assertEquals(params.excludedAddrs, parceled.excludedAddrs); - assertEquals(params.metered, parceled.metered); - - // Ensure that we do not miss any field if added in the future - final long numFields = Arrays.stream(DhcpServingParams.class.getDeclaredFields()) - .filter(f -> !Modifier.isStatic(f.getModifiers())) - .count(); - assertEquals(7, numFields); - } - - @Test(expected = InvalidParameterException.class) - public void testFromParcelableObject_NullArgument() throws InvalidParameterException { - DhcpServingParams.fromParcelableObject(null); - } - - private static int[] toIntArray(Collection<Inet4Address> addrs) { - return addrs.stream().mapToInt(Inet4AddressUtils::inet4AddressToIntHTH).toArray(); - } - - private static <T> void assertContains(@NonNull Set<T> set, @NonNull Set<T> subset) { - for (final T elem : subset) { - assertContains(set, elem); - } - } - - private static <T> void assertContains(@NonNull Set<T> set, @Nullable T elem) { - assertTrue("Set does not contain " + elem, set.contains(elem)); - } - - @NonNull - private static Inet4Address parseAddr(@NonNull String inet4Addr) { - return (Inet4Address) parseNumericAddress(inet4Addr); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/unit/src/android/net/ip/IpClientTest.java deleted file mode 100644 index 5f8000634ffa..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/ip/IpClientTest.java +++ /dev/null @@ -1,547 +0,0 @@ -/* - * Copyright (C) 2017 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.ip; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import android.app.AlarmManager; -import android.content.ContentResolver; -import android.content.Context; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.INetd; -import android.net.IpPrefix; -import android.net.LinkAddress; -import android.net.LinkProperties; -import android.net.MacAddress; -import android.net.NetworkStackIpMemoryStore; -import android.net.RouteInfo; -import android.net.ipmemorystore.NetworkAttributes; -import android.net.shared.InitialConfiguration; -import android.net.shared.ProvisioningConfiguration; -import android.net.util.InterfaceParams; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.R; -import com.android.server.NetworkObserver; -import com.android.server.NetworkObserverRegistry; -import com.android.server.NetworkStackService; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.net.InetAddress; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * Tests for IpClient. - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IpClientTest { - private static final int DEFAULT_AVOIDBADWIFI_CONFIG_VALUE = 1; - - private static final String VALID = "VALID"; - private static final String INVALID = "INVALID"; - private static final String TEST_IFNAME = "test_wlan0"; - private static final int TEST_IFINDEX = 1001; - // See RFC 7042#section-2.1.2 for EUI-48 documentation values. - private static final MacAddress TEST_MAC = MacAddress.fromString("00:00:5E:00:53:01"); - private static final int TEST_TIMEOUT_MS = 400; - private static final String TEST_L2KEY = "some l2key"; - private static final String TEST_GROUPHINT = "some grouphint"; - - @Mock private Context mContext; - @Mock private ConnectivityManager mCm; - @Mock private NetworkObserverRegistry mObserverRegistry; - @Mock private INetd mNetd; - @Mock private Resources mResources; - @Mock private IIpClientCallbacks mCb; - @Mock private AlarmManager mAlarm; - @Mock private IpClient.Dependencies mDependencies; - @Mock private ContentResolver mContentResolver; - @Mock private NetworkStackService.NetworkStackServiceManager mNetworkStackServiceManager; - @Mock private NetworkStackIpMemoryStore mIpMemoryStore; - - private NetworkObserver mObserver; - private InterfaceParams mIfParams; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); - when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); - when(mContext.getResources()).thenReturn(mResources); - when(mDependencies.getNetd(any())).thenReturn(mNetd); - when(mResources.getInteger(R.integer.config_networkAvoidBadWifi)) - .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE); - when(mContext.getContentResolver()).thenReturn(mContentResolver); - - mIfParams = null; - } - - private void setTestInterfaceParams(String ifname) { - mIfParams = (ifname != null) - ? new InterfaceParams(ifname, TEST_IFINDEX, TEST_MAC) - : null; - when(mDependencies.getInterfaceParams(anyString())).thenReturn(mIfParams); - } - - private IpClient makeIpClient(String ifname) throws Exception { - setTestInterfaceParams(ifname); - final IpClient ipc = new IpClient(mContext, ifname, mCb, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false); - verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname); - ArgumentCaptor<NetworkObserver> arg = ArgumentCaptor.forClass(NetworkObserver.class); - verify(mObserverRegistry, times(1)).registerObserverForNonblockingCallback(arg.capture()); - mObserver = arg.getValue(); - reset(mObserverRegistry); - reset(mNetd); - // Verify IpClient doesn't call onLinkPropertiesChange() when it starts. - verify(mCb, never()).onLinkPropertiesChange(any()); - reset(mCb); - return ipc; - } - - private static LinkProperties makeEmptyLinkProperties(String iface) { - final LinkProperties empty = new LinkProperties(); - empty.setInterfaceName(iface); - return empty; - } - - private void verifyNetworkAttributesStored(final String l2Key, - final NetworkAttributes attributes) { - // TODO : when storing is implemented, turn this on - // verify(mIpMemoryStore).storeNetworkAttributes(eq(l2Key), eq(attributes), any()); - } - - @Test - public void testNullInterfaceNameMostDefinitelyThrows() throws Exception { - setTestInterfaceParams(null); - try { - final IpClient ipc = new IpClient(mContext, null, mCb, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - ipc.shutdown(); - fail(); - } catch (NullPointerException npe) { - // Phew; null interface names not allowed. - } - } - - @Test - public void testNullCallbackMostDefinitelyThrows() throws Exception { - final String ifname = "lo"; - setTestInterfaceParams(ifname); - try { - final IpClient ipc = new IpClient(mContext, ifname, null, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - ipc.shutdown(); - fail(); - } catch (NullPointerException npe) { - // Phew; null callbacks not allowed. - } - } - - @Test - public void testInvalidInterfaceDoesNotThrow() throws Exception { - setTestInterfaceParams(TEST_IFNAME); - final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - verifyNoMoreInteractions(mIpMemoryStore); - ipc.shutdown(); - } - - @Test - public void testInterfaceNotFoundFailsImmediately() throws Exception { - setTestInterfaceParams(null); - final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mObserverRegistry, - mNetworkStackServiceManager, mDependencies); - ipc.startProvisioning(new ProvisioningConfiguration()); - verify(mCb, times(1)).onProvisioningFailure(any()); - verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any()); - ipc.shutdown(); - } - - @Test - public void testDefaultProvisioningConfiguration() throws Exception { - final String iface = TEST_IFNAME; - final IpClient ipc = makeIpClient(iface); - - ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() - .withoutIPv4() - // TODO: mock IpReachabilityMonitor's dependencies (NetworkInterface, PowerManager) - // and enable it in this test - .withoutIpReachabilityMonitor() - .build(); - - ipc.startProvisioning(config); - verify(mCb, times(1)).setNeighborDiscoveryOffload(true); - verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false); - verify(mCb, never()).onProvisioningFailure(any()); - verify(mIpMemoryStore, never()).storeNetworkAttributes(any(), any(), any()); - - ipc.shutdown(); - verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false); - verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface); - verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)) - .onLinkPropertiesChange(makeEmptyLinkProperties(iface)); - } - - @Test - public void testProvisioningWithInitialConfiguration() throws Exception { - final String iface = TEST_IFNAME; - final IpClient ipc = makeIpClient(iface); - final String l2Key = TEST_L2KEY; - final String groupHint = TEST_GROUPHINT; - - String[] addresses = { - "fe80::a4be:f92:e1f7:22d1/64", - "fe80::f04a:8f6:6a32:d756/64", - "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64" - }; - String[] prefixes = { "fe80::/64", "fd2c:4e57:8e3c::/64" }; - - ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() - .withoutIPv4() - .withoutIpReachabilityMonitor() - .withInitialConfiguration(conf(links(addresses), prefixes(prefixes), ips())) - .build(); - - ipc.startProvisioning(config); - verify(mCb, times(1)).setNeighborDiscoveryOffload(true); - verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false); - verify(mCb, never()).onProvisioningFailure(any()); - ipc.setL2KeyAndGroupHint(l2Key, groupHint); - - for (String addr : addresses) { - String[] parts = addr.split("/"); - verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)) - .interfaceAddAddress(iface, parts[0], Integer.parseInt(parts[1])); - } - - final int lastAddr = addresses.length - 1; - - // Add N - 1 addresses - for (int i = 0; i < lastAddr; i++) { - mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[i]), iface); - verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any()); - reset(mCb); - } - - // Add Nth address - mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[lastAddr]), iface); - LinkProperties want = linkproperties(links(addresses), routes(prefixes)); - want.setInterfaceName(iface); - verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(want); - verifyNetworkAttributesStored(l2Key, new NetworkAttributes.Builder() - .setGroupHint(groupHint) - .build()); - - ipc.shutdown(); - verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(iface, false); - verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(iface); - verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)) - .onLinkPropertiesChange(makeEmptyLinkProperties(iface)); - verifyNoMoreInteractions(mIpMemoryStore); - } - - @Test - public void testIsProvisioned() throws Exception { - InitialConfiguration empty = conf(links(), prefixes()); - IsProvisionedTestCase[] testcases = { - // nothing - notProvisionedCase(links(), routes(), dns(), null), - notProvisionedCase(links(), routes(), dns(), empty), - - // IPv4 - provisionedCase(links("192.0.2.12/24"), routes(), dns(), empty), - - // IPv6 - notProvisionedCase( - links("fe80::a4be:f92:e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"), - routes(), dns(), empty), - notProvisionedCase( - links("fe80::a4be:f92:e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"), - routes("fe80::/64", "fd2c:4e57:8e3c::/64"), dns("fd00:1234:5678::1000"), empty), - provisionedCase( - links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), - routes("::/0"), - dns("2001:db8:dead:beef:f00::02"), empty), - - // Initial configuration - provisionedCase( - links("fe80::e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"), - routes("fe80::/64", "fd2c:4e57:8e3c::/64"), - dns(), - conf(links("fe80::e1f7:22d1/64", "fd2c:4e57:8e3c:0:548d:2db2:4fcf:ef75/64"), - prefixes( "fe80::/64", "fd2c:4e57:8e3c::/64"), ips())) - }; - - for (IsProvisionedTestCase testcase : testcases) { - if (IpClient.isProvisioned(testcase.lp, testcase.config) != testcase.isProvisioned) { - fail(testcase.errorMessage()); - } - } - } - - static class IsProvisionedTestCase { - boolean isProvisioned; - LinkProperties lp; - InitialConfiguration config; - - String errorMessage() { - return String.format("expected %s with config %s to be %s, but was %s", - lp, config, provisioned(isProvisioned), provisioned(!isProvisioned)); - } - - static String provisioned(boolean isProvisioned) { - return isProvisioned ? "provisioned" : "not provisioned"; - } - } - - static IsProvisionedTestCase provisionedCase(Set<LinkAddress> lpAddrs, Set<RouteInfo> lpRoutes, - Set<InetAddress> lpDns, InitialConfiguration config) { - return provisioningTest(true, lpAddrs, lpRoutes, lpDns, config); - } - - static IsProvisionedTestCase notProvisionedCase(Set<LinkAddress> lpAddrs, - Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config) { - return provisioningTest(false, lpAddrs, lpRoutes, lpDns, config); - } - - static IsProvisionedTestCase provisioningTest(boolean isProvisioned, Set<LinkAddress> lpAddrs, - Set<RouteInfo> lpRoutes, Set<InetAddress> lpDns, InitialConfiguration config) { - IsProvisionedTestCase testcase = new IsProvisionedTestCase(); - testcase.isProvisioned = isProvisioned; - testcase.lp = new LinkProperties(); - testcase.lp.setLinkAddresses(lpAddrs); - for (RouteInfo route : lpRoutes) { - testcase.lp.addRoute(route); - } - for (InetAddress dns : lpDns) { - testcase.lp.addDnsServer(dns); - } - testcase.config = config; - return testcase; - } - - @Test - public void testInitialConfigurations() throws Exception { - InitialConfigurationTestCase[] testcases = { - validConf("valid IPv4 configuration", - links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("192.0.2.2")), - validConf("another valid IPv4 configuration", - links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns()), - validConf("valid IPv6 configurations", - links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), - prefixes("2001:db8:dead:beef::/64", "fe80::/64"), - dns("2001:db8:dead:beef:f00::02")), - validConf("valid IPv6 configurations", - links("fe80::1/64"), prefixes("fe80::/64"), dns()), - validConf("valid IPv6/v4 configuration", - links("2001:db8:dead:beef:f00::a0/48", "192.0.2.12/24"), - prefixes("2001:db8:dead:beef::/64", "192.0.2.0/24"), - dns("192.0.2.2", "2001:db8:dead:beef:f00::02")), - validConf("valid IPv6 configuration without any GUA.", - links("fd00:1234:5678::1/48"), - prefixes("fd00:1234:5678::/48"), - dns("fd00:1234:5678::1000")), - - invalidConf("empty configuration", links(), prefixes(), dns()), - invalidConf("v4 addr and dns not in any prefix", - links("192.0.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")), - invalidConf("v4 addr not in any prefix", - links("198.51.2.12/24"), prefixes("198.51.100.0/24"), dns("192.0.2.2")), - invalidConf("v4 dns addr not in any prefix", - links("192.0.2.12/24"), prefixes("192.0.2.0/24"), dns("198.51.100.2")), - invalidConf("v6 addr not in any prefix", - links("2001:db8:dead:beef:f00::a0/64", "fe80::1/64"), - prefixes("2001:db8:dead:beef::/64"), - dns("2001:db8:dead:beef:f00::02")), - invalidConf("v6 dns addr not in any prefix", - links("fe80::1/64"), prefixes("fe80::/64"), dns("2001:db8:dead:beef:f00::02")), - invalidConf("default ipv6 route and no GUA", - links("fd01:1111:2222:3333::a0/128"), prefixes("::/0"), dns()), - invalidConf("invalid v6 prefix length", - links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/32"), - dns()), - invalidConf("another invalid v6 prefix length", - links("2001:db8:dead:beef:f00::a0/128"), prefixes("2001:db8:dead:beef::/72"), - dns()) - }; - - for (InitialConfigurationTestCase testcase : testcases) { - if (testcase.config.isValid() != testcase.isValid) { - fail(testcase.errorMessage()); - } - } - } - - static class InitialConfigurationTestCase { - String descr; - boolean isValid; - InitialConfiguration config; - public String errorMessage() { - return String.format("%s: expected configuration %s to be %s, but was %s", - descr, config, validString(isValid), validString(!isValid)); - } - static String validString(boolean isValid) { - return isValid ? VALID : INVALID; - } - } - - static InitialConfigurationTestCase validConf(String descr, Set<LinkAddress> links, - Set<IpPrefix> prefixes, Set<InetAddress> dns) { - return confTestCase(descr, true, conf(links, prefixes, dns)); - } - - static InitialConfigurationTestCase invalidConf(String descr, Set<LinkAddress> links, - Set<IpPrefix> prefixes, Set<InetAddress> dns) { - return confTestCase(descr, false, conf(links, prefixes, dns)); - } - - static InitialConfigurationTestCase confTestCase( - String descr, boolean isValid, InitialConfiguration config) { - InitialConfigurationTestCase testcase = new InitialConfigurationTestCase(); - testcase.descr = descr; - testcase.isValid = isValid; - testcase.config = config; - return testcase; - } - - static LinkProperties linkproperties(Set<LinkAddress> addresses, Set<RouteInfo> routes) { - LinkProperties lp = new LinkProperties(); - lp.setLinkAddresses(addresses); - for (RouteInfo route : routes) { - lp.addRoute(route); - } - return lp; - } - - static InitialConfiguration conf(Set<LinkAddress> links, Set<IpPrefix> prefixes) { - return conf(links, prefixes, new HashSet<>()); - } - - static InitialConfiguration conf( - Set<LinkAddress> links, Set<IpPrefix> prefixes, Set<InetAddress> dns) { - InitialConfiguration conf = new InitialConfiguration(); - conf.ipAddresses.addAll(links); - conf.directlyConnectedRoutes.addAll(prefixes); - conf.dnsServers.addAll(dns); - return conf; - } - - static Set<RouteInfo> routes(String... routes) { - return mapIntoSet(routes, (r) -> new RouteInfo(new IpPrefix(r))); - } - - static Set<IpPrefix> prefixes(String... prefixes) { - return mapIntoSet(prefixes, IpPrefix::new); - } - - static Set<LinkAddress> links(String... addresses) { - return mapIntoSet(addresses, LinkAddress::new); - } - - static Set<InetAddress> ips(String... addresses) { - return mapIntoSet(addresses, InetAddress::getByName); - } - - static Set<InetAddress> dns(String... addresses) { - return ips(addresses); - } - - static <A, B> Set<B> mapIntoSet(A[] in, Fn<A, B> fn) { - Set<B> out = new HashSet<>(in.length); - for (A item : in) { - try { - out.add(fn.call(item)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - return out; - } - - interface Fn<A,B> { - B call(A a) throws Exception; - } - - @Test - public void testAll() { - List<String> list1 = Arrays.asList(); - List<String> list2 = Arrays.asList("foo"); - List<String> list3 = Arrays.asList("bar", "baz"); - List<String> list4 = Arrays.asList("foo", "bar", "baz"); - - assertTrue(InitialConfiguration.all(list1, (x) -> false)); - assertFalse(InitialConfiguration.all(list2, (x) -> false)); - assertTrue(InitialConfiguration.all(list3, (x) -> true)); - assertTrue(InitialConfiguration.all(list2, (x) -> x.charAt(0) == 'f')); - assertFalse(InitialConfiguration.all(list4, (x) -> x.charAt(0) == 'f')); - } - - @Test - public void testAny() { - List<String> list1 = Arrays.asList(); - List<String> list2 = Arrays.asList("foo"); - List<String> list3 = Arrays.asList("bar", "baz"); - List<String> list4 = Arrays.asList("foo", "bar", "baz"); - - assertFalse(InitialConfiguration.any(list1, (x) -> true)); - assertTrue(InitialConfiguration.any(list2, (x) -> true)); - assertTrue(InitialConfiguration.any(list2, (x) -> x.charAt(0) == 'f')); - assertFalse(InitialConfiguration.any(list3, (x) -> x.charAt(0) == 'f')); - assertTrue(InitialConfiguration.any(list4, (x) -> x.charAt(0) == 'f')); - } - - @Test - public void testFindAll() { - List<String> list1 = Arrays.asList(); - List<String> list2 = Arrays.asList("foo"); - List<String> list3 = Arrays.asList("foo", "bar", "baz"); - - assertEquals(list1, IpClient.findAll(list1, (x) -> true)); - assertEquals(list1, IpClient.findAll(list3, (x) -> false)); - assertEquals(list3, IpClient.findAll(list3, (x) -> true)); - assertEquals(list2, IpClient.findAll(list3, (x) -> x.charAt(0) == 'f')); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.java b/packages/NetworkStack/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.java deleted file mode 100644 index 64b168ae2b5a..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/ip/IpReachabilityMonitorTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2017 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.ip; - -import static org.mockito.Mockito.anyString; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.net.util.InterfaceParams; -import android.net.util.SharedLog; -import android.os.Handler; -import android.os.Looper; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Tests for IpReachabilityMonitor. - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class IpReachabilityMonitorTest { - - @Mock IpReachabilityMonitor.Callback mCallback; - @Mock IpReachabilityMonitor.Dependencies mDependencies; - @Mock SharedLog mLog; - @Mock Context mContext; - Handler mHandler; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mLog.forSubComponent(anyString())).thenReturn(mLog); - mHandler = new Handler(Looper.getMainLooper()); - } - - IpReachabilityMonitor makeMonitor() { - final InterfaceParams ifParams = new InterfaceParams("fake0", 1, null); - return new IpReachabilityMonitor( - mContext, ifParams, mHandler, mLog, mCallback, false, mDependencies); - } - - @Test - public void testNothing() { - IpReachabilityMonitor monitor = makeMonitor(); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/util/ConnectivityPacketSummaryTest.java b/packages/NetworkStack/tests/unit/src/android/net/util/ConnectivityPacketSummaryTest.java deleted file mode 100644 index 71be8b38d3fe..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/util/ConnectivityPacketSummaryTest.java +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (C) 2016 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.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.net.MacAddress; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import libcore.util.HexEncoding; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests for ConnectivityPacketSummary. - * - * @hide - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class ConnectivityPacketSummaryTest { - private static final MacAddress MYHWADDR = MacAddress.fromString("80:7a:bf:6f:48:f3"); - - private String getSummary(String hexBytes) { - hexBytes = hexBytes.replaceAll("\\s+", ""); - final byte[] bytes = HexEncoding.decode(hexBytes.toCharArray(), false); - return ConnectivityPacketSummary.summarize(MYHWADDR, bytes); - } - - @Test - public void testParseICMPv6DADProbe() { - final String packet = - // Ethernet - "3333FF6F48F3 807ABF6F48F3 86DD" + - // IPv6 - "600000000018 3A FF" + - "00000000000000000000000000000000" + - "FF0200000000000000000001FF6F48F3" + - // ICMPv6 - "87 00 A8E7" + - "00000000" + - "FE80000000000000827ABFFFFE6F48F3"; - - final String expected = - "TX 80:7a:bf:6f:48:f3 > 33:33:ff:6f:48:f3 ipv6" + - " :: > ff02::1:ff6f:48f3 icmp6" + - " ns fe80::827a:bfff:fe6f:48f3"; - - assertEquals(expected, getSummary(packet)); - } - - @Test - public void testParseICMPv6RS() { - final String packet = - // Ethernet - "333300000002 807ABF6F48F3 86DD" + - // IPv6 - "600000000010 3A FF" + - "FE80000000000000827ABFFFFE6F48F3" + - "FF020000000000000000000000000002" + - // ICMPv6 RS - "85 00 6973" + - "00000000" + - "01 01 807ABF6F48F3"; - - final String expected = - "TX 80:7a:bf:6f:48:f3 > 33:33:00:00:00:02 ipv6" + - " fe80::827a:bfff:fe6f:48f3 > ff02::2 icmp6" + - " rs slla 80:7a:bf:6f:48:f3"; - - assertEquals(expected, getSummary(packet)); - } - - @Test - public void testParseICMPv6RA() { - final String packet = - // Ethernet - "807ABF6F48F3 100E7E263FC1 86DD" + - // IPv6 - "600000000068 3A FF" + - "FE80000000000000FA000004FD000001" + - "FE80000000000000827ABFFFFE6F48F3" + - // ICMPv6 RA - "86 00 8141" + - "40 00 0E10" + - "00000000" + - "00000000" + - "01 01 00005E000265" + - "05 01 0000000005DC" + - "19 05 000000000E10" + - " 20014860486000000000000000008844" + - " 20014860486000000000000000008888" + - "03 04 40 C0" + - " 00278D00" + - " 00093A80" + - " 00000000" + - " 2401FA000004FD000000000000000000"; - - final String expected = - "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" + - " fe80::fa00:4:fd00:1 > fe80::827a:bfff:fe6f:48f3 icmp6" + - " ra slla 00:00:5e:00:02:65 mtu 1500"; - - assertEquals(expected, getSummary(packet)); - } - - @Test - public void testParseICMPv6NS() { - final String packet = - // Ethernet - "807ABF6F48F3 100E7E263FC1 86DD" + - // IPv6 - "6C0000000020 3A FF" + - "FE80000000000000FA000004FD000001" + - "FF0200000000000000000001FF01C146" + - // ICMPv6 NS - "87 00 8AD4" + - "00000000" + - "2401FA000004FD0015EA6A5C7B01C146" + - "01 01 00005E000265"; - - final String expected = - "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" + - " fe80::fa00:4:fd00:1 > ff02::1:ff01:c146 icmp6" + - " ns 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 slla 00:00:5e:00:02:65"; - - assertEquals(expected, getSummary(packet)); - } - - @Test - public void testInvalidICMPv6NDLength() { - final String packet = - // Ethernet - "807ABF6F48F3 100E7E263FC1 86DD" + - // IPv6 - "600000000068 3A FF" + - "FE80000000000000FA000004FD000001" + - "FE80000000000000827ABFFFFE6F48F3" + - // ICMPv6 RA - "86 00 8141" + - "40 00 0E10" + - "00000000" + - "00000000" + - "01 01 00005E000265" + - "00 00 0102030405D6"; - - final String expected = - "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" + - " fe80::fa00:4:fd00:1 > fe80::827a:bfff:fe6f:48f3 icmp6" + - " ra slla 00:00:5e:00:02:65 <malformed>"; - - assertEquals(expected, getSummary(packet)); - } - - @Test - public void testParseICMPv6NA() { - final String packet = - // Ethernet - "00005E000265 807ABF6F48F3 86DD" + - "600000000020 3A FF" + - "2401FA000004FD0015EA6A5C7B01C146" + - "FE80000000000000FA000004FD000001" + - "88 00 E8126" + - "0000000" + - "2401FA000004FD0015EA6A5C7B01C146" + - "02 01 807ABF6F48F3"; - - final String expected = - "TX 80:7a:bf:6f:48:f3 > 00:00:5e:00:02:65 ipv6" + - " 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 > fe80::fa00:4:fd00:1 icmp6" + - " na 2401:fa00:4:fd00:15ea:6a5c:7b01:c146 tlla 80:7a:bf:6f:48:f3"; - - assertEquals(expected, getSummary(packet)); - } - - @Test - public void testParseARPRequest() { - final String packet = - // Ethernet - "FFFFFFFFFFFF 807ABF6F48F3 0806" + - // ARP - "0001 0800 06 04" + - // Request - "0001" + - "807ABF6F48F3 64706ADB" + - "000000000000 64706FFD"; - - final String expected = - "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff arp" + - " who-has 100.112.111.253"; - - assertEquals(expected, getSummary(packet)); - } - - @Test - public void testParseARPReply() { - final String packet = - // Ethernet - "807ABF6F48F3 288A1CA8DFC1 0806" + - // ARP - "0001 0800 06 04" + - // Reply - "0002" + - "288A1CA8DFC1 64706FFD"+ - "807ABF6F48F3 64706ADB" + - // Ethernet padding to packet min size. - "0000000000000000000000000000"; - - final String expected = - "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 arp" + - " reply 100.112.111.253 28:8a:1c:a8:df:c1"; - - assertEquals(expected, getSummary(packet)); - } - - @Test - public void testParseDHCPv4Discover() { - final String packet = - // Ethernet - "FFFFFFFFFFFF 807ABF6F48F3 0800" + - // IPv4 - "451001580000400040113986" + - "00000000" + - "FFFFFFFF" + - // UDP - "0044 0043" + - "0144 5559" + - // DHCPv4 - "01 01 06 00" + - "79F7ACA4" + - "0000 0000" + - "00000000" + - "00000000" + - "00000000" + - "00000000" + - "807ABF6F48F300000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "63 82 53 63" + - "35 01 01" + - "3D 07 01807ABF6F48F3" + - "39 02 05DC" + - "3C 12 616E64726F69642D646863702D372E312E32" + - "0C 18 616E64726F69642D36623030366333313333393835343139" + - "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" + - "FF" + - "00"; - - final String expectedPrefix = - "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" + - " 0.0.0.0 > 255.255.255.255 udp" + - " 68 > 67 dhcp4" + - " 80:7a:bf:6f:48:f3 DISCOVER"; - - assertTrue(getSummary(packet).startsWith(expectedPrefix)); - } - - @Test - public void testParseDHCPv4Offer() { - final String packet = - // Ethernet - "807ABF6F48F3 288A1CA8DFC1 0800" + - // IPv4 - "4500013D4D2C0000401188CB" + - "64706FFD" + - "64706ADB" + - // UDP - "0043 0044" + - "0129 371D" + - // DHCPv4 - "02 01 06 01" + - "79F7ACA4" + - "0000 0000" + - "00000000" + - "64706ADB" + - "00000000" + - "00000000" + - "807ABF6F48F300000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "63 82 53 63" + - "35 01 02" + - "36 04 AC188A0B" + - "33 04 00000708" + - "01 04 FFFFF000" + - "03 04 64706FFE" + - "06 08 08080808" + - " 08080404" + - "FF0001076165313A363636FF"; - - final String expectedPrefix = - "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" + - " 100.112.111.253 > 100.112.106.219 udp" + - " 67 > 68 dhcp4" + - " 80:7a:bf:6f:48:f3 OFFER"; - - assertTrue(getSummary(packet).startsWith(expectedPrefix)); - } - - @Test - public void testParseDHCPv4Request() { - final String packet = - // Ethernet - "FFFFFFFFFFFF 807ABF6F48F3 0800" + - // IPv4 - "45100164000040004011397A" + - "00000000" + - "FFFFFFFF" + - // UDP - "0044 0043" + - "0150 E5C7" + - // DHCPv4 - "01 01 06 00" + - "79F7ACA4" + - "0001 0000" + - "00000000" + - "00000000" + - "00000000" + - "00000000" + - "807ABF6F48F300000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "63 82 53 63" + - "35 01 03" + - "3D 07 01807ABF6F48F3" + - "32 04 64706ADB" + - "36 04 AC188A0B" + - "39 02 05DC" + - "3C 12 616E64726F69642D646863702D372E312E32" + - "0C 18 616E64726F69642D36623030366333313333393835343139" + - "37 0A 01 03 06 0F 1A 1C 33 3A 3B 2B" + - "FF" + - "00"; - - final String expectedPrefix = - "TX 80:7a:bf:6f:48:f3 > ff:ff:ff:ff:ff:ff ipv4" + - " 0.0.0.0 > 255.255.255.255 udp" + - " 68 > 67 dhcp4" + - " 80:7a:bf:6f:48:f3 REQUEST"; - - assertTrue(getSummary(packet).startsWith(expectedPrefix)); - } - - @Test - public void testParseDHCPv4Ack() { - final String packet = - // Ethernet - "807ABF6F48F3 288A1CA8DFC1 0800" + - // IPv4 - "4500013D4D3B0000401188BC" + - "64706FFD" + - "64706ADB" + - // UDP - "0043 0044" + - "0129 341C" + - // DHCPv4 - "02 01 06 01" + - "79F7ACA4" + - "0001 0000" + - "00000000" + - "64706ADB" + - "00000000" + - "00000000" + - "807ABF6F48F300000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "0000000000000000000000000000000000000000000000000000000000000000" + - "63 82 53 63" + - "35 01 05" + - "36 04 AC188A0B" + - "33 04 00000708" + - "01 04 FFFFF000" + - "03 04 64706FFE" + - "06 08 08080808" + - " 08080404" + - "FF0001076165313A363636FF"; - - final String expectedPrefix = - "RX 28:8a:1c:a8:df:c1 > 80:7a:bf:6f:48:f3 ipv4" + - " 100.112.111.253 > 100.112.106.219 udp" + - " 67 > 68 dhcp4" + - " 80:7a:bf:6f:48:f3 ACK"; - - assertTrue(getSummary(packet).startsWith(expectedPrefix)); - } -} diff --git a/packages/NetworkStack/tests/unit/src/android/net/util/PacketReaderTest.java b/packages/NetworkStack/tests/unit/src/android/net/util/PacketReaderTest.java deleted file mode 100644 index 289dcade99a6..000000000000 --- a/packages/NetworkStack/tests/unit/src/android/net/util/PacketReaderTest.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Copyright (C) 2016 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.util; - -import static android.net.util.PacketReader.DEFAULT_RECV_BUF_SIZE; -import static android.system.OsConstants.AF_INET6; -import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_DGRAM; -import static android.system.OsConstants.SOCK_NONBLOCK; -import static android.system.OsConstants.SOL_SOCKET; -import static android.system.OsConstants.SO_SNDTIMEO; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import android.os.Handler; -import android.os.HandlerThread; -import android.system.ErrnoException; -import android.system.Os; -import android.system.StructTimeval; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.FileDescriptor; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.InetSocketAddress; -import java.net.SocketException; -import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Tests for PacketReader. - * - * @hide - */ -@RunWith(AndroidJUnit4.class) -@SmallTest -public class PacketReaderTest { - static final InetAddress LOOPBACK6 = Inet6Address.getLoopbackAddress(); - static final StructTimeval TIMEO = StructTimeval.fromMillis(500); - - protected CountDownLatch mLatch; - protected FileDescriptor mLocalSocket; - protected InetSocketAddress mLocalSockName; - protected byte[] mLastRecvBuf; - protected boolean mStopped; - protected HandlerThread mHandlerThread; - protected PacketReader mReceiver; - - class UdpLoopbackReader extends PacketReader { - public UdpLoopbackReader(Handler h) { - super(h); - } - - @Override - protected FileDescriptor createFd() { - FileDescriptor s = null; - try { - s = Os.socket(AF_INET6, SOCK_DGRAM | SOCK_NONBLOCK, IPPROTO_UDP); - Os.bind(s, LOOPBACK6, 0); - mLocalSockName = (InetSocketAddress) Os.getsockname(s); - Os.setsockoptTimeval(s, SOL_SOCKET, SO_SNDTIMEO, TIMEO); - } catch (ErrnoException|SocketException e) { - closeFd(s); - fail(); - return null; - } - - mLocalSocket = s; - return s; - } - - @Override - protected void handlePacket(byte[] recvbuf, int length) { - mLastRecvBuf = Arrays.copyOf(recvbuf, length); - mLatch.countDown(); - } - - @Override - protected void onStart() { - mStopped = false; - mLatch.countDown(); - } - - @Override - protected void onStop() { - mStopped = true; - mLatch.countDown(); - } - }; - - @Before - public void setUp() { - resetLatch(); - mLocalSocket = null; - mLocalSockName = null; - mLastRecvBuf = null; - mStopped = false; - - mHandlerThread = new HandlerThread(PacketReaderTest.class.getSimpleName()); - mHandlerThread.start(); - } - - @After - public void tearDown() throws Exception { - if (mReceiver != null) { - mHandlerThread.getThreadHandler().post(() -> { mReceiver.stop(); }); - waitForActivity(); - } - mReceiver = null; - mHandlerThread.quit(); - mHandlerThread = null; - } - - void resetLatch() { mLatch = new CountDownLatch(1); } - - void waitForActivity() throws Exception { - try { - mLatch.await(1000, TimeUnit.MILLISECONDS); - } finally { - resetLatch(); - } - } - - void sendPacket(byte[] contents) throws Exception { - final DatagramSocket sender = new DatagramSocket(); - sender.connect(mLocalSockName); - sender.send(new DatagramPacket(contents, contents.length)); - sender.close(); - } - - @Test - public void testBasicWorking() throws Exception { - final Handler h = mHandlerThread.getThreadHandler(); - mReceiver = new UdpLoopbackReader(h); - - h.post(() -> { mReceiver.start(); }); - waitForActivity(); - assertTrue(mLocalSockName != null); - assertEquals(LOOPBACK6, mLocalSockName.getAddress()); - assertTrue(0 < mLocalSockName.getPort()); - assertTrue(mLocalSocket != null); - assertFalse(mStopped); - - final byte[] one = "one 1".getBytes("UTF-8"); - sendPacket(one); - waitForActivity(); - assertEquals(1, mReceiver.numPacketsReceived()); - assertTrue(Arrays.equals(one, mLastRecvBuf)); - assertFalse(mStopped); - - final byte[] two = "two 2".getBytes("UTF-8"); - sendPacket(two); - waitForActivity(); - assertEquals(2, mReceiver.numPacketsReceived()); - assertTrue(Arrays.equals(two, mLastRecvBuf)); - assertFalse(mStopped); - - mReceiver.stop(); - waitForActivity(); - assertEquals(2, mReceiver.numPacketsReceived()); - assertTrue(Arrays.equals(two, mLastRecvBuf)); - assertTrue(mStopped); - mReceiver = null; - } - - class NullPacketReader extends PacketReader { - public NullPacketReader(Handler h, int recvbufsize) { - super(h, recvbufsize); - } - - @Override - public FileDescriptor createFd() { return null; } - } - - @Test - public void testMinimalRecvBufSize() throws Exception { - final Handler h = mHandlerThread.getThreadHandler(); - - for (int i : new int[]{-1, 0, 1, DEFAULT_RECV_BUF_SIZE-1}) { - final PacketReader b = new NullPacketReader(h, i); - assertEquals(DEFAULT_RECV_BUF_SIZE, b.recvBufSize()); - } - } -} diff --git a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java deleted file mode 100644 index 832b7124dc05..000000000000 --- a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +++ /dev/null @@ -1,1022 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity; - -import static android.net.CaptivePortal.APP_RETURN_DISMISSED; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; -import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_EVALUATION_TYPE; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL; -import static android.net.util.DataStallUtils.CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD; -import static android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS; -import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; - -import static org.junit.Assert.assertArrayEquals; -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.ArgumentMatchers.eq; -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.annotation.NonNull; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.net.ConnectivityManager; -import android.net.DnsResolver; -import android.net.INetworkMonitorCallbacks; -import android.net.InetAddresses; -import android.net.LinkProperties; -import android.net.Network; -import android.net.NetworkCapabilities; -import android.net.NetworkInfo; -import android.net.captiveportal.CaptivePortalProbeResult; -import android.net.metrics.IpConnectivityLog; -import android.net.shared.PrivateDnsConfig; -import android.net.util.SharedLog; -import android.net.wifi.WifiInfo; -import android.net.wifi.WifiManager; -import android.os.Bundle; -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.Looper; -import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; -import android.provider.Settings; -import android.telephony.CellSignalStrength; -import android.telephony.TelephonyManager; -import android.util.ArrayMap; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.networkstack.R; -import com.android.networkstack.metrics.DataStallDetectionStats; -import com.android.networkstack.metrics.DataStallStatsUtils; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.Spy; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.InetAddress; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Random; -import java.util.concurrent.Executor; - -import javax.net.ssl.SSLHandshakeException; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class NetworkMonitorTest { - private static final String LOCATION_HEADER = "location"; - - private @Mock Context mContext; - private @Mock Resources mResources; - private @Mock IpConnectivityLog mLogger; - private @Mock SharedLog mValidationLogger; - private @Mock NetworkInfo mNetworkInfo; - private @Mock DnsResolver mDnsResolver; - private @Mock ConnectivityManager mCm; - private @Mock TelephonyManager mTelephony; - private @Mock WifiManager mWifi; - private @Mock HttpURLConnection mHttpConnection; - private @Mock HttpURLConnection mHttpsConnection; - private @Mock HttpURLConnection mFallbackConnection; - private @Mock HttpURLConnection mOtherFallbackConnection; - private @Mock Random mRandom; - private @Mock NetworkMonitor.Dependencies mDependencies; - private @Mock INetworkMonitorCallbacks mCallbacks; - private @Spy Network mCleartextDnsNetwork = new Network(TEST_NETID); - private @Mock Network mNetwork; - private @Mock DataStallStatsUtils mDataStallStatsUtils; - private @Mock WifiInfo mWifiInfo; - private @Captor ArgumentCaptor<String> mNetworkTestedRedirectUrlCaptor; - - private HashSet<WrappedNetworkMonitor> mCreatedNetworkMonitors; - private HashSet<BroadcastReceiver> mRegisteredReceivers; - - private static final int TEST_NETID = 4242; - private static final String TEST_HTTP_URL = "http://www.google.com/gen_204"; - private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204"; - private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204"; - private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204"; - private static final String TEST_MCCMNC = "123456"; - - private static final int RETURN_CODE_DNS_SUCCESS = 0; - private static final int RETURN_CODE_DNS_TIMEOUT = 255; - private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5; - - private static final int HANDLER_TIMEOUT_MS = 1000; - - private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties(); - - private static final NetworkCapabilities METERED_CAPABILITIES = new NetworkCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_INTERNET); - - private static final NetworkCapabilities NOT_METERED_CAPABILITIES = new NetworkCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .addCapability(NET_CAPABILITY_INTERNET) - .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); - - private static final NetworkCapabilities NO_INTERNET_CAPABILITIES = new NetworkCapabilities() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - - /** - * Fakes DNS responses. - * - * Allows test methods to configure the IP addresses that will be resolved by - * Network#getAllByName and by DnsResolver#query. - */ - class FakeDns { - private final ArrayMap<String, List<InetAddress>> mAnswers = new ArrayMap<>(); - private boolean mNonBypassPrivateDnsWorking = true; - - /** Whether DNS queries on mNonBypassPrivateDnsWorking should succeed. */ - private void setNonBypassPrivateDnsWorking(boolean working) { - mNonBypassPrivateDnsWorking = working; - } - - /** Clears all DNS entries. */ - private synchronized void clearAll() { - mAnswers.clear(); - } - - /** Returns the answer for a given name on the given mock network. */ - private synchronized List<InetAddress> getAnswer(Object mock, String hostname) { - if (mock == mNetwork && !mNonBypassPrivateDnsWorking) { - return null; - } - if (mAnswers.containsKey(hostname)) { - return mAnswers.get(hostname); - } - return mAnswers.get("*"); - } - - /** Sets the answer for a given name. */ - private synchronized void setAnswer(String hostname, String[] answer) - throws UnknownHostException { - if (answer == null) { - mAnswers.remove(hostname); - } else { - List<InetAddress> answerList = new ArrayList<>(); - for (String addr : answer) { - answerList.add(InetAddresses.parseNumericAddress(addr)); - } - mAnswers.put(hostname, answerList); - } - } - - /** Simulates a getAllByName call for the specified name on the specified mock network. */ - private InetAddress[] getAllByName(Object mock, String hostname) - throws UnknownHostException { - List<InetAddress> answer = getAnswer(mock, hostname); - if (answer == null || answer.size() == 0) { - throw new UnknownHostException(hostname); - } - return answer.toArray(new InetAddress[0]); - } - - /** Starts mocking DNS queries. */ - private void startMocking() throws UnknownHostException { - // Queries on mNetwork using getAllByName. - doAnswer(invocation -> { - return getAllByName(invocation.getMock(), invocation.getArgument(0)); - }).when(mNetwork).getAllByName(any()); - - // Queries on mCleartextDnsNetwork using DnsResolver#query. - doAnswer(invocation -> { - String hostname = (String) invocation.getArgument(1); - Executor executor = (Executor) invocation.getArgument(3); - DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(5); - - List<InetAddress> answer = getAnswer(invocation.getMock(), hostname); - if (answer != null && answer.size() > 0) { - new Handler(Looper.getMainLooper()).post(() -> { - executor.execute(() -> callback.onAnswer(answer, 0)); - }); - } - // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE. - return null; - }).when(mDnsResolver).query(any(), any(), anyInt(), any(), any(), any()); - - // Queries on mCleartextDnsNetwork using using DnsResolver#query with QueryType. - doAnswer(invocation -> { - String hostname = (String) invocation.getArgument(1); - Executor executor = (Executor) invocation.getArgument(4); - DnsResolver.Callback<List<InetAddress>> callback = invocation.getArgument(6); - - List<InetAddress> answer = getAnswer(invocation.getMock(), hostname); - if (answer != null && answer.size() > 0) { - new Handler(Looper.getMainLooper()).post(() -> { - executor.execute(() -> callback.onAnswer(answer, 0)); - }); - } - // If no answers, do nothing. sendDnsProbeWithTimeout will time out and throw UHE. - return null; - }).when(mDnsResolver).query(any(), any(), anyInt(), anyInt(), any(), any(), any()); - } - } - - private FakeDns mFakeDns; - - @Before - public void setUp() throws IOException { - MockitoAnnotations.initMocks(this); - when(mDependencies.getPrivateDnsBypassNetwork(any())).thenReturn(mCleartextDnsNetwork); - when(mDependencies.getDnsResolver()).thenReturn(mDnsResolver); - when(mDependencies.getRandom()).thenReturn(mRandom); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())) - .thenReturn(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); - when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CAPTIVE_PORTAL_USE_HTTPS), - anyInt())).thenReturn(1); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTP_URL), any())) - .thenReturn(TEST_HTTP_URL); - when(mDependencies.getSetting(any(), eq(Settings.Global.CAPTIVE_PORTAL_HTTPS_URL), any())) - .thenReturn(TEST_HTTPS_URL); - - doReturn(mCleartextDnsNetwork).when(mNetwork).getPrivateDnsBypassingCopy(); - - when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mCm); - when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony); - when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi); - when(mContext.getResources()).thenReturn(mResources); - - when(mResources.getString(anyInt())).thenReturn(""); - when(mResources.getStringArray(anyInt())).thenReturn(new String[0]); - - when(mNetworkInfo.getType()).thenReturn(ConnectivityManager.TYPE_WIFI); - setFallbackUrl(TEST_FALLBACK_URL); - setOtherFallbackUrls(TEST_OTHER_FALLBACK_URL); - setFallbackSpecs(null); // Test with no fallback spec by default - when(mRandom.nextInt()).thenReturn(0); - - // DNS probe timeout should not be defined more than half of HANDLER_TIMEOUT_MS. Otherwise, - // it will fail the test because of timeout expired for querying AAAA and A sequentially. - when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) - .thenReturn(200); - - doAnswer((invocation) -> { - URL url = invocation.getArgument(0); - switch(url.toString()) { - case TEST_HTTP_URL: - return mHttpConnection; - case TEST_HTTPS_URL: - return mHttpsConnection; - case TEST_FALLBACK_URL: - return mFallbackConnection; - case TEST_OTHER_FALLBACK_URL: - return mOtherFallbackConnection; - default: - fail("URL not mocked: " + url.toString()); - return null; - } - }).when(mCleartextDnsNetwork).openConnection(any()); - when(mHttpConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); - when(mHttpsConnection.getRequestProperties()).thenReturn(new ArrayMap<>()); - - mFakeDns = new FakeDns(); - mFakeDns.startMocking(); - mFakeDns.setAnswer("*", new String[]{"2001:db8::1", "192.0.2.2"}); - - when(mContext.registerReceiver(any(BroadcastReceiver.class), any())).then((invocation) -> { - mRegisteredReceivers.add(invocation.getArgument(0)); - return new Intent(); - }); - - doAnswer((invocation) -> { - mRegisteredReceivers.remove(invocation.getArgument(0)); - return null; - }).when(mContext).unregisterReceiver(any()); - - setMinDataStallEvaluateInterval(500); - setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); - setValidDataStallDnsTimeThreshold(500); - setConsecutiveDnsTimeoutThreshold(5); - - mCreatedNetworkMonitors = new HashSet<>(); - mRegisteredReceivers = new HashSet<>(); - } - - @After - public void tearDown() { - mFakeDns.clearAll(); - assertTrue(mCreatedNetworkMonitors.size() > 0); - // Make a local copy of mCreatedNetworkMonitors because during the iteration below, - // WrappedNetworkMonitor#onQuitting will delete elements from it on the handler threads. - WrappedNetworkMonitor[] networkMonitors = mCreatedNetworkMonitors.toArray( - new WrappedNetworkMonitor[0]); - for (WrappedNetworkMonitor nm : networkMonitors) { - nm.notifyNetworkDisconnected(); - nm.awaitQuit(); - } - assertEquals("NetworkMonitor still running after disconnect", - 0, mCreatedNetworkMonitors.size()); - assertEquals("BroadcastReceiver still registered after disconnect", - 0, mRegisteredReceivers.size()); - } - - private class WrappedNetworkMonitor extends NetworkMonitor { - private long mProbeTime = 0; - private final ConditionVariable mQuitCv = new ConditionVariable(false); - - WrappedNetworkMonitor() { - super(mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, - mDependencies, mDataStallStatsUtils); - } - - @Override - protected long getLastProbeTime() { - return mProbeTime; - } - - protected void setLastProbeTime(long time) { - mProbeTime = time; - } - - @Override - protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) { - generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); - } - - @Override - protected void onQuitting() { - assertTrue(mCreatedNetworkMonitors.remove(this)); - mQuitCv.open(); - } - - protected void awaitQuit() { - assertTrue("NetworkMonitor did not quit after " + HANDLER_TIMEOUT_MS + "ms", - mQuitCv.block(HANDLER_TIMEOUT_MS)); - } - } - - private WrappedNetworkMonitor makeMonitor(NetworkCapabilities nc) { - final WrappedNetworkMonitor nm = new WrappedNetworkMonitor(); - nm.start(); - setNetworkCapabilities(nm, nc); - waitForIdle(nm.getHandler()); - mCreatedNetworkMonitors.add(nm); - return nm; - } - - private WrappedNetworkMonitor makeMeteredNetworkMonitor() { - final WrappedNetworkMonitor nm = makeMonitor(METERED_CAPABILITIES); - return nm; - } - - private WrappedNetworkMonitor makeNotMeteredNetworkMonitor() { - final WrappedNetworkMonitor nm = makeMonitor(NOT_METERED_CAPABILITIES); - return nm; - } - - private void setNetworkCapabilities(NetworkMonitor nm, NetworkCapabilities nc) { - nm.notifyNetworkCapabilitiesChanged(nc); - waitForIdle(nm.getHandler()); - } - - private void waitForIdle(Handler handler) { - final ConditionVariable cv = new ConditionVariable(false); - handler.post(cv::open); - if (!cv.block(HANDLER_TIMEOUT_MS)) { - fail("Timed out waiting for handler"); - } - } - - @Test - public void testGetIntSetting() throws Exception { - WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); - - // No config resource, no device config. Expect to get default resource. - doThrow(new Resources.NotFoundException()) - .when(mResources).getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout)); - doAnswer(invocation -> { - int defaultValue = invocation.getArgument(2); - return defaultValue; - }).when(mDependencies).getDeviceConfigPropertyInt(any(), - eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), - anyInt()); - when(mResources.getInteger(eq(R.integer.default_captive_portal_dns_probe_timeout))) - .thenReturn(42); - assertEquals(42, wnm.getIntSetting(mContext, - R.integer.config_captive_portal_dns_probe_timeout, - NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, - R.integer.default_captive_portal_dns_probe_timeout)); - - // Set device config. Expect to get device config. - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT), anyInt())) - .thenReturn(1234); - assertEquals(1234, wnm.getIntSetting(mContext, - R.integer.config_captive_portal_dns_probe_timeout, - NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, - R.integer.default_captive_portal_dns_probe_timeout)); - - // Set config resource. Expect to get config resource. - when(mResources.getInteger(eq(R.integer.config_captive_portal_dns_probe_timeout))) - .thenReturn(5678); - assertEquals(5678, wnm.getIntSetting(mContext, - R.integer.config_captive_portal_dns_probe_timeout, - NetworkMonitor.CONFIG_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT, - R.integer.default_captive_portal_dns_probe_timeout)); - } - - @Test - public void testIsCaptivePortal_HttpProbeIsPortal() throws IOException { - setSslException(mHttpsConnection); - setPortal302(mHttpConnection); - - runPortalNetworkTest(); - } - - @Test - public void testIsCaptivePortal_HttpsProbeIsNotPortal() throws IOException { - setStatus(mHttpsConnection, 204); - setStatus(mHttpConnection, 500); - - runNotPortalNetworkTest(); - } - - @Test - public void testIsCaptivePortal_FallbackProbeIsPortal() throws IOException { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setPortal302(mFallbackConnection); - - runPortalNetworkTest(); - } - - @Test - public void testIsCaptivePortal_FallbackProbeIsNotPortal() throws IOException { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 500); - - // Fallback probe did not see portal, HTTPS failed -> inconclusive - runFailedNetworkTest(); - } - - @Test - public void testIsCaptivePortal_OtherFallbackProbeIsPortal() throws IOException { - // Set all fallback probes but one to invalid URLs to verify they are being skipped - setFallbackUrl(TEST_FALLBACK_URL); - setOtherFallbackUrls(TEST_FALLBACK_URL + "," + TEST_OTHER_FALLBACK_URL); - - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 500); - setPortal302(mOtherFallbackConnection); - - // TEST_OTHER_FALLBACK_URL is third - when(mRandom.nextInt()).thenReturn(2); - - // First check always uses the first fallback URL: inconclusive - final NetworkMonitor monitor = runNetworkTest(NETWORK_TEST_RESULT_INVALID); - assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); - verify(mFallbackConnection, times(1)).getResponseCode(); - verify(mOtherFallbackConnection, never()).getResponseCode(); - - // Second check uses the URL chosen by Random - final CaptivePortalProbeResult result = monitor.isCaptivePortal(); - assertTrue(result.isPortal()); - verify(mOtherFallbackConnection, times(1)).getResponseCode(); - } - - @Test - public void testIsCaptivePortal_AllProbesFailed() throws IOException { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 404); - - runFailedNetworkTest(); - verify(mFallbackConnection, times(1)).getResponseCode(); - verify(mOtherFallbackConnection, never()).getResponseCode(); - } - - @Test - public void testIsCaptivePortal_InvalidUrlSkipped() throws IOException { - setFallbackUrl("invalid"); - setOtherFallbackUrls("otherinvalid," + TEST_OTHER_FALLBACK_URL + ",yetanotherinvalid"); - - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setPortal302(mOtherFallbackConnection); - - runPortalNetworkTest(); - verify(mOtherFallbackConnection, times(1)).getResponseCode(); - verify(mFallbackConnection, never()).getResponseCode(); - } - - private void setupFallbackSpec() throws IOException { - setFallbackSpecs("http://example.com@@/@@204@@/@@" - + "@@,@@" - + TEST_OTHER_FALLBACK_URL + "@@/@@30[12]@@/@@https://(www\\.)?google.com/?.*"); - - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - - // Use the 2nd fallback spec - when(mRandom.nextInt()).thenReturn(1); - } - - @Test - public void testIsCaptivePortal_FallbackSpecIsPartial() throws IOException { - setupFallbackSpec(); - set302(mOtherFallbackConnection, "https://www.google.com/test?q=3"); - - // HTTPS failed, fallback spec went through -> partial connectivity - runPartialConnectivityNetworkTest(); - verify(mOtherFallbackConnection, times(1)).getResponseCode(); - verify(mFallbackConnection, never()).getResponseCode(); - } - - @Test - public void testIsCaptivePortal_FallbackSpecIsPortal() throws IOException { - setupFallbackSpec(); - set302(mOtherFallbackConnection, "http://login.portal.example.com"); - - runPortalNetworkTest(); - } - - @Test - public void testIsCaptivePortal_IgnorePortals() throws IOException { - setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE); - setSslException(mHttpsConnection); - setPortal302(mHttpConnection); - - runNotPortalNetworkTest(); - } - - @Test - public void testIsDataStall_EvaluationDisabled() { - setDataStallEvaluationType(0); - WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - assertFalse(wrappedMonitor.isDataStall()); - } - - @Test - public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() { - WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); - assertTrue(wrappedMonitor.isDataStall()); - } - - @Test - public void testIsDataStall_EvaluationDnsOnMeteredNetwork() { - WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - assertFalse(wrappedMonitor.isDataStall()); - - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); - assertTrue(wrappedMonitor.isDataStall()); - } - - @Test - public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() { - WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, 3); - assertFalse(wrappedMonitor.isDataStall()); - // Reset consecutive timeout counts. - makeDnsSuccessEvent(wrappedMonitor, 1); - makeDnsTimeoutEvent(wrappedMonitor, 2); - assertFalse(wrappedMonitor.isDataStall()); - - makeDnsTimeoutEvent(wrappedMonitor, 3); - assertTrue(wrappedMonitor.isDataStall()); - - // Set the value to larger than the default dns log size. - setConsecutiveDnsTimeoutThreshold(51); - wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, 50); - assertFalse(wrappedMonitor.isDataStall()); - - makeDnsTimeoutEvent(wrappedMonitor, 1); - assertTrue(wrappedMonitor.isDataStall()); - } - - @Test - public void testIsDataStall_EvaluationDnsWithDnsTimeThreshold() { - // Test dns events happened in valid dns time threshold. - WrappedNetworkMonitor wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); - assertFalse(wrappedMonitor.isDataStall()); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - assertTrue(wrappedMonitor.isDataStall()); - - // Test dns events happened before valid dns time threshold. - setValidDataStallDnsTimeThreshold(0); - wrappedMonitor = makeMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); - assertFalse(wrappedMonitor.isDataStall()); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - assertFalse(wrappedMonitor.isDataStall()); - } - - @Test - public void testBrokenNetworkNotValidated() throws Exception { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 404); - - runFailedNetworkTest(); - } - - @Test - public void testNoInternetCapabilityValidated() throws Exception { - runNetworkTest(NO_INTERNET_CAPABILITIES, NETWORK_TEST_RESULT_VALID); - verify(mCleartextDnsNetwork, never()).openConnection(any()); - } - - @Test - public void testLaunchCaptivePortalApp() throws Exception { - setSslException(mHttpsConnection); - setPortal302(mHttpConnection); - - final NetworkMonitor nm = makeMonitor(METERED_CAPABILITIES); - nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, METERED_CAPABILITIES); - - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .showProvisioningNotification(any(), any()); - - assertEquals(1, mRegisteredReceivers.size()); - - // Check that startCaptivePortalApp sends the expected intent. - nm.launchCaptivePortalApp(); - - final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); - final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class); - verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1)) - .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture()); - final Bundle bundle = bundleCaptor.getValue(); - final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK); - assertEquals(TEST_NETID, bundleNetwork.netId); - // network is passed both in bundle and as parameter, as the bundle is opaque to the - // framework and only intended for the captive portal app, but the framework needs - // the network to identify the right NetworkMonitor. - assertEquals(TEST_NETID, networkCaptor.getValue().netId); - - // Have the app report that the captive portal is dismissed, and check that we revalidate. - setStatus(mHttpsConnection, 204); - setStatus(mHttpConnection, 204); - - nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null); - - assertEquals(0, mRegisteredReceivers.size()); - } - - @Test - public void testPrivateDnsSuccess() throws Exception { - setStatus(mHttpsConnection, 204); - setStatus(mHttpConnection, 204); - mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::53"}); - - WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); - wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); - wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), eq(null)); - } - - @Test - public void testPrivateDnsResolutionRetryUpdate() throws Exception { - // Set a private DNS hostname that doesn't resolve and expect validation to fail. - mFakeDns.setAnswer("dns.google", new String[0]); - setStatus(mHttpsConnection, 204); - setStatus(mHttpConnection, 204); - - WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); - wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); - wnm.notifyNetworkConnected(TEST_LINK_PROPERTIES, NOT_METERED_CAPABILITIES); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_INVALID), eq(null)); - - // Fix DNS and retry, expect validation to succeed. - reset(mCallbacks); - mFakeDns.setAnswer("dns.google", new String[]{"2001:db8::1"}); - - wnm.forceReevaluation(Process.myUid()); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), eq(null)); - - // Change configuration to an invalid DNS name, expect validation to fail. - reset(mCallbacks); - mFakeDns.setAnswer("dns.bad", new String[0]); - wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.bad", new InetAddress[0])); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_INVALID), eq(null)); - - // Change configuration back to working again, but make private DNS not work. - // Expect validation to fail. - reset(mCallbacks); - mFakeDns.setNonBypassPrivateDnsWorking(false); - wnm.notifyPrivateDnsSettingsChanged(new PrivateDnsConfig("dns.google", new InetAddress[0])); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_INVALID), eq(null)); - - // Make private DNS work again. Expect validation to succeed. - reset(mCallbacks); - mFakeDns.setNonBypassPrivateDnsWorking(true); - wnm.forceReevaluation(Process.myUid()); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), eq(null)); - } - - @Test - public void testDataStall_StallSuspectedAndSendMetrics() throws IOException { - WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, 5); - assertTrue(wrappedMonitor.isDataStall()); - verify(mDataStallStatsUtils, times(1)).write(makeEmptyDataStallDetectionStats(), any()); - } - - @Test - public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException { - WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, 3); - assertFalse(wrappedMonitor.isDataStall()); - verify(mDataStallStatsUtils, never()).write(makeEmptyDataStallDetectionStats(), any()); - } - - @Test - public void testCollectDataStallMetrics() { - WrappedNetworkMonitor wrappedMonitor = makeNotMeteredNetworkMonitor(); - - when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE); - when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC); - when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC); - - DataStallDetectionStats.Builder stats = - new DataStallDetectionStats.Builder() - .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) - .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR) - .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */, - true /* roaming */, - TEST_MCCMNC /* networkMccmnc */, - TEST_MCCMNC /* simMccmnc */, - CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */); - generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); - - assertEquals(wrappedMonitor.buildDataStallDetectionStats( - NetworkCapabilities.TRANSPORT_CELLULAR), stats.build()); - - when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo); - - stats = new DataStallDetectionStats.Builder() - .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) - .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI) - .setWiFiData(mWifiInfo); - generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); - - assertEquals( - wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI), - stats.build()); - } - - @Test - public void testIgnoreHttpsProbe() throws Exception { - setSslException(mHttpsConnection); - setStatus(mHttpConnection, 204); - - final NetworkMonitor nm = runNetworkTest(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY); - - nm.setAcceptPartialConnectivity(); - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(NETWORK_TEST_RESULT_VALID), any()); - } - - @Test - public void testIsPartialConnectivity() throws IOException { - setStatus(mHttpsConnection, 500); - setStatus(mHttpConnection, 204); - setStatus(mFallbackConnection, 500); - runPartialConnectivityNetworkTest(); - - setStatus(mHttpsConnection, 500); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 204); - runPartialConnectivityNetworkTest(); - } - - private void assertIpAddressArrayEquals(String[] expected, InetAddress[] actual) { - String[] actualStrings = new String[actual.length]; - for (int i = 0; i < actual.length; i++) { - actualStrings[i] = actual[i].getHostAddress(); - } - assertArrayEquals("Array of IP addresses differs", expected, actualStrings); - } - - @Test - public void testSendDnsProbeWithTimeout() throws Exception { - WrappedNetworkMonitor wnm = makeNotMeteredNetworkMonitor(); - final int shortTimeoutMs = 200; - - // Clear the wildcard DNS response created in setUp. - mFakeDns.setAnswer("*", null); - - String[] expected = new String[]{"2001:db8::"}; - mFakeDns.setAnswer("www.google.com", expected); - InetAddress[] actual = wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); - assertIpAddressArrayEquals(expected, actual); - - expected = new String[]{"2001:db8::", "192.0.2.1"}; - mFakeDns.setAnswer("www.googleapis.com", expected); - actual = wnm.sendDnsProbeWithTimeout("www.googleapis.com", shortTimeoutMs); - assertIpAddressArrayEquals(expected, actual); - - mFakeDns.setAnswer("www.google.com", new String[0]); - try { - wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); - fail("No DNS results, expected UnknownHostException"); - } catch (UnknownHostException e) { - } - - mFakeDns.setAnswer("www.google.com", null); - try { - wnm.sendDnsProbeWithTimeout("www.google.com", shortTimeoutMs); - fail("DNS query timed out, expected UnknownHostException"); - } catch (UnknownHostException e) { - } - } - - private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { - for (int i = 0; i < count; i++) { - wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( - RETURN_CODE_DNS_TIMEOUT); - } - } - - private void makeDnsSuccessEvent(WrappedNetworkMonitor wrappedMonitor, int count) { - for (int i = 0; i < count; i++) { - wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( - RETURN_CODE_DNS_SUCCESS); - } - } - - private DataStallDetectionStats makeEmptyDataStallDetectionStats() { - return new DataStallDetectionStats.Builder().build(); - } - - private void setDataStallEvaluationType(int type) { - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(CONFIG_DATA_STALL_EVALUATION_TYPE), anyInt())).thenReturn(type); - } - - private void setMinDataStallEvaluateInterval(int time) { - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(CONFIG_DATA_STALL_MIN_EVALUATE_INTERVAL), anyInt())).thenReturn(time); - } - - private void setValidDataStallDnsTimeThreshold(int time) { - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(CONFIG_DATA_STALL_VALID_DNS_TIME_THRESHOLD), anyInt())).thenReturn(time); - } - - private void setConsecutiveDnsTimeoutThreshold(int num) { - when(mDependencies.getDeviceConfigPropertyInt(any(), - eq(CONFIG_DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD), anyInt())).thenReturn(num); - } - - private void setFallbackUrl(String url) { - when(mDependencies.getSetting(any(), - eq(Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL), any())).thenReturn(url); - } - - private void setOtherFallbackUrls(String urls) { - when(mDependencies.getDeviceConfigProperty(any(), - eq(CAPTIVE_PORTAL_OTHER_FALLBACK_URLS), any())).thenReturn(urls); - } - - private void setFallbackSpecs(String specs) { - when(mDependencies.getDeviceConfigProperty(any(), - eq(CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS), any())).thenReturn(specs); - } - - private void setCaptivePortalMode(int mode) { - when(mDependencies.getSetting(any(), - eq(Settings.Global.CAPTIVE_PORTAL_MODE), anyInt())).thenReturn(mode); - } - - private void runPortalNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_INVALID); - assertEquals(1, mRegisteredReceivers.size()); - assertNotNull(mNetworkTestedRedirectUrlCaptor.getValue()); - } - - private void runNotPortalNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_VALID); - assertEquals(0, mRegisteredReceivers.size()); - assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); - } - - private void runFailedNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_INVALID); - assertEquals(0, mRegisteredReceivers.size()); - assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); - } - - private void runPartialConnectivityNetworkTest() { - runNetworkTest(NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY); - assertEquals(0, mRegisteredReceivers.size()); - assertNull(mNetworkTestedRedirectUrlCaptor.getValue()); - } - - private NetworkMonitor runNetworkTest(int testResult) { - return runNetworkTest(METERED_CAPABILITIES, testResult); - } - - private NetworkMonitor runNetworkTest(NetworkCapabilities nc, int testResult) { - final NetworkMonitor monitor = makeMonitor(nc); - monitor.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc); - try { - verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) - .notifyNetworkTested(eq(testResult), mNetworkTestedRedirectUrlCaptor.capture()); - } catch (RemoteException e) { - fail("Unexpected exception: " + e); - } - waitForIdle(monitor.getHandler()); - - return monitor; - } - - private void setSslException(HttpURLConnection connection) throws IOException { - doThrow(new SSLHandshakeException("Invalid cert")).when(connection).getResponseCode(); - } - - private void set302(HttpURLConnection connection, String location) throws IOException { - setStatus(connection, 302); - doReturn(location).when(connection).getHeaderField(LOCATION_HEADER); - } - - private void setPortal302(HttpURLConnection connection) throws IOException { - set302(connection, "http://login.example.com"); - } - - private void setStatus(HttpURLConnection connection, int status) throws IOException { - doReturn(status).when(connection).getResponseCode(); - } - - private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) { - for (int i = 0; i < num; i++) { - stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */); - } - } -} - diff --git a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java deleted file mode 100644 index 87346e5d6a28..000000000000 --- a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/IpMemoryStoreServiceTest.java +++ /dev/null @@ -1,663 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity.ipmemorystore; - -import static com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService.InterruptMaintenance; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doReturn; - -import android.app.job.JobScheduler; -import android.content.Context; -import android.net.ipmemorystore.Blob; -import android.net.ipmemorystore.IOnBlobRetrievedListener; -import android.net.ipmemorystore.IOnL2KeyResponseListener; -import android.net.ipmemorystore.IOnNetworkAttributesRetrievedListener; -import android.net.ipmemorystore.IOnSameL3NetworkResponseListener; -import android.net.ipmemorystore.IOnStatusListener; -import android.net.ipmemorystore.NetworkAttributes; -import android.net.ipmemorystore.NetworkAttributesParcelable; -import android.net.ipmemorystore.SameL3NetworkResponse; -import android.net.ipmemorystore.SameL3NetworkResponseParcelable; -import android.net.ipmemorystore.Status; -import android.net.ipmemorystore.StatusParcelable; -import android.os.ConditionVariable; -import android.os.IBinder; -import android.os.RemoteException; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.File; -import java.lang.reflect.Modifier; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -/** Unit tests for {@link IpMemoryStoreService}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class IpMemoryStoreServiceTest { - private static final String TEST_CLIENT_ID = "testClientId"; - private static final String TEST_DATA_NAME = "testData"; - - private static final int TEST_DATABASE_SIZE_THRESHOLD = 100 * 1024; //100KB - private static final int DEFAULT_TIMEOUT_MS = 5000; - private static final int LONG_TIMEOUT_MS = 30000; - private static final int FAKE_KEY_COUNT = 20; - private static final String[] FAKE_KEYS; - static { - FAKE_KEYS = new String[FAKE_KEY_COUNT]; - for (int i = 0; i < FAKE_KEYS.length; ++i) { - FAKE_KEYS[i] = "fakeKey" + i; - } - } - - @Mock - private Context mMockContext; - @Mock - private JobScheduler mMockJobScheduler; - private File mDbFile; - - private IpMemoryStoreService mService; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - final Context context = InstrumentationRegistry.getContext(); - final File dir = context.getFilesDir(); - mDbFile = new File(dir, "test.db"); - doReturn(mDbFile).when(mMockContext).getDatabasePath(anyString()); - doReturn(mMockJobScheduler).when(mMockContext) - .getSystemService(Context.JOB_SCHEDULER_SERVICE); - mService = new IpMemoryStoreService(mMockContext) { - @Override - protected int getDbSizeThreshold() { - return TEST_DATABASE_SIZE_THRESHOLD; - } - - @Override - boolean isDbSizeOverThreshold() { - // Add a 100ms delay here for pausing maintenance job a while. Interrupted flag can - // be set at this time. - waitForMs(100); - return super.isDbSizeOverThreshold(); - } - }; - } - - @After - public void tearDown() { - mService.shutdown(); - mDbFile.delete(); - } - - /** Helper method to make a vanilla IOnStatusListener */ - private IOnStatusListener onStatus(Consumer<Status> functor) { - return new IOnStatusListener() { - @Override - public void onComplete(final StatusParcelable statusParcelable) throws RemoteException { - functor.accept(new Status(statusParcelable)); - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - /** Helper method to make an IOnBlobRetrievedListener */ - private interface OnBlobRetrievedListener { - void onBlobRetrieved(Status status, String l2Key, String name, byte[] data); - } - private IOnBlobRetrievedListener onBlobRetrieved(final OnBlobRetrievedListener functor) { - return new IOnBlobRetrievedListener() { - @Override - public void onBlobRetrieved(final StatusParcelable statusParcelable, - final String l2Key, final String name, final Blob blob) throws RemoteException { - functor.onBlobRetrieved(new Status(statusParcelable), l2Key, name, - null == blob ? null : blob.data); - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - /** Helper method to make an IOnNetworkAttributesRetrievedListener */ - private interface OnNetworkAttributesRetrievedListener { - void onNetworkAttributesRetrieved(Status status, String l2Key, NetworkAttributes attr); - } - private IOnNetworkAttributesRetrievedListener onNetworkAttributesRetrieved( - final OnNetworkAttributesRetrievedListener functor) { - return new IOnNetworkAttributesRetrievedListener() { - @Override - public void onNetworkAttributesRetrieved(final StatusParcelable status, - final String l2Key, final NetworkAttributesParcelable attributes) - throws RemoteException { - functor.onNetworkAttributesRetrieved(new Status(status), l2Key, - null == attributes ? null : new NetworkAttributes(attributes)); - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - /** Helper method to make an IOnSameNetworkResponseListener */ - private interface OnSameL3NetworkResponseListener { - void onSameL3NetworkResponse(Status status, SameL3NetworkResponse answer); - } - private IOnSameL3NetworkResponseListener onSameResponse( - final OnSameL3NetworkResponseListener functor) { - return new IOnSameL3NetworkResponseListener() { - @Override - public void onSameL3NetworkResponse(final StatusParcelable status, - final SameL3NetworkResponseParcelable sameL3Network) - throws RemoteException { - functor.onSameL3NetworkResponse(new Status(status), - null == sameL3Network ? null : new SameL3NetworkResponse(sameL3Network)); - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - /** Helper method to make an IOnL2KeyResponseListener */ - private interface OnL2KeyResponseListener { - void onL2KeyResponse(Status status, String key); - } - private IOnL2KeyResponseListener onL2KeyResponse(final OnL2KeyResponseListener functor) { - return new IOnL2KeyResponseListener() { - @Override - public void onL2KeyResponse(final StatusParcelable status, final String key) - throws RemoteException { - functor.onL2KeyResponse(new Status(status), key); - } - - @Override - public IBinder asBinder() { - return null; - } - - @Override - public int getInterfaceVersion() { - return this.VERSION; - } - }; - } - - // Helper method to factorize some boilerplate - private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor) { - doLatched(timeoutMessage, functor, DEFAULT_TIMEOUT_MS); - } - - private void doLatched(final String timeoutMessage, final Consumer<CountDownLatch> functor, - final int timeout) { - final CountDownLatch latch = new CountDownLatch(1); - functor.accept(latch); - try { - if (!latch.await(timeout, TimeUnit.MILLISECONDS)) { - fail(timeoutMessage); - } - } catch (InterruptedException e) { - fail("Thread was interrupted"); - } - } - - // Helper methods to factorize more boilerplate - private void storeAttributes(final String l2Key, final NetworkAttributes na) { - storeAttributes("Did not complete storing attributes", l2Key, na); - } - private void storeAttributes(final String timeoutMessage, final String l2Key, - final NetworkAttributes na) { - doLatched(timeoutMessage, latch -> mService.storeNetworkAttributes(l2Key, na.toParcelable(), - onStatus(status -> { - assertTrue("Store not successful : " + status.resultCode, status.isSuccess()); - latch.countDown(); - }))); - } - - /** Insert large data that db size will be over threshold for maintenance test usage. */ - private void insertFakeDataAndOverThreshold() { - try { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - na.setGroupHint("hint1"); - na.setMtu(219); - na.setDnsAddresses(Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6"))); - final byte[] data = new byte[]{-3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34}; - final long time = System.currentTimeMillis() - 1; - for (int i = 0; i < 1000; i++) { - int errorCode = IpMemoryStoreDatabase.storeNetworkAttributes( - mService.mDb, - "fakeKey" + i, - // Let first 100 records get expiry. - i < 100 ? time : time + TimeUnit.HOURS.toMillis(i), - na.build()); - assertEquals(errorCode, Status.SUCCESS); - - errorCode = IpMemoryStoreDatabase.storeBlob( - mService.mDb, "fakeKey" + i, TEST_CLIENT_ID, TEST_DATA_NAME, data); - assertEquals(errorCode, Status.SUCCESS); - } - - // After added 5000 records, db size is larger than fake threshold(100KB). - assertTrue(mService.isDbSizeOverThreshold()); - } catch (final UnknownHostException e) { - fail("Insert fake data fail"); - } - } - - /** Wait for assigned time. */ - private void waitForMs(long ms) { - try { - Thread.sleep(ms); - } catch (final InterruptedException e) { - fail("Thread was interrupted"); - } - } - - @Test - public void testNetworkAttributes() throws UnknownHostException { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - na.setAssignedV4AddressExpiry(System.currentTimeMillis() + 7_200_000); - na.setGroupHint("hint1"); - na.setMtu(219); - final String l2Key = FAKE_KEYS[0]; - NetworkAttributes attributes = na.build(); - storeAttributes(l2Key, attributes); - - doLatched("Did not complete retrieving attributes", latch -> - mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved( - (status, key, attr) -> { - assertTrue("Retrieve network attributes not successful : " - + status.resultCode, status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(attributes, attr); - latch.countDown(); - }))); - - final NetworkAttributes.Builder na2 = new NetworkAttributes.Builder(); - na.setDnsAddresses(Arrays.asList( - new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")})); - final NetworkAttributes attributes2 = na2.build(); - storeAttributes("Did not complete storing attributes 2", l2Key, attributes2); - - doLatched("Did not complete retrieving attributes 2", latch -> - mService.retrieveNetworkAttributes(l2Key, onNetworkAttributesRetrieved( - (status, key, attr) -> { - assertTrue("Retrieve network attributes not successful : " - + status.resultCode, status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(attributes.assignedV4Address, attr.assignedV4Address); - assertEquals(attributes.assignedV4AddressExpiry, - attr.assignedV4AddressExpiry); - assertEquals(attributes.groupHint, attr.groupHint); - assertEquals(attributes.mtu, attr.mtu); - assertEquals(attributes2.dnsAddresses, attr.dnsAddresses); - latch.countDown(); - }))); - - doLatched("Did not complete retrieving attributes 3", latch -> - mService.retrieveNetworkAttributes(l2Key + "nonexistent", - onNetworkAttributesRetrieved( - (status, key, attr) -> { - assertTrue("Retrieve network attributes not successful : " - + status.resultCode, status.isSuccess()); - assertEquals(l2Key + "nonexistent", key); - assertNull("Retrieved data not stored", attr); - latch.countDown(); - } - ))); - - // Verify that this test does not miss any new field added later. - // If any field is added to NetworkAttributes it must be tested here for storing - // and retrieving. - assertEquals(5, Arrays.stream(NetworkAttributes.class.getDeclaredFields()) - .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); - } - - @Test - public void testInvalidAttributes() { - doLatched("Did not complete storing bad attributes", latch -> - mService.storeNetworkAttributes("key", null, onStatus(status -> { - assertFalse("Success storing on a null key", - status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - latch.countDown(); - }))); - - final NetworkAttributes na = new NetworkAttributes.Builder().setMtu(2).build(); - doLatched("Did not complete storing bad attributes", latch -> - mService.storeNetworkAttributes(null, na.toParcelable(), onStatus(status -> { - assertFalse("Success storing null attributes on a null key", - status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - latch.countDown(); - }))); - - doLatched("Did not complete storing bad attributes", latch -> - mService.storeNetworkAttributes(null, null, onStatus(status -> { - assertFalse("Success storing null attributes on a null key", - status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - latch.countDown(); - }))); - - doLatched("Did not complete retrieving bad attributes", latch -> - mService.retrieveNetworkAttributes(null, onNetworkAttributesRetrieved( - (status, key, attr) -> { - assertFalse("Success retrieving attributes for a null key", - status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - assertNull(key); - assertNull(attr); - latch.countDown(); - }))); - } - - @Test - public void testPrivateData() { - final Blob b = new Blob(); - b.data = new byte[] { -3, 6, 8, -9, 12, -128, 0, 89, 112, 91, -34 }; - final String l2Key = FAKE_KEYS[0]; - doLatched("Did not complete storing private data", latch -> - mService.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b, - onStatus(status -> { - assertTrue("Store status not successful : " + status.resultCode, - status.isSuccess()); - latch.countDown(); - }))); - - doLatched("Did not complete retrieving private data", latch -> - mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, onBlobRetrieved( - (status, key, name, data) -> { - assertTrue("Retrieve blob status not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(name, TEST_DATA_NAME); - assertTrue(Arrays.equals(b.data, data)); - latch.countDown(); - }))); - - // Most puzzling error message ever - doLatched("Did not complete retrieving nothing", latch -> - mService.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME + "2", onBlobRetrieved( - (status, key, name, data) -> { - assertTrue("Retrieve blob status not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(l2Key, key); - assertEquals(name, TEST_DATA_NAME + "2"); - assertNull(data); - latch.countDown(); - }))); - } - - @Test - public void testFindL2Key() throws UnknownHostException { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - na.setGroupHint("hint0"); - storeAttributes(FAKE_KEYS[0], na.build()); - - na.setDnsAddresses(Arrays.asList( - new InetAddress[] {Inet6Address.getByName("8D56:9AF1::08EE:20F1")})); - na.setMtu(219); - storeAttributes(FAKE_KEYS[1], na.build()); - na.setMtu(null); - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - na.setDnsAddresses(Arrays.asList( - new InetAddress[] {Inet6Address.getByName("0A1C:2E40:480A::1CA6")})); - na.setGroupHint("hint1"); - storeAttributes(FAKE_KEYS[2], na.build()); - na.setMtu(219); - storeAttributes(FAKE_KEYS[3], na.build()); - na.setMtu(240); - storeAttributes(FAKE_KEYS[4], na.build()); - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("5.6.7.8")); - storeAttributes(FAKE_KEYS[5], na.build()); - - // Matches key 5 exactly - doLatched("Did not finish finding L2Key", latch -> - mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(FAKE_KEYS[5], key); - latch.countDown(); - }))); - - // MTU matches key 4 but v4 address matches key 5. The latter is stronger. - na.setMtu(240); - doLatched("Did not finish finding L2Key", latch -> - mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(FAKE_KEYS[5], key); - latch.countDown(); - }))); - - // Closest to key 3 (indeed, identical) - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - na.setMtu(219); - doLatched("Did not finish finding L2Key", latch -> - mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(FAKE_KEYS[3], key); - latch.countDown(); - }))); - - // Group hint alone must not be strong enough to override the rest - na.setGroupHint("hint0"); - doLatched("Did not finish finding L2Key", latch -> - mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(FAKE_KEYS[3], key); - latch.countDown(); - }))); - - // Still closest to key 3, though confidence is lower - na.setGroupHint("hint1"); - na.setDnsAddresses(null); - doLatched("Did not finish finding L2Key", latch -> - mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(FAKE_KEYS[3], key); - latch.countDown(); - }))); - - // But changing the MTU makes this closer to key 4 - na.setMtu(240); - doLatched("Did not finish finding L2Key", latch -> - mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(FAKE_KEYS[4], key); - latch.countDown(); - }))); - - // MTU alone not strong enough to make this group-close - na.setGroupHint(null); - na.setDnsAddresses(null); - na.setAssignedV4Address(null); - doLatched("Did not finish finding L2Key", latch -> - mService.findL2Key(na.build().toParcelable(), onL2KeyResponse((status, key) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertNull(key); - latch.countDown(); - }))); - } - - private void assertNetworksSameness(final String key1, final String key2, final int sameness) { - doLatched("Did not finish evaluating sameness", latch -> - mService.isSameNetwork(key1, key2, onSameResponse((status, answer) -> { - assertTrue("Retrieve network sameness not successful : " + status.resultCode, - status.isSuccess()); - assertEquals(sameness, answer.getNetworkSameness()); - latch.countDown(); - }))); - } - - @Test - public void testIsSameNetwork() throws UnknownHostException { - final NetworkAttributes.Builder na = new NetworkAttributes.Builder(); - na.setAssignedV4Address((Inet4Address) Inet4Address.getByName("1.2.3.4")); - na.setGroupHint("hint1"); - na.setMtu(219); - na.setDnsAddresses(Arrays.asList(Inet6Address.getByName("0A1C:2E40:480A::1CA6"))); - - storeAttributes(FAKE_KEYS[0], na.build()); - // 0 and 1 have identical attributes - storeAttributes(FAKE_KEYS[1], na.build()); - - // Hopefully only the MTU being different still means it's the same network - na.setMtu(200); - storeAttributes(FAKE_KEYS[2], na.build()); - - // Hopefully different MTU, assigned V4 address and grouphint make a different network, - // even with identical DNS addresses - na.setAssignedV4Address(null); - na.setGroupHint("hint2"); - storeAttributes(FAKE_KEYS[3], na.build()); - - assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[1], SameL3NetworkResponse.NETWORK_SAME); - assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[2], SameL3NetworkResponse.NETWORK_SAME); - assertNetworksSameness(FAKE_KEYS[1], FAKE_KEYS[2], SameL3NetworkResponse.NETWORK_SAME); - assertNetworksSameness(FAKE_KEYS[0], FAKE_KEYS[3], SameL3NetworkResponse.NETWORK_DIFFERENT); - assertNetworksSameness(FAKE_KEYS[0], "neverInsertedKey", - SameL3NetworkResponse.NETWORK_NEVER_CONNECTED); - - doLatched("Did not finish evaluating sameness", latch -> - mService.isSameNetwork(null, null, onSameResponse((status, answer) -> { - assertFalse("Retrieve network sameness suspiciously successful : " - + status.resultCode, status.isSuccess()); - assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode); - assertNull(answer); - latch.countDown(); - }))); - } - - - @Test - public void testFullMaintenance() { - insertFakeDataAndOverThreshold(); - - final InterruptMaintenance im = new InterruptMaintenance(0/* Fake JobId */); - // Do full maintenance and then db size should go down and meet the threshold. - doLatched("Maintenance unexpectedly completed successfully", latch -> - mService.fullMaintenance(onStatus((status) -> { - assertTrue("Execute full maintenance failed: " - + status.resultCode, status.isSuccess()); - latch.countDown(); - }), im), LONG_TIMEOUT_MS); - - // Assume that maintenance is successful, db size shall meet the threshold. - assertFalse(mService.isDbSizeOverThreshold()); - } - - @Test - public void testInterruptMaintenance() { - insertFakeDataAndOverThreshold(); - - final InterruptMaintenance im = new InterruptMaintenance(0/* Fake JobId */); - - // Test interruption immediately. - im.setInterrupted(true); - // Do full maintenance and the expectation is not completed by interruption. - doLatched("Maintenance unexpectedly completed successfully", latch -> - mService.fullMaintenance(onStatus((status) -> { - assertFalse(status.isSuccess()); - latch.countDown(); - }), im), LONG_TIMEOUT_MS); - - // Assume that no data are removed, db size shall be over the threshold. - assertTrue(mService.isDbSizeOverThreshold()); - - // Reset the flag and test interruption during maintenance. - im.setInterrupted(false); - - final ConditionVariable latch = new ConditionVariable(); - // Do full maintenance and the expectation is not completed by interruption. - mService.fullMaintenance(onStatus((status) -> { - assertFalse(status.isSuccess()); - latch.open(); - }), im); - - // Give a little bit of time for maintenance to start up for realism - waitForMs(50); - // Interrupt maintenance job. - im.setInterrupted(true); - - if (!latch.block(LONG_TIMEOUT_MS)) { - fail("Maintenance unexpectedly completed successfully"); - } - - // Assume that only do dropAllExpiredRecords method in previous maintenance, db size shall - // still be over the threshold. - assertTrue(mService.isDbSizeOverThreshold()); - } -} diff --git a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java b/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java deleted file mode 100644 index 3d3aabc66e70..000000000000 --- a/packages/NetworkStack/tests/unit/src/com/android/server/connectivity/ipmemorystore/RelevanceUtilsTests.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity.ipmemorystore; - -import static com.android.server.connectivity.ipmemorystore.RelevanceUtils.CAPPED_RELEVANCE; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Unit tests for {@link RelevanceUtils}. */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RelevanceUtilsTests { - @Test - public void testComputeRelevanceForTargetDate() { - final long dayInMillis = 24L * 60 * 60 * 1000; - final long base = 1_000_000L; // any given point in time - // Relevance when the network expires in 1000 years must be capped - assertEquals(CAPPED_RELEVANCE, RelevanceUtils.computeRelevanceForTargetDate( - base + 1000L * dayInMillis, base)); - // Relevance when expiry is before the date must be 0 - assertEquals(0, RelevanceUtils.computeRelevanceForTargetDate(base - 1, base)); - // Make sure the relevance for a given target date is higher if the expiry is further - // in the future - assertTrue(RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base) - < RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base)); - - // Make sure the relevance falls slower as the expiry is closing in. This is to ensure - // the decay is indeed logarithmic. - final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(base, base); - final int relevance50DaysBeforeExpiry = - RelevanceUtils.computeRelevanceForTargetDate(base + 50 * dayInMillis, base); - final int relevance100DaysBeforeExpiry = - RelevanceUtils.computeRelevanceForTargetDate(base + 100 * dayInMillis, base); - final int relevance150DaysBeforeExpiry = - RelevanceUtils.computeRelevanceForTargetDate(base + 150 * dayInMillis, base); - assertEquals(0, relevanceAtExpiry); - assertTrue(relevance50DaysBeforeExpiry - relevanceAtExpiry - < relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry); - assertTrue(relevance100DaysBeforeExpiry - relevance50DaysBeforeExpiry - < relevance150DaysBeforeExpiry - relevance100DaysBeforeExpiry); - } - - @Test - public void testIncreaseRelevance() { - long expiry = System.currentTimeMillis(); - - final long firstBump = RelevanceUtils.bumpExpiryDate(expiry); - // Though a few milliseconds might have elapsed, the first bump should push the duration - // to days in the future, so unless this test takes literal days between these two lines, - // this should always pass. - assertTrue(firstBump > expiry); - - expiry = 0; - long lastDifference = Long.MAX_VALUE; - // The relevance should be capped in at most this many steps. Otherwise, fail. - final int steps = 1000; - for (int i = 0; i < steps; ++i) { - final long newExpiry = RelevanceUtils.bumpExpiryDuration(expiry); - if (newExpiry == expiry) { - // The relevance should be capped. Make sure it is, then exit without failure. - assertEquals(newExpiry, RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS); - return; - } - // Make sure the new expiry is further in the future than last time. - assertTrue(newExpiry > expiry); - // Also check that it was not bumped as much as the last bump, because the - // decay must be exponential. - assertTrue(newExpiry - expiry < lastDifference); - lastDifference = newExpiry - expiry; - expiry = newExpiry; - } - fail("Relevance failed to go to the maximum value after " + steps + " bumps"); - } - - @Test - public void testContinuity() { - final long expiry = System.currentTimeMillis(); - - // Relevance at expiry and after expiry should be the cap. - final int relevanceBeforeMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry, - expiry - (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1_000_000)); - assertEquals(relevanceBeforeMaxLifetime, CAPPED_RELEVANCE); - final int relevanceForMaxLifetime = RelevanceUtils.computeRelevanceForTargetDate(expiry, - expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS); - assertEquals(relevanceForMaxLifetime, CAPPED_RELEVANCE); - - // If the max relevance is reached at the cap lifetime, one millisecond less than this - // should be very close. Strictly speaking this is a bit brittle, but it should be - // good enough for the purposes of the memory store. - final int relevanceForOneMillisecLessThanCap = RelevanceUtils.computeRelevanceForTargetDate( - expiry, expiry - RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS + 1); - assertTrue(relevanceForOneMillisecLessThanCap <= CAPPED_RELEVANCE); - assertTrue(relevanceForOneMillisecLessThanCap >= CAPPED_RELEVANCE - 10); - - // Likewise the relevance one millisecond before expiry should be very close to 0. It's - // fine if it rounds down to 0. - final int relevanceOneMillisecBeforeExpiry = RelevanceUtils.computeRelevanceForTargetDate( - expiry, expiry - 1); - assertTrue(relevanceOneMillisecBeforeExpiry <= 10); - assertTrue(relevanceOneMillisecBeforeExpiry >= 0); - - final int relevanceAtExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, expiry); - assertEquals(relevanceAtExpiry, 0); - final int relevanceAfterExpiry = RelevanceUtils.computeRelevanceForTargetDate(expiry, - expiry + 1_000_000); - assertEquals(relevanceAfterExpiry, 0); - } - - // testIncreaseRelevance makes sure bumping the expiry continuously always yields a - // monotonically increasing date as a side effect, but this tests that the relevance (as - // opposed to the expiry date) increases monotonically with increasing periods. - @Test - public void testMonotonicity() { - // Hopefully the relevance is granular enough to give a different value for every one - // of this number of steps. - final int steps = 40; - final long expiry = System.currentTimeMillis(); - - int lastRelevance = -1; - for (int i = 0; i < steps; ++i) { - final long date = expiry - i * (RelevanceUtils.CAPPED_RELEVANCE_LIFETIME_MS / steps); - final int relevance = RelevanceUtils.computeRelevanceForTargetDate(expiry, date); - assertTrue(relevance > lastRelevance); - lastRelevance = relevance; - } - } -} diff --git a/packages/NetworkStack/tests/unit/src/com/android/server/util/SharedLogTest.java b/packages/NetworkStack/tests/unit/src/com/android/server/util/SharedLogTest.java deleted file mode 100644 index b1db051d2bd8..000000000000 --- a/packages/NetworkStack/tests/unit/src/com/android/server/util/SharedLogTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.net.util.SharedLog; - -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.ByteArrayOutputStream; -import java.io.PrintWriter; - -@RunWith(AndroidJUnit4.class) -@SmallTest -public class SharedLogTest { - private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}"; - private static final String TIMESTAMP = "HH:MM:SS"; - - @Test - public void testBasicOperation() { - final SharedLog logTop = new SharedLog("top"); - logTop.mark("first post!"); - - final SharedLog logLevel2a = logTop.forSubComponent("twoA"); - final SharedLog logLevel2b = logTop.forSubComponent("twoB"); - logLevel2b.e("2b or not 2b"); - logLevel2b.e("No exception", null); - logLevel2b.e("Wait, here's one", new Exception("Test")); - logLevel2a.w("second post?"); - - final SharedLog logLevel3 = logLevel2a.forSubComponent("three"); - logTop.log("still logging"); - logLevel3.log("3 >> 2"); - logLevel2a.mark("ok: last post"); - - final String[] expected = { - " - MARK first post!", - " - [twoB] ERROR 2b or not 2b", - " - [twoB] ERROR No exception", - // No stacktrace in shared log, only in logcat - " - [twoB] ERROR Wait, here's one: Test", - " - [twoA] WARN second post?", - " - still logging", - " - [twoA.three] 3 >> 2", - " - [twoA] MARK ok: last post", - }; - // Verify the logs are all there and in the correct order. - verifyLogLines(expected, logTop); - - // In fact, because they all share the same underlying LocalLog, - // every subcomponent SharedLog's dump() is identical. - verifyLogLines(expected, logLevel2a); - verifyLogLines(expected, logLevel2b); - verifyLogLines(expected, logLevel3); - } - - private static void verifyLogLines(String[] expected, SharedLog log) { - final ByteArrayOutputStream ostream = new ByteArrayOutputStream(); - final PrintWriter pw = new PrintWriter(ostream, true); - log.dump(null, pw, null); - - final String dumpOutput = ostream.toString(); - assertTrue(dumpOutput != null); - assertTrue(!"".equals(dumpOutput)); - - final String[] lines = dumpOutput.split("\n"); - assertEquals(expected.length, lines.length); - - for (int i = 0; i < expected.length; i++) { - String got = lines[i]; - String want = expected[i]; - assertTrue(String.format("'%s' did not contain '%s'", got, want), got.endsWith(want)); - assertTrue(String.format("'%s' did not contain a %s timestamp", got, TIMESTAMP), - got.replaceFirst(TIMESTAMP_PATTERN, TIMESTAMP).contains(TIMESTAMP)); - } - } -} diff --git a/packages/ExtServices/Android.bp b/packages/PrintRecommendationService/Android.bp index db94eec17ce8..6d28bdb7943b 100644 --- a/packages/ExtServices/Android.bp +++ b/packages/PrintRecommendationService/Android.bp @@ -13,14 +13,11 @@ // limitations under the License. android_app { - name: "ExtServices", + name: "PrintRecommendationService", srcs: ["src/**/*.java"], - platform_apis: true, - certificate: "platform", - aaptflags: ["--shared-lib"], - export_package_resources: true, - optimize: { - proguard_flags_files: ["proguard.proguard"], - }, - privileged: true, + sdk_version: "system_current", + static_libs: [ + "androidx.annotation_annotation", + "androidx.core_core", + ], } diff --git a/packages/PrintRecommendationService/Android.mk b/packages/PrintRecommendationService/Android.mk deleted file mode 100644 index d27a6efe58d6..000000000000 --- a/packages/PrintRecommendationService/Android.mk +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (C) 2016 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. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := optional -LOCAL_USE_AAPT2 := true - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := PrintRecommendationService - -LOCAL_SDK_VERSION := system_current - -LOCAL_STATIC_JAVA_LIBRARIES := androidx.annotation_annotation -LOCAL_STATIC_ANDROID_LIBRARIES := androidx.core_core - -include $(BUILD_PACKAGE) - -include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/packages/SettingsLib/tests/Android.mk b/packages/SettingsLib/tests/Android.mk deleted file mode 100644 index 333c41d33f40..000000000000 --- a/packages/SettingsLib/tests/Android.mk +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (C) 2016 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. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -# Include all makefiles in subdirectories -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp new file mode 100644 index 000000000000..1ccac1fcfb9b --- /dev/null +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -0,0 +1,40 @@ +// Copyright (C) 2015 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. + +android_test { + name: "SettingsLibTests", + defaults: ["SettingsLibDefaults"], + + certificate: "platform", + + srcs: ["src/**/*.java"], + + libs: [ + "android.test.runner", + "telephony-common", + "android.test.base", + ], + + platform_apis: true, + test_suites: ["device-tests"], + + static_libs: [ + "android-support-test", + "espresso-core", + "mockito-target-minus-junit4", + "truth-prebuilt", + ], + + dxflags: ["--multi-dex"], +} diff --git a/packages/SettingsLib/tests/integ/Android.mk b/packages/SettingsLib/tests/integ/Android.mk deleted file mode 100644 index c893b6dcffbf..000000000000 --- a/packages/SettingsLib/tests/integ/Android.mk +++ /dev/null @@ -1,47 +0,0 @@ -# Copyright (C) 2015 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. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests -LOCAL_CERTIFICATE := platform - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common android.test.base - -LOCAL_JACK_FLAGS := --multi-dex native - -LOCAL_PACKAGE_NAME := SettingsLibTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_USE_AAPT2 := true - -LOCAL_STATIC_JAVA_LIBRARIES := \ - android-support-test \ - espresso-core \ - mockito-target-minus-junit4 \ - truth-prebuilt - -# Code coverage puts us over the dex limit, so enable multi-dex for coverage-enabled builds -ifeq (true,$(EMMA_INSTRUMENT)) -LOCAL_JACK_FLAGS := --multi-dex native -LOCAL_DX_FLAGS := --multi-dex -endif # EMMA_INSTRUMENT - -include frameworks/base/packages/SettingsLib/common.mk - -include $(BUILD_PACKAGE) diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp new file mode 100644 index 000000000000..20c2cf9c900c --- /dev/null +++ b/packages/SettingsLib/tests/robotests/Android.bp @@ -0,0 +1,40 @@ +// Copyright (C) 2016 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. + +//########################################################### +// SettingsLib Shell app just for Robolectric test target. # +//########################################################### + +android_app { + name: "SettingsLibShell", + defaults: ["SettingsLibDefaults"], + platform_apis: true, + + privileged: true, + + resource_dirs: ["res"], +} + +//############################################ +// SettingsLib Robolectric test target. # +//############################################ +android_robolectric_test { + name: "SettingsLibRoboTests", + srcs: ["src/**/*.java"], + java_resource_dirs: ["config"], + instrumentation_for: "SettingsLibShell", + test_options: { + timeout: 36000, + }, +} diff --git a/packages/SettingsLib/tests/robotests/Android.mk b/packages/SettingsLib/tests/robotests/Android.mk deleted file mode 100644 index d15a3ef2946d..000000000000 --- a/packages/SettingsLib/tests/robotests/Android.mk +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (C) 2016 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. - -############################################################ -# SettingsLib Shell app just for Robolectric test target. # -############################################################ -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_PACKAGE_NAME := SettingsLibShell -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_MODULE_TAGS := optional - -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res - -LOCAL_USE_AAPT2 := true - -include frameworks/base/packages/SettingsLib/common.mk - -include $(BUILD_PACKAGE) - -############################################# -# SettingsLib Robolectric test target. # -############################################# -include $(CLEAR_VARS) - -LOCAL_MODULE := SettingsLibRoboTests - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_JAVA_RESOURCE_DIRS := config - -LOCAL_JAVA_LIBRARIES := \ - robolectric_android-all-stub \ - Robolectric_all-target \ - mockito-robolectric-prebuilt \ - truth-prebuilt - -LOCAL_INSTRUMENTATION_FOR := SettingsLibShell - -LOCAL_MODULE_TAGS := optional - -include $(BUILD_STATIC_JAVA_LIBRARY) - -############################################################# -# SettingsLib runner target to run the previous target. # -############################################################# -include $(CLEAR_VARS) - -LOCAL_MODULE := RunSettingsLibRoboTests - -LOCAL_JAVA_LIBRARIES := \ - SettingsLibRoboTests \ - robolectric_android-all-stub \ - Robolectric_all-target \ - mockito-robolectric-prebuilt \ - truth-prebuilt - -LOCAL_TEST_PACKAGE := SettingsLibShell - -LOCAL_ROBOTEST_TIMEOUT := 36000 - -include external/robolectric-shadows/run_robotests.mk
\ No newline at end of file diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index 99c48cb0eaec..151ea6a5a3df 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -12,6 +12,7 @@ cwren@google.com dupin@google.com ethibodeau@google.com evanlaird@google.com +hyunyoungs@google.com jmonk@google.com jaggies@google.com jjaggi@google.com @@ -23,6 +24,8 @@ kprevas@google.com lynhan@google.com madym@google.com mankoff@google.com +mrcasey@google.com +mrenouf@google.com nbenbernou@google.com nesciosquid@google.com ngmatthew@google.com diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java index b0e1c56aec30..c1931f0133be 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbDebuggingActivity.java @@ -86,6 +86,7 @@ public class UsbDebuggingActivity extends AlertActivity mAlwaysAllow = (CheckBox)checkbox.findViewById(com.android.internal.R.id.alwaysUse); mAlwaysAllow.setText(getString(R.string.usb_debugging_always)); ap.mView = checkbox; + window.setCloseOnTouchOutside(false); setupAlert(); diff --git a/proto/Android.bp b/proto/Android.bp index e924a4dcd201..ad4e04cb5d53 100644 --- a/proto/Android.bp +++ b/proto/Android.bp @@ -5,7 +5,7 @@ java_library_static { type: "nano", }, srcs: ["src/**/*.proto"], - no_framework_libs: true, + sdk_version: "core_platform", // Pin java_version until jarjar is certified to support later versions. http://b/72703434 java_version: "1.8", target: { @@ -25,6 +25,5 @@ java_library_static { type: "nano", }, srcs: ["src/metrics_constants.proto"], - no_framework_libs: true, sdk_version: "system_current", } diff --git a/services/backup/OWNERS b/services/backup/OWNERS index 1c9a43acfa65..9c21e8fe5e45 100644 --- a/services/backup/OWNERS +++ b/services/backup/OWNERS @@ -1,7 +1,9 @@ -artikz@google.com +alsutton@google.com +anniemeng@google.com brufino@google.com bryanmawhinney@google.com ctate@google.com jorlow@google.com -mkarpinski@google.com +nathch@google.com +rthakohov@google.com diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index b3b5e45496ac..e72c9bf88be8 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -25,8 +25,8 @@ import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeValid; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; @@ -77,6 +77,7 @@ import android.net.INetworkStatsService; import android.net.ISocketKeepaliveCallback; import android.net.ITetheringEventCallback; import android.net.InetAddresses; +import android.net.IpMemoryStore; import android.net.IpPrefix; import android.net.LinkProperties; import android.net.LinkProperties.CompareResult; @@ -90,6 +91,7 @@ import android.net.NetworkFactory; import android.net.NetworkInfo; import android.net.NetworkInfo.DetailedState; import android.net.NetworkMisc; +import android.net.NetworkMonitorManager; import android.net.NetworkPolicyManager; import android.net.NetworkQuotaInfo; import android.net.NetworkRequest; @@ -168,6 +170,7 @@ import com.android.internal.util.MessageUtils; import com.android.internal.util.WakeupMessage; import com.android.internal.util.XmlUtils; import com.android.server.am.BatteryStatsService; +import com.android.server.connectivity.AutodestructReference; import com.android.server.connectivity.DataConnectionStats; import com.android.server.connectivity.DnsManager; import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate; @@ -189,6 +192,7 @@ import com.android.server.net.BaseNetdEventCallback; import com.android.server.net.BaseNetworkObserver; import com.android.server.net.LockdownVpnTracker; import com.android.server.net.NetworkPolicyManagerInternal; +import com.android.server.net.NetworkStatsFactory; import com.android.server.utils.PriorityDump; import com.google.android.collect.Lists; @@ -963,6 +967,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + mTethering = makeTethering(); mPermissionMonitor = new PermissionMonitor(mContext, mNetd); @@ -1010,8 +1016,6 @@ public class ConnectivityService extends IConnectivityManager.Stub final DataConnectionStats dataConnectionStats = new DataConnectionStats(mContext); dataConnectionStats.startMonitoring(); - mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); - mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler); mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager, mContext.getSystemService(NotificationManager.class)); @@ -1782,8 +1786,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // callback from each caller type. Need to re-factor NetdEventListenerService to allow // multiple NetworkMonitor registrants. if (nai != null && nai.satisfies(mDefaultRequest)) { - Binder.withCleanCallingIdentity(() -> - nai.networkMonitor().notifyDnsResponse(returnCode)); + nai.networkMonitor().notifyDnsResponse(returnCode); } } @@ -2573,11 +2576,11 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: { - if (nai.everConnected && !nai.networkMisc.explicitlySelected) { - loge("ERROR: already-connected network explicitly selected."); + if (nai.everConnected) { + loge("ERROR: cannot call explicitlySelected on already-connected network"); } - nai.networkMisc.explicitlySelected = true; - nai.networkMisc.acceptUnvalidated = msg.arg1 == 1; + nai.networkMisc.explicitlySelected = (msg.arg1 == 1); + nai.networkMisc.acceptUnvalidated = (msg.arg1 == 1) && (msg.arg2 == 1); // Mark the network as temporarily accepting partial connectivity so that it // will be validated (and possibly become default) even if it only provides // partial internet access. Note that if user connects to partial connectivity @@ -2585,7 +2588,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // out of wifi coverage) and if the same wifi is available again, the device // will auto connect to this wifi even though the wifi has "no internet". // TODO: Evaluate using a separate setting in IpMemoryStore. - nai.networkMisc.acceptPartialConnectivity = msg.arg1 == 1; + nai.networkMisc.acceptPartialConnectivity = (msg.arg2 == 1); break; } case NetworkAgent.EVENT_SOCKET_KEEPALIVE: { @@ -2603,21 +2606,12 @@ public class ConnectivityService extends IConnectivityManager.Stub final NetworkAgentInfo nai = getNetworkAgentInfoForNetId(msg.arg2); if (nai == null) break; - final boolean partialConnectivity = - (msg.arg1 == NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY) - || (nai.networkMisc.acceptPartialConnectivity - && nai.partialConnectivity); - // Once a network is determined to have partial connectivity, it cannot - // go back to full connectivity without a disconnect. This is because - // NetworkMonitor can only communicate either PARTIAL_CONNECTIVITY or VALID, - // but not both. - // TODO: Provide multi-testResult to improve the communication between - // ConnectivityService and NetworkMonitor, so that ConnectivityService could - // know the real status of network. + final boolean wasPartial = nai.partialConnectivity; + nai.partialConnectivity = ((msg.arg1 & NETWORK_VALIDATION_RESULT_PARTIAL) != 0); final boolean partialConnectivityChanged = - (partialConnectivity && !nai.partialConnectivity); + (wasPartial != nai.partialConnectivity); - final boolean valid = (msg.arg1 == NETWORK_TEST_RESULT_VALID); + final boolean valid = ((msg.arg1 & NETWORK_VALIDATION_RESULT_VALID) != 0); final boolean wasValidated = nai.lastValidated; final boolean wasDefault = isDefaultNetwork(nai); if (nai.everCaptivePortalDetected && !nai.captivePortalLoginNotified @@ -2647,25 +2641,38 @@ public class ConnectivityService extends IConnectivityManager.Stub if (oldScore != nai.getCurrentScore()) sendUpdatedScoreToFactories(nai); if (valid) { handleFreshlyValidatedNetwork(nai); - // Clear NO_INTERNET and LOST_INTERNET notifications if network becomes - // valid. + // Clear NO_INTERNET, PARTIAL_CONNECTIVITY and LOST_INTERNET + // notifications if network becomes valid. mNotifier.clearNotification(nai.network.netId, NotificationType.NO_INTERNET); mNotifier.clearNotification(nai.network.netId, NotificationType.LOST_INTERNET); + mNotifier.clearNotification(nai.network.netId, + NotificationType.PARTIAL_CONNECTIVITY); } } else if (partialConnectivityChanged) { - nai.partialConnectivity = partialConnectivity; updateCapabilities(nai.getCurrentScore(), nai, nai.networkCapabilities); } updateInetCondition(nai); // Let the NetworkAgent know the state of its network Bundle redirectUrlBundle = new Bundle(); redirectUrlBundle.putString(NetworkAgent.REDIRECT_URL_KEY, redirectUrl); + // TODO: Evaluate to update partial connectivity to status to NetworkAgent. nai.asyncChannel.sendMessage( NetworkAgent.CMD_REPORT_NETWORK_STATUS, (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK), 0, redirectUrlBundle); + + // If NetworkMonitor detects partial connectivity before + // EVENT_PROMPT_UNVALIDATED arrives, show the partial connectivity notification + // immediately. Re-notify partial connectivity silently if no internet + // notification already there. + if (!wasPartial && nai.partialConnectivity) { + // Remove delayed message if there is a pending message. + mHandler.removeMessages(EVENT_PROMPT_UNVALIDATED, nai.network); + handlePromptUnvalidated(nai.network); + } + if (wasValidated && !nai.lastValidated) { handleNetworkUnvalidated(nai); } @@ -2767,29 +2774,31 @@ public class ConnectivityService extends IConnectivityManager.Stub } private class NetworkMonitorCallbacks extends INetworkMonitorCallbacks.Stub { - private final NetworkAgentInfo mNai; + private final int mNetId; + private final AutodestructReference<NetworkAgentInfo> mNai; private NetworkMonitorCallbacks(NetworkAgentInfo nai) { - mNai = nai; + mNetId = nai.network.netId; + mNai = new AutodestructReference(nai); } @Override public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) { mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, - new Pair<>(mNai, networkMonitor))); + new Pair<>(mNai.getAndDestroy(), networkMonitor))); } @Override public void notifyNetworkTested(int testResult, @Nullable String redirectUrl) { mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage(EVENT_NETWORK_TESTED, - testResult, mNai.network.netId, redirectUrl)); + testResult, mNetId, redirectUrl)); } @Override public void notifyPrivateDnsConfigResolved(PrivateDnsConfigParcel config) { mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage( EVENT_PRIVATE_DNS_CONFIG_RESOLVED, - 0, mNai.network.netId, PrivateDnsConfig.fromParcel(config))); + 0, mNetId, PrivateDnsConfig.fromParcel(config))); } @Override @@ -2807,15 +2816,13 @@ public class ConnectivityService extends IConnectivityManager.Stub } mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage( EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_SHOW, - mNai.network.netId, - pendingIntent)); + mNetId, pendingIntent)); } @Override public void hideProvisioningNotification() { mTrackerHandler.sendMessage(mTrackerHandler.obtainMessage( - EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE, - mNai.network.netId)); + EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE, mNetId)); } @Override @@ -2857,11 +2864,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Notify the NetworkAgentInfo/NetworkMonitor in case NetworkMonitor needs to cancel or // schedule DNS resolutions. If a DNS resolution is required the // result will be sent back to us. - try { - nai.networkMonitor().notifyPrivateDnsChanged(cfg.toParcel()); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } + nai.networkMonitor().notifyPrivateDnsChanged(cfg.toParcel()); // With Private DNS bypass support, we can proceed to update the // Private DNS config immediately, even if we're in strict mode @@ -3027,11 +3030,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Disable wakeup packet monitoring for each interface. wakeupModifyInterface(iface, nai.networkCapabilities, false); } - try { - nai.networkMonitor().notifyNetworkDisconnected(); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } + nai.networkMonitor().notifyNetworkDisconnected(); mNetworkAgentInfos.remove(nai.messenger); nai.clatd.update(); synchronized (mNetworkForNetId) { @@ -3441,11 +3440,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // Inform NetworkMonitor that partial connectivity is acceptable. This will likely // result in a partial connectivity result which will be processed by // maybeHandleNetworkMonitorMessage. - try { - nai.networkMonitor().setAcceptPartialConnectivity(); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } + // + // TODO: NetworkMonitor does not refer to the "never ask again" bit. The bit is stored + // per network. Therefore, NetworkMonitor may still do https probe. + nai.networkMonitor().setAcceptPartialConnectivity(); } } @@ -3477,11 +3475,7 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); if (nai == null) return; if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return; - try { - nai.networkMonitor().launchCaptivePortalApp(); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } + nai.networkMonitor().launchCaptivePortalApp(); }); } @@ -3516,7 +3510,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } @Override - public void appResponse(final int response) throws RemoteException { + public void appResponse(final int response) { if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) { enforceSettingsPermission(); } @@ -3526,16 +3520,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (nai == null) return; // nai.networkMonitor() is thread-safe - final INetworkMonitor nm = nai.networkMonitor(); + final NetworkMonitorManager nm = nai.networkMonitor(); if (nm == null) return; - - final long token = Binder.clearCallingIdentity(); - try { - nm.notifyCaptivePortalAppFinished(response); - } finally { - // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException - Binder.restoreCallingIdentity(token); - } + nm.notifyCaptivePortalAppFinished(response); } @Override @@ -3611,21 +3598,31 @@ public class ConnectivityService extends IConnectivityManager.Stub private void showNetworkNotification(NetworkAgentInfo nai, NotificationType type) { final String action; + final boolean highPriority; switch (type) { case LOGGED_IN: action = Settings.ACTION_WIFI_SETTINGS; mHandler.removeMessages(EVENT_TIMEOUT_NOTIFICATION); mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_TIMEOUT_NOTIFICATION, nai.network.netId, 0), TIMEOUT_NOTIFICATION_DELAY_MS); + // High priority because it is a direct result of the user logging in to a portal. + highPriority = true; break; case NO_INTERNET: action = ConnectivityManager.ACTION_PROMPT_UNVALIDATED; + // High priority because it is only displayed for explicitly selected networks. + highPriority = true; break; case LOST_INTERNET: action = ConnectivityManager.ACTION_PROMPT_LOST_VALIDATION; + // High priority because it could help the user avoid unexpected data usage. + highPriority = true; break; case PARTIAL_CONNECTIVITY: action = ConnectivityManager.ACTION_PROMPT_PARTIAL_CONNECTIVITY; + // Don't bother the user with a high-priority notification if the network was not + // explicitly selected by the user. + highPriority = nai.networkMisc.explicitlySelected; break; default: Slog.wtf(TAG, "Unknown notification type " + type); @@ -3642,23 +3639,41 @@ public class ConnectivityService extends IConnectivityManager.Stub PendingIntent pendingIntent = PendingIntent.getActivityAsUser( mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT, null, UserHandle.CURRENT); - mNotifier.showNotification(nai.network.netId, type, nai, null, pendingIntent, true); + + mNotifier.showNotification(nai.network.netId, type, nai, null, pendingIntent, highPriority); + } + + private boolean shouldPromptUnvalidated(NetworkAgentInfo nai) { + // Don't prompt if the network is validated, and don't prompt on captive portals + // because we're already prompting the user to sign in. + if (nai.everValidated || nai.everCaptivePortalDetected) { + return false; + } + + // If a network has partial connectivity, always prompt unless the user has already accepted + // partial connectivity and selected don't ask again. This ensures that if the device + // automatically connects to a network that has partial Internet access, the user will + // always be able to use it, either because they've already chosen "don't ask again" or + // because we have prompt them. + if (nai.partialConnectivity && !nai.networkMisc.acceptPartialConnectivity) { + return true; + } + + // If a network has no Internet access, only prompt if the network was explicitly selected + // and if the user has not already told us to use the network regardless of whether it + // validated or not. + if (nai.networkMisc.explicitlySelected && !nai.networkMisc.acceptUnvalidated) { + return true; + } + + return false; } private void handlePromptUnvalidated(Network network) { if (VDBG || DDBG) log("handlePromptUnvalidated " + network); NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network); - // Only prompt if the network is unvalidated or network has partial internet connectivity - // and was explicitly selected by the user, and if we haven't already been told to switch - // to it regardless of whether it validated or not. Also don't prompt on captive portals - // because we're already prompting the user to sign in. - if (nai == null || nai.everValidated || nai.everCaptivePortalDetected - || !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated - // TODO: Once the value of acceptPartialConnectivity is moved to IpMemoryStore, - // we should reevaluate how to handle acceptPartialConnectivity when network just - // connected. - || nai.networkMisc.acceptPartialConnectivity) { + if (nai == null || !shouldPromptUnvalidated(nai)) { return; } // TODO: Evaluate if it's needed to wait 8 seconds for triggering notification when @@ -4106,11 +4121,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) { return; } - try { - nai.networkMonitor().forceReevaluation(uid); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } + nai.networkMonitor().forceReevaluation(uid); } /** @@ -4377,7 +4388,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // the underlyingNetworks list. if (underlyingNetworks == null) { NetworkAgentInfo defaultNai = getDefaultNetwork(); - if (defaultNai != null && defaultNai.linkProperties != null) { + if (defaultNai != null) { underlyingNetworks = new Network[] { defaultNai.network }; } } @@ -4386,7 +4397,11 @@ public class ConnectivityService extends IConnectivityManager.Stub for (Network network : underlyingNetworks) { LinkProperties lp = getLinkProperties(network); if (lp != null) { - interfaces.add(lp.getInterfaceName()); + for (String iface : lp.getAllInterfaceNames()) { + if (!TextUtils.isEmpty(iface)) { + interfaces.add(iface); + } + } } } if (!interfaces.isEmpty()) { @@ -5543,11 +5558,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // Start or stop DNS64 detection and 464xlat according to network state. networkAgent.clatd.update(); notifyIfacesChangedForNetworkStats(); - try { - networkAgent.networkMonitor().notifyLinkPropertiesChanged(newLp); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } + networkAgent.networkMonitor().notifyLinkPropertiesChanged(newLp); if (networkAgent.everConnected) { notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED); } @@ -6531,15 +6542,11 @@ public class ConnectivityService extends IConnectivityManager.Stub // command must be sent after updating LinkProperties to maximize chances of // NetworkMonitor seeing the correct LinkProperties when starting. // TODO: pass LinkProperties to the NetworkMonitor in the notifyNetworkConnected call. - try { - if (networkAgent.networkMisc.acceptPartialConnectivity) { - networkAgent.networkMonitor().setAcceptPartialConnectivity(); - } - networkAgent.networkMonitor().notifyNetworkConnected( - networkAgent.linkProperties, networkAgent.networkCapabilities); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); + if (networkAgent.networkMisc.acceptPartialConnectivity) { + networkAgent.networkMonitor().setAcceptPartialConnectivity(); } + networkAgent.networkMonitor().notifyNetworkConnected( + networkAgent.linkProperties, networkAgent.networkCapabilities); scheduleUnvalidatedPrompt(networkAgent); // Whether a particular NetworkRequest listen should cause signal strength thresholds to @@ -6781,8 +6788,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * Notify NetworkStatsService that the set of active ifaces has changed, or that one of the - * properties tracked by NetworkStatsService on an active iface has changed. + * Notify NetworkStatsService and NetworkStatsFactory that the set of active ifaces has changed, + * or that one of the active iface's trackedproperties has changed. */ private void notifyIfacesChangedForNetworkStats() { ensureRunningOnConnectivityServiceThread(); @@ -6791,11 +6798,17 @@ public class ConnectivityService extends IConnectivityManager.Stub if (activeLinkProperties != null) { activeIface = activeLinkProperties.getInterfaceName(); } + + // CAUTION: Ordering matters between updateVpnInfos() and forceUpdateIfaces(), which + // triggers a new poll. Trigger the poll first to ensure a snapshot is taken before + // switching to the new state. This ensures that traffic does not get mis-attributed to + // incorrect apps (including VPN app). try { mStatsService.forceUpdateIfaces( - getDefaultNetworks(), getAllVpnInfo(), getAllNetworkState(), activeIface); + getDefaultNetworks(), getAllNetworkState(), activeIface); } catch (Exception ignored) { } + NetworkStatsFactory.updateVpnInfos(getAllVpnInfo()); } @Override @@ -6897,6 +6910,11 @@ public class ConnectivityService extends IConnectivityManager.Stub final int userId = UserHandle.getCallingUserId(); + Binder.withCleanCallingIdentity(() -> { + final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext); + ipMemoryStore.factoryReset(); + }); + // Turn airplane mode off setAirplaneMode(false); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 6dbe3ade09f0..6d08b5ab9b87 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -36,7 +36,6 @@ import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.STATS_PER_UID; import static android.net.NetworkStats.TAG_ALL; import static android.net.NetworkStats.TAG_NONE; -import static android.net.NetworkStats.UID_ALL; import static android.net.TrafficStats.UID_TETHERING; import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; @@ -1231,7 +1230,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { public NetworkStats getNetworkStatsDetail() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - return mStatsFactory.readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null); + return mStatsFactory.readNetworkStatsDetail(); } catch (IOException e) { throw new IllegalStateException(e); } @@ -1540,7 +1539,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub { public NetworkStats getNetworkStatsUidDetail(int uid, String[] ifaces) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - return mStatsFactory.readNetworkStatsDetail(uid, ifaces, TAG_ALL, null); + return mStatsFactory.readNetworkStatsDetail(uid, ifaces, TAG_ALL); } catch (IOException e) { throw new IllegalStateException(e); } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 99365def5da1..534d76c00349 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -1315,12 +1315,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { return; } if (VDBG) { - log("notifyUserMobileDataStateChangedForSubscriberPhoneID: subId=" + phoneId - + " state=" + state); + log("notifyUserMobileDataStateChangedForSubscriberPhoneID: PhoneId=" + phoneId + + " subId=" + subId + " state=" + state); } synchronized (mRecords) { if (validatePhoneId(phoneId)) { - mMessageWaiting[phoneId] = state; + mUserMobileDataState[phoneId] = state; for (Record r : mRecords) { if (r.matchPhoneStateListenerEvent( PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) && diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index e698b841a2fd..2a598774a2e1 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -2520,8 +2520,9 @@ public class AudioService extends IAudioService.Stub AudioSystem.muteMicrophone(on); Binder.restoreCallingIdentity(identity); if (on != currentMute) { - mContext.sendBroadcast(new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED) - .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)); + mContext.sendBroadcastAsUser( + new Intent(AudioManager.ACTION_MICROPHONE_MUTE_CHANGED) + .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), UserHandle.ALL); } } } diff --git a/services/core/java/com/android/server/connectivity/AutodestructReference.java b/services/core/java/com/android/server/connectivity/AutodestructReference.java new file mode 100644 index 000000000000..009a43e58285 --- /dev/null +++ b/services/core/java/com/android/server/connectivity/AutodestructReference.java @@ -0,0 +1,42 @@ +/* + * 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.server.connectivity; + +import android.annotation.NonNull; + +import java.util.concurrent.atomic.AtomicReference; + +/** + * A ref that autodestructs at the first usage of it. + * @param <T> The type of the held object + * @hide + */ +public class AutodestructReference<T> { + private final AtomicReference<T> mHeld; + public AutodestructReference(@NonNull T obj) { + if (null == obj) throw new NullPointerException("Autodestruct reference to null"); + mHeld = new AtomicReference<>(obj); + } + + /** Get the ref and destruct it. NPE if already destructed. */ + @NonNull + public T getAndDestroy() { + final T obj = mHeld.getAndSet(null); + if (null == obj) throw new NullPointerException("Already autodestructed"); + return obj; + } +} diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java index e10d7373359b..9bae902eb7b1 100644 --- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java +++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java @@ -216,6 +216,7 @@ public class KeepaliveTracker { public String toString() { return "KeepaliveInfo [" + + " type=" + mType + " network=" + mNai.network + " startedState=" + startedStateString(mStartedState) + " " @@ -561,7 +562,7 @@ public class KeepaliveTracker { if (KeepaliveInfo.STARTING == ki.mStartedState) { if (SUCCESS == reason) { // Keepalive successfully started. - if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name()); + Log.d(TAG, "Started keepalive " + slot + " on " + nai.name()); ki.mStartedState = KeepaliveInfo.STARTED; try { ki.mCallback.onStarted(slot); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 34772d062fd2..864a793b8f40 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -25,12 +25,12 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.NetworkMisc; +import android.net.NetworkMonitorManager; import android.net.NetworkRequest; import android.net.NetworkState; import android.os.Handler; import android.os.INetworkManagementService; import android.os.Messenger; -import android.os.RemoteException; import android.os.SystemClock; import android.util.Log; import android.util.SparseArray; @@ -247,7 +247,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public final Nat464Xlat clatd; // Set after asynchronous creation of the NetworkMonitor. - private volatile INetworkMonitor mNetworkMonitor; + private volatile NetworkMonitorManager mNetworkMonitor; private static final String TAG = ConnectivityService.class.getSimpleName(); private static final boolean VDBG = false; @@ -278,7 +278,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { * Inform NetworkAgentInfo that a new NetworkMonitor was created. */ public void onNetworkMonitorCreated(INetworkMonitor networkMonitor) { - mNetworkMonitor = networkMonitor; + mNetworkMonitor = new NetworkMonitorManager(networkMonitor); } /** @@ -290,13 +290,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { */ public void setNetworkCapabilities(NetworkCapabilities nc) { networkCapabilities = nc; - final INetworkMonitor nm = mNetworkMonitor; + final NetworkMonitorManager nm = mNetworkMonitor; if (nm != null) { - try { - nm.notifyNetworkCapabilitiesChanged(nc); - } catch (RemoteException e) { - Log.e(TAG, "Error notifying NetworkMonitor of updated NetworkCapabilities", e); - } + nm.notifyNetworkCapabilitiesChanged(nc); } } @@ -317,11 +313,11 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { } /** - * Get the INetworkMonitor in this NetworkAgentInfo. + * Get the NetworkMonitorManager in this NetworkAgentInfo. * * <p>This will be null before {@link #onNetworkMonitorCreated(INetworkMonitor)} is called. */ - public INetworkMonitor networkMonitor() { + public NetworkMonitorManager networkMonitor() { return mNetworkMonitor; } diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java index 0910dac27337..077c4057a3a0 100644 --- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java +++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java @@ -232,14 +232,25 @@ public class NetworkNotificationManager { title = r.getString(R.string.network_switch_metered, toTransport); details = r.getString(R.string.network_switch_metered_detail, toTransport, fromTransport); + } else if (notifyType == NotificationType.NO_INTERNET + || notifyType == NotificationType.PARTIAL_CONNECTIVITY) { + // NO_INTERNET and PARTIAL_CONNECTIVITY notification for non-WiFi networks + // are sent, but they are not implemented yet. + return; } else { Slog.wtf(TAG, "Unknown notification type " + notifyType + " on network transport " + getTransportName(transportType)); return; } - - final String channelId = highPriority ? SystemNotificationChannels.NETWORK_ALERTS : - SystemNotificationChannels.NETWORK_STATUS; + // When replacing an existing notification for a given network, don't alert, just silently + // update the existing notification. Note that setOnlyAlertOnce() will only work for the + // same id, and the id used here is the NotificationType which is different in every type of + // notification. This is required because the notification metrics only track the ID but not + // the tag. + final boolean hasPreviousNotification = previousNotifyType != null; + final String channelId = (highPriority && !hasPreviousNotification) + ? SystemNotificationChannels.NETWORK_ALERTS + : SystemNotificationChannels.NETWORK_STATUS; Notification.Builder builder = new Notification.Builder(mContext, channelId) .setWhen(System.currentTimeMillis()) .setShowWhen(notifyType == NotificationType.NETWORK_SWITCH) diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 1275302cd8c9..e7a8b132d850 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -953,30 +953,22 @@ public class Vpn { return false; } - LinkProperties lp = makeLinkProperties(); - final boolean hadInternetCapability = mNetworkCapabilities.hasCapability( - NetworkCapabilities.NET_CAPABILITY_INTERNET); - final boolean willHaveInternetCapability = providesRoutesToMostDestinations(lp); - if (hadInternetCapability != willHaveInternetCapability) { - // A seamless handover would have led to a change to INTERNET capability, which - // is supposed to be immutable for a given network. In this case bail out and do not - // perform handover. - Log.i(TAG, "Handover not possible due to changes to INTERNET capability"); - return false; - } - - agent.sendLinkProperties(lp); + agent.sendLinkProperties(makeLinkProperties()); return true; } private void agentConnect() { LinkProperties lp = makeLinkProperties(); - if (providesRoutesToMostDestinations(lp)) { - mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } else { - mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } + // VPN either provide a default route (IPv4 or IPv6 or both), or they are a split tunnel + // that falls back to the default network, which by definition provides INTERNET (unless + // there is no default network, in which case none of this matters in any sense). + // Also, always setting the INTERNET bit guarantees that when a VPN applies to an app, + // the VPN will always be reported as the network by getDefaultNetwork and callbacks + // registered with registerDefaultNetworkCallback. This in turn protects the invariant + // that an app calling ConnectivityManager#bindProcessToNetwork(getDefaultNetwork()) + // behaves the same as when it uses the default network. + mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, null); @@ -1850,10 +1842,11 @@ public class Vpn { if (!profile.searchDomains.isEmpty()) { config.searchDomains = Arrays.asList(profile.searchDomains.split(" +")); } - startLegacyVpn(config, racoon, mtpd); + startLegacyVpn(config, racoon, mtpd, profile); } - private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd) { + private synchronized void startLegacyVpn(VpnConfig config, String[] racoon, String[] mtpd, + VpnProfile profile) { stopLegacyVpnPrivileged(); // Prepare for the new request. @@ -1861,7 +1854,7 @@ public class Vpn { updateState(DetailedState.CONNECTING, "startLegacyVpn"); // Start a new LegacyVpnRunner and we are done! - mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd); + mLegacyVpnRunner = new LegacyVpnRunner(config, racoon, mtpd, profile); mLegacyVpnRunner.start(); } @@ -1927,6 +1920,7 @@ public class Vpn { private final String mOuterInterface; private final AtomicInteger mOuterConnection = new AtomicInteger(ConnectivityManager.TYPE_NONE); + private final VpnProfile mProfile; private long mBringupStartTime = -1; @@ -1953,7 +1947,7 @@ public class Vpn { } }; - public LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd) { + LegacyVpnRunner(VpnConfig config, String[] racoon, String[] mtpd, VpnProfile profile) { super(TAG); mConfig = config; mDaemons = new String[] {"racoon", "mtpd"}; @@ -1969,6 +1963,8 @@ public class Vpn { // registering mOuterInterface = mConfig.interfaze; + mProfile = profile; + if (!TextUtils.isEmpty(mOuterInterface)) { final ConnectivityManager cm = ConnectivityManager.from(mContext); for (Network network : cm.getAllNetworks()) { @@ -2181,7 +2177,7 @@ public class Vpn { } // Add a throw route for the VPN server endpoint, if one was specified. - String endpoint = parameters[5]; + String endpoint = parameters[5].isEmpty() ? mProfile.server : parameters[5]; if (!endpoint.isEmpty()) { try { InetAddress addr = InetAddress.parseNumericAddress(endpoint); diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java index cebc47217831..7c1c1c7ce403 100644 --- a/services/core/java/com/android/server/net/NetworkStatsAccess.java +++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java @@ -109,7 +109,7 @@ public final class NetworkStatsAccess { final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); boolean hasCarrierPrivileges = tm != null && - tm.checkCarrierPrivilegesForPackage(callingPackage) == + tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; boolean isDeviceOwner = dpmi != null && dpmi.isActiveAdminWithPolicy(callingUid, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER); diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java index 2e64965a6181..7687718b0693 100644 --- a/services/core/java/com/android/server/net/NetworkStatsFactory.java +++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java @@ -16,6 +16,7 @@ package com.android.server.net; +import static android.net.NetworkStats.INTERFACES_ALL; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.TAG_ALL; import static android.net.NetworkStats.TAG_NONE; @@ -33,6 +34,7 @@ import android.os.SystemClock; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.net.VpnInfo; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ProcFileReader; @@ -66,15 +68,29 @@ public class NetworkStatsFactory { /** Path to {@code /proc/net/xt_qtaguid/stats}. */ private final File mStatsXtUid; - private boolean mUseBpfStats; + private final boolean mUseBpfStats; private INetd mNetdService; - // A persistent Snapshot since device start for eBPF stats - @GuardedBy("mPersistSnapshot") - private final NetworkStats mPersistSnapshot; + /** + * Guards persistent data access in this class + * + * <p>In order to prevent deadlocks, critical sections protected by this lock SHALL NOT call out + * to other code that will acquire other locks within the system server. See b/134244752. + */ + private static final Object sPersistentDataLock = new Object(); + + /** Set containing info about active VPNs and their underlying networks. */ + private static volatile VpnInfo[] sVpnInfos = new VpnInfo[0]; + + // A persistent snapshot of cumulative stats since device start + @GuardedBy("sPersistentDataLock") + private NetworkStats mPersistSnapshot; + + // The persistent snapshot of tun and 464xlat adjusted stats since device start + @GuardedBy("sPersistentDataLock") + private NetworkStats mTunAnd464xlatAdjustedStats; - // TODO: only do adjustments in NetworkStatsService and remove this. /** * (Stacked interface) -> (base interface) association for all connected ifaces since boot. * @@ -91,6 +107,24 @@ public class NetworkStatsFactory { } /** + * Set active VPN information for data usage migration purposes + * + * <p>Traffic on TUN-based VPNs inherently all appear to be originated from the VPN providing + * app's UID. This method is used to support migration of VPN data usage, ensuring data is + * accurately billed to the real owner of the traffic. + * + * @param vpnArray The snapshot of the currently-running VPNs. + */ + public static void updateVpnInfos(VpnInfo[] vpnArray) { + sVpnInfos = vpnArray.clone(); + } + + @VisibleForTesting + public static VpnInfo[] getVpnInfos() { + return sVpnInfos.clone(); + } + + /** * Get a set of interfaces containing specified ifaces and stacked interfaces. * * <p>The added stacked interfaces are ifaces stacked on top of the specified ones, or ifaces @@ -146,6 +180,7 @@ public class NetworkStatsFactory { mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats"); mUseBpfStats = useBpfStats; mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1); + mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1); } public NetworkStats readBpfNetworkStatsDev() throws IOException { @@ -263,53 +298,44 @@ public class NetworkStatsFactory { return stats; } - /** - * @deprecated Use NetworkStatsService#getDetailedUidStats which also accounts for - * VPN traffic - */ - @Deprecated public NetworkStats readNetworkStatsDetail() throws IOException { - return readNetworkStatsDetail(UID_ALL, null, TAG_ALL, null); - } - - public NetworkStats readNetworkStatsDetail(int limitUid, String[] limitIfaces, int limitTag, - NetworkStats lastStats) throws IOException { - final NetworkStats stats = - readNetworkStatsDetailInternal(limitUid, limitIfaces, limitTag, lastStats); - - // No locking here: apply464xlatAdjustments behaves fine with an add-only ConcurrentHashMap. - // TODO: remove this and only apply adjustments in NetworkStatsService. - stats.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats); - - return stats; + return readNetworkStatsDetail(UID_ALL, INTERFACES_ALL, TAG_ALL); } - @GuardedBy("mPersistSnapshot") + @GuardedBy("sPersistentDataLock") private void requestSwapActiveStatsMapLocked() throws RemoteException { // Ask netd to do a active map stats swap. When the binder call successfully returns, // the system server should be able to safely read and clean the inactive map // without race problem. - if (mUseBpfStats) { - if (mNetdService == null) { - mNetdService = NetdService.getInstance(); - } - mNetdService.trafficSwapActiveStatsMap(); + if (mNetdService == null) { + mNetdService = NetdService.getInstance(); } + mNetdService.trafficSwapActiveStatsMap(); } - // TODO: delete the lastStats parameter - private NetworkStats readNetworkStatsDetailInternal(int limitUid, String[] limitIfaces, - int limitTag, NetworkStats lastStats) throws IOException { - if (USE_NATIVE_PARSING) { - final NetworkStats stats; - if (lastStats != null) { - stats = lastStats; - stats.setElapsedRealtime(SystemClock.elapsedRealtime()); - } else { - stats = new NetworkStats(SystemClock.elapsedRealtime(), -1); - } - if (mUseBpfStats) { - synchronized (mPersistSnapshot) { + /** + * Reads the detailed UID stats based on the provided parameters + * + * @param limitUid the UID to limit this query to + * @param limitIfaces the interfaces to limit this query to. Use {@link + * NetworkStats.INTERFACES_ALL} to select all interfaces + * @param limitTag the tags to limit this query to + * @return the NetworkStats instance containing network statistics at the present time. + */ + public NetworkStats readNetworkStatsDetail( + int limitUid, String[] limitIfaces, int limitTag) throws IOException { + // In order to prevent deadlocks, anything protected by this lock MUST NOT call out to other + // code that will acquire other locks within the system server. See b/134244752. + synchronized (sPersistentDataLock) { + // Take a reference. If this gets swapped out, we still have the old reference. + final VpnInfo[] vpnArray = sVpnInfos; + // Take a defensive copy. mPersistSnapshot is mutated in some cases below + final NetworkStats prev = mPersistSnapshot.clone(); + + if (USE_NATIVE_PARSING) { + final NetworkStats stats = + new NetworkStats(SystemClock.elapsedRealtime(), 0 /* initialSize */); + if (mUseBpfStats) { try { requestSwapActiveStatsMapLocked(); } catch (RemoteException e) { @@ -318,32 +344,66 @@ public class NetworkStatsFactory { // Stats are always read from the inactive map, so they must be read after the // swap if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL, - null, TAG_ALL, mUseBpfStats) != 0) { + INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) { throw new IOException("Failed to parse network stats"); } + + // BPF stats are incremental; fold into mPersistSnapshot. mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime()); mPersistSnapshot.combineAllValues(stats); - NetworkStats result = mPersistSnapshot.clone(); - result.filter(limitUid, limitIfaces, limitTag); - return result; + } else { + if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), UID_ALL, + INTERFACES_ALL, TAG_ALL, mUseBpfStats) != 0) { + throw new IOException("Failed to parse network stats"); + } + if (SANITY_CHECK_NATIVE) { + final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, + UID_ALL, INTERFACES_ALL, TAG_ALL); + assertEquals(javaStats, stats); + } + + mPersistSnapshot = stats; } } else { - if (nativeReadNetworkStatsDetail(stats, mStatsXtUid.getAbsolutePath(), limitUid, - limitIfaces, limitTag, mUseBpfStats) != 0) { - throw new IOException("Failed to parse network stats"); - } - if (SANITY_CHECK_NATIVE) { - final NetworkStats javaStats = javaReadNetworkStatsDetail(mStatsXtUid, limitUid, - limitIfaces, limitTag); - assertEquals(javaStats, stats); - } - return stats; + mPersistSnapshot = javaReadNetworkStatsDetail(mStatsXtUid, UID_ALL, INTERFACES_ALL, + TAG_ALL); } - } else { - return javaReadNetworkStatsDetail(mStatsXtUid, limitUid, limitIfaces, limitTag); + + NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray); + + // Filter return values + adjustedStats.filter(limitUid, limitIfaces, limitTag); + return adjustedStats; } } + @GuardedBy("sPersistentDataLock") + private NetworkStats adjustForTunAnd464Xlat( + NetworkStats uidDetailStats, NetworkStats previousStats, VpnInfo[] vpnArray) { + // Calculate delta from last snapshot + final NetworkStats delta = uidDetailStats.subtract(previousStats); + + // Apply 464xlat adjustments before VPN adjustments. If VPNs are using v4 on a v6 only + // network, the overhead is their fault. + // No locking here: apply464xlatAdjustments behaves fine with an add-only + // ConcurrentHashMap. + delta.apply464xlatAdjustments(sStackedIfaces, mUseBpfStats); + + // Migrate data usage over a VPN to the TUN network. + for (VpnInfo info : vpnArray) { + delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces); + } + + // Filter out debug entries as that may lead to over counting. + delta.filterDebugEntries(); + + // Update mTunAnd464xlatAdjustedStats with migrated delta. + mTunAnd464xlatAdjustedStats.combineAllValues(delta); + mTunAnd464xlatAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime()); + + return mTunAnd464xlatAdjustedStats.clone(); + } + /** * Parse and return {@link NetworkStats} with UID-level details. Values are * expected to monotonically increase since device boot. diff --git a/services/core/java/com/android/server/net/NetworkStatsObservers.java b/services/core/java/com/android/server/net/NetworkStatsObservers.java index d8408730dd25..2564daeaa1c0 100644 --- a/services/core/java/com/android/server/net/NetworkStatsObservers.java +++ b/services/core/java/com/android/server/net/NetworkStatsObservers.java @@ -39,7 +39,6 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.VpnInfo; import java.util.concurrent.atomic.AtomicInteger; @@ -104,9 +103,9 @@ class NetworkStatsObservers { public void updateStats(NetworkStats xtSnapshot, NetworkStats uidSnapshot, ArrayMap<String, NetworkIdentitySet> activeIfaces, ArrayMap<String, NetworkIdentitySet> activeUidIfaces, - VpnInfo[] vpnArray, long currentTime) { + long currentTime) { StatsContext statsContext = new StatsContext(xtSnapshot, uidSnapshot, activeIfaces, - activeUidIfaces, vpnArray, currentTime); + activeUidIfaces, currentTime); getHandler().sendMessage(mHandler.obtainMessage(MSG_UPDATE_STATS, statsContext)); } @@ -354,7 +353,7 @@ class NetworkStatsObservers { // thread will update it. We pass a null VPN array because usage is aggregated by uid // for this snapshot, so VPN traffic can't be reattributed to responsible apps. mRecorder.recordSnapshotLocked(statsContext.mXtSnapshot, statsContext.mActiveIfaces, - null /* vpnArray */, statsContext.mCurrentTime); + statsContext.mCurrentTime); } /** @@ -396,7 +395,7 @@ class NetworkStatsObservers { // thread will update it. We pass the VPN info so VPN traffic is reattributed to // responsible apps. mRecorder.recordSnapshotLocked(statsContext.mUidSnapshot, statsContext.mActiveUidIfaces, - statsContext.mVpnArray, statsContext.mCurrentTime); + statsContext.mCurrentTime); } /** @@ -427,18 +426,16 @@ class NetworkStatsObservers { NetworkStats mUidSnapshot; ArrayMap<String, NetworkIdentitySet> mActiveIfaces; ArrayMap<String, NetworkIdentitySet> mActiveUidIfaces; - VpnInfo[] mVpnArray; long mCurrentTime; StatsContext(NetworkStats xtSnapshot, NetworkStats uidSnapshot, ArrayMap<String, NetworkIdentitySet> activeIfaces, ArrayMap<String, NetworkIdentitySet> activeUidIfaces, - VpnInfo[] vpnArray, long currentTime) { + long currentTime) { mXtSnapshot = xtSnapshot; mUidSnapshot = uidSnapshot; mActiveIfaces = activeIfaces; mActiveUidIfaces = activeUidIfaces; - mVpnArray = vpnArray; mCurrentTime = currentTime; } } diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java index bdff50053fae..06ec341d9e46 100644 --- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java +++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java @@ -23,7 +23,6 @@ import static android.text.format.DateUtils.YEAR_IN_MILLIS; import static com.android.internal.util.Preconditions.checkNotNull; -import android.annotation.Nullable; import android.net.NetworkStats; import android.net.NetworkStats.NonMonotonicObserver; import android.net.NetworkStatsHistory; @@ -37,7 +36,6 @@ import android.util.MathUtils; import android.util.Slog; import android.util.proto.ProtoOutputStream; -import com.android.internal.net.VpnInfo; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; @@ -202,18 +200,12 @@ public class NetworkStatsRecorder { } /** - * Record any delta that occurred since last {@link NetworkStats} snapshot, - * using the given {@link Map} to identify network interfaces. First - * snapshot is considered bootstrap, and is not counted as delta. - * - * @param vpnArray Optional info about the currently active VPN, if any. This is used to - * redistribute traffic from the VPN app to the underlying responsible apps. - * This should always be set to null if the provided snapshot is aggregated - * across all UIDs (e.g. contains UID_ALL buckets), regardless of VPN state. + * Record any delta that occurred since last {@link NetworkStats} snapshot, using the given + * {@link Map} to identify network interfaces. First snapshot is considered bootstrap, and is + * not counted as delta. */ public void recordSnapshotLocked(NetworkStats snapshot, - Map<String, NetworkIdentitySet> ifaceIdent, @Nullable VpnInfo[] vpnArray, - long currentTimeMillis) { + Map<String, NetworkIdentitySet> ifaceIdent, long currentTimeMillis) { final HashSet<String> unknownIfaces = Sets.newHashSet(); // skip recording when snapshot missing @@ -232,12 +224,6 @@ public class NetworkStatsRecorder { final long end = currentTimeMillis; final long start = end - delta.getElapsedRealtime(); - if (vpnArray != null) { - for (VpnInfo info : vpnArray) { - delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces); - } - } - NetworkStats.Entry entry = null; for (int i = 0; i < delta.size(); i++) { entry = delta.getValues(i, entry); diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 484efd6bfd74..2fc78d6f4760 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -130,7 +130,6 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.net.VpnInfo; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FileRotator; @@ -266,10 +265,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @GuardedBy("mStatsLock") private Network[] mDefaultNetworks = new Network[0]; - /** Set containing info about active VPNs and their underlying networks. */ - @GuardedBy("mStatsLock") - private VpnInfo[] mVpnInfos = new VpnInfo[0]; - private final DropBoxNonMonotonicObserver mNonMonotonicObserver = new DropBoxNonMonotonicObserver(); @@ -292,22 +287,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** Data layer operation counters for splicing into other structures. */ private NetworkStats mUidOperations = new NetworkStats(0L, 10); - /** - * Snapshot containing most recent network stats for all UIDs across all interfaces and tags - * since boot. - * - * <p>Maintains migrated VPN stats which are result of performing TUN migration on {@link - * #mLastUidDetailSnapshot}. - */ - @GuardedBy("mStatsLock") - private NetworkStats mTunAdjustedStats; - /** - * Used by {@link #mTunAdjustedStats} to migrate VPN traffic over delta between this snapshot - * and latest snapshot. - */ - @GuardedBy("mStatsLock") - private NetworkStats mLastUidDetailSnapshot; - /** Must be set in factory by calling #setHandler. */ private Handler mHandler; private Handler.Callback mHandlerCallback; @@ -821,39 +800,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public NetworkStats getDetailedUidStats(String[] requiredIfaces) { try { - // Get the latest snapshot from NetworkStatsFactory. - // TODO: Querying for INTERFACES_ALL may incur performance penalty. Consider restricting - // this to limited set of ifaces. - NetworkStats uidDetailStats = getNetworkStatsUidDetail(INTERFACES_ALL); - - // Migrate traffic from VPN UID over delta and update mTunAdjustedStats. - NetworkStats result; - synchronized (mStatsLock) { - migrateTunTraffic(uidDetailStats, mVpnInfos); - result = mTunAdjustedStats.clone(); - } - - // Apply filter based on ifacesToQuery. final String[] ifacesToQuery = NetworkStatsFactory.augmentWithStackedInterfaces(requiredIfaces); - result.filter(UID_ALL, ifacesToQuery, TAG_ALL); - return result; + return getNetworkStatsUidDetail(ifacesToQuery); } catch (RemoteException e) { Log.wtf(TAG, "Error compiling UID stats", e); return new NetworkStats(0L, 0); } } - @VisibleForTesting - NetworkStats getTunAdjustedStats() { - synchronized (mStatsLock) { - if (mTunAdjustedStats == null) { - return null; - } - return mTunAdjustedStats.clone(); - } - } - @Override public String[] getMobileIfaces() { return mMobileIfaces; @@ -897,7 +852,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @Override public void forceUpdateIfaces( Network[] defaultNetworks, - VpnInfo[] vpnArray, NetworkState[] networkStates, String activeIface) { checkNetworkStackPermission(mContext); @@ -905,7 +859,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final long token = Binder.clearCallingIdentity(); try { - updateIfaces(defaultNetworks, vpnArray, networkStates, activeIface); + updateIfaces(defaultNetworks, networkStates, activeIface); } finally { Binder.restoreCallingIdentity(token); } @@ -1171,13 +1125,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void updateIfaces( Network[] defaultNetworks, - VpnInfo[] vpnArray, NetworkState[] networkStates, String activeIface) { synchronized (mStatsLock) { mWakeLock.acquire(); try { - mVpnInfos = vpnArray; mActiveIface = activeIface; updateIfacesLocked(defaultNetworks, networkStates); } finally { @@ -1187,10 +1139,9 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } /** - * Inspect all current {@link NetworkState} to derive mapping from {@code - * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo} - * are active on a single {@code iface}, they are combined under a single - * {@link NetworkIdentitySet}. + * Inspect all current {@link NetworkState} to derive mapping from {@code iface} to {@link + * NetworkStatsHistory}. When multiple {@link NetworkInfo} are active on a single {@code iface}, + * they are combined under a single {@link NetworkIdentitySet}. */ @GuardedBy("mStatsLock") private void updateIfacesLocked(Network[] defaultNetworks, NetworkState[] states) { @@ -1307,55 +1258,24 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // For xt/dev, we pass a null VPN array because usage is aggregated by UID, so VPN traffic // can't be reattributed to responsible apps. Trace.traceBegin(TRACE_TAG_NETWORK, "recordDev"); - mDevRecorder.recordSnapshotLocked( - devSnapshot, mActiveIfaces, null /* vpnArray */, currentTime); + mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, currentTime); Trace.traceEnd(TRACE_TAG_NETWORK); Trace.traceBegin(TRACE_TAG_NETWORK, "recordXt"); - mXtRecorder.recordSnapshotLocked( - xtSnapshot, mActiveIfaces, null /* vpnArray */, currentTime); + mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, currentTime); Trace.traceEnd(TRACE_TAG_NETWORK); // For per-UID stats, pass the VPN info so VPN traffic is reattributed to responsible apps. - VpnInfo[] vpnArray = mVpnInfos; Trace.traceBegin(TRACE_TAG_NETWORK, "recordUid"); - mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime); + mUidRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime); Trace.traceEnd(TRACE_TAG_NETWORK); Trace.traceBegin(TRACE_TAG_NETWORK, "recordUidTag"); - mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, vpnArray, currentTime); + mUidTagRecorder.recordSnapshotLocked(uidSnapshot, mActiveUidIfaces, currentTime); Trace.traceEnd(TRACE_TAG_NETWORK); // We need to make copies of member fields that are sent to the observer to avoid // a race condition between the service handler thread and the observer's mStatsObservers.updateStats(xtSnapshot, uidSnapshot, new ArrayMap<>(mActiveIfaces), - new ArrayMap<>(mActiveUidIfaces), vpnArray, currentTime); - - migrateTunTraffic(uidSnapshot, vpnArray); - } - - /** - * Updates {@link #mTunAdjustedStats} with the delta containing traffic migrated off of VPNs. - */ - @GuardedBy("mStatsLock") - private void migrateTunTraffic(NetworkStats uidDetailStats, VpnInfo[] vpnInfoArray) { - if (mTunAdjustedStats == null) { - // Either device booted or system server restarted, hence traffic cannot be migrated - // correctly without knowing the past state of VPN's underlying networks. - mTunAdjustedStats = uidDetailStats; - mLastUidDetailSnapshot = uidDetailStats; - return; - } - // Migrate delta traffic from VPN to other apps. - NetworkStats delta = uidDetailStats.subtract(mLastUidDetailSnapshot); - for (VpnInfo info : vpnInfoArray) { - delta.migrateTun(info.ownerUid, info.vpnIface, info.underlyingIfaces); - } - // Filter out debug entries as that may lead to over counting. - delta.filterDebugEntries(); - // Update #mTunAdjustedStats with migrated delta. - mTunAdjustedStats.combineAllValues(delta); - mTunAdjustedStats.setElapsedRealtime(uidDetailStats.getElapsedRealtime()); - // Update last snapshot. - mLastUidDetailSnapshot = uidDetailStats; + new ArrayMap<>(mActiveUidIfaces), currentTime); } /** @@ -1728,8 +1648,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ private NetworkStats getNetworkStatsUidDetail(String[] ifaces) throws RemoteException { - - // TODO: remove 464xlat adjustments from NetworkStatsFactory and apply all at once here. final NetworkStats uidSnapshot = mNetworkManager.getNetworkStatsUidDetail(UID_ALL, ifaces); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 58a1dc146808..0188f7c8bfaa 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2189,6 +2189,11 @@ public class NotificationManagerService extends SystemService { @Override public boolean areNotificationsEnabledForPackage(String pkg, int uid) { checkCallerIsSystemOrSameApp(pkg); + if (UserHandle.getCallingUserId() != UserHandle.getUserId(uid)) { + getContext().enforceCallingPermission( + android.Manifest.permission.INTERACT_ACROSS_USERS, + "canNotifyAsPackage for uid " + uid); + } return mRankingHelper.getImportance(pkg, uid) != IMPORTANCE_NONE; } diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java index fde13acb8f38..eb883c9e6db9 100644 --- a/services/core/java/com/android/server/pm/InstantAppRegistry.java +++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java @@ -38,7 +38,6 @@ import android.os.storage.StorageManager; import android.provider.Settings; import android.util.ArrayMap; import android.util.AtomicFile; -import android.util.ByteStringUtils; import android.util.PackageUtils; import android.util.Slog; import android.util.SparseArray; @@ -52,6 +51,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.XmlUtils; import libcore.io.IoUtils; +import libcore.util.HexEncoding; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -66,7 +66,6 @@ import java.nio.charset.StandardCharsets; import java.security.SecureRandom; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import java.util.Set; import java.util.function.Predicate; @@ -232,7 +231,7 @@ class InstantAppRegistry { @UserIdInt int userId) { byte[] randomBytes = new byte[8]; new SecureRandom().nextBytes(randomBytes); - String id = ByteStringUtils.toHexString(randomBytes).toLowerCase(Locale.US); + String id = HexEncoding.encodeToString(randomBytes, false /* upperCase */); File appDir = getInstantApplicationDir(packageName, userId); if (!appDir.exists() && !appDir.mkdirs()) { Slog.e(LOG_TAG, "Cannot create instant app cookie directory"); diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 662352679c3b..6ae05ff50455 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -508,13 +508,25 @@ public class PackageDexOptimizer { */ private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter, boolean isUsedByOtherApps) { - int flags = info.flags; - boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0; // When a priv app is configured to run out of box, only verify it. if (info.isPrivilegedApp() && DexManager.isPackageSelectedToRunOob(info.packageName)) { return "verify"; } - if (vmSafeMode) { + + // We force vmSafeMode on debuggable apps as well: + // - the runtime ignores their compiled code + // - they generally have lots of methods that could make the compiler used run + // out of memory (b/130828957) + // Note that forcing the compiler filter here applies to all compilations (even if they + // are done via adb shell commands). That's ok because right now the runtime will ignore + // the compiled code anyway. The alternative would have been to update either + // PackageDexOptimizer#canOptimizePackage or PackageManagerService#getOptimizablePackages + // but that would have the downside of possibly producing a big odex files which would + // be ignored anyway. + boolean vmSafeModeOrDebuggable = ((info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0) + || ((info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0); + + if (vmSafeModeOrDebuggable) { return getSafeModeCompilerFilter(targetCompilerFilter); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ba2b37f676af..f938b65b48c3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -168,8 +168,8 @@ import android.content.pm.InstantAppRequest; import android.content.pm.InstantAppResolveInfo; import android.content.pm.InstrumentationInfo; import android.content.pm.IntentFilterVerificationInfo; -import android.content.pm.PackageBackwardCompatibility; import android.content.pm.KeySet; +import android.content.pm.PackageBackwardCompatibility; import android.content.pm.PackageCleanItem; import android.content.pm.PackageInfo; import android.content.pm.PackageInfoLite; @@ -259,7 +259,6 @@ import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Base64; -import android.util.ByteStringUtils; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.ExceptionUtils; @@ -299,8 +298,6 @@ import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; -import com.android.internal.util.function.QuadFunction; -import com.android.internal.util.function.TriFunction; import com.android.server.AttributeCache; import com.android.server.DeviceIdleController; import com.android.server.EventLogTags; @@ -336,6 +333,7 @@ import dalvik.system.CloseGuard; import dalvik.system.VMRuntime; import libcore.io.IoUtils; +import libcore.util.HexEncoding; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -380,7 +378,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; -import java.util.function.BiFunction; import java.util.function.Predicate; /** @@ -9972,8 +9969,9 @@ public class PackageManagerService extends IPackageManager.Stub // lib signing cert could have rotated beyond the one expected, check to see // if the new one has been blessed by the old - if (!libPkg.mSigningDetails.hasSha256Certificate( - ByteStringUtils.fromHexToByteArray(expectedCertDigests[0]))) { + byte[] digestBytes = HexEncoding.decode( + expectedCertDigests[0], false /* allowSingleChar */); + if (!libPkg.mSigningDetails.hasSha256Certificate(digestBytes)) { throw new PackageManagerException( INSTALL_FAILED_MISSING_SHARED_LIBRARY, "Package " + packageName + " requires differently signed" + @@ -18307,6 +18305,12 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isPackageDeviceAdminOnAnyUser(String packageName) { final int callingUid = Binder.getCallingUid(); + if (checkUidPermission(android.Manifest.permission.MANAGE_USERS, callingUid) + != PERMISSION_GRANTED) { + EventLog.writeEvent(0x534e4554, "128599183", -1, ""); + throw new SecurityException(android.Manifest.permission.MANAGE_USERS + + " permission is required to call this API"); + } if (getInstantAppPackageName(callingUid) != null && !isCallerSameApp(packageName, callingUid)) { return false; diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java index 88d9e52ccf51..9de6469d6453 100644 --- a/services/core/java/com/android/server/pm/dex/DexLogger.java +++ b/services/core/java/com/android/server/pm/dex/DexLogger.java @@ -16,12 +16,12 @@ package com.android.server.pm.dex; +import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; + import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.os.RemoteException; - import android.util.ArraySet; -import android.util.ByteStringUtils; import android.util.EventLog; import android.util.PackageUtils; import android.util.Slog; @@ -31,11 +31,11 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; +import libcore.util.HexEncoding; + import java.io.File; import java.util.Set; -import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo; - /** * This class is responsible for logging data about secondary dex files. * The data logged includes hashes of the name and content of each file. @@ -91,7 +91,7 @@ public class DexLogger implements DexManager.Listener { String message = PackageUtils.computeSha256Digest(dexFileName.getBytes()); // Valid SHA256 will be 256 bits, 32 bytes. if (hash.length == 32) { - message = message + ' ' + ByteStringUtils.toHexString(hash); + message = message + ' ' + HexEncoding.encodeToString(hash); } writeDclEvent(ownerUid, message); diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java index 296a6526f5a6..28c171bdeb98 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerService.java +++ b/services/core/java/com/android/server/timezone/RulesManagerService.java @@ -108,7 +108,7 @@ public final class RulesManagerService extends IRulesManager.Stub { private static RulesManagerService create(Context context) { RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context); - File baseVersionFile = new File(TimeZoneDataFiles.getRuntimeModuleTzVersionFile()); + File baseVersionFile = new File(TimeZoneDataFiles.getTimeZoneModuleTzVersionFile()); File tzDataDir = new File(TimeZoneDataFiles.getDataTimeZoneRootDir()); return new RulesManagerService( helper /* permissionHelper */, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9c6b52fb2349..d3b25fd1afa0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3934,6 +3934,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isSeparateProfileChallengeAllowed(int userHandle) { + if (!isCallerWithSystemUid()) { + throw new SecurityException("Caller must be system"); + } ComponentName profileOwner = getProfileOwner(userHandle); // Profile challenge is supported on N or newer release. return profileOwner != null && diff --git a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl index 1e688d0874de..30893b215001 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStore.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; interface IIpMemoryStore { oneway void storeNetworkAttributes(String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes, android.net.ipmemorystore.IOnStatusListener listener); diff --git a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl index cf02c26c2fe3..535ae2cf25e4 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/IIpMemoryStoreCallbacks.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; interface IIpMemoryStoreCallbacks { oneway void onIpMemoryStoreFetched(in android.net.IIpMemoryStore ipMemoryStore); diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl index 291dbef817e6..6d2dc0ccaaac 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/Blob.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ipmemorystore; parcelable Blob { byte[] data; diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl index 52f40d49abd5..48c1fb8c180a 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnBlobRetrievedListener.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ipmemorystore; interface IOnBlobRetrievedListener { oneway void onBlobRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in String name, in android.net.ipmemorystore.Blob data); diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl index 785351435d73..aebc7240bc9e 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnL2KeyResponseListener.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ipmemorystore; interface IOnL2KeyResponseListener { oneway void onL2KeyResponse(in android.net.ipmemorystore.StatusParcelable status, in String l2Key); diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl index 3dd2ae6e9bab..b66db5ab21cb 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnNetworkAttributesRetrievedListener.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ipmemorystore; interface IOnNetworkAttributesRetrievedListener { oneway void onNetworkAttributesRetrieved(in android.net.ipmemorystore.StatusParcelable status, in String l2Key, in android.net.ipmemorystore.NetworkAttributesParcelable attributes); diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl index 46d4ecb9ed7c..e9f2db445d38 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnSameL3NetworkResponseListener.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ipmemorystore; interface IOnSameL3NetworkResponseListener { oneway void onSameL3NetworkResponse(in android.net.ipmemorystore.StatusParcelable status, in android.net.ipmemorystore.SameL3NetworkResponseParcelable response); diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl index 54e654b80c9e..49172cea9587 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/IOnStatusListener.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ipmemorystore; interface IOnStatusListener { oneway void onComplete(in android.net.ipmemorystore.StatusParcelable status); diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl index 9531ea3963fb..188db20b531a 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/NetworkAttributesParcelable.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ipmemorystore; parcelable NetworkAttributesParcelable { byte[] assignedV4Address; diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl index 414272b49f1d..7a2ed48241e7 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/SameL3NetworkResponseParcelable.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ipmemorystore; parcelable SameL3NetworkResponseParcelable { String l2Key1; diff --git a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl index 92c6779b5dc0..d9b067875e84 100644 --- a/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl +++ b/services/net/aidl/ipmemorystore/3/android/net/ipmemorystore/StatusParcelable.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ipmemorystore; parcelable StatusParcelable { int resultCode; diff --git a/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl b/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl index 31891de7230a..07ff32111bb1 100644 --- a/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl +++ b/services/net/aidl/networkstack/3/android/net/DhcpResultsParcelable.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; parcelable DhcpResultsParcelable { android.net.StaticIpConfiguration baseConfiguration; diff --git a/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl b/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl index 029968b6f324..8aa68bd1c7bf 100644 --- a/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl +++ b/services/net/aidl/networkstack/3/android/net/INetworkMonitor.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; interface INetworkMonitor { oneway void start(); diff --git a/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl index ee9871ddcd15..ea93729da5e7 100644 --- a/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl +++ b/services/net/aidl/networkstack/3/android/net/INetworkMonitorCallbacks.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; interface INetworkMonitorCallbacks { oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor); diff --git a/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl b/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl index 7da11e476c0e..e3a83d17eb0b 100644 --- a/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl +++ b/services/net/aidl/networkstack/3/android/net/INetworkStackConnector.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; interface INetworkStackConnector { oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb); diff --git a/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl b/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl index f6ca6f7a78e2..3112a081735a 100644 --- a/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl +++ b/services/net/aidl/networkstack/3/android/net/INetworkStackStatusCallback.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; interface INetworkStackStatusCallback { oneway void onStatusAvailable(int statusCode); diff --git a/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl b/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl index c80a78785b3b..f846b26af808 100644 --- a/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl +++ b/services/net/aidl/networkstack/3/android/net/InitialConfigurationParcelable.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; parcelable InitialConfigurationParcelable { android.net.LinkAddress[] ipAddresses; diff --git a/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl index 65de8833e6c5..de75940f5a50 100644 --- a/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl +++ b/services/net/aidl/networkstack/3/android/net/NattKeepalivePacketDataParcelable.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; parcelable NattKeepalivePacketDataParcelable { byte[] srcAddress; diff --git a/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl b/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl index 2de790bb7754..cf0fbce94c91 100644 --- a/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl +++ b/services/net/aidl/networkstack/3/android/net/PrivateDnsConfigParcel.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; parcelable PrivateDnsConfigParcel { String hostname; diff --git a/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl b/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl index 3a6c30496fd8..c0f2d4d1747e 100644 --- a/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl +++ b/services/net/aidl/networkstack/3/android/net/ProvisioningConfigurationParcelable.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; parcelable ProvisioningConfigurationParcelable { boolean enableIPv4; diff --git a/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl b/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl index e121c064f7ac..5926794c2e8a 100644 --- a/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl +++ b/services/net/aidl/networkstack/3/android/net/TcpKeepalivePacketDataParcelable.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net; parcelable TcpKeepalivePacketDataParcelable { byte[] srcAddress; diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl index 67193ae904bc..7ab156f10553 100644 --- a/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl +++ b/services/net/aidl/networkstack/3/android/net/dhcp/DhcpServingParamsParcel.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.dhcp; parcelable DhcpServingParamsParcel { int serverAddr; diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl index 914315855496..d281ecfee61d 100644 --- a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl +++ b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServer.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.dhcp; interface IDhcpServer { oneway void start(in android.net.INetworkStackStatusCallback cb); diff --git a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl index dcc4489d52a6..98be0ab1d540 100644 --- a/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl +++ b/services/net/aidl/networkstack/3/android/net/dhcp/IDhcpServerCallbacks.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.dhcp; interface IDhcpServerCallbacks { oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server); diff --git a/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl b/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl index 176a5ce85373..85c8676ab8d0 100644 --- a/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl +++ b/services/net/aidl/networkstack/3/android/net/ip/IIpClient.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ip; interface IIpClient { oneway void completedPreDhcpAction(); diff --git a/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl b/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl index d6bc8089a0be..7fe39ed1ed7a 100644 --- a/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl +++ b/services/net/aidl/networkstack/3/android/net/ip/IIpClientCallbacks.aidl @@ -1,3 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a frozen snapshot of an AIDL interface (or parcelable). Do not +// try to edit this file. It looks like you are doing that because you have +// modified an AIDL interface in a backward-incompatible way, e.g., deleting a +// function from an interface or a field from a parcelable and it broke the +// build. That breakage is intended. +// +// You must not make a backward incompatible changes to the AIDL files built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + package android.net.ip; interface IIpClientCallbacks { oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient); diff --git a/services/net/java/android/net/IpMemoryStoreClient.java b/services/net/java/android/net/IpMemoryStoreClient.java index 3d562022a2e5..014b5289bace 100644 --- a/services/net/java/android/net/IpMemoryStoreClient.java +++ b/services/net/java/android/net/IpMemoryStoreClient.java @@ -212,4 +212,16 @@ public abstract class IpMemoryStoreClient { null, null, null)); } } + + /** + * Wipe the data in the database upon network factory reset. + */ + public void factoryReset() { + try { + runWhenServiceReady(service -> ignoringRemoteException( + () -> service.factoryReset())); + } catch (ExecutionException m) { + Log.e(TAG, "Error executing factory reset", m); + } + } } diff --git a/services/net/java/android/net/NetworkMonitorManager.java b/services/net/java/android/net/NetworkMonitorManager.java new file mode 100644 index 000000000000..0f41302c0b15 --- /dev/null +++ b/services/net/java/android/net/NetworkMonitorManager.java @@ -0,0 +1,201 @@ +/* + * 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.os.Binder; +import android.os.RemoteException; +import android.util.Log; + +/** + * A convenience wrapper for INetworkMonitor. + * + * Wraps INetworkMonitor calls, making them a bit more friendly to use. Currently handles: + * - Clearing calling identity + * - Ignoring RemoteExceptions + * - Converting to stable parcelables + * + * By design, all methods on INetworkMonitor are asynchronous oneway IPCs and are thus void. All the + * wrapper methods in this class return a boolean that callers can use to determine whether + * RemoteException was thrown. + */ +public class NetworkMonitorManager { + + @NonNull private final INetworkMonitor mNetworkMonitor; + @NonNull private final String mTag; + + public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager, + @NonNull String tag) { + mNetworkMonitor = networkMonitorManager; + mTag = tag; + } + + public NetworkMonitorManager(@NonNull INetworkMonitor networkMonitorManager) { + this(networkMonitorManager, NetworkMonitorManager.class.getSimpleName()); + } + + private void log(String s, Throwable e) { + Log.e(mTag, s, e); + } + + // CHECKSTYLE:OFF Generated code + + public boolean start() { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.start(); + return true; + } catch (RemoteException e) { + log("Error in start", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean launchCaptivePortalApp() { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.launchCaptivePortalApp(); + return true; + } catch (RemoteException e) { + log("Error in launchCaptivePortalApp", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean notifyCaptivePortalAppFinished(int response) { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.notifyCaptivePortalAppFinished(response); + return true; + } catch (RemoteException e) { + log("Error in notifyCaptivePortalAppFinished", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean setAcceptPartialConnectivity() { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.setAcceptPartialConnectivity(); + return true; + } catch (RemoteException e) { + log("Error in setAcceptPartialConnectivity", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean forceReevaluation(int uid) { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.forceReevaluation(uid); + return true; + } catch (RemoteException e) { + log("Error in forceReevaluation", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean notifyPrivateDnsChanged(PrivateDnsConfigParcel config) { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.notifyPrivateDnsChanged(config); + return true; + } catch (RemoteException e) { + log("Error in notifyPrivateDnsChanged", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean notifyDnsResponse(int returnCode) { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.notifyDnsResponse(returnCode); + return true; + } catch (RemoteException e) { + log("Error in notifyDnsResponse", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean notifyNetworkConnected(LinkProperties lp, NetworkCapabilities nc) { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.notifyNetworkConnected(lp, nc); + return true; + } catch (RemoteException e) { + log("Error in notifyNetworkConnected", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean notifyNetworkDisconnected() { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.notifyNetworkDisconnected(); + return true; + } catch (RemoteException e) { + log("Error in notifyNetworkDisconnected", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean notifyLinkPropertiesChanged(LinkProperties lp) { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.notifyLinkPropertiesChanged(lp); + return true; + } catch (RemoteException e) { + log("Error in notifyLinkPropertiesChanged", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public boolean notifyNetworkCapabilitiesChanged(NetworkCapabilities nc) { + final long token = Binder.clearCallingIdentity(); + try { + mNetworkMonitor.notifyNetworkCapabilitiesChanged(nc); + return true; + } catch (RemoteException e) { + log("Error in notifyNetworkCapabilitiesChanged", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + // CHECKSTYLE:ON Generated code +} diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java index 6b5842ff9065..787fda33717a 100644 --- a/services/net/java/android/net/NetworkStackClient.java +++ b/services/net/java/android/net/NetworkStackClient.java @@ -26,6 +26,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; @@ -33,15 +34,21 @@ import android.net.ip.IIpClientCallbacks; import android.net.util.SharedLog; import android.os.Binder; import android.os.Build; +import android.os.Environment; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.SystemClock; import android.os.UserHandle; +import android.provider.Settings; +import android.text.format.DateUtils; +import android.util.ArraySet; import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import java.io.File; import java.io.PrintWriter; import java.util.ArrayList; @@ -54,6 +61,23 @@ public class NetworkStackClient { private static final int NETWORKSTACK_TIMEOUT_MS = 10_000; private static final String IN_PROCESS_SUFFIX = ".InProcess"; + private static final String PREFS_FILE = "NetworkStackClientPrefs.xml"; + private static final String PREF_KEY_LAST_CRASH_TIME = "lastcrash_time"; + private static final String CONFIG_MIN_CRASH_INTERVAL_MS = "min_crash_interval"; + private static final String CONFIG_MIN_UPTIME_BEFORE_CRASH_MS = "min_uptime_before_crash"; + private static final String CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH = + "always_ratelimit_networkstack_crash"; + + // Even if the network stack is lost, do not crash the system more often than this. + // Connectivity would be broken, but if the user needs the device for something urgent + // (like calling emergency services) we should not bootloop the device. + // This is the default value: the actual value can be adjusted via device config. + private static final long DEFAULT_MIN_CRASH_INTERVAL_MS = 6 * DateUtils.HOUR_IN_MILLIS; + + // Even if the network stack is lost, do not crash the system server if it was less than + // this much after boot. This avoids bootlooping the device, and crashes should address very + // infrequent failures, not failures on boot. + private static final long DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS = 30 * DateUtils.MINUTE_IN_MILLIS; private static NetworkStackClient sInstance; @@ -67,12 +91,28 @@ public class NetworkStackClient { @GuardedBy("mLog") private final SharedLog mLog = new SharedLog(TAG); - private volatile boolean mNetworkStackStartRequested = false; + private volatile boolean mWasSystemServerInitialized = false; + + @GuardedBy("mHealthListeners") + private final ArraySet<NetworkStackHealthListener> mHealthListeners = new ArraySet<>(); private interface NetworkStackCallback { void onNetworkStackConnected(INetworkStackConnector connector); } + /** + * Callback interface for severe failures of the NetworkStack. + * + * <p>Useful for health monitors such as PackageWatchdog. + */ + public interface NetworkStackHealthListener { + /** + * Called when there is a severe failure of the network stack. + * @param packageName Package name of the network stack. + */ + void onNetworkStackFailure(@NonNull String packageName); + } + private NetworkStackClient() { } /** @@ -86,6 +126,15 @@ public class NetworkStackClient { } /** + * Add a {@link NetworkStackHealthListener} to listen to network stack health events. + */ + public void registerHealthListener(@NonNull NetworkStackHealthListener listener) { + synchronized (mHealthListeners) { + mHealthListeners.add(listener); + } + } + + /** * Create a DHCP server according to the specified parameters. * * <p>The server will be returned asynchronously through the provided callbacks. @@ -147,6 +196,16 @@ public class NetworkStackClient { } private class NetworkStackConnection implements ServiceConnection { + @NonNull + private final Context mContext; + @NonNull + private final String mPackageName; + + private NetworkStackConnection(@NonNull Context context, @NonNull String packageName) { + mContext = context; + mPackageName = packageName; + } + @Override public void onServiceConnected(ComponentName name, IBinder service) { logi("Network stack service connected"); @@ -155,14 +214,14 @@ public class NetworkStackClient { @Override public void onServiceDisconnected(ComponentName name) { - // The system has lost its network stack (probably due to a crash in the - // network stack process): better crash rather than stay in a bad state where all - // networking is broken. // onServiceDisconnected is not being called on device shutdown, so this method being // called always indicates a bad state for the system server. - maybeCrashWithTerribleFailure("Lost network stack"); + // This code path is only run by the system server: only the system server binds + // to the NetworkStack as a service. Other processes get the NetworkStack from + // the ServiceManager. + maybeCrashWithTerribleFailure("Lost network stack", mContext, mPackageName); } - }; + } private void registerNetworkStackService(@NonNull IBinder service) { final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service); @@ -189,7 +248,7 @@ public class NetworkStackClient { */ public void init() { log("Network stack init"); - mNetworkStackStartRequested = true; + mWasSystemServerInitialized = true; } /** @@ -202,6 +261,7 @@ public class NetworkStackClient { */ public void start(Context context) { log("Starting network stack"); + final PackageManager pm = context.getPackageManager(); // Try to bind in-process if the device was shipped with an in-process version @@ -216,16 +276,19 @@ public class NetworkStackClient { } if (intent == null) { - maybeCrashWithTerribleFailure("Could not resolve the network stack"); + maybeCrashWithTerribleFailure("Could not resolve the network stack", context, null); return; } + final String packageName = intent.getComponent().getPackageName(); + // Start the network stack. The service will be added to the service manager in // NetworkStackConnection.onServiceConnected(). - if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), + if (!context.bindServiceAsUser(intent, new NetworkStackConnection(context, packageName), Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { maybeCrashWithTerribleFailure( - "Could not bind to network stack in-process, or in app with " + intent); + "Could not bind to network stack in-process, or in app with " + intent, + context, packageName); return; } @@ -274,11 +337,93 @@ public class NetworkStackClient { } } - private void maybeCrashWithTerribleFailure(@NonNull String message) { + private void maybeCrashWithTerribleFailure(@NonNull String message, + @NonNull Context context, @Nullable String packageName) { logWtf(message, null); - if (Build.IS_DEBUGGABLE) { + // Called DeviceConfig to minimize merge conflicts + final DeviceConfigStub DeviceConfig = new DeviceConfigStub(context); + // uptime is monotonic even after a framework restart + final long uptime = SystemClock.elapsedRealtime(); + final long now = System.currentTimeMillis(); + final long minCrashIntervalMs = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY, + CONFIG_MIN_CRASH_INTERVAL_MS, DEFAULT_MIN_CRASH_INTERVAL_MS); + final long minUptimeBeforeCrash = DeviceConfig.getLong(DeviceConfig.NAMESPACE_CONNECTIVITY, + CONFIG_MIN_UPTIME_BEFORE_CRASH_MS, DEFAULT_MIN_UPTIME_BEFORE_CRASH_MS); + final boolean alwaysRatelimit = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_CONNECTIVITY, + CONFIG_ALWAYS_RATELIMIT_NETWORKSTACK_CRASH, false); + + final SharedPreferences prefs = getSharedPreferences(context); + final long lastCrashTime = tryGetLastCrashTime(prefs); + + // Only crash if there was enough time since boot, and (if known) enough time passed since + // the last crash. + // time and lastCrashTime may be unreliable if devices have incorrect clock time, but they + // are only used to limit the number of crashes compared to only using the time since boot, + // which would also be OK behavior by itself. + // - If lastCrashTime is incorrectly more than the current time, only look at uptime + // - If it is much less than current time, only look at uptime + // - If current time is during the next few hours after last crash time, don't crash. + // Considering that this only matters if last boot was some time ago, it's likely that + // time will be set correctly. Otherwise, not crashing is not a big problem anyway. Being + // in this last state would also not last for long since the window is only a few hours. + final boolean alwaysCrash = Build.IS_DEBUGGABLE && !alwaysRatelimit; + final boolean justBooted = uptime < minUptimeBeforeCrash; + final boolean haveLastCrashTime = (lastCrashTime != 0) && (lastCrashTime < now); + final boolean haveKnownRecentCrash = + haveLastCrashTime && (now < lastCrashTime + minCrashIntervalMs); + if (alwaysCrash || (!justBooted && !haveKnownRecentCrash)) { + // The system is not bound to its network stack (for example due to a crash in the + // network stack process): better crash rather than stay in a bad state where all + // networking is broken. + // Using device-encrypted SharedPreferences as DeviceConfig does not have a synchronous + // API to persist settings before a crash. + tryWriteLastCrashTime(prefs, now); throw new IllegalStateException(message); } + + // Here the system crashed recently already. Inform listeners that something is + // definitely wrong. + if (packageName != null) { + final ArraySet<NetworkStackHealthListener> listeners; + synchronized (mHealthListeners) { + listeners = new ArraySet<>(mHealthListeners); + } + for (NetworkStackHealthListener listener : listeners) { + listener.onNetworkStackFailure(packageName); + } + } + } + + @Nullable + private SharedPreferences getSharedPreferences(@NonNull Context context) { + try { + final File prefsFile = new File( + Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM), PREFS_FILE); + return context.createDeviceProtectedStorageContext() + .getSharedPreferences(prefsFile, Context.MODE_PRIVATE); + } catch (Throwable e) { + logWtf("Error loading shared preferences", e); + return null; + } + } + + private long tryGetLastCrashTime(@Nullable SharedPreferences prefs) { + if (prefs == null) return 0L; + try { + return prefs.getLong(PREF_KEY_LAST_CRASH_TIME, 0L); + } catch (Throwable e) { + logWtf("Error getting last crash time", e); + return 0L; + } + } + + private void tryWriteLastCrashTime(@Nullable SharedPreferences prefs, long value) { + if (prefs == null) return; + try { + prefs.edit().putLong(PREF_KEY_LAST_CRASH_TIME, value).commit(); + } catch (Throwable e) { + logWtf("Error writing last crash time", e); + } } /** @@ -343,14 +488,16 @@ public class NetworkStackClient { private void requestConnector(@NonNull NetworkStackCallback request) { // TODO: PID check. final int caller = Binder.getCallingUid(); - if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID) + if (caller != Process.SYSTEM_UID + && caller != Process.NETWORK_STACK_UID + && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID) && !UserHandle.isSameApp(caller, Process.PHONE_UID)) { // Don't even attempt to obtain the connector and give a nice error message throw new SecurityException( "Only the system server should try to bind to the network stack."); } - if (!mNetworkStackStartRequested) { + if (!mWasSystemServerInitialized) { // The network stack is not being started in this process, e.g. this process is not // the system server. Get a remote connector registered by the system server. final INetworkStackConnector connector = getRemoteConnector(); @@ -388,4 +535,36 @@ public class NetworkStackClient { pw.println(); pw.println("pendingNetStackRequests length: " + requestsQueueLength); } + + /** + * Stub class to replicate DeviceConfig behavior with minimal merge conflicts. + */ + private class DeviceConfigStub { + private final Context mContext; + + // Namespace is actually unused, but is here to replicate the final API. + private static final String NAMESPACE_CONNECTIVITY = "connectivity"; + + private DeviceConfigStub(Context context) { + mContext = context; + } + + private long getLong( + @NonNull String namespace, @NonNull String key, long defaultVal) { + // Temporary solution until DeviceConfig is available + try { + return Settings.Global.getLong( + mContext.getContentResolver(), TAG + "_" + key, defaultVal); + } catch (Throwable e) { + logWtf("Could not obtain setting " + key, e); + return defaultVal; + } + } + + private boolean getBoolean( + @NonNull String namespace, @NonNull String key, boolean defaultVal) { + // Temporary solution until DeviceConfig is available + return getLong(namespace, key, defaultVal ? 1 : 0) != 0; + } + } } diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java index f8d7e845da2c..1e653cd29aed 100644 --- a/services/net/java/android/net/ip/IpClientManager.java +++ b/services/net/java/android/net/ip/IpClientManager.java @@ -21,6 +21,7 @@ import android.net.NattKeepalivePacketData; import android.net.ProxyInfo; import android.net.TcpKeepalivePacketData; import android.net.shared.ProvisioningConfiguration; +import android.net.util.KeepalivePacketDataUtil; import android.os.Binder; import android.os.RemoteException; import android.util.Log; @@ -229,7 +230,8 @@ public class IpClientManager { public boolean addKeepalivePacketFilter(int slot, NattKeepalivePacketData pkt) { final long token = Binder.clearCallingIdentity(); try { - mIpClient.addNattKeepalivePacketFilter(slot, pkt.toStableParcelable()); + mIpClient.addNattKeepalivePacketFilter( + slot, KeepalivePacketDataUtil.toStableParcelable(pkt)); return true; } catch (RemoteException e) { log("Error adding Keepalive Packet Filter ", e); diff --git a/services/net/java/android/net/ipmemorystore/NetworkAttributes.java b/services/net/java/android/net/ipmemorystore/NetworkAttributes.java index e76976991797..818515ac9af1 100644 --- a/services/net/java/android/net/ipmemorystore/NetworkAttributes.java +++ b/services/net/java/android/net/ipmemorystore/NetworkAttributes.java @@ -127,6 +127,7 @@ public class NetworkAttributes { @Nullable private static InetAddress getByAddressOrNull(@Nullable final byte[] address) { + if (null == address) return null; try { return InetAddress.getByAddress(address); } catch (UnknownHostException e) { @@ -227,7 +228,9 @@ public class NetworkAttributes { } /** - * Set the lease expiry timestamp of assigned v4 address. + * Set the lease expiry timestamp of assigned v4 address. Long.MAX_VALUE is used + * to represent "infinite lease". + * * @param assignedV4AddressExpiry The lease expiry timestamp of assigned v4 address. * @return This builder. */ diff --git a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java index ca6f3029d496..395ad98f38e0 100644 --- a/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java +++ b/services/net/java/android/net/ipmemorystore/OnNetworkAttributesRetrievedListener.java @@ -40,8 +40,8 @@ public interface OnNetworkAttributesRetrievedListener { // NonNull, but still don't crash the system server if null if (null != listener) { listener.onNetworkAttributesRetrieved( - new Status(statusParcelable), l2Key, - new NetworkAttributes(networkAttributesParcelable)); + new Status(statusParcelable), l2Key, null == networkAttributesParcelable + ? null : new NetworkAttributes(networkAttributesParcelable)); } } diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java new file mode 100644 index 000000000000..9a51729212f7 --- /dev/null +++ b/services/net/java/android/net/util/KeepalivePacketDataUtil.java @@ -0,0 +1,39 @@ +/* + * 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.util; + +import android.annotation.NonNull; +import android.net.NattKeepalivePacketData; +import android.net.NattKeepalivePacketDataParcelable; + +/** @hide */ +public final class KeepalivePacketDataUtil { + /** + * Convert this NattKeepalivePacketData to a NattKeepalivePacketDataParcelable. + */ + @NonNull + public static NattKeepalivePacketDataParcelable toStableParcelable( + NattKeepalivePacketData pkt) { + final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable(); + + parcel.srcAddress = pkt.srcAddress.getAddress(); + parcel.srcPort = pkt.srcPort; + parcel.dstAddress = pkt.dstAddress.getAddress(); + parcel.dstPort = pkt.dstPort; + return parcel; + } +} diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 85d0c4cddff1..36103e3c5131 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -49,7 +49,6 @@ import static android.telephony.CarrierConfigManager.KEY_DATA_WARNING_THRESHOLD_ import static android.telephony.CarrierConfigManager.KEY_MONTHLY_DATA_CYCLE_DAY_INT; import static android.telephony.SubscriptionPlan.BYTES_UNLIMITED; import static android.telephony.SubscriptionPlan.LIMIT_BEHAVIOR_DISABLED; -import static android.text.format.Time.TIMEZONE_UTC; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_JOBS; import static com.android.server.net.NetworkPolicyManagerInternal.QUOTA_TYPE_MULTIPATH; @@ -128,7 +127,6 @@ import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.MediumTest; import android.text.TextUtils; -import android.text.format.Time; import android.util.DataUnit; import android.util.Log; import android.util.Pair; @@ -185,6 +183,7 @@ import java.util.Calendar; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.TimeZone; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -223,6 +222,7 @@ public class NetworkPolicyManagerServiceTest { * Path on assets where files used by {@link NetPolicyXml} are located. */ private static final String NETPOLICY_DIR = "NetworkPolicyManagerServiceTest/netpolicy"; + private static final String TIMEZONE_UTC = "UTC"; private BroadcastInterceptingContext mServiceContext; private File mPolicyDir; @@ -1771,7 +1771,7 @@ public class NetworkPolicyManagerServiceTest { private static NetworkPolicy buildFakeMobilePolicy(int cycleDay, long warningBytes, long limitBytes, boolean inferred){ final NetworkTemplate template = buildTemplateMobileAll(FAKE_SUBSCRIBER_ID); - return new NetworkPolicy(template, cycleDay, new Time().timezone, warningBytes, + return new NetworkPolicy(template, cycleDay, TimeZone.getDefault().getID(), warningBytes, limitBytes, SNOOZE_NEVER, SNOOZE_NEVER, true, inferred); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index f02c3f062f35..5622622e925e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -34,6 +34,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; import static android.content.pm.PackageManager.FEATURE_WATCH; import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION_CODES.O_MR1; import static android.os.Build.VERSION_CODES.P; @@ -106,6 +107,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.testing.TestablePermissions; import android.text.Html; import android.util.ArrayMap; import android.util.AtomicFile; @@ -3145,4 +3147,21 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(0, captor.getValue().getNotification().flags); } + + @Test + public void testAreNotificationsEnabledForPackage_crossUser() throws Exception { + try { + mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), + mUid + UserHandle.PER_USER_RANGE); + fail("Cannot call cross user without permission"); + } catch (SecurityException e) { + // pass + } + + // cross user, with permission, no problem + TestablePermissions perms = mContext.getTestablePermissions(); + perms.setPermission(android.Manifest.permission.INTERACT_ACROSS_USERS, PERMISSION_GRANTED); + mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(), + mUid + UserHandle.PER_USER_RANGE); + } } diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java index 6a3469c5fa24..e61542824083 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java @@ -177,15 +177,11 @@ public final class UsbDescriptorParser { * Audio Class Specific */ case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE: - if (mDeviceDescriptor.getDevClass() == UsbDescriptor.CLASSID_AUDIO) { - descriptor = UsbACInterface.allocDescriptor(this, stream, length, type); - } + descriptor = UsbACInterface.allocDescriptor(this, stream, length, type); break; case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT: - if (mDeviceDescriptor.getDevClass() == UsbDescriptor.CLASSID_AUDIO) { - descriptor = UsbACEndpoint.allocDescriptor(this, length, type); - } + descriptor = UsbACEndpoint.allocDescriptor(this, length, type); break; default: diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp new file mode 100644 index 000000000000..3adc05e2cab9 --- /dev/null +++ b/startop/apps/test/Android.bp @@ -0,0 +1,23 @@ +// +// 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. +// + +android_app { + name: "startop_test_app", + srcs: [ + "src/EmptyActivity.java", + "src/LayoutInflation.java", + ], +} diff --git a/packages/ExtServices/tests/AndroidManifest.xml b/startop/apps/test/AndroidManifest.xml index 42293b5a0b3e..6b08118b73b7 100644 --- a/packages/ExtServices/tests/AndroidManifest.xml +++ b/startop/apps/test/AndroidManifest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project +<!-- Copyright (C) 2019 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,17 +15,23 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="android.ext.services.tests.unit"> - - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> - - <application> - <uses-library android:name="android.test.runner" /> + package="com.android.startop.test"> + + <application + android:allowBackup="true" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true"> + <activity android:name=".EmptyActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name=".LayoutInflation" android:exported="true" /> </application> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="android.ext.services" - android:label="ExtServices Test Cases"> - </instrumentation> - -</manifest>
\ No newline at end of file +</manifest> diff --git a/startop/apps/test/README.md b/startop/apps/test/README.md new file mode 100644 index 000000000000..53c184bdd1c8 --- /dev/null +++ b/startop/apps/test/README.md @@ -0,0 +1,26 @@ +This directory contains a simple Android app that is meant to help in doing +controlled startup performance experiments. + +This app is structured as a number of activities that each are useful for a +different aspect of startup testing. + +# Activities + +## EmptyActivity + +This is the simplest possible Android activity. Starting this exercises only the +system parts of startup without any app-specific behavior. + + adb shell am start -n com.android.startop.test/.EmptyActivity + +## LayoutInflation + +This activity inflates a reasonably complex layout to see the impact of layout +inflation. The layout is supported by the viewcompiler, so this can be used for +testing precompiled layout performance. + +The activity adds an `inflate#activity_main` slice to atrace around the time +spent in view inflation to make it easier to focus on the time spent in view +inflation. + + adb shell am start -n com.android.startop.test/.LayoutInflation diff --git a/startop/apps/test/res/drawable-v24/ic_launcher_foreground.xml b/startop/apps/test/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 000000000000..c7bd21dbd869 --- /dev/null +++ b/startop/apps/test/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="108dp" + android:height="108dp" + android:viewportHeight="108" + android:viewportWidth="108"> + <path + android:fillType="evenOdd" + android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z" + android:strokeColor="#00000000" + android:strokeWidth="1"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="78.5885" + android:endY="90.9159" + android:startX="48.7653" + android:startY="61.0927" + android:type="linear"> + <item + android:color="#44000000" + android:offset="0.0" /> + <item + android:color="#00000000" + android:offset="1.0" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillColor="#FFFFFF" + android:fillType="nonZero" + android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z" + android:strokeColor="#00000000" + android:strokeWidth="1" /> +</vector> diff --git a/startop/apps/test/res/drawable/ic_launcher_background.xml b/startop/apps/test/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000000..d5fccc538c17 --- /dev/null +++ b/startop/apps/test/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="utf-8"?> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportHeight="108" + android:viewportWidth="108"> + <path + android:fillColor="#26A69A" + android:pathData="M0,0h108v108h-108z" /> + <path + android:fillColor="#00000000" + android:pathData="M9,0L9,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,0L19,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M29,0L29,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M39,0L39,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M49,0L49,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M59,0L59,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M69,0L69,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M79,0L79,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M89,0L89,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M99,0L99,108" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,9L108,9" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,19L108,19" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,29L108,29" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,39L108,39" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,49L108,49" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,59L108,59" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,69L108,69" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,79L108,79" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,89L108,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M0,99L108,99" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,29L89,29" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,39L89,39" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,49L89,49" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,59L89,59" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,69L89,69" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M19,79L89,79" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M29,19L29,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M39,19L39,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M49,19L49,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M59,19L59,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M69,19L69,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> + <path + android:fillColor="#00000000" + android:pathData="M79,19L79,89" + android:strokeColor="#33FFFFFF" + android:strokeWidth="0.8" /> +</vector> diff --git a/startop/apps/test/res/layout/activity_main.xml b/startop/apps/test/res/layout/activity_main.xml new file mode 100644 index 000000000000..16f5641f2325 --- /dev/null +++ b/startop/apps/test/res/layout/activity_main.xml @@ -0,0 +1,230 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".MainActivity" > + + <LinearLayout + android:layout_width="0dp" + android:layout_weight="0.5" + android:layout_height="match_parent" + android:orientation="vertical"> + + <CheckBox + android:id="@+id/checkBox5" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="CheckBox" /> + + <EditText + android:id="@+id/myEditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="EditText" /> + + <EditText + android:id="@+id/myEditText2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="EditText" /> + + <Button + android:id="@+id/myButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Button" /> + + <Button + android:id="@+id/myButton2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Button" /> + + <ProgressBar + android:id="@+id/progressBar" + style="?android:attr/progressBarStyle" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <CheckBox + android:id="@+id/checkBox2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="CheckBox" /> + + <RadioButton + android:id="@+id/radioButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="RadioButton" /> + + <CheckedTextView + android:id="@+id/checkedTextView2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="CheckedTextView" /> + + <TextView + android:id="@+id/textView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="TextView" /> + + <ToggleButton + android:id="@+id/toggleButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="ToggleButton" /> + + <Switch + android:id="@+id/switch1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Switch" /> + + <CheckBox + android:id="@+id/checkBox3" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="CheckBox" /> + + <CheckBox + android:id="@+id/checkBox4" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="CheckBox" /> + + <EditText + android:id="@+id/editText2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ems="10" + android:inputType="textPassword" /> + + <RadioButton + android:id="@+id/radioButton2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="RadioButton" /> + + <EditText + android:id="@+id/editText3" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:ems="10" + android:inputType="numberDecimal" /> + + <SearchView + android:layout_width="match_parent" + android:layout_height="match_parent" > + + </SearchView> + </LinearLayout> + + <LinearLayout + android:layout_width="0dp" + android:layout_weight="0.5" + android:layout_height="match_parent" + android:orientation="vertical"> + + <Button + android:id="@+id/myButton3" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Button" /> + + <Button + android:id="@+id/myButton4" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Button" /> + + <Button + android:id="@+id/button14" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Button" /> + + <EditText + android:id="@+id/myEditText3" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="EditText" /> + + <Button + android:id="@+id/button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="Button" /> + + <EditText + android:id="@+id/myEditText4" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="EditText" /> + + <ToggleButton + android:id="@+id/toggleButton2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="ToggleButton" /> + + <ToggleButton + android:id="@+id/toggleButton5" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="ToggleButton" /> + + <ToggleButton + android:id="@+id/toggleButton4" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="ToggleButton" /> + + <ToggleButton + android:id="@+id/toggleButton3" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="ToggleButton" /> + + <ProgressBar + android:id="@+id/progressBar2" + style="?android:attr/progressBarStyleHorizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <EditText + android:id="@+id/editText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ems="10" + android:inputType="date" /> + + <CheckedTextView + android:id="@+id/checkedTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="CheckedTextView" /> + + <SeekBar + android:id="@+id/seekBar" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <ToggleButton + android:id="@+id/toggleButton6" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="ToggleButton" /> + + <ToggleButton + android:id="@+id/toggleButton7" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="ToggleButton" /> + + </LinearLayout> +</LinearLayout> diff --git a/startop/apps/test/res/mipmap-anydpi-v26/ic_launcher.xml b/startop/apps/test/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000000..eca70cfe52ea --- /dev/null +++ b/startop/apps/test/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon>
\ No newline at end of file diff --git a/startop/apps/test/res/mipmap-anydpi-v26/ic_launcher_round.xml b/startop/apps/test/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000000..eca70cfe52ea --- /dev/null +++ b/startop/apps/test/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/ic_launcher_background" /> + <foreground android:drawable="@drawable/ic_launcher_foreground" /> +</adaptive-icon>
\ No newline at end of file diff --git a/startop/apps/test/res/mipmap-hdpi/ic_launcher.png b/startop/apps/test/res/mipmap-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..a2f5908281d0 --- /dev/null +++ b/startop/apps/test/res/mipmap-hdpi/ic_launcher.png diff --git a/startop/apps/test/res/mipmap-hdpi/ic_launcher_round.png b/startop/apps/test/res/mipmap-hdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000000..1b5239980811 --- /dev/null +++ b/startop/apps/test/res/mipmap-hdpi/ic_launcher_round.png diff --git a/startop/apps/test/res/mipmap-mdpi/ic_launcher.png b/startop/apps/test/res/mipmap-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..ff10afd6e182 --- /dev/null +++ b/startop/apps/test/res/mipmap-mdpi/ic_launcher.png diff --git a/startop/apps/test/res/mipmap-mdpi/ic_launcher_round.png b/startop/apps/test/res/mipmap-mdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000000..115a4c768a20 --- /dev/null +++ b/startop/apps/test/res/mipmap-mdpi/ic_launcher_round.png diff --git a/startop/apps/test/res/mipmap-xhdpi/ic_launcher.png b/startop/apps/test/res/mipmap-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..dcd3cd808335 --- /dev/null +++ b/startop/apps/test/res/mipmap-xhdpi/ic_launcher.png diff --git a/startop/apps/test/res/mipmap-xhdpi/ic_launcher_round.png b/startop/apps/test/res/mipmap-xhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000000..459ca609d3ae --- /dev/null +++ b/startop/apps/test/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/startop/apps/test/res/mipmap-xxhdpi/ic_launcher.png b/startop/apps/test/res/mipmap-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..8ca12fe024be --- /dev/null +++ b/startop/apps/test/res/mipmap-xxhdpi/ic_launcher.png diff --git a/startop/apps/test/res/mipmap-xxhdpi/ic_launcher_round.png b/startop/apps/test/res/mipmap-xxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000000..8e19b410a1b1 --- /dev/null +++ b/startop/apps/test/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/startop/apps/test/res/mipmap-xxxhdpi/ic_launcher.png b/startop/apps/test/res/mipmap-xxxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 000000000000..b824ebdd48db --- /dev/null +++ b/startop/apps/test/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/startop/apps/test/res/mipmap-xxxhdpi/ic_launcher_round.png b/startop/apps/test/res/mipmap-xxxhdpi/ic_launcher_round.png Binary files differnew file mode 100644 index 000000000000..4c19a13c239c --- /dev/null +++ b/startop/apps/test/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/startop/apps/test/res/values/colors.xml b/startop/apps/test/res/values/colors.xml new file mode 100644 index 000000000000..3ab3e9cbce07 --- /dev/null +++ b/startop/apps/test/res/values/colors.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="colorPrimary">#3F51B5</color> + <color name="colorPrimaryDark">#303F9F</color> + <color name="colorAccent">#FF4081</color> +</resources> diff --git a/startop/apps/test/res/values/strings.xml b/startop/apps/test/res/values/strings.xml new file mode 100644 index 000000000000..18419b58fad0 --- /dev/null +++ b/startop/apps/test/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="app_name">Startup Testing Swiss Army Knife</string> +</resources> diff --git a/startop/apps/test/src/EmptyActivity.java b/startop/apps/test/src/EmptyActivity.java new file mode 100644 index 000000000000..5202938686c0 --- /dev/null +++ b/startop/apps/test/src/EmptyActivity.java @@ -0,0 +1,26 @@ +// +// 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.startop.test; + +import android.app.Activity; + +/** + * The simplest possible Android activity, for testing startup with no + * app-specific behavior. + */ +public class EmptyActivity extends Activity { +} diff --git a/startop/apps/test/src/LayoutInflation.java b/startop/apps/test/src/LayoutInflation.java new file mode 100644 index 000000000000..daa4e4556f8e --- /dev/null +++ b/startop/apps/test/src/LayoutInflation.java @@ -0,0 +1,39 @@ +// +// 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.startop.test; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Trace; +import android.view.LayoutInflater; +import android.view.View; + +/** + * This activity inflates a reasonably complex layout to see the impact of + * layout inflation. The layout is supported by the viewcompiler, so this can be + * used for testing precompiled layout performance. + */ +public class LayoutInflation extends Activity { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + LayoutInflater inflater = LayoutInflater.from(this); + Trace.beginSection("inflate layout:activity_main"); + View view = inflater.inflate(R.layout.activity_main, /*root=*/null); + Trace.endSection(); + setContentView(view); + } +} diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 9adeea04f5df..1822cee89eaa 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -144,6 +144,16 @@ public final class Call { public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; + + /** + * Extra key used to indicate whether a {@link CallScreeningService} has requested to silence + * the ringtone for a call. If the {@link InCallService} declares + * {@link TelecomManager#METADATA_IN_CALL_SERVICE_RINGING} in its manifest, it should not + * play a ringtone for an incoming call with this extra key set. + */ + public static final String EXTRA_SILENT_RINGING_REQUESTED = + "android.telecom.extra.SILENT_RINGING_REQUESTED"; + /** * Call event sent from a {@link Call} via {@link #sendCallEvent(String, Bundle)} to inform * Telecom that the user has requested that the current {@link Call} should be handed over diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java index 27a8638cdc75..8b04b0109f02 100644 --- a/telecomm/java/android/telecom/CallScreeningService.java +++ b/telecomm/java/android/telecom/CallScreeningService.java @@ -137,12 +137,14 @@ public abstract class CallScreeningService extends Service { public static class CallResponse { private final boolean mShouldDisallowCall; private final boolean mShouldRejectCall; + private final boolean mShouldSilenceCall; private final boolean mShouldSkipCallLog; private final boolean mShouldSkipNotification; private CallResponse( boolean shouldDisallowCall, boolean shouldRejectCall, + boolean shouldSilenceCall, boolean shouldSkipCallLog, boolean shouldSkipNotification) { if (!shouldDisallowCall @@ -154,6 +156,7 @@ public abstract class CallScreeningService extends Service { mShouldRejectCall = shouldRejectCall; mShouldSkipCallLog = shouldSkipCallLog; mShouldSkipNotification = shouldSkipNotification; + mShouldSilenceCall = shouldSilenceCall; } /* @@ -172,6 +175,13 @@ public abstract class CallScreeningService extends Service { } /* + * @return Whether the ringtone should be silenced for the incoming call. + */ + public boolean getSilenceCall() { + return mShouldSilenceCall; + } + + /* * @return Whether the incoming call should not be displayed in the call log. */ public boolean getSkipCallLog() { @@ -188,6 +198,7 @@ public abstract class CallScreeningService extends Service { public static class Builder { private boolean mShouldDisallowCall; private boolean mShouldRejectCall; + private boolean mShouldSilenceCall; private boolean mShouldSkipCallLog; private boolean mShouldSkipNotification; @@ -209,6 +220,21 @@ public abstract class CallScreeningService extends Service { } /** + * Sets whether ringing should be silenced for the incoming call. When set + * to {@code true}, the Telecom framework will not play a ringtone for the call. + * The call will, however, still be sent to the default dialer app if it is not blocked. + * A {@link CallScreeningService} can use this to ensure a potential nuisance call is + * still surfaced to the user, but in a less intrusive manner. + * + * Setting this to true only makes sense when the call has not been disallowed + * using {@link #setDisallowCall(boolean)}. + */ + public @NonNull Builder setSilenceCall(boolean shouldSilenceCall) { + mShouldSilenceCall = shouldSilenceCall; + return this; + } + + /** * Sets whether the incoming call should not be displayed in the call log. This property * should only be set to true if the call is disallowed. * <p> @@ -234,6 +260,7 @@ public abstract class CallScreeningService extends Service { return new CallResponse( mShouldDisallowCall, mShouldRejectCall, + mShouldSilenceCall, mShouldSkipCallLog, mShouldSkipNotification); } @@ -285,10 +312,11 @@ public abstract class CallScreeningService extends Service { public abstract void onScreenCall(@NonNull Call.Details callDetails); /** - * Responds to the given incoming call, either allowing it or disallowing it. + * Responds to the given incoming call, either allowing it, silencing it or disallowing it. * <p> * The {@link CallScreeningService} calls this method to inform the system whether the call - * should be silently blocked or not. + * should be silently blocked or not. In the event that it should not be blocked, it may + * also be requested to ring silently. * <p> * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is * {@link Call.Details#DIRECTION_INCOMING}. @@ -310,6 +338,8 @@ public abstract class CallScreeningService extends Service { !response.getSkipCallLog(), !response.getSkipNotification(), new ComponentName(getPackageName(), getClass().getName())); + } else if (response.getSilenceCall()) { + mCallScreeningAdapter.silenceCall(callDetails.getTelecomCallId()); } else { mCallScreeningAdapter.allowCall(callDetails.getTelecomCallId()); } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index a8d70a67d2ee..1f701587412e 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -32,6 +32,7 @@ import android.os.Build; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -848,15 +849,40 @@ public class TelecomManager { /** * Returns the current SIM call manager. Apps must be prepared for this method to return - * {@code null}, indicating that there currently exists no user-chosen default - * {@code PhoneAccount}. + * {@code null}, indicating that there currently exists no SIM call manager {@link PhoneAccount} + * for the default voice subscription. * - * @return The phone account handle of the current sim call manager. + * @return The phone account handle of the current sim call manager for the default voice + * subscription. + * @see SubscriptionManager#getDefaultVoiceSubscriptionId() */ public PhoneAccountHandle getSimCallManager() { try { if (isServiceConnected()) { - return getTelecomService().getSimCallManager(); + return getTelecomService().getSimCallManager( + SubscriptionManager.getDefaultSubscriptionId()); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelecomService#getSimCallManager"); + } + return null; + } + + /** + * Returns current SIM call manager for the Telephony Subscription ID specified. Apps must be + * prepared for this method to return {@code null}, indicating that there currently exists no + * SIM call manager {@link PhoneAccount} for the subscription specified. + * + * @param subscriptionId The Telephony Subscription ID that the SIM call manager should be + * queried for. + * @return The phone account handle of the current sim call manager. + * @see SubscriptionManager#getActiveSubscriptionInfoList() + * @hide + */ + public PhoneAccountHandle getSimCallManagerForSubscription(int subscriptionId) { + try { + if (isServiceConnected()) { + return getTelecomService().getSimCallManager(subscriptionId); } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelecomService#getSimCallManager"); @@ -865,9 +891,10 @@ public class TelecomManager { } /** - * Returns the current SIM call manager for the specified user. Apps must be prepared for this - * method to return {@code null}, indicating that there currently exists no user-chosen default - * {@code PhoneAccount}. + * Returns the current SIM call manager for the user-chosen default Telephony Subscription ID + * (see {@link SubscriptionManager#getDefaultSubscriptionId()}) and the specified user. Apps + * must be prepared for this method to return {@code null}, indicating that there currently + * exists no SIM call manager {@link PhoneAccount} for the default voice subscription. * * @return The phone account handle of the current sim call manager. * @@ -888,8 +915,8 @@ public class TelecomManager { /** * Returns the current connection manager. Apps must be prepared for this method to return - * {@code null}, indicating that there currently exists no user-chosen default - * {@code PhoneAccount}. + * {@code null}, indicating that there currently exists no Connection Manager + * {@link PhoneAccount} for the default voice subscription. * * @return The phone account handle of the current connection manager. * @hide diff --git a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl index d255ed169c98..3ee3285793c4 100644 --- a/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/ICallScreeningAdapter.aidl @@ -28,6 +28,8 @@ import android.content.ComponentName; oneway interface ICallScreeningAdapter { void allowCall(String callId); + void silenceCall(String callId); + void disallowCall( String callId, boolean shouldReject, diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index a814c03ff9ad..bdd4bb38fa58 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -97,7 +97,7 @@ interface ITelecomService { /** * @see TelecomServiceImpl#getSimCallManager */ - PhoneAccountHandle getSimCallManager(); + PhoneAccountHandle getSimCallManager(int subId); /** * @see TelecomServiceImpl#getSimCallManagerForUser diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 0ebbbc623c45..77e35e1530bf 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -672,6 +672,22 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_DATA_SERVICE_WLAN_PACKAGE_OVERRIDE_STRING = "carrier_data_service_wlan_package_override_string"; + /** + * Override the device's configuration for the cellular data service class to use + * for this SIM card. + * @hide + */ + public static final String KEY_CARRIER_DATA_SERVICE_WWAN_CLASS_OVERRIDE_STRING = + "carrier_data_service_wwan_class_override_string"; + + /** + * Override the device's configuration for the IWLAN data service class to use + * for this SIM card. + * @hide + */ + public static final String KEY_CARRIER_DATA_SERVICE_WLAN_CLASS_OVERRIDE_STRING = + "carrier_data_service_wlan_class_override_string"; + /** Flag specifying whether VoLTE TTY is supported. */ public static final String KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL = "carrier_volte_tty_supported_bool"; @@ -1021,6 +1037,15 @@ public class CarrierConfigManager { "support_manage_ims_conference_call_bool"; /** + * Determines whether the IMS conference merge process supports and returns its participants + * data. When {@code true}, on merge complete, conference call would have a list of its + * participants returned in XML format, {@code false otherwise}. + * @hide + */ + public static final String KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL = + "support_ims_conference_event_package_bool"; + + /** * Determines whether High Definition audio property is displayed in the dialer UI. * If {@code false}, remove the HD audio property from the connection so that HD audio related * UI is not displayed. If {@code true}, keep HD audio property as it is configured. @@ -2375,6 +2400,14 @@ public class CarrierConfigManager { "carrier_network_service_wlan_package_override_string"; /** + * Decides when clients try to bind to iwlan network service, which class name will + * the binding intent go to. + * @hide + */ + public static final String KEY_CARRIER_NETWORK_SERVICE_WLAN_CLASS_OVERRIDE_STRING = + "carrier_network_service_wlan_class_override_string"; + + /** * Decides when clients try to bind to wwan (cellular) network service, which package name will * the binding intent go to. * @hide @@ -2383,12 +2416,28 @@ public class CarrierConfigManager { "carrier_network_service_wwan_package_override_string"; /** + * Decides when clients try to bind to wwan (cellular) network service, which class name will + * the binding intent go to. + * @hide + */ + public static final String KEY_CARRIER_NETWORK_SERVICE_WWAN_CLASS_OVERRIDE_STRING = + "carrier_network_service_wwan_class_override_string"; + + /** * The package name of qualified networks service that telephony binds to. * * @hide */ public static final String KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_PACKAGE_OVERRIDE_STRING = "carrier_qualified_networks_service_package_override_string"; + + /** + * The class name of qualified networks service that telephony binds to. + * + * @hide + */ + public static final String KEY_CARRIER_QUALIFIED_NETWORKS_SERVICE_CLASS_OVERRIDE_STRING = + "carrier_qualified_networks_service_class_override_string"; /** * A list of 4 LTE RSCP thresholds above which a signal level is considered POOR, * MODERATE, GOOD, or EXCELLENT, to be used in SignalStrength reporting. @@ -2864,6 +2913,7 @@ public class CarrierConfigManager { 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); + sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_BOOL, true); sDefaults.putBoolean(KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL, false); sDefaults.putBoolean(KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL, false); sDefaults.putInt(KEY_IMS_CONFERENCE_SIZE_LIMIT_INT, 5); diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index daad41f6a249..e9af8a8ab1ec 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -16,6 +16,8 @@ package android.telephony; +import static android.telephony.TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -341,6 +343,7 @@ public class ServiceState implements Parcelable { private String mOperatorAlphaLongRaw; private String mOperatorAlphaShortRaw; + private boolean mIsIwlanPreferred; /** * get String description of roaming type @@ -427,6 +430,7 @@ public class ServiceState implements Parcelable { mNrFrequencyRange = s.mNrFrequencyRange; mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw; mOperatorAlphaShortRaw = s.mOperatorAlphaShortRaw; + mIsIwlanPreferred = s.mIsIwlanPreferred; } /** @@ -463,6 +467,7 @@ public class ServiceState implements Parcelable { mNrFrequencyRange = in.readInt(); mOperatorAlphaLongRaw = in.readString(); mOperatorAlphaShortRaw = in.readString(); + mIsIwlanPreferred = in.readBoolean(); } public void writeToParcel(Parcel out, int flags) { @@ -492,6 +497,7 @@ public class ServiceState implements Parcelable { out.writeInt(mNrFrequencyRange); out.writeString(mOperatorAlphaLongRaw); out.writeString(mOperatorAlphaShortRaw); + out.writeBoolean(mIsIwlanPreferred); } public int describeContents() { @@ -853,7 +859,8 @@ public class ServiceState implements Parcelable { mNetworkRegistrationInfos, mNrFrequencyRange, mOperatorAlphaLongRaw, - mOperatorAlphaShortRaw); + mOperatorAlphaShortRaw, + mIsIwlanPreferred); } } @@ -885,7 +892,8 @@ public class ServiceState implements Parcelable { && equalsHandlesNulls(mOperatorAlphaShortRaw, s.mOperatorAlphaShortRaw) && mNetworkRegistrationInfos.size() == s.mNetworkRegistrationInfos.size() && mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos) - && mNrFrequencyRange == s.mNrFrequencyRange; + && mNrFrequencyRange == s.mNrFrequencyRange + && mIsIwlanPreferred == s.mIsIwlanPreferred; } } @@ -1043,6 +1051,7 @@ public class ServiceState implements Parcelable { .append(", mNrFrequencyRange=").append(mNrFrequencyRange) .append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw) .append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw) + .append(", mIsIwlanPreferred=").append(mIsIwlanPreferred) .append("}").toString(); } } @@ -1085,6 +1094,7 @@ public class ServiceState implements Parcelable { } mOperatorAlphaLongRaw = null; mOperatorAlphaShortRaw = null; + mIsIwlanPreferred = false; } public void setStateOutOfService() { @@ -1459,20 +1469,9 @@ public class ServiceState implements Parcelable { /** @hide */ @UnsupportedAppUsage public int getRilDataRadioTechnology() { - NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - NetworkRegistrationInfo wlanRegInfo = getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); - if (wlanRegInfo != null - && wlanRegInfo.getAccessNetworkTechnology() == TelephonyManager.NETWORK_TYPE_IWLAN - && wlanRegInfo.getRegistrationState() - == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) { - return RIL_RADIO_TECHNOLOGY_IWLAN; - } else if (wwanRegInfo != null) { - return networkTypeToRilRadioTechnology(wwanRegInfo.getAccessNetworkTechnology()); - } - return RIL_RADIO_TECHNOLOGY_UNKNOWN; + return networkTypeToRilRadioTechnology(getDataNetworkType()); } + /** * @hide * @Deprecated to be removed Q3 2013 use {@link #getRilDataRadioTechnology} or @@ -1609,25 +1608,45 @@ public class ServiceState implements Parcelable { } /** @hide */ + public static int networkTypeToAccessNetworkType(@TelephonyManager.NetworkType + int networkType) { + return rilRadioTechnologyToAccessNetworkType(networkTypeToRilRadioTechnology(networkType)); + } + + /** + * Get current data network type. + * + * Note that for IWLAN AP-assisted mode device, which is reporting both camped access networks + * (cellular RAT and IWLAN)at the same time, this API is simulating the old legacy mode device + * behavior, + * + * @return Current data network type + * @hide + */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public @TelephonyManager.NetworkType int getDataNetworkType() { - final NetworkRegistrationInfo iwlanRegState = getNetworkRegistrationInfo( + final NetworkRegistrationInfo iwlanRegInfo = getNetworkRegistrationInfo( NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); - if (iwlanRegState != null && iwlanRegState.getRegistrationState() - == NetworkRegistrationInfo.REGISTRATION_STATE_HOME) { - // If the device is on IWLAN, return IWLAN as the network type. This is to simulate the - // behavior of legacy mode device. In the future caller should use - // requestNetworkRegistrationInfo() to retrieve the actual data network type on cellular - // or on IWLAN. - return iwlanRegState.getAccessNetworkTechnology(); + final NetworkRegistrationInfo wwanRegInfo = getNetworkRegistrationInfo( + NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + + // For legacy mode device, or AP-assisted mode device but IWLAN is out of service, use + // the RAT from cellular. + if (iwlanRegInfo == null || !iwlanRegInfo.isInService()) { + return (wwanRegInfo != null) ? wwanRegInfo.getAccessNetworkTechnology() + : TelephonyManager.NETWORK_TYPE_UNKNOWN; } - final NetworkRegistrationInfo regState = getNetworkRegistrationInfo( - NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); - if (regState != null) { - return regState.getAccessNetworkTechnology(); + // At this point, it must be an AP-assisted mode device and IWLAN is in service. We should + // use the RAT from IWLAN service is cellular is out of service, or when both are in service + // and any APN type of data is preferred on IWLAN. + if (!wwanRegInfo.isInService() || mIsIwlanPreferred) { + return iwlanRegInfo.getAccessNetworkTechnology(); } - return TelephonyManager.NETWORK_TYPE_UNKNOWN; + + // If both cellular and IWLAN are in service, but no APN is preferred on IWLAN, still use + // the RAT from cellular. + return wwanRegInfo.getAccessNetworkTechnology(); } /** @hide */ @@ -1719,6 +1738,36 @@ public class ServiceState implements Parcelable { return false; } + /** + * + * Returns whether the bearerBitmask includes a networkType that matches the accessNetworkType. + * + * The NetworkType refers to NetworkType in TelephonyManager. For example + * {@link TelephonyManager#NETWORK_TYPE_GPRS}. + * + * The accessNetworkType refers to {@link AccessNetworkType}. + * + * @hide + * */ + public static boolean networkBitmaskHasAccessNetworkType( + @TelephonyManager.NetworkTypeBitMask int networkBitmask, int accessNetworkType) { + if (networkBitmask == NETWORK_TYPE_BITMASK_UNKNOWN) return true; + if (accessNetworkType == AccessNetworkType.UNKNOWN) return false; + + int networkType = 1; + while (networkBitmask != 0) { + if ((networkBitmask & 1) != 0) { + if (networkTypeToAccessNetworkType(networkType) == accessNetworkType) { + return true; + } + } + networkBitmask = networkBitmask >> 1; + networkType++; + } + + return false; + } + /** @hide */ public static int getBitmaskForTech(int radioTech) { if (radioTech >= 1) { @@ -1976,4 +2025,28 @@ public class ServiceState implements Parcelable { public String getOperatorAlphaShortRaw() { return mOperatorAlphaShortRaw; } + + /** + * Set to {@code true} if any data network is preferred on IWLAN. + * + * @param isIwlanPreferred {@code true} if IWLAN is preferred. + * @hide + */ + public void setIwlanPreferred(boolean isIwlanPreferred) { + mIsIwlanPreferred = isIwlanPreferred; + } + + /** + * @return {@code true} if any data network is preferred on IWLAN. + * + * Note only when this value is true, {@link #getDataNetworkType()} will return + * {@link TelephonyManager#NETWORK_TYPE_IWLAN} when AP-assisted mode device camps on both + * cellular and IWLAN. This value does not affect legacy mode devices as the data network + * type is directly reported by the modem. + * + * @hide + */ + public boolean isIwlanPreferred() { + return mIsIwlanPreferred; + } } diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 0ee08e14324d..63e3801a9b4f 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -22,22 +22,25 @@ import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; import android.app.PendingIntent; -import android.content.ActivityNotFoundException; import android.content.ContentValues; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.BaseBundle; import android.os.Build; import android.os.Bundle; import android.os.RemoteException; import android.os.ServiceManager; +import android.provider.Telephony; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; +import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.IMms; import com.android.internal.telephony.ISms; +import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.SmsRawData; import java.util.ArrayList; @@ -54,22 +57,22 @@ import java.util.Map; /** * Manages SMS operations such as sending data, text, and pdu SMS messages. - * Get this object by calling the static method {@link #getDefault()}. + * Get this object by calling the static method {@link #getDefault()}. To create an instance of + * {@link SmsManager} associated with a specific subscription ID, call + * {@link #getSmsManagerForSubscriptionId(int)}. This is typically used for devices that support + * multiple active subscriptions at once. * * <p>For information about how to behave as the default SMS app on Android 4.4 (API level 19) * and higher, see {@link android.provider.Telephony}. + * + * @see SubscriptionManager#getActiveSubscriptionInfoList() */ public final class SmsManager { private static final String TAG = "SmsManager"; - /** - * A psuedo-subId that represents the default subId at any given time. The actual subId it - * represents changes as the default subId is changed. - */ - private static final int DEFAULT_SUBSCRIPTION_ID = -1002; - /** Singleton object constructed during class initialization. */ - private static final SmsManager sInstance = new SmsManager(DEFAULT_SUBSCRIPTION_ID); + private static final SmsManager sInstance = new SmsManager( + SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); private static final Object sLockObject = new Object(); /** @hide */ @@ -110,7 +113,7 @@ public final class SmsManager { * Whether MMS is enabled for the current carrier (boolean type) */ public static final String - MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL; + MMS_CONFIG_MMS_ENABLED = CarrierConfigManager.KEY_MMS_MMS_ENABLED_BOOL; /** * Whether group MMS is enabled for the current carrier (boolean type) */ @@ -278,12 +281,6 @@ public final class SmsManager { public static final String MMS_CONFIG_CLOSE_CONNECTION = CarrierConfigManager.KEY_MMS_CLOSE_CONNECTION_BOOL; - /* - * Forwarded constants from SimDialogActivity. - */ - private static String DIALOG_TYPE_KEY = "dialog_type"; - private static final int SMS_PICK = 2; - /** * 3gpp2 SMS priority is not specified * @hide @@ -296,6 +293,18 @@ public final class SmsManager { public static final int SMS_MESSAGE_PERIOD_NOT_SPECIFIED = -1; /** + * Extra key passed into a PendingIntent when the SMS operation failed due to there being no + * default set. + */ + private static final String NO_DEFAULT_EXTRA = "noDefault"; + + // result of asking the user for a subscription to perform an operation. + private interface SubscriptionResolverResult { + void onSuccess(int subId); + void onFailure(); + } + + /** * Send a text based SMS. * * <p class="note"><strong>Note:</strong> Using this method requires that your app has the @@ -307,6 +316,15 @@ public final class SmsManager { * responsible for writing its sent messages to the SMS Provider). For information about * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p> * + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the SMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the + * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions + * where this operation may fail. + * </p> + * * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use @@ -350,15 +368,51 @@ public final class SmsManager { throw new IllegalArgumentException("Invalid message body"); } - try { - // If the subscription is invalid or default, we will use the default phone to send the - // SMS and possibly fail later in the SMS sending process. + final Context context = ActivityThread.currentApplication().getApplicationContext(); + // We will only show the SMS disambiguation dialog in the case that the message is being + // persisted. This is for two reasons: + // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific + // subscription and require special permissions. These messages are usually not sent by + // the device user and should not have an SMS disambiguation dialog associated with them + // because the device user did not trigger them. + // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the SEND_SMS + // permission. If we call resolveSubscriptionForOperation from a carrier/OEM app that has + // the correct MODIFY_PHONE_STATE or carrier permissions, but no SEND_SMS, it will throw + // an incorrect SecurityException. + if (persistMessage) { + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + ISms iSms = getISmsServiceOrThrow(); + try { + iSms.sendTextForSubscriber(subId, packageName, + destinationAddress, scAddress, text, sentIntent, deliveryIntent, + persistMessage); + } catch (RemoteException e) { + Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - " + + e.getMessage()); + notifySmsGenericError(sentIntent); + } + } + + @Override + public void onFailure() { + notifySmsErrorNoDefaultSet(context, sentIntent); + } + }); + } else { + // Not persisting the message, used by sendTextMessageWithoutPersisting() and is not + // visible to the user. ISms iSms = getISmsServiceOrThrow(); - iSms.sendTextForSubscriber(getSubscriptionId(), packageName, - destinationAddress, scAddress, text, sentIntent, deliveryIntent, - persistMessage); - } catch (RemoteException ex) { - // ignore it + try { + iSms.sendTextForSubscriber(getSubscriptionId(), packageName, + destinationAddress, scAddress, text, sentIntent, deliveryIntent, + persistMessage); + } catch (RemoteException e) { + Log.e(TAG, "sendTextMessageInternal (no persist): Couldn't send SMS, exception - " + + e.getMessage()); + notifySmsGenericError(sentIntent); + } } } @@ -375,6 +429,17 @@ public final class SmsManager { * privileges (see {@link TelephonyManager#hasCarrierPrivileges}), or that the calling app is * the default IMS app (see * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}). + * </p> + * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the SMS being sent on the subscription associated with logical + * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the + * correct subscription. + * </p> * * @see #sendTextMessage(String, String, String, PendingIntent, PendingIntent) */ @@ -394,6 +459,16 @@ public final class SmsManager { * A variant of {@link SmsManager#sendTextMessage} that allows self to be the caller. This is * for internal use only. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the SMS being sent on the subscription associated with logical + * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the + * correct subscription. + * </p> + * * @param persistMessage whether to persist the sent message in the SMS app. the caller must be * the Phone process if set to false. * @@ -417,13 +492,22 @@ public final class SmsManager { destinationAddress, scAddress, text, sentIntent, deliveryIntent, persistMessage); } catch (RemoteException ex) { - // ignore it + notifySmsGenericError(sentIntent); } } /** * Send a text based SMS with messaging options. * + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the SMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the + * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions + * where this operation may fail. + * </p> + * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC @@ -494,16 +578,59 @@ public final class SmsManager { validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED; } - try { - ISms iSms = getISmsServiceOrThrow(); - if (iSms != null) { - iSms.sendTextForSubscriberWithOptions(getSubscriptionId(), - ActivityThread.currentPackageName(), destinationAddress, scAddress, text, - sentIntent, deliveryIntent, persistMessage, priority, expectMore, - validityPeriod); + final int finalPriority = priority; + final int finalValidity = validityPeriod; + final Context context = ActivityThread.currentApplication().getApplicationContext(); + // We will only show the SMS disambiguation dialog in the case that the message is being + // persisted. This is for two reasons: + // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific + // subscription and require special permissions. These messages are usually not sent by + // the device user and should not have an SMS disambiguation dialog associated with them + // because the device user did not trigger them. + // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the SEND_SMS + // permission. If we call resolveSubscriptionForOperation from a carrier/OEM app that has + // the correct MODIFY_PHONE_STATE or carrier permissions, but no SEND_SMS, it will throw + // an incorrect SecurityException. + if (persistMessage) { + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + try { + ISms iSms = getISmsServiceOrThrow(); + if (iSms != null) { + iSms.sendTextForSubscriberWithOptions(subId, + ActivityThread.currentPackageName(), destinationAddress, + scAddress, + text, sentIntent, deliveryIntent, persistMessage, finalPriority, + expectMore, finalValidity); + } + } catch (RemoteException e) { + Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - " + + e.getMessage()); + notifySmsGenericError(sentIntent); + } + } + + @Override + public void onFailure() { + notifySmsErrorNoDefaultSet(context, sentIntent); + } + }); + } else { + try { + ISms iSms = getISmsServiceOrThrow(); + if (iSms != null) { + iSms.sendTextForSubscriberWithOptions(getSubscriptionId(), + ActivityThread.currentPackageName(), destinationAddress, + scAddress, + text, sentIntent, deliveryIntent, persistMessage, finalPriority, + expectMore, finalValidity); + } + } catch (RemoteException e) { + Log.e(TAG, "sendTextMessageInternal(no persist): Couldn't send SMS, exception - " + + e.getMessage()); + notifySmsGenericError(sentIntent); } - } catch (RemoteException ex) { - // ignore it } } @@ -515,6 +642,16 @@ public final class SmsManager { * privileges. * </p> * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the SMS being sent on the subscription associated with logical + * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the + * correct subscription. + * </p> + * * @see #sendTextMessage(String, String, String, PendingIntent, * PendingIntent, int, boolean, int) * @hide @@ -535,6 +672,16 @@ public final class SmsManager { * <p>Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or carrier * privileges per {@link android.telephony.TelephonyManager#hasCarrierPrivileges}. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the SMS being injected on the subscription associated with + * logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is + * delivered to the correct subscription. + * </p> + * * @param pdu is the byte array of pdu to be injected into android application framework * @param format is the format of SMS pdu ({@link SmsMessage#FORMAT_3GPP} or * {@link SmsMessage#FORMAT_3GPP2}) @@ -562,7 +709,13 @@ public final class SmsManager { getSubscriptionId(), pdu, format, receivedIntent); } } catch (RemoteException ex) { - // ignore it + try { + if (receivedIntent != null) { + receivedIntent.send(Telephony.Sms.Intents.RESULT_SMS_GENERIC_ERROR); + } + } catch (PendingIntent.CanceledException cx) { + // Don't worry about it, we do not need to notify the caller if this is the case. + } } } @@ -594,6 +747,16 @@ public final class SmsManager { * responsible for writing its sent messages to the SMS Provider). For information about * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p> * + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the SMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the + * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions + * where this operation may fail. + * </p> + * + * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC @@ -629,11 +792,22 @@ public final class SmsManager { } /** - * @hide * Similar method as #sendMultipartTextMessage(String, String, ArrayList, ArrayList, ArrayList) - * With an additional argument - * @param packageName serves as the default package name if ActivityThread.currentpackageName is - * null. + * With an additional argument. + * + * <p class="note"><strong>Note:</strong> This method is intended for internal use the Telephony + * framework and will never trigger an SMS disambiguation dialog. If this method is called on a + * device that has multiple active subscriptions, this {@link SmsManager} instance has been + * created with {@link #getDefault()}, and no user-defined default subscription is defined, the + * subscription ID associated with this message will be INVALID, which will result in the SMS + * being sent on the subscription associated with logical slot 0. Use + * {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the correct + * subscription. + * </p> + * + * @param packageName serves as the default package name if + * {@link ActivityThread#currentPackageName()} is null. + * @hide */ public void sendMultipartTextMessageExternal( String destinationAddress, String scAddress, ArrayList<String> parts, @@ -657,13 +831,52 @@ public final class SmsManager { } if (parts.size() > 1) { - try { - ISms iSms = getISmsServiceOrThrow(); - iSms.sendMultipartTextForSubscriber(getSubscriptionId(), - packageName, destinationAddress, scAddress, parts, - sentIntents, deliveryIntents, persistMessage); - } catch (RemoteException ex) { - // ignore it + final Context context = ActivityThread.currentApplication().getApplicationContext(); + // We will only show the SMS disambiguation dialog in the case that the message is being + // persisted. This is for two reasons: + // 1) Messages that are not persisted are sent by carrier/OEM apps for a specific + // subscription and require special permissions. These messages are usually not sent + // by the device user and should not have an SMS disambiguation dialog associated + // with them because the device user did not trigger them. + // 2) The SMS disambiguation dialog ONLY checks to make sure that the user has the + // SEND_SMS permission. If we call resolveSubscriptionForOperation from a carrier/OEM + // app that has the correct MODIFY_PHONE_STATE or carrier permissions, but no + // SEND_SMS, it will throw an incorrect SecurityException. + if (persistMessage) { + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + try { + ISms iSms = getISmsServiceOrThrow(); + iSms.sendMultipartTextForSubscriber(subId, packageName, + destinationAddress, scAddress, parts, sentIntents, + deliveryIntents, persistMessage); + } catch (RemoteException e) { + Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - " + + e.getMessage()); + notifySmsGenericError(sentIntents); + } + } + + @Override + public void onFailure() { + notifySmsErrorNoDefaultSet(context, sentIntents); + } + }); + } else { + // Called by apps that are not user facing, don't show disambiguation dialog. + try { + ISms iSms = getISmsServiceOrThrow(); + if (iSms != null) { + iSms.sendMultipartTextForSubscriber(getSubscriptionId(), packageName, + destinationAddress, scAddress, parts, sentIntents, deliveryIntents, + persistMessage); + } + } catch (RemoteException e) { + Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - " + + e.getMessage()); + notifySmsGenericError(sentIntents); + } } } else { PendingIntent sentIntent = null; @@ -682,6 +895,15 @@ public final class SmsManager { /** * Send a multi-part text based SMS without writing it into the SMS Provider. * + * <p> + * If this method is called on a device with multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the SMS sent on the subscription associated with slot + * 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent using the + * correct subscription. + * </p> + * * <p>Requires Permission: * {@link android.Manifest.permission#MODIFY_PHONE_STATE} or the calling app has carrier * privileges. @@ -713,6 +935,15 @@ public final class SmsManager { * responsible for writing its sent messages to the SMS Provider). For information about * how to behave as the default SMS app, see {@link android.provider.Telephony}.</p> * + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the SMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the + * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions + * where this operation may fail. + * </p> + * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC @@ -780,24 +1011,56 @@ public final class SmsManager { } if (priority < 0x00 || priority > 0x03) { - priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED; + priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED; } if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) { - validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED; + validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED; } if (parts.size() > 1) { - try { - ISms iSms = getISmsServiceOrThrow(); - if (iSms != null) { - iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(), - ActivityThread.currentPackageName(), destinationAddress, scAddress, - parts, sentIntents, deliveryIntents, persistMessage, priority, - expectMore, validityPeriod); + final int finalPriority = priority; + final int finalValidity = validityPeriod; + final Context context = ActivityThread.currentApplication().getApplicationContext(); + if (persistMessage) { + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + try { + ISms iSms = getISmsServiceOrThrow(); + if (iSms != null) { + iSms.sendMultipartTextForSubscriberWithOptions(subId, + ActivityThread.currentPackageName(), destinationAddress, + scAddress, parts, sentIntents, deliveryIntents, + persistMessage, finalPriority, expectMore, finalValidity); + } + } catch (RemoteException e) { + Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - " + + e.getMessage()); + notifySmsGenericError(sentIntents); + } + } + + @Override + public void onFailure() { + notifySmsErrorNoDefaultSet(context, sentIntents); + } + }); + } else { + // Sent by apps that are not user visible, so don't show SIM disambiguation dialog. + try { + ISms iSms = getISmsServiceOrThrow(); + if (iSms != null) { + iSms.sendMultipartTextForSubscriberWithOptions(getSubscriptionId(), + ActivityThread.currentPackageName(), destinationAddress, + scAddress, parts, sentIntents, deliveryIntents, + persistMessage, finalPriority, expectMore, finalValidity); + } + } catch (RemoteException e) { + Log.e(TAG, "sendMultipartTextMessageInternal (no persist): Couldn't send SMS - " + + e.getMessage()); + notifySmsGenericError(sentIntents); } - } catch (RemoteException ex) { - // ignore it } } else { PendingIntent sentIntent = null; @@ -822,6 +1085,16 @@ public final class SmsManager { * privileges. * </p> * + * <p class="note"><strong>Note:</strong> This method is intended for internal use the Telephony + * framework and will never trigger an SMS disambiguation dialog. If this method is called on a + * device that has multiple active subscriptions, this {@link SmsManager} instance has been + * created with {@link #getDefault()}, and no user-defined default subscription is defined, the + * subscription ID associated with this message will be INVALID, which will result in the SMS + * being sent on the subscription associated with logical slot 0. Use + * {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the correct + * subscription. + * </p> + * * @see #sendMultipartTextMessage(String, String, ArrayList, ArrayList, * ArrayList, int, boolean, int) * @hide @@ -835,12 +1108,21 @@ public final class SmsManager { validityPeriod); } - /** + /** * Send a data based SMS to a specific application port. * * <p class="note"><strong>Note:</strong> Using this method requires that your app has the * {@link android.Manifest.permission#SEND_SMS} permission.</p> * + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the SMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the + * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions + * where this operation may fail. + * </p> + * * @param destinationAddress the address to send the message to * @param scAddress is the service center address or null to use * the current default SMSC @@ -876,20 +1158,41 @@ public final class SmsManager { throw new IllegalArgumentException("Invalid message data"); } - try { - ISms iSms = getISmsServiceOrThrow(); - iSms.sendDataForSubscriber(getSubscriptionId(), ActivityThread.currentPackageName(), - destinationAddress, scAddress, destinationPort & 0xFFFF, - data, sentIntent, deliveryIntent); - } catch (RemoteException ex) { - // ignore it - } + final Context context = ActivityThread.currentApplication().getApplicationContext(); + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + try { + ISms iSms = getISmsServiceOrThrow(); + iSms.sendDataForSubscriber(subId, ActivityThread.currentPackageName(), + destinationAddress, scAddress, destinationPort & 0xFFFF, data, + sentIntent, deliveryIntent); + } catch (RemoteException e) { + Log.e(TAG, "sendDataMessage: Couldn't send SMS - Exception: " + e.getMessage()); + notifySmsGenericError(sentIntent); + } + } + @Override + public void onFailure() { + notifySmsErrorNoDefaultSet(context, sentIntent); + } + }); } /** * A variant of {@link SmsManager#sendDataMessage} that allows self to be the caller. This is * for internal use only. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the SMS being sent on the subscription associated with logical + * slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the SMS is sent on the + * correct subscription. + * </p> + * * @hide */ public void sendDataMessageWithSelfPermissions( @@ -908,30 +1211,60 @@ public final class SmsManager { iSms.sendDataForSubscriberWithSelfPermissions(getSubscriptionId(), ActivityThread.currentPackageName(), destinationAddress, scAddress, destinationPort & 0xFFFF, data, sentIntent, deliveryIntent); - } catch (RemoteException ex) { - // ignore it + } catch (RemoteException e) { + Log.e(TAG, "sendDataMessageWithSelfPermissions: Couldn't send SMS - Exception: " + + e.getMessage()); + notifySmsGenericError(sentIntent); } } /** * Get the SmsManager associated with the default subscription id. The instance will always be - * associated with the default subscription id, even if the default subscription id is changed. + * associated with the default subscription id, even if the default subscription id changes. + * + * <p class="note"><strong>Note:</strong> For devices that support multiple active subscriptions + * at a time, SmsManager will track the subscription set by the user as the default SMS + * subscription. If the user has not set a default, {@link SmsManager} may + * start an activity to kick off a subscription disambiguation dialog. Most operations will not + * complete until the user has chosen the subscription that will be associated with the + * operation. If the user cancels the dialog without choosing a subscription, one of the + * following will happen, depending on the target SDK version of the application. For + * compatibility purposes, if the target SDK level is <= 28, telephony will still send the SMS + * over the first available subscription. If the target SDK level is > 28, the operation will + * fail to complete. + * </p> * - * @return the SmsManager associated with the default subscription id + * <p class="note"><strong>Note:</strong> If this method is used to perform an operation on a + * device that has multiple active subscriptions, the user has not set a default SMS + * subscription, and the operation is being performed while the application is not in the + * foreground, the SMS disambiguation dialog will not be shown. The result of the operation will + * conclude as if the user cancelled the disambiguation dialog and the operation will finish as + * outlined above, depending on the target SDK version of the calling application. It is safer + * to use {@link #getSmsManagerForSubscriptionId(int)} if the application will perform the + * operation while in the background because this can cause unpredictable results, such as the + * operation being sent over the wrong subscription or failing completely, depending on the + * user's default SMS subscription setting. + * </p> + * + * @return the {@link SmsManager} associated with the default subscription id. + * + * @see SubscriptionManager#getDefaultSmsSubscriptionId() */ public static SmsManager getDefault() { return sInstance; } /** - * Get the the instance of the SmsManager associated with a particular subscription id + * Get the instance of the SmsManager associated with a particular subscription ID. * - * @param subId an SMS subscription id, typically accessed using - * {@link android.telephony.SubscriptionManager} - * @return the instance of the SmsManager associated with subId + * <p class="note"><strong>Note:</strong> Constructing an {@link SmsManager} in this manner will + * never cause an SMS disambiguation dialog to appear, unlike {@link #getDefault()}. + * </p> + * + * @see SubscriptionManager#getActiveSubscriptionInfoList() + * @see SubscriptionManager#getDefaultSmsSubscriptionId() */ public static SmsManager getSmsManagerForSubscriptionId(int subId) { - // TODO(shri): Add javadoc link once SubscriptionManager is made public api synchronized(sLockObject) { SmsManager smsManager = sSubInstances.get(subId); if (smsManager == null) { @@ -949,60 +1282,188 @@ public final class SmsManager { /** * Get the associated subscription id. If the instance was returned by {@link #getDefault()}, * then this method may return different values at different points in time (if the user - * changes the default subscription id). It will return < 0 if the default subscription id - * cannot be determined. - * - * Additionally, to support legacy applications that are not multi-SIM aware, - * if the following are true: - * - We are using a multi-SIM device - * - A default SMS SIM has not been selected - * - At least one SIM subscription is available - * then ask the user to set the default SMS SIM. + * changes the default subscription id). + * + * <p class="note"><strong>Note:</strong> This method used to display a disambiguation dialog to + * the user asking them to choose a default subscription to send SMS messages over if they + * haven't chosen yet. Starting in API level 29, we allow the user to not have a default set as + * a valid option for the default SMS subscription on multi-SIM devices. We no longer show the + * disambiguation dialog and return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if the + * device has multiple active subscriptions and no default is set. + * </p> * - * @return associated subscription id + * @return associated subscription ID or {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if + * the default subscription id cannot be determined or the device has multiple active + * subscriptions and and no default is set ("ask every time") by the user. */ public int getSubscriptionId() { - final int subId = getSubIdOrDefault(); + try { + return (mSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) + ? getISmsServiceOrThrow().getPreferredSmsSubscription() : mSubId; + } catch (RemoteException e) { + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + } + + /** + * Resolves the subscription id to use for the associated operation if + * {@link #getSubscriptionId()} returns {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. + * + * If app targets API level 28 or below and they are either sending the SMS from the background + * or the device has more than one active subscription available and no default is set, we will + * use the first logical slot to send the SMS and possibly fail later in the SMS sending + * process. + * + * Regardless of the API level, if the app is the foreground app, then we will show the SMS + * disambiguation dialog. If the app is in the background and tries to perform an operation, we + * will not show the disambiguation dialog. + * + * See {@link #getDefault()} for a detailed explanation of how this method operates. + * + * @param resolverResult The callback that will be called when the subscription is resolved or + * fails to be resolved. + */ + private void resolveSubscriptionForOperation(SubscriptionResolverResult resolverResult) { + int subId = getSubscriptionId(); boolean isSmsSimPickActivityNeeded = false; final Context context = ActivityThread.currentApplication().getApplicationContext(); try { ISms iSms = getISmsService(); if (iSms != null) { + // Determines if the SMS SIM pick activity should be shown. This is only shown if: + // 1) The device has multiple active subscriptions and an SMS default subscription + // hasn't been set, and + // 2) SmsManager is being called from the foreground app. + // Android does not allow background activity starts, so we need to block this. + // if Q+, do not perform requested operation if these two operations are not set. If + // <P, perform these operations on phone 0 (for compatibility purposes, since we + // used to not wait for the result of this activity). isSmsSimPickActivityNeeded = iSms.isSmsSimPickActivityNeeded(subId); } } catch (RemoteException ex) { - Log.e(TAG, "Exception in getSubscriptionId"); + Log.e(TAG, "resolveSubscriptionForOperation", ex); + } + if (!isSmsSimPickActivityNeeded) { + sendResolverResult(resolverResult, subId, false /*pickActivityShown*/); + 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()); + 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(), + new IIntegerConsumer.Stub() { + @Override + public void accept(int subId) { + // Runs on binder thread attached to this app's process. + sendResolverResult(resolverResult, subId, true /*pickActivityShown*/); + } + }); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to launch activity", ex); + // pickActivityShown is true here because we want to call sendResolverResult and always + // have this operation fail. This is because we received a RemoteException here, which + // means that telephony is not available and the next operation to Telephony will fail + // as well anyways, so we might as well shortcut fail here first. + sendResolverResult(resolverResult, subId, true /*pickActivityShown*/); + } + } + + private void sendResolverResult(SubscriptionResolverResult resolverResult, int subId, + boolean pickActivityShown) { + if (SubscriptionManager.isValidSubscriptionId(subId)) { + resolverResult.onSuccess(subId); + return; + } + + if (getTargetSdkVersion() <= Build.VERSION_CODES.P && !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. + // This will resolve to performing the operation on phone 0 in telephony. + resolverResult.onSuccess(subId); + } else { + // Fail if the app targets Q or above or it targets P and below and the disambiguation + // dialog was shown and the user clicked out of it. + resolverResult.onFailure(); } + } + + 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; + } - if (isSmsSimPickActivityNeeded) { - Log.d(TAG, "getSubscriptionId isSmsSimPickActivityNeeded is true"); - // ask the user for a default SMS SIM. - Intent intent = new Intent(); - intent.setClassName("com.android.settings", - "com.android.settings.sim.SimDialogActivity"); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(DIALOG_TYPE_KEY, SMS_PICK); + private static ITelephony getITelephony() { + ITelephony binder = ITelephony.Stub.asInterface( + ServiceManager.getService(Context.TELEPHONY_SERVICE)); + if (binder == null) { + throw new RuntimeException("Could not find Telephony Service."); + } + return binder; + } + + private static void notifySmsErrorNoDefaultSet(Context context, PendingIntent pendingIntent) { + if (pendingIntent != null) { + Intent errorMessage = new Intent(); + errorMessage.putExtra(NO_DEFAULT_EXTRA, true); try { - context.startActivity(intent); - } catch (ActivityNotFoundException anfe) { - // If Settings is not installed, only log the error as we do not want to break - // legacy applications. - Log.e(TAG, "Unable to launch Settings application."); + pendingIntent.send(context, RESULT_ERROR_GENERIC_FAILURE, errorMessage); + } catch (PendingIntent.CanceledException e) { + // Don't worry about it, we do not need to notify the caller if this is the case. } } + } - return subId; + private static void notifySmsErrorNoDefaultSet(Context context, + List<PendingIntent> pendingIntents) { + if (pendingIntents != null) { + for (PendingIntent pendingIntent : pendingIntents) { + Intent errorMessage = new Intent(); + errorMessage.putExtra(NO_DEFAULT_EXTRA, true); + try { + pendingIntent.send(context, RESULT_ERROR_GENERIC_FAILURE, errorMessage); + } catch (PendingIntent.CanceledException e) { + // Don't worry about it, we do not need to notify the caller if this is the + // case. + } + } + } } - /** - * @return the subscription ID associated with this {@link SmsManager} or the default set by the - * user if this instance was created using {@link SmsManager#getDefault}. - * - * If there is no default set by the user, this method returns - * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. - */ - private int getSubIdOrDefault() { - return (mSubId == DEFAULT_SUBSCRIPTION_ID) ? getDefaultSmsSubscriptionId() : mSubId; + private static void notifySmsGenericError(PendingIntent pendingIntent) { + if (pendingIntent != null) { + try { + pendingIntent.send(RESULT_ERROR_GENERIC_FAILURE); + } catch (PendingIntent.CanceledException e) { + // Don't worry about it, we do not need to notify the caller if this is the case. + } + } + } + + private static void notifySmsGenericError(List<PendingIntent> pendingIntents) { + if (pendingIntents != null) { + for (PendingIntent pendingIntent : pendingIntents) { + try { + pendingIntent.send(RESULT_ERROR_GENERIC_FAILURE); + } catch (PendingIntent.CanceledException e) { + // Don't worry about it, we do not need to notify the caller if this is the + // case. + } + } + } } /** @@ -1026,6 +1487,16 @@ public final class SmsManager { * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * 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, @@ -1061,6 +1532,16 @@ public final class SmsManager { * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param messageIndex is the record index of the message on ICC * @return true for success * @@ -1092,6 +1573,16 @@ public final class SmsManager { * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param messageIndex record index of message to update * @param newStatus new message status (STATUS_ON_ICC_READ, * STATUS_ON_ICC_UNREAD, STATUS_ON_ICC_SENT, @@ -1124,6 +1615,16 @@ public final class SmsManager { * ICC (Integrated Circuit Card) is the card of the device. * For example, this can be the SIM or USIM for GSM. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @return <code>ArrayList</code> of <code>SmsMessage</code> objects * * {@hide} @@ -1156,6 +1657,16 @@ public final class SmsManager { * Note: This call is blocking, callers may want to avoid calling it from * the main thread of an application. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param ranType as defined in class SmsManager, the value can be one of these: @@ -1172,8 +1683,9 @@ public final class SmsManager { try { ISms iSms = getISmsService(); if (iSms != null) { - // If getSubIdOrDefault() returns INVALID, we will use the default phone internally. - success = iSms.enableCellBroadcastForSubscriber(getSubIdOrDefault(), + // If getSubscriptionId() returns INVALID or an inactive subscription, we will use + // the default phone internally. + success = iSms.enableCellBroadcastForSubscriber(getSubscriptionId(), messageIdentifier, ranType); } } catch (RemoteException ex) { @@ -1192,6 +1704,16 @@ public final class SmsManager { * Note: This call is blocking, callers may want to avoid calling it from * the main thread of an application. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param messageIdentifier Message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param ranType as defined in class SmsManager, the value can be one of these: @@ -1209,8 +1731,9 @@ public final class SmsManager { try { ISms iSms = getISmsService(); if (iSms != null) { - // If getSubIdOrDefault() returns INVALID, we will use the default phone internally. - success = iSms.disableCellBroadcastForSubscriber(getSubIdOrDefault(), + // If getSubscriptionId() returns INVALID or an inactive subscription, we will use + // the default phone internally. + success = iSms.disableCellBroadcastForSubscriber(getSubscriptionId(), messageIdentifier, ranType); } } catch (RemoteException ex) { @@ -1230,6 +1753,16 @@ public final class SmsManager { * Note: This call is blocking, callers may want to avoid calling it from * the main thread of an application. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param startMessageId first message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param endMessageId last message identifier as specified in TS 23.041 (3GPP) @@ -1253,8 +1786,9 @@ public final class SmsManager { try { ISms iSms = getISmsService(); if (iSms != null) { - // If getSubIdOrDefault() returns INVALID, we will use the default phone internally. - success = iSms.enableCellBroadcastRangeForSubscriber(getSubIdOrDefault(), + // If getSubscriptionId() returns INVALID or an inactive subscription, we will use + // the default phone internally. + success = iSms.enableCellBroadcastRangeForSubscriber(getSubscriptionId(), startMessageId, endMessageId, ranType); } } catch (RemoteException ex) { @@ -1273,6 +1807,16 @@ public final class SmsManager { * Note: This call is blocking, callers may want to avoid calling it from * the main thread of an application. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param startMessageId first message identifier as specified in TS 23.041 (3GPP) * or C.R1001-G (3GPP2) * @param endMessageId last message identifier as specified in TS 23.041 (3GPP) @@ -1297,8 +1841,9 @@ public final class SmsManager { try { ISms iSms = getISmsService(); if (iSms != null) { - // If getSubIdOrDefault() returns INVALID, we will use the default phone internally. - success = iSms.disableCellBroadcastRangeForSubscriber(getSubIdOrDefault(), + // If getSubscriptionId() returns INVALID or an inactive subscription, we will use + // the default phone internally. + success = iSms.disableCellBroadcastRangeForSubscriber(getSubscriptionId(), startMessageId, endMessageId, ranType); } } catch (RemoteException ex) { @@ -1312,6 +1857,16 @@ public final class SmsManager { * Create a list of <code>SmsMessage</code>s from a list of RawSmsData * records returned by <code>getAllMessagesFromIcc()</code> * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param records SMS EF records, returned by * <code>getAllMessagesFromIcc</code> * @return <code>ArrayList</code> of <code>SmsMessage</code> objects. @@ -1339,6 +1894,16 @@ public final class SmsManager { * SMS over IMS is supported if IMS is registered and SMS is supported * on IMS. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @return true if SMS over IMS is supported, false otherwise * * @see #getImsSmsFormat() @@ -1359,8 +1924,17 @@ public final class SmsManager { } /** - * Gets SMS format supported on IMS. SMS over IMS format is - * either 3GPP or 3GPP2. + * Gets SMS format supported on IMS. SMS over IMS format is either 3GPP or 3GPP2. + * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> * * @return SmsMessage.FORMAT_3GPP, * SmsMessage.FORMAT_3GPP2 @@ -1384,19 +1958,24 @@ public final class SmsManager { } /** - * Get default sms subscription id + * Get default sms subscription id. * - * @return the default SMS subscription id + * <p class="note"><strong>Note:</strong>This returns a value different from + * {@link SubscriptionManager#getDefaultSmsSubscriptionId} if the user has not chosen a default. + * In this case it returns the active subscription id if there's only one active subscription + * available. + * + * @return the user-defined default SMS subscription id, or the active subscription id if + * there's only one active subscription available, otherwise + * {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}. */ public static int getDefaultSmsSubscriptionId() { - ISms iSms = null; try { - iSms = ISms.Stub.asInterface(ServiceManager.getService("isms")); - return iSms.getPreferredSmsSubscription(); - } catch (RemoteException ex) { - return -1; - } catch (NullPointerException ex) { - return -1; + return getISmsService().getPreferredSmsSubscription(); + } catch (RemoteException e) { + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } catch (NullPointerException e) { + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; } } @@ -1567,6 +2146,15 @@ public final class SmsManager { /** * Send an MMS message * + * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param context application context * @param contentUri the content Uri from which the message pdu will be read * @param locationUrl the optional location url where message should be sent to @@ -1597,6 +2185,15 @@ public final class SmsManager { /** * Download an MMS message from carrier by a given location URL * + * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param context application context * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained * from the MMS WAP push notification @@ -1620,9 +2217,8 @@ public final class SmsManager { if (iMms == null) { return; } - iMms.downloadMessage( - getSubscriptionId(), ActivityThread.currentPackageName(), locationUrl, - contentUri, configOverrides, downloadedIntent); + iMms.downloadMessage(getSubscriptionId(), ActivityThread.currentPackageName(), + locationUrl, contentUri, configOverrides, downloadedIntent); } catch (RemoteException e) { // Ignore it } @@ -1860,6 +2456,15 @@ public final class SmsManager { * * You can only send a failed text message or a draft text message. * + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the SMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the + * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions + * where this operation may fail. + * </p> + * * @param messageUri the URI of the stored message * @param scAddress is the service center address or null to use the current default SMSC * @param sentIntent if not NULL this <code>PendingIntent</code> is @@ -1887,14 +2492,25 @@ public final class SmsManager { if (messageUri == null) { throw new IllegalArgumentException("Empty message URI"); } - try { - ISms iSms = getISmsServiceOrThrow(); - iSms.sendStoredText( - getSubscriptionId(), ActivityThread.currentPackageName(), messageUri, - scAddress, sentIntent, deliveryIntent); - } catch (RemoteException ex) { - // ignore it - } + final Context context = ActivityThread.currentApplication().getApplicationContext(); + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + try { + ISms iSms = getISmsServiceOrThrow(); + iSms.sendStoredText(subId, ActivityThread.currentPackageName(), messageUri, + scAddress, sentIntent, deliveryIntent); + } catch (RemoteException e) { + Log.e(TAG, "sendStoredTextMessage: Couldn't send SMS - Exception: " + + e.getMessage()); + notifySmsGenericError(sentIntent); + } + } + @Override + public void onFailure() { + notifySmsErrorNoDefaultSet(context, sentIntent); + } + }); } /** @@ -1904,6 +2520,15 @@ public final class SmsManager { * The provided <code>PendingIntent</code> lists should match the part number of the * divided text of the stored message by using <code>divideMessage</code> * + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the SMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_ERROR_GENERIC_FAILURE} and an extra string {@code "noDefault"} containing the + * boolean value {@code true}. See {@link #getDefault()} for more information on the conditions + * where this operation may fail. + * </p> + * * @param messageUri the URI of the stored message * @param scAddress is the service center address or null to use * the current default SMSC @@ -1935,14 +2560,25 @@ public final class SmsManager { if (messageUri == null) { throw new IllegalArgumentException("Empty message URI"); } - try { - ISms iSms = getISmsServiceOrThrow(); - iSms.sendStoredMultipartText( - getSubscriptionId(), ActivityThread.currentPackageName(), messageUri, - scAddress, sentIntents, deliveryIntents); - } catch (RemoteException ex) { - // ignore it - } + final Context context = ActivityThread.currentApplication().getApplicationContext(); + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + try { + ISms iSms = getISmsServiceOrThrow(); + iSms.sendStoredMultipartText(subId, ActivityThread.currentPackageName(), + messageUri, scAddress, sentIntents, deliveryIntents); + } catch (RemoteException e) { + Log.e(TAG, "sendStoredTextMessage: Couldn't send SMS - Exception: " + + e.getMessage()); + notifySmsGenericError(sentIntents); + } + } + @Override + public void onFailure() { + notifySmsErrorNoDefaultSet(context, sentIntents); + } + }); } /** @@ -1951,6 +2587,15 @@ public final class SmsManager { * This is used for sending a previously sent, but failed-to-send, message or * for sending a text message that has been stored as a draft. * + * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @param messageUri the URI of the stored message * @param configOverrides the carrier-specific messaging configuration values to override for * sending the message. @@ -2024,6 +2669,16 @@ public final class SmsManager { /** * Get carrier-dependent configuration values. * + * <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 + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @return bundle key/values pairs of configuration values */ public Bundle getCarrierConfigValues() { @@ -2039,7 +2694,7 @@ public final class SmsManager { } /** - * Create a single use app specific incoming SMS request for the the calling package. + * Create a single use app specific incoming SMS request for the calling package. * * This method returns a token that if included in a subsequent incoming SMS message will cause * {@code intent} to be sent with the SMS data. @@ -2050,6 +2705,15 @@ public final class SmsManager { * An app can only have one request at a time, if the app already has a request pending it will * be replaced with a new request. * + * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation + * dialog. If this method is called on a device that has multiple active subscriptions, this + * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined + * default subscription is defined, the subscription ID associated with this message will be + * INVALID, which will result in the operation being completed on the subscription associated + * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the + * operation is performed on the correct subscription. + * </p> + * * @return Token to include in an SMS message. The token will be 11 characters long. * @see android.provider.Telephony.Sms.Intents#getMessagesFromIntent */ @@ -2134,5 +2798,4 @@ public final class SmsManager { config.getBoolean(MMS_CONFIG_SUPPORT_HTTP_CHARSET_HEADER)); return filtered; } - } diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index 7d4bcb740f61..b705d71c61d9 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -843,20 +843,16 @@ public class SmsMessage { } /** - * GSM: - * For an SMS-STATUS-REPORT message, this returns the status field from - * the status report. This field indicates the status of a previously - * submitted SMS, if requested. See TS 23.040, 9.2.3.15 TP-Status for a - * description of values. - * CDMA: - * For not interfering with status codes from GSM, the value is - * shifted to the bits 31-16. - * The value is composed of an error class (bits 25-24) and a status code (bits 23-16). - * Possible codes are described in C.S0015-B, v2.0, 4.5.21. + * GSM: For an SMS-STATUS-REPORT message, this returns the status field from the status report. + * This field indicates the status of a previously submitted SMS, if requested. + * See TS 23.040, 9.2.3.15 TP-Status for a description of values. + * CDMA: For not interfering with status codes from GSM, the value is shifted to the bits 31-16. + * The value is composed of an error class (bits 25-24) and a status code (bits 23-16). Possible + * codes are described in C.S0015-B, v2.0, 4.5.21. * - * @return 0 indicates the previously sent message was received. - * See TS 23.040, 9.9.2.3.15 and C.S0015-B, v2.0, 4.5.21 - * for a description of other possible values. + * @return 0 for GSM or 2 shifted left by 16 for CDMA indicates the previously sent message was + * received. See TS 23.040, 9.2.3.15 and C.S0015-B, v2.0, 4.5.21 for a description of + * other possible values. */ public int getStatus() { return mWrappedSmsMessage.getStatus(); diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 70471d719205..f2a82333a5c1 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -731,19 +731,19 @@ public class SubscriptionInfo implements Parcelable { public String toString() { String iccIdToPrint = givePrintableIccid(mIccId); String cardStringToPrint = givePrintableIccid(mCardString); - return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex + return "{id=" + mId + " iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex + " carrierId=" + mCarrierId + " displayName=" + mDisplayName + " carrierName=" + mCarrierName + " nameSource=" + mNameSource - + " iconTint=" + mIconTint + " mNumber=" + mNumber - + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc - + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded - + " accessRules " + Arrays.toString(mAccessRules) + + " iconTint=" + mIconTint + " mNumber=" + Rlog.pii(Build.IS_DEBUGGABLE, mNumber) + + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc=" + mMcc + + " mnc=" + mMnc + " mCountryIso=" + mCountryIso + " isEmbedded=" + mIsEmbedded + + " accessRules=" + Arrays.toString(mAccessRules) + " cardString=" + cardStringToPrint + " cardId=" + mCardId - + " isOpportunistic " + mIsOpportunistic + " mGroupUUID=" + mGroupUUID + + " isOpportunistic=" + mIsOpportunistic + " mGroupUUID=" + mGroupUUID + " mIsGroupDisabled=" + mIsGroupDisabled + " profileClass=" + mProfileClass - + " ehplmns = " + Arrays.toString(mEhplmns) - + " hplmns = " + Arrays.toString(mHplmns) + + " ehplmns=" + Arrays.toString(mEhplmns) + + " hplmns=" + Arrays.toString(mHplmns) + " subscriptionType=" + mSubscriptionType + " mGroupOwner=" + mGroupOwner + "}"; } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index addd9e0591b0..12422c679bd0 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -302,11 +302,27 @@ public class SubscriptionManager { * subscription. * * Default value is 0. + * + * @deprecated Replaced by {@link #DATA_ENABLED_OVERRIDE_RULES} + * @hide */ - /** @hide */ + @Deprecated public static final String WHITE_LISTED_APN_DATA = "white_listed_apn_data"; /** + * 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. + * + * @hide + */ + public static final String DATA_ENABLED_OVERRIDE_RULES = "data_enabled_override_rules"; + + /** * 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. @@ -3159,4 +3175,24 @@ public class SubscriptionManager { return result; } + + /** + * Get active data subscription id. + * See {@link PhoneStateListener#onActiveDataSubscriptionIdChanged(int)} for the details. + * + * @return Active data subscription id + * + * //TODO: Refactor this API in b/134702460 + * @hide + */ + public static int getActiveDataSubscriptionId() { + try { + ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); + if (iSub != null) { + return iSub.getActiveDataSubscriptionId(); + } + } catch (RemoteException ex) { + } + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 57316466c3fd..a88434d79691 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -93,6 +93,8 @@ import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.RILConstants; import com.android.internal.telephony.TelephonyProperties; +import dalvik.system.VMRuntime; + import java.io.FileInputStream; import java.io.IOException; import java.lang.annotation.Retention; @@ -103,6 +105,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.UUID; import java.util.concurrent.Executor; import java.util.function.Consumer; import java.util.regex.Matcher; @@ -1484,6 +1487,48 @@ public class TelephonyManager { */ public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; + /** + * Integer intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED} + * to indicate if the SIM combination in DSDS has limitation or compatible issue. + * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios. + * + * @hide + */ + public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = + "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE"; + + /** @hide */ + @IntDef({ + EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE, + EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA + }) + @Retention(RetentionPolicy.SOURCE) + public @interface SimCombinationWarningType{} + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate there's no SIM combination warning. + * @hide + */ + public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; + + /** + * Used as an int value for {@link #EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE} + * to indicate two active SIMs are both CDMA hence there might be functional limitation. + * @hide + */ + public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; + + /** + * String intent extra to be used with {@link #ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED} + * to indicate what's the name of SIM combination it has limitation or compatible issue. + * e.g. two CDMA SIMs may disrupt each other's voice call in certain scenarios, and the + * name will be "operator1 & operator2". + * + * @hide + */ + public static final String EXTRA_SIM_COMBINATION_NAMES = + "android.telephony.extra.SIM_COMBINATION_NAMES"; // // // Device Info @@ -2209,7 +2254,7 @@ public class TelephonyManager { @UnsupportedAppUsage public String getNetworkOperatorForPhone(int phoneId) { return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_NUMERIC, ""); - } + } /** @@ -2409,24 +2454,15 @@ public class TelephonyManager { public @interface NetworkType{} /** + * Return the current data network type. + * + * @deprecated use {@link #getDataNetworkType()} * @return the NETWORK_TYPE_xxxx for current data connection. */ + @Deprecated + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public @NetworkType int getNetworkType() { - try { - ITelephony telephony = getITelephony(); - if (telephony != null) { - return telephony.getNetworkType(); - } else { - // This can happen when the ITelephony interface is not up yet. - return NETWORK_TYPE_UNKNOWN; - } - } catch(RemoteException ex) { - // This shouldn't happen in the normal case - return NETWORK_TYPE_UNKNOWN; - } catch (NullPointerException ex) { - // This could happen before phone restarts due to crashing - return NETWORK_TYPE_UNKNOWN; - } + return getNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); } /** @@ -2457,7 +2493,7 @@ public class TelephonyManager { * @hide */ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public int getNetworkType(int subId) { try { ITelephony telephony = getITelephony(); @@ -2509,7 +2545,7 @@ public class TelephonyManager { @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public @NetworkType int getDataNetworkType() { - return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); + return getDataNetworkType(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); } /** @@ -4741,7 +4777,8 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony == null) return DATA_ACTIVITY_NONE; - return telephony.getDataActivity(); + return telephony.getDataActivity( + getSubId(SubscriptionManager.getActiveDataSubscriptionId())); } catch (RemoteException ex) { // the phone process is restarting. return DATA_ACTIVITY_NONE; @@ -4789,7 +4826,8 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony == null) return DATA_DISCONNECTED; - return telephony.getDataState(); + return telephony.getDataState( + getSubId(SubscriptionManager.getActiveDataSubscriptionId())); } catch (RemoteException ex) { // the phone process is restarting. return DATA_DISCONNECTED; @@ -5231,18 +5269,30 @@ public class TelephonyManager { * Returns the MMS user agent. */ public String getMmsUserAgent() { - if (mContext == null) return null; - return mContext.getResources().getString( - com.android.internal.R.string.config_mms_user_agent); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getMmsUserAgent(getSubId()); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; } /** * Returns the MMS user agent profile URL. */ public String getMmsUAProfUrl() { - if (mContext == null) return null; - return mContext.getResources().getString( - com.android.internal.R.string.config_mms_user_agent_profile_url); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getMmsUAProfUrl(getSubId()); + } + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return null; } /** @@ -7241,7 +7291,7 @@ public class TelephonyManager { * @hide */ public boolean getTetherApnRequired() { - return getTetherApnRequired(getSubId(SubscriptionManager.getDefaultDataSubscriptionId())); + return getTetherApnRequired(getSubId(SubscriptionManager.getActiveDataSubscriptionId())); } /** @@ -8007,7 +8057,7 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) return telephony.isDataConnectivityPossible(getSubId(SubscriptionManager - .getDefaultDataSubscriptionId())); + .getActiveDataSubscriptionId())); } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#isDataAllowed", e); } @@ -8245,9 +8295,8 @@ public class TelephonyManager { ITelephony telephony = getITelephony(); if (telephony != null) retVal = telephony.isUserDataEnabled(subId); - } catch (RemoteException e) { + } catch (RemoteException | NullPointerException e) { Log.e(TAG, "Error calling ITelephony#isUserDataEnabled", e); - } catch (NullPointerException e) { } return retVal; } @@ -9121,6 +9170,10 @@ public class TelephonyManager { } } catch (RemoteException e) { Log.e(TAG, "Error calling ITelephony#getServiceStateForSubscriber", e); + } catch (NullPointerException e) { + AnomalyReporter.reportAnomaly( + UUID.fromString("a3ab0b9d-f2aa-4baf-911d-7096c0d4645a"), + "getServiceStateForSubscriber " + subId + " NPE"); } return null; } @@ -10968,4 +11021,52 @@ public class TelephonyManager { } return true; } + + /** + * Set allowing mobile data during voice call. + * + * @param allow {@code true} if allowing using data during voice call, {@code false} if + * disallowed + * + * @return {@code false} if the setting is changed. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + public boolean setDataAllowedDuringVoiceCall(boolean allow) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.setDataAllowedDuringVoiceCall(getSubId(), allow); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + return false; + } + + /** + * Check whether data is allowed during voice call. Note this is for dual sim device that + * data might be disabled on non-default data subscription but explicitly turned on by settings. + * + * @return {@code true} if data is allowed during voice call. + * + * @hide + */ + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + public boolean isDataAllowedInVoiceCall() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.isDataAllowedInVoiceCall(getSubId()); + } + } catch (RemoteException ex) { + if (!isSystemProcess()) { + ex.rethrowAsRuntimeException(); + } + } + return false; + } } diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java index f0c819da2dde..041e9093ff88 100644 --- a/telephony/java/android/telephony/data/ApnSetting.java +++ b/telephony/java/android/telephony/data/ApnSetting.java @@ -1310,6 +1310,9 @@ public class ApnSetting implements Parcelable { * @hide */ public static String getApnTypeString(int apnType) { + if (apnType == TYPE_ALL) { + return "*"; + } String apnTypeString = APN_TYPE_INT_MAP.get(apnType); return apnTypeString == null ? "Unknown" : apnTypeString; } diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java index b144ff761874..aaa98eb7bd59 100644 --- a/telephony/java/android/telephony/emergency/EmergencyNumber.java +++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java @@ -299,7 +299,7 @@ public final class EmergencyNumber implements Parcelable, Comparable<EmergencyNu * Get the dialing number of the emergency number. * * The character in the number string is only the dial pad - * character('0'-'9', '*', or '#'). For example: 911. + * character('0'-'9', '*', '+', or '#'). For example: 911. * * @return the dialing number. */ diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java index fba390c26242..02f0cef04a21 100644 --- a/telephony/java/android/telephony/ims/ImsSsInfo.java +++ b/telephony/java/android/telephony/ims/ImsSsInfo.java @@ -250,6 +250,8 @@ public final class ImsSsInfo implements Parcelable { out.writeInt(mStatus); out.writeString(mIcbNum); out.writeInt(mProvisionStatus); + out.writeInt(mClirInterrogationStatus); + out.writeInt(mClirOutgoingState); } @Override @@ -273,6 +275,8 @@ public final class ImsSsInfo implements Parcelable { mStatus = in.readInt(); mIcbNum = in.readString(); mProvisionStatus = in.readInt(); + mClirInterrogationStatus = in.readInt(); + mClirOutgoingState = in.readInt(); } public static final Creator<ImsSsInfo> CREATOR = diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java index a9f10b1b460f..9f22d0a49806 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java @@ -549,4 +549,22 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { */ public void onAppCallbackDied(int uid, int subscriptionId) { } + + // Following two methods exist to workaround b/124210145 + /** @hide */ + @SystemApi + @TestApi + @Override + public android.os.IBinder asBinder() { + return super.asBinder(); + } + + /** @hide */ + @SystemApi + @TestApi + @Override + public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, + int flags) throws RemoteException { + return super.onTransact(code, data, reply, flags); + } } diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java index 5ce612db49d6..cced44759527 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java @@ -294,4 +294,23 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { */ public void onAppCallbackDied(int uid, int subscriptionId) { } + + + // Following two methods exist to workaround b/124210145 + /** @hide */ + @SystemApi + @TestApi + @Override + public android.os.IBinder asBinder() { + return super.asBinder(); + } + + /** @hide */ + @SystemApi + @TestApi + @Override + public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, + int flags) throws RemoteException { + return super.onTransact(code, data, reply, flags); + } } diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index bb5c251b69e1..cde6db4888fa 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -94,7 +94,7 @@ public class DctConstants { public static final int EVENT_ROAMING_SETTING_CHANGE = BASE + 48; public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49; public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50; - public static final int EVENT_APN_WHITE_LIST_CHANGE = BASE + 51; + public static final int EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED = BASE + 51; /***** Constants *****/ diff --git a/packages/NetworkPermissionConfig/src/com/android/server/NetworkPermissionConfig.java b/telephony/java/com/android/internal/telephony/IIntegerConsumer.aidl index c904e2363689..252460e56330 100644 --- a/packages/NetworkPermissionConfig/src/com/android/server/NetworkPermissionConfig.java +++ b/telephony/java/com/android/internal/telephony/IIntegerConsumer.aidl @@ -14,13 +14,10 @@ * limitations under the License. */ -package com.android.server; +package com.android.internal.telephony; -import android.app.Application; - -/** - * Empty application for NetworkPermissionConfig that only exists because - * soong builds complain if APKs have no source file. - */ -public class NetworkPermissionConfig extends Application { -} +// Copies consumer pattern for an operation that requires an integer result from another process to +// finish. +oneway interface IIntegerConsumer { + void accept(int result); +}
\ No newline at end of file diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index f248893ec638..a481c29ed90b 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -285,4 +285,6 @@ interface ISub { boolean isActiveSubId(int subId, String callingPackage); boolean setAlwaysAllowMmsData(int subId, boolean alwaysAllow); + + int getActiveDataSubscriptionId(); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 6b927bb084cf..4de19173965b 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -18,6 +18,7 @@ package com.android.internal.telephony; import android.app.PendingIntent; import android.content.Intent; +import android.content.IntentSender; import android.os.Bundle; import android.os.IBinder; import android.os.Messenger; @@ -52,6 +53,7 @@ import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsRegistrationCallback; import com.android.ims.internal.IImsServiceFeatureCallback; import com.android.internal.telephony.CellNetworkScanResult; +import com.android.internal.telephony.IIntegerConsumer; import com.android.internal.telephony.INumberVerificationCallback; import com.android.internal.telephony.OperatorInfo; @@ -306,18 +308,36 @@ interface ITelephony { */ List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg); - @UnsupportedAppUsage - int getCallState(); + @UnsupportedAppUsage + int getCallState(); /** * Returns the call state for a slot. */ int getCallStateForSlot(int slotIndex); - @UnsupportedAppUsage - int getDataActivity(); - @UnsupportedAppUsage - int getDataState(); + /** + * Returns a constant indicating the type of activity on a data connection + * (cellular). + * + * @see #DATA_ACTIVITY_NONE + * @see #DATA_ACTIVITY_IN + * @see #DATA_ACTIVITY_OUT + * @see #DATA_ACTIVITY_INOUT + * @see #DATA_ACTIVITY_DORMANT + */ + int getDataActivity(int subId); + + /** + * Returns a constant indicating the current data connection state + * (cellular). + * + * @see #DATA_DISCONNECTED + * @see #DATA_CONNECTING + * @see #DATA_CONNECTED + * @see #DATA_SUSPENDED + */ + int getDataState(int subId); /** * Returns the current active phone type as integer. @@ -458,13 +478,6 @@ interface ITelephony { void sendDialerSpecialCode(String callingPackageName, String inputCode); /** - * Returns the network type for data transmission - * Legacy call, permission-free - */ - @UnsupportedAppUsage - int getNetworkType(); - - /** * Returns the network type of a subId. * @param subId user preferred subId. * @param callingPackage package making the call. @@ -1619,7 +1632,7 @@ interface ITelephony { * <p> * See {@link UiccCardInfo} for more details on the kind of information available. * - * @param callingPackage package making the call, used to evaluate carrier privileges + * @param callingPackage package making the call, used to evaluate carrier privileges * @return a list of UiccCardInfo objects, representing information on the currently inserted * UICCs and eUICCs. Each UiccCardInfo in the list will have private information filtered out if * the caller does not have adequate permissions for that card. @@ -1980,4 +1993,31 @@ interface ITelephony { boolean isDataEnabledForApn(int apnType, int subId, String callingPackage); boolean isApnMetered(int apnType, int subId); + + /** + * Enqueue a pending sms Consumer, which will answer with the user specified selection for an + * outgoing SmsManager operation. + */ + oneway void enqueueSmsPickResult(String callingPackage, IIntegerConsumer subIdResult); + + /** + * Returns the MMS user agent. + */ + String getMmsUserAgent(int subId); + + /** + * Returns the MMS user agent profile URL. + */ + String getMmsUAProfUrl(int subId); + + /** + * Set allowing mobile data during voice call. + */ + boolean setDataAllowedDuringVoiceCall(int subId, boolean allow); + + /** + * Check whether data is allowed during voice call. Note this is for dual sim device that + * data might be disabled on non-default data subscription but explicitly turned on by settings. + */ + boolean isDataAllowedInVoiceCall(int subId); } diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java index 7574a6e028f4..73d49dd16eaf 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java +++ b/telephony/java/com/android/internal/telephony/TelephonyPermissions.java @@ -75,6 +75,16 @@ public final class TelephonyPermissions { callingPackage, message); } + /** Identical to checkCallingOrSelfReadPhoneState but never throws SecurityException */ + public static boolean checkCallingOrSelfReadPhoneStateNoThrow( + Context context, int subId, String callingPackage, String message) { + try { + return checkCallingOrSelfReadPhoneState(context, subId, callingPackage, message); + } catch (SecurityException se) { + return false; + } + } + /** * Check whether the app with the given pid/uid can read phone state. * diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 1992e090c2da..4bd0213f3672 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -728,12 +728,12 @@ public class SmsMessage extends SmsMessageBase { // being reported refers to. The MsgStatus subparameter // is primarily useful to indicate error conditions -- a // message without this subparameter is assumed to - // indicate successful delivery (status == 0). - if (! mBearerData.messageStatusSet) { + // indicate successful delivery. + if (!mBearerData.messageStatusSet) { Rlog.d(LOG_TAG, "DELIVERY_ACK message without msgStatus (" + (mUserData == null ? "also missing" : "does have") + " userData)."); - status = 0; + status = (BearerData.ERROR_NONE << 8) | BearerData.STATUS_DELIVERED; } else { status = mBearerData.errorClass << 8; status |= mBearerData.messageStatus; diff --git a/test-base/Android.bp b/test-base/Android.bp index 8aa0aaf363dd..69c296e7ee9c 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -25,7 +25,7 @@ java_sdk_library { srcs: ["src/**/*.java"], errorprone: { - javacflags: ["-Xep:DepAnn:ERROR"], + javacflags: ["-Xep:DepAnn:ERROR"], }, hostdex: true, @@ -96,3 +96,14 @@ java_library_static { ], } +// Make the current.txt available for use by the cts/tests/signature tests. +// ======================================================================== +filegroup { + name: "android-test-base-current.txt", + visibility: [ + "//cts/tests/signature/api", + ], + srcs: [ + "api/current.txt", + ], +} diff --git a/test-mock/Android.bp b/test-mock/Android.bp index e1d6e01d6d06..a5cd1751151f 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -30,3 +30,15 @@ java_sdk_library { srcs_lib_whitelist_pkgs: ["android"], compile_dex: true, } + +// Make the current.txt available for use by the cts/tests/signature tests. +// ======================================================================== +filegroup { + name: "android-test-mock-current.txt", + visibility: [ + "//cts/tests/signature/api", + ], + srcs: [ + "api/current.txt", + ], +} diff --git a/test-runner/Android.bp b/test-runner/Android.bp index 35212020be7b..75f5b5a96eb1 100644 --- a/test-runner/Android.bp +++ b/test-runner/Android.bp @@ -40,7 +40,7 @@ java_sdk_library { "junit.textui", ], - compile_dex: true + compile_dex: true, } // Build the android.test.runner-minus-junit library @@ -86,3 +86,14 @@ java_library_static { java_version: "1.8", } +// Make the current.txt available for use by the cts/tests/signature tests. +// ======================================================================== +filegroup { + name: "android-test-runner-current.txt", + visibility: [ + "//cts/tests/signature/api", + ], + srcs: [ + "api/current.txt", + ], +} diff --git a/tests/CanvasCompare/res/drawable/sunset1.jpg b/tests/CanvasCompare/res/drawable/sunset1.jpg Binary files differindex 92851f3df924..3b4e056b70d0 100644 --- a/tests/CanvasCompare/res/drawable/sunset1.jpg +++ b/tests/CanvasCompare/res/drawable/sunset1.jpg diff --git a/tests/FrameworkPerf/res/drawable-161dpi/wallpaper_goldengate_scale.jpg b/tests/FrameworkPerf/res/drawable-161dpi/wallpaper_goldengate_scale.jpg Binary files differindex 2271091909bc..8b7c6db267b9 100644 --- a/tests/FrameworkPerf/res/drawable-161dpi/wallpaper_goldengate_scale.jpg +++ b/tests/FrameworkPerf/res/drawable-161dpi/wallpaper_goldengate_scale.jpg diff --git a/tests/FrameworkPerf/res/drawable-nodpi/wallpaper_goldengate.jpg b/tests/FrameworkPerf/res/drawable-nodpi/wallpaper_goldengate.jpg Binary files differindex 2271091909bc..8b7c6db267b9 100644 --- a/tests/FrameworkPerf/res/drawable-nodpi/wallpaper_goldengate.jpg +++ b/tests/FrameworkPerf/res/drawable-nodpi/wallpaper_goldengate.jpg diff --git a/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg Binary files differindex 92851f3df924..086c05542836 100644 --- a/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg +++ b/tests/HwAccelerationTest/res/drawable-hdpi/sunset1.jpg diff --git a/tests/HwAccelerationTest/res/drawable/sunset1.jpg b/tests/HwAccelerationTest/res/drawable/sunset1.jpg Binary files differindex 92851f3df924..3b4e056b70d0 100644 --- a/tests/HwAccelerationTest/res/drawable/sunset1.jpg +++ b/tests/HwAccelerationTest/res/drawable/sunset1.jpg diff --git a/tests/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg b/tests/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg Binary files differindex 755232deea0e..14d6027bf006 100644 --- a/tests/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg +++ b/tests/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg diff --git a/tests/net/Android.bp b/tests/net/Android.bp index 306cc515c870..135bf90a48f1 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -6,10 +6,10 @@ java_defaults { static_libs: [ "FrameworksNetCommonTests", "frameworks-base-testutils", - "frameworks-net-testutils", "framework-protos", "androidx.test.rules", "mockito-target-minus-junit4", + "net-tests-utils", "platform-test-annotations", "services.core", "services.net", diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp index db1ccb446ce3..e44d46088c43 100644 --- a/tests/net/common/Android.bp +++ b/tests/net/common/Android.bp @@ -21,12 +21,12 @@ java_library { srcs: ["java/**/*.java", "java/**/*.kt"], static_libs: [ "androidx.test.rules", - "frameworks-net-testutils", "junit", "mockito-target-minus-junit4", + "net-tests-utils", "platform-test-annotations", ], libs: [ "android.test.base.stubs", ], -}
\ No newline at end of file +} diff --git a/tests/net/common/java/android/net/IpPrefixTest.java b/tests/net/common/java/android/net/IpPrefixTest.java index 719960d48604..985e10df3961 100644 --- a/tests/net/common/java/android/net/IpPrefixTest.java +++ b/tests/net/common/java/android/net/IpPrefixTest.java @@ -16,16 +16,18 @@ package android.net; +import static com.android.testutils.MiscAssertsKt.assertEqualBothWays; +import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; +import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay; +import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.os.Parcel; - import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -171,56 +173,46 @@ public class IpPrefixTest { } - private void assertAreEqual(Object o1, Object o2) { - assertTrue(o1.equals(o2)); - assertTrue(o2.equals(o1)); - } - - private void assertAreNotEqual(Object o1, Object o2) { - assertFalse(o1.equals(o2)); - assertFalse(o2.equals(o1)); - } - @Test public void testEquals() { IpPrefix p1, p2; p1 = new IpPrefix("192.0.2.251/23"); p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23); - assertAreEqual(p1, p2); + assertEqualBothWays(p1, p2); p1 = new IpPrefix("192.0.2.5/23"); - assertAreEqual(p1, p2); + assertEqualBothWays(p1, p2); p1 = new IpPrefix("192.0.2.5/24"); - assertAreNotEqual(p1, p2); + assertNotEqualEitherWay(p1, p2); p1 = new IpPrefix("192.0.4.5/23"); - assertAreNotEqual(p1, p2); + assertNotEqualEitherWay(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122"); p2 = new IpPrefix(IPV6_BYTES, 122); assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString()); - assertAreEqual(p1, p2); + assertEqualBothWays(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122"); - assertAreEqual(p1, p2); + assertEqualBothWays(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123"); - assertAreNotEqual(p1, p2); + assertNotEqualEitherWay(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef::/122"); - assertAreNotEqual(p1, p2); + assertNotEqualEitherWay(p1, p2); // 192.0.2.4/32 != c000:0204::/32. byte[] ipv6bytes = new byte[16]; System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length); p1 = new IpPrefix(ipv6bytes, 32); - assertAreEqual(p1, new IpPrefix("c000:0204::/32")); + assertEqualBothWays(p1, new IpPrefix("c000:0204::/32")); p2 = new IpPrefix(IPV4_BYTES, 32); - assertAreNotEqual(p1, p2); + assertNotEqualEitherWay(p1, p2); } @Test @@ -356,25 +348,6 @@ public class IpPrefixTest { assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress()); } - public IpPrefix passThroughParcel(IpPrefix p) { - Parcel parcel = Parcel.obtain(); - IpPrefix p2 = null; - try { - p.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - p2 = IpPrefix.CREATOR.createFromParcel(parcel); - } finally { - parcel.recycle(); - } - assertNotNull(p2); - return p2; - } - - public void assertParcelingIsLossless(IpPrefix p) { - IpPrefix p2 = passThroughParcel(p); - assertEquals(p, p2); - } - @Test public void testParceling() { IpPrefix p; @@ -386,5 +359,7 @@ public class IpPrefixTest { p = new IpPrefix("192.0.2.0/25"); assertParcelingIsLossless(p); assertTrue(p.isIPv4()); + + assertFieldCountEquals(2, IpPrefix.class); } } diff --git a/tests/net/common/java/android/net/LinkAddressTest.java b/tests/net/common/java/android/net/LinkAddressTest.java index d462441b22fa..b2e573b6c74b 100644 --- a/tests/net/common/java/android/net/LinkAddressTest.java +++ b/tests/net/common/java/android/net/LinkAddressTest.java @@ -27,15 +27,17 @@ import static android.system.OsConstants.RT_SCOPE_LINK; import static android.system.OsConstants.RT_SCOPE_SITE; import static android.system.OsConstants.RT_SCOPE_UNIVERSE; +import static com.android.testutils.MiscAssertsKt.assertEqualBothWays; +import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay; +import static com.android.testutils.ParcelUtilsKt.assertParcelSane; +import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.os.Parcel; - import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -217,67 +219,56 @@ public class LinkAddressTest { l1.isSameAddressAs(l2)); } - private void assertLinkAddressesEqual(LinkAddress l1, LinkAddress l2) { - assertTrue(l1 + " unexpectedly not equal to " + l2, l1.equals(l2)); - assertTrue(l2 + " unexpectedly not equal to " + l1, l2.equals(l1)); - assertEquals(l1.hashCode(), l2.hashCode()); - } - - private void assertLinkAddressesNotEqual(LinkAddress l1, LinkAddress l2) { - assertFalse(l1 + " unexpectedly equal to " + l2, l1.equals(l2)); - assertFalse(l2 + " unexpectedly equal to " + l1, l2.equals(l1)); - } - @Test public void testEqualsAndSameAddressAs() { LinkAddress l1, l2, l3; l1 = new LinkAddress("2001:db8::1/64"); l2 = new LinkAddress("2001:db8::1/64"); - assertLinkAddressesEqual(l1, l2); + assertEqualBothWays(l1, l2); assertIsSameAddressAs(l1, l2); l2 = new LinkAddress("2001:db8::1/65"); - assertLinkAddressesNotEqual(l1, l2); + assertNotEqualEitherWay(l1, l2); assertIsNotSameAddressAs(l1, l2); l2 = new LinkAddress("2001:db8::2/64"); - assertLinkAddressesNotEqual(l1, l2); + assertNotEqualEitherWay(l1, l2); assertIsNotSameAddressAs(l1, l2); l1 = new LinkAddress("192.0.2.1/24"); l2 = new LinkAddress("192.0.2.1/24"); - assertLinkAddressesEqual(l1, l2); + assertEqualBothWays(l1, l2); assertIsSameAddressAs(l1, l2); l2 = new LinkAddress("192.0.2.1/23"); - assertLinkAddressesNotEqual(l1, l2); + assertNotEqualEitherWay(l1, l2); assertIsNotSameAddressAs(l1, l2); l2 = new LinkAddress("192.0.2.2/24"); - assertLinkAddressesNotEqual(l1, l2); + assertNotEqualEitherWay(l1, l2); assertIsNotSameAddressAs(l1, l2); // Check equals() and isSameAddressAs() on identical addresses with different flags. l1 = new LinkAddress(V6_ADDRESS, 64); l2 = new LinkAddress(V6_ADDRESS, 64, 0, RT_SCOPE_UNIVERSE); - assertLinkAddressesEqual(l1, l2); + assertEqualBothWays(l1, l2); assertIsSameAddressAs(l1, l2); l2 = new LinkAddress(V6_ADDRESS, 64, IFA_F_DEPRECATED, RT_SCOPE_UNIVERSE); - assertLinkAddressesNotEqual(l1, l2); + assertNotEqualEitherWay(l1, l2); assertIsSameAddressAs(l1, l2); // Check equals() and isSameAddressAs() on identical addresses with different scope. l1 = new LinkAddress(V4_ADDRESS, 24); l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_UNIVERSE); - assertLinkAddressesEqual(l1, l2); + assertEqualBothWays(l1, l2); assertIsSameAddressAs(l1, l2); l2 = new LinkAddress(V4_ADDRESS, 24, 0, RT_SCOPE_HOST); - assertLinkAddressesNotEqual(l1, l2); + assertNotEqualEitherWay(l1, l2); assertIsSameAddressAs(l1, l2); // Addresses with the same start or end bytes aren't equal between families. @@ -291,10 +282,10 @@ public class LinkAddressTest { assertTrue(Arrays.equals(ipv4Bytes, l2FirstIPv6Bytes)); assertTrue(Arrays.equals(ipv4Bytes, l3LastIPv6Bytes)); - assertLinkAddressesNotEqual(l1, l2); + assertNotEqualEitherWay(l1, l2); assertIsNotSameAddressAs(l1, l2); - assertLinkAddressesNotEqual(l1, l3); + assertNotEqualEitherWay(l1, l3); assertIsNotSameAddressAs(l1, l3); // Because we use InetAddress, an IPv4 address is equal to its IPv4-mapped address. @@ -302,7 +293,7 @@ public class LinkAddressTest { String addressString = V4 + "/24"; l1 = new LinkAddress(addressString); l2 = new LinkAddress("::ffff:" + addressString); - assertLinkAddressesEqual(l1, l2); + assertEqualBothWays(l1, l2); assertIsSameAddressAs(l1, l2); } @@ -319,25 +310,6 @@ public class LinkAddressTest { assertNotEquals(l1.hashCode(), l2.hashCode()); } - private LinkAddress passThroughParcel(LinkAddress l) { - Parcel p = Parcel.obtain(); - LinkAddress l2 = null; - try { - l.writeToParcel(p, 0); - p.setDataPosition(0); - l2 = LinkAddress.CREATOR.createFromParcel(p); - } finally { - p.recycle(); - } - assertNotNull(l2); - return l2; - } - - private void assertParcelingIsLossless(LinkAddress l) { - LinkAddress l2 = passThroughParcel(l); - assertEquals(l, l2); - } - @Test public void testParceling() { LinkAddress l; @@ -346,7 +318,7 @@ public class LinkAddressTest { assertParcelingIsLossless(l); l = new LinkAddress(V4 + "/28", IFA_F_PERMANENT, RT_SCOPE_LINK); - assertParcelingIsLossless(l); + assertParcelSane(l, 4); } private void assertGlobalPreferred(LinkAddress l, String msg) { diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index e1c4238f1279..b0464d9e656f 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -16,6 +16,8 @@ package android.net; +import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -31,8 +33,6 @@ import android.util.ArraySet; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.util.TestUtils; - import org.junit.Test; import org.junit.runner.RunWith; @@ -942,13 +942,13 @@ public class LinkPropertiesTest { source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96")); - TestUtils.assertParcelingIsLossless(source); + assertParcelingIsLossless(source); } @Test public void testParcelUninitialized() throws Exception { LinkProperties empty = new LinkProperties(); - TestUtils.assertParcelingIsLossless(empty); + assertParcelingIsLossless(empty); } @Test diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java index 6bc7c1bb30f8..2ca0d1a81e13 100644 --- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java +++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java @@ -38,6 +38,9 @@ import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES; +import static com.android.testutils.ParcelUtilsKt.assertParcelSane; +import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -45,7 +48,6 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; import android.util.ArraySet; @@ -267,9 +269,9 @@ public class NetworkCapabilitiesTest { .setUids(uids) .addCapability(NET_CAPABILITY_EIMS) .addCapability(NET_CAPABILITY_NOT_METERED); - assertEqualsThroughMarshalling(netCap); + assertParcelingIsLossless(netCap); netCap.setSSID(TEST_SSID); - assertEqualsThroughMarshalling(netCap); + assertParcelSane(netCap, 11); } @Test @@ -542,18 +544,6 @@ public class NetworkCapabilitiesTest { nc1.combineCapabilities(nc3); } - private void assertEqualsThroughMarshalling(NetworkCapabilities netCap) { - Parcel p = Parcel.obtain(); - netCap.writeToParcel(p, /* flags */ 0); - p.setDataPosition(0); - byte[] marshalledData = p.marshall(); - - p = Parcel.obtain(); - p.unmarshall(marshalledData, 0, marshalledData.length); - p.setDataPosition(0); - assertEquals(NetworkCapabilities.CREATOR.createFromParcel(p), netCap); - } - @Test public void testSet() { NetworkCapabilities nc1 = new NetworkCapabilities(); diff --git a/tests/net/common/java/android/net/NetworkTest.java b/tests/net/common/java/android/net/NetworkTest.java index 38bc744a0a06..11d44b86bc50 100644 --- a/tests/net/common/java/android/net/NetworkTest.java +++ b/tests/net/common/java/android/net/NetworkTest.java @@ -18,13 +18,10 @@ package android.net; 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.junit.Assert.fail; -import android.net.LocalServerSocket; -import android.net.LocalSocket; -import android.net.LocalSocketAddress; -import android.net.Network; import android.platform.test.annotations.AppModeFull; import androidx.test.filters.SmallTest; @@ -40,7 +37,6 @@ import java.net.DatagramSocket; import java.net.Inet6Address; import java.net.InetAddress; import java.net.SocketException; -import java.util.Objects; @RunWith(AndroidJUnit4.class) @SmallTest @@ -123,29 +119,29 @@ public class NetworkTest { Network three = new Network(3); // None of the hashcodes are zero. - assertNotEqual(0, one.hashCode()); - assertNotEqual(0, two.hashCode()); - assertNotEqual(0, three.hashCode()); + assertNotEquals(0, one.hashCode()); + assertNotEquals(0, two.hashCode()); + assertNotEquals(0, three.hashCode()); // All the hashcodes are distinct. - assertNotEqual(one.hashCode(), two.hashCode()); - assertNotEqual(one.hashCode(), three.hashCode()); - assertNotEqual(two.hashCode(), three.hashCode()); + assertNotEquals(one.hashCode(), two.hashCode()); + assertNotEquals(one.hashCode(), three.hashCode()); + assertNotEquals(two.hashCode(), three.hashCode()); // None of the handles are zero. - assertNotEqual(0, one.getNetworkHandle()); - assertNotEqual(0, two.getNetworkHandle()); - assertNotEqual(0, three.getNetworkHandle()); + assertNotEquals(0, one.getNetworkHandle()); + assertNotEquals(0, two.getNetworkHandle()); + assertNotEquals(0, three.getNetworkHandle()); // All the handles are distinct. - assertNotEqual(one.getNetworkHandle(), two.getNetworkHandle()); - assertNotEqual(one.getNetworkHandle(), three.getNetworkHandle()); - assertNotEqual(two.getNetworkHandle(), three.getNetworkHandle()); + assertNotEquals(one.getNetworkHandle(), two.getNetworkHandle()); + assertNotEquals(one.getNetworkHandle(), three.getNetworkHandle()); + assertNotEquals(two.getNetworkHandle(), three.getNetworkHandle()); // The handles are not equal to the hashcodes. - assertNotEqual(one.hashCode(), one.getNetworkHandle()); - assertNotEqual(two.hashCode(), two.getNetworkHandle()); - assertNotEqual(three.hashCode(), three.getNetworkHandle()); + assertNotEquals(one.hashCode(), one.getNetworkHandle()); + assertNotEquals(two.hashCode(), two.getNetworkHandle()); + assertNotEquals(three.hashCode(), three.getNetworkHandle()); // Adjust as necessary to test an implementation's specific constants. // When running with runtest, "adb logcat -s TestRunner" can be useful. @@ -154,15 +150,11 @@ public class NetworkTest { assertEquals(16290598925L, three.getNetworkHandle()); } - private static <T> void assertNotEqual(T t1, T t2) { - assertFalse(Objects.equals(t1, t2)); - } - @Test public void testGetPrivateDnsBypassingCopy() { final Network copy = mNetwork.getPrivateDnsBypassingCopy(); assertEquals(mNetwork.netId, copy.netId); - assertNotEqual(copy.netId, copy.getNetIdForResolv()); - assertNotEqual(mNetwork.getNetIdForResolv(), copy.getNetIdForResolv()); + assertNotEquals(copy.netId, copy.getNetIdForResolv()); + assertNotEquals(mNetwork.getNetIdForResolv(), copy.getNetIdForResolv()); } } diff --git a/tests/net/common/java/android/net/RouteInfoTest.java b/tests/net/common/java/android/net/RouteInfoTest.java index 2edbd403b5b5..5ce84363082f 100644 --- a/tests/net/common/java/android/net/RouteInfoTest.java +++ b/tests/net/common/java/android/net/RouteInfoTest.java @@ -18,7 +18,11 @@ package android.net; import static android.net.RouteInfo.RTN_UNREACHABLE; -import android.os.Parcel; +import static com.android.testutils.MiscAssertsKt.assertEqualBothWays; +import static com.android.testutils.MiscAssertsKt.assertNotEqualEitherWay; +import static com.android.testutils.ParcelUtilsKt.assertParcelSane; +import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; + import android.test.suitebuilder.annotation.SmallTest; import junit.framework.TestCase; @@ -109,47 +113,37 @@ public class RouteInfoTest extends TestCase { assertFalse(ipv4Default.matches(Address("2001:db8::f00"))); } - private void assertAreEqual(Object o1, Object o2) { - assertTrue(o1.equals(o2)); - assertTrue(o2.equals(o1)); - } - - private void assertAreNotEqual(Object o1, Object o2) { - assertFalse(o1.equals(o2)); - assertFalse(o2.equals(o1)); - } - public void testEquals() { // IPv4 RouteInfo r1 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0"); RouteInfo r2 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "wlan0"); - assertAreEqual(r1, r2); + assertEqualBothWays(r1, r2); RouteInfo r3 = new RouteInfo(Prefix("2001:db8:ace::/49"), Address("2001:db8::1"), "wlan0"); RouteInfo r4 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::2"), "wlan0"); RouteInfo r5 = new RouteInfo(Prefix("2001:db8:ace::/48"), Address("2001:db8::1"), "rmnet0"); - assertAreNotEqual(r1, r3); - assertAreNotEqual(r1, r4); - assertAreNotEqual(r1, r5); + assertNotEqualEitherWay(r1, r3); + assertNotEqualEitherWay(r1, r4); + assertNotEqualEitherWay(r1, r5); // IPv6 r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0"); r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "wlan0"); - assertAreEqual(r1, r2); + assertEqualBothWays(r1, r2); r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0"); r4 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.2"), "wlan0"); r5 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), "rmnet0"); - assertAreNotEqual(r1, r3); - assertAreNotEqual(r1, r4); - assertAreNotEqual(r1, r5); + assertNotEqualEitherWay(r1, r3); + assertNotEqualEitherWay(r1, r4); + assertNotEqualEitherWay(r1, r5); // Interfaces (but not destinations or gateways) can be null. r1 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null); r2 = new RouteInfo(Prefix("192.0.2.0/25"), Address("192.0.2.1"), null); r3 = new RouteInfo(Prefix("192.0.2.0/24"), Address("192.0.2.1"), "wlan0"); - assertAreEqual(r1, r2); - assertAreNotEqual(r1, r3); + assertEqualBothWays(r1, r2); + assertNotEqualEitherWay(r1, r3); } public void testHostAndDefaultRoutes() { @@ -257,25 +251,6 @@ public class RouteInfoTest extends TestCase { // No exceptions? Good. } - public RouteInfo passThroughParcel(RouteInfo r) { - Parcel p = Parcel.obtain(); - RouteInfo r2 = null; - try { - r.writeToParcel(p, 0); - p.setDataPosition(0); - r2 = RouteInfo.CREATOR.createFromParcel(p); - } finally { - p.recycle(); - } - assertNotNull(r2); - return r2; - } - - public void assertParcelingIsLossless(RouteInfo r) { - RouteInfo r2 = passThroughParcel(r); - assertEquals(r, r2); - } - public void testParceling() { RouteInfo r; @@ -283,6 +258,6 @@ public class RouteInfoTest extends TestCase { assertParcelingIsLossless(r); r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0"); - assertParcelingIsLossless(r); + assertParcelSane(r, 6); } } diff --git a/tests/net/common/java/android/net/StaticIpConfigurationTest.java b/tests/net/common/java/android/net/StaticIpConfigurationTest.java index 5096be221cbf..b5f23bf19a3c 100644 --- a/tests/net/common/java/android/net/StaticIpConfigurationTest.java +++ b/tests/net/common/java/android/net/StaticIpConfigurationTest.java @@ -18,6 +18,7 @@ package android.net; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -34,7 +35,6 @@ import java.net.InetAddress; import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Objects; @RunWith(AndroidJUnit4.class) @SmallTest @@ -61,10 +61,6 @@ public class StaticIpConfigurationTest { assertEquals(0, s.dnsServers.size()); } - private static <T> void assertNotEquals(T t1, T t2) { - assertFalse(Objects.equals(t1, t2)); - } - private StaticIpConfiguration makeTestObject() { StaticIpConfiguration s = new StaticIpConfiguration(); s.ipAddress = ADDR; diff --git a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java index 0ce7c91c04d0..f4f804aff08d 100644 --- a/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java +++ b/tests/net/common/java/android/net/apf/ApfCapabilitiesTest.java @@ -16,6 +16,8 @@ package android.net.apf; +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; @@ -24,9 +26,6 @@ import static org.junit.Assert.assertTrue; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.util.ParcelableTestUtil; -import com.android.internal.util.TestUtils; - import org.junit.Test; import org.junit.runner.RunWith; @@ -40,9 +39,7 @@ public class ApfCapabilitiesTest { assertEquals(456, caps.maximumApfProgramSize); assertEquals(789, caps.apfPacketFormat); - ParcelableTestUtil.assertFieldCountEquals(3, ApfCapabilities.class); - - TestUtils.assertParcelingIsLossless(caps); + assertParcelSane(caps, 3); } @Test diff --git a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt b/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt index 8d055c93c4c5..0b7b74097cc6 100644 --- a/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt +++ b/tests/net/common/java/android/net/metrics/ApfProgramEventTest.kt @@ -16,11 +16,9 @@ package android.net.metrics; -import android.os.Parcelable import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.ParcelableTestUtil -import com.android.internal.util.TestUtils +import com.android.testutils.assertParcelSane import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue @@ -30,11 +28,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @SmallTest class ApfProgramEventTest { - private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) { - ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java) - TestUtils.assertParcelingIsLossless(obj) - } - private infix fun Int.hasFlag(flag: Int) = (this and (1 shl flag)) != 0 @Test @@ -55,7 +48,7 @@ class ApfProgramEventTest { assertEquals(5, apfProgramEvent.programLength) assertEquals(ApfProgramEvent.flagsFor(true, true), apfProgramEvent.flags) - testParcel(apfProgramEvent, 6) + assertParcelSane(apfProgramEvent, 6) } @Test diff --git a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt b/tests/net/common/java/android/net/metrics/ApfStatsTest.kt index f8eb40cccd35..46a8c8e5b509 100644 --- a/tests/net/common/java/android/net/metrics/ApfStatsTest.kt +++ b/tests/net/common/java/android/net/metrics/ApfStatsTest.kt @@ -16,11 +16,9 @@ package android.net.metrics -import android.os.Parcelable import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.ParcelableTestUtil -import com.android.internal.util.TestUtils +import com.android.testutils.assertParcelSane import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -28,11 +26,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @SmallTest class ApfStatsTest { - private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) { - ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java) - TestUtils.assertParcelingIsLossless(obj) - } - @Test fun testBuilderAndParcel() { val apfStats = ApfStats.Builder() @@ -59,6 +52,6 @@ class ApfStatsTest { assertEquals(8, apfStats.programUpdatesAllowingMulticast) assertEquals(9, apfStats.maxProgramSize) - testParcel(apfStats, 10) + assertParcelSane(apfStats, 10) } } diff --git a/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt b/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt index 36e9f8c94f6a..8d7a9c405024 100644 --- a/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt +++ b/tests/net/common/java/android/net/metrics/DhcpClientEventTest.kt @@ -16,11 +16,9 @@ package android.net.metrics -import android.os.Parcelable import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.ParcelableTestUtil -import com.android.internal.util.TestUtils +import com.android.testutils.assertParcelSane import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -30,11 +28,6 @@ private const val FAKE_MESSAGE = "test" @RunWith(AndroidJUnit4::class) @SmallTest class DhcpClientEventTest { - private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) { - ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java) - TestUtils.assertParcelingIsLossless(obj) - } - @Test fun testBuilderAndParcel() { val dhcpClientEvent = DhcpClientEvent.Builder() @@ -45,6 +38,6 @@ class DhcpClientEventTest { assertEquals(FAKE_MESSAGE, dhcpClientEvent.msg) assertEquals(Integer.MAX_VALUE, dhcpClientEvent.durationMs) - testParcel(dhcpClientEvent, 2) + assertParcelSane(dhcpClientEvent, 2) } } diff --git a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt index e9d5e6db1c7e..236f72eafbdc 100644 --- a/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt +++ b/tests/net/common/java/android/net/metrics/DhcpErrorEventTest.kt @@ -1,10 +1,10 @@ package android.net.metrics -import android.net.metrics.DhcpErrorEvent.errorCodeWithOption import android.net.metrics.DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH +import android.net.metrics.DhcpErrorEvent.errorCodeWithOption import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.TestUtils.parcelingRoundTrip +import com.android.testutils.parcelingRoundTrip import java.lang.reflect.Modifier import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull @@ -62,4 +62,4 @@ class DhcpErrorEventTest { fun testToString_InvalidErrorCode() { assertNotNull(DhcpErrorEvent(TEST_ERROR_CODE).toString()) } -}
\ No newline at end of file +} diff --git a/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt b/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt index 5144ca56bf28..64be50837fc9 100644 --- a/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt +++ b/tests/net/common/java/android/net/metrics/IpManagerEventTest.kt @@ -16,11 +16,9 @@ package android.net.metrics -import android.os.Parcelable import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.ParcelableTestUtil -import com.android.internal.util.TestUtils +import com.android.testutils.assertParcelSane import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -28,11 +26,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @SmallTest class IpManagerEventTest { - private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) { - ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java) - TestUtils.assertParcelingIsLossless(obj) - } - @Test fun testConstructorAndParcel() { (IpManagerEvent.PROVISIONING_OK..IpManagerEvent.ERROR_INTERFACE_NOT_FOUND).forEach { @@ -40,7 +33,7 @@ class IpManagerEventTest { assertEquals(it, ipManagerEvent.eventType) assertEquals(Long.MAX_VALUE, ipManagerEvent.durationMs) - testParcel(ipManagerEvent, 2) + assertParcelSane(ipManagerEvent, 2) } } } diff --git a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt b/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt index d76ebf67ff1d..55b5e492dd47 100644 --- a/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt +++ b/tests/net/common/java/android/net/metrics/IpReachabilityEventTest.kt @@ -16,11 +16,9 @@ package android.net.metrics -import android.os.Parcelable import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.ParcelableTestUtil -import com.android.internal.util.TestUtils +import com.android.testutils.assertParcelSane import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -28,18 +26,13 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @SmallTest class IpReachabilityEventTest { - private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) { - ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java) - TestUtils.assertParcelingIsLossless(obj) - } - @Test fun testConstructorAndParcel() { (IpReachabilityEvent.PROBE..IpReachabilityEvent.PROVISIONING_LOST_ORGANIC).forEach { val ipReachabilityEvent = IpReachabilityEvent(it) assertEquals(it, ipReachabilityEvent.eventType) - testParcel(ipReachabilityEvent, 1) + assertParcelSane(ipReachabilityEvent, 1) } } } diff --git a/tests/net/common/java/android/net/metrics/NetworkEventTest.kt b/tests/net/common/java/android/net/metrics/NetworkEventTest.kt index 8b52e81eea1e..41430b03a1eb 100644 --- a/tests/net/common/java/android/net/metrics/NetworkEventTest.kt +++ b/tests/net/common/java/android/net/metrics/NetworkEventTest.kt @@ -16,11 +16,9 @@ package android.net.metrics -import android.os.Parcelable import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.ParcelableTestUtil -import com.android.internal.util.TestUtils +import com.android.testutils.assertParcelSane import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -28,11 +26,6 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) @SmallTest class NetworkEventTest { - private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) { - ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java) - TestUtils.assertParcelingIsLossless(obj) - } - @Test fun testConstructorAndParcel() { (NetworkEvent.NETWORK_CONNECTED..NetworkEvent.NETWORK_PARTIAL_CONNECTIVITY).forEach { @@ -44,7 +37,7 @@ class NetworkEventTest { assertEquals(it, networkEvent.eventType) assertEquals(Long.MAX_VALUE, networkEvent.durationMs) - testParcel(networkEvent, 2) + assertParcelSane(networkEvent, 2) } } } diff --git a/tests/net/common/java/android/net/metrics/RaEventTest.kt b/tests/net/common/java/android/net/metrics/RaEventTest.kt index f38d32844230..d9b720332fbe 100644 --- a/tests/net/common/java/android/net/metrics/RaEventTest.kt +++ b/tests/net/common/java/android/net/metrics/RaEventTest.kt @@ -16,11 +16,9 @@ package android.net.metrics -import android.os.Parcelable import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.ParcelableTestUtil -import com.android.internal.util.TestUtils +import com.android.testutils.assertParcelSane import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -30,11 +28,6 @@ private const val NO_LIFETIME: Long = -1L @RunWith(AndroidJUnit4::class) @SmallTest class RaEventTest { - private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) { - ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java) - TestUtils.assertParcelingIsLossless(obj) - } - @Test fun testConstructorAndParcel() { var raEvent = RaEvent.Builder().build() @@ -74,6 +67,6 @@ class RaEventTest { assertEquals(5, raEvent.rdnssLifetime) assertEquals(6, raEvent.dnsslLifetime) - testParcel(raEvent, 6) + assertParcelSane(raEvent, 6) } } diff --git a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt b/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt index c0cef8fe91fd..51c0d41bf4d5 100644 --- a/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt +++ b/tests/net/common/java/android/net/metrics/ValidationProbeEventTest.kt @@ -16,11 +16,9 @@ package android.net.metrics -import android.os.Parcelable import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.util.ParcelableTestUtil -import com.android.internal.util.TestUtils +import com.android.testutils.assertParcelSane import java.lang.reflect.Modifier import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -33,11 +31,6 @@ private const val REVALIDATION: Int = 2 shl 8 @RunWith(AndroidJUnit4::class) @SmallTest class ValidationProbeEventTest { - private fun <T: Parcelable> testParcel(obj: T, fieldCount: Int) { - ParcelableTestUtil.assertFieldCountEquals(fieldCount, obj::class.java) - TestUtils.assertParcelingIsLossless(obj) - } - private infix fun Int.hasType(type: Int) = (type and this) == type @Test @@ -58,7 +51,7 @@ class ValidationProbeEventTest { assertTrue(validationProbeEvent.probeType hasType FIRST_VALIDATION) assertEquals(ValidationProbeEvent.DNS_SUCCESS, validationProbeEvent.returnCode) - testParcel(validationProbeEvent, 3) + assertParcelSane(validationProbeEvent, 3) } @Test diff --git a/tests/net/common/java/android/net/util/SocketUtilsTest.kt b/tests/net/common/java/android/net/util/SocketUtilsTest.kt new file mode 100644 index 000000000000..9c7cfb0c716e --- /dev/null +++ b/tests/net/common/java/android/net/util/SocketUtilsTest.kt @@ -0,0 +1,76 @@ +/* + * 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.util; + +import android.system.NetlinkSocketAddress +import android.system.Os +import android.system.OsConstants.AF_INET +import android.system.OsConstants.ETH_P_ALL +import android.system.OsConstants.IPPROTO_UDP +import android.system.OsConstants.RTMGRP_NEIGH +import android.system.OsConstants.SOCK_DGRAM +import android.system.PacketSocketAddress +import androidx.test.filters.SmallTest +import androidx.test.runner.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Test +import org.junit.runner.RunWith + +private const val TEST_INDEX = 123 +private const val TEST_PORT = 555 +@RunWith(AndroidJUnit4::class) +@SmallTest +class SocketUtilsTest { + @Test + fun testMakeNetlinkSocketAddress() { + val nlAddress = SocketUtils.makeNetlinkSocketAddress(TEST_PORT, RTMGRP_NEIGH) + if (nlAddress is NetlinkSocketAddress) { + assertEquals(TEST_PORT, nlAddress.getPortId()) + assertEquals(RTMGRP_NEIGH, nlAddress.getGroupsMask()) + } else { + fail("Not NetlinkSocketAddress object") + } + } + + @Test + fun testMakePacketSocketAddress() { + val pkAddress = SocketUtils.makePacketSocketAddress(ETH_P_ALL, TEST_INDEX) + assertTrue("Not PacketSocketAddress object", pkAddress is PacketSocketAddress) + + val ff = 0xff.toByte() + val pkAddress2 = SocketUtils.makePacketSocketAddress(TEST_INDEX, + byteArrayOf(ff, ff, ff, ff, ff, ff)) + assertTrue("Not PacketSocketAddress object", pkAddress2 is PacketSocketAddress) + } + + @Test + fun testCloseSocket() { + // Expect no exception happening with null object. + SocketUtils.closeSocket(null) + + val fd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) + assertTrue(fd.valid()) + SocketUtils.closeSocket(fd) + assertFalse(fd.valid()) + // Expecting socket should be still invalid even closed socket again. + SocketUtils.closeSocket(fd) + assertFalse(fd.valid()) + } +} diff --git a/tests/net/util/Android.bp b/tests/net/deflake/Android.bp index d8c502d46871..1c48c749aacc 100644 --- a/tests/net/util/Android.bp +++ b/tests/net/deflake/Android.bp @@ -14,17 +14,16 @@ // limitations under the License. // -// Common utilities for network tests. -java_library { - name: "frameworks-net-testutils", - srcs: ["java/**/*.java"], - // test_current to be also appropriate for CTS tests - sdk_version: "test_current", - static_libs: [ - "androidx.annotation_annotation", +java_test_host { + name: "FrameworksNetDeflakeTest", + srcs: ["src/**/*.kt"], + libs: [ "junit", + "tradefed", ], - libs: [ - "android.test.base.stubs", + static_libs: [ + "kotlin-test", + "net-host-tests-utils", ], + data: [":FrameworksNetTests"], }
\ No newline at end of file diff --git a/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt b/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt new file mode 100644 index 000000000000..62855255fec2 --- /dev/null +++ b/tests/net/deflake/src/com/android/server/net/FrameworksNetDeflakeTest.kt @@ -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 com.android.server.net + +import com.android.testutils.host.DeflakeHostTestBase +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner +import org.junit.runner.RunWith + +@RunWith(DeviceJUnit4ClassRunner::class) +class FrameworksNetDeflakeTest: DeflakeHostTestBase() { + override val runCount = 20 + override val testApkFilename = "FrameworksNetTests.apk" + override val testClasses = listOf("com.android.server.ConnectivityServiceTest") +}
\ No newline at end of file diff --git a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java index fd555c1e9115..899295a019d2 100644 --- a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java +++ b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java @@ -202,8 +202,7 @@ public class NetworkStatsManagerTest { assertFalse(stats.hasNextBucket()); } - private void assertBucketMatches(Entry expected, - NetworkStats.Bucket actual) { + private void assertBucketMatches(Entry expected, NetworkStats.Bucket actual) { assertEquals(expected.uid, actual.getUid()); assertEquals(expected.rxBytes, actual.getRxBytes()); assertEquals(expected.rxPackets, actual.getRxPackets()); diff --git a/tests/net/java/android/net/IpMemoryStoreTest.java b/tests/net/java/android/net/IpMemoryStoreTest.java index 6e69b34fe474..b81ca36429ff 100644 --- a/tests/net/java/android/net/IpMemoryStoreTest.java +++ b/tests/net/java/android/net/IpMemoryStoreTest.java @@ -321,4 +321,11 @@ public class IpMemoryStoreTest { eq(TEST_OTHER_DATA_NAME), any()); assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue())); } + + @Test + public void testFactoryReset() throws RemoteException { + startIpMemoryStore(true /* supplyService */); + mStore.factoryReset(); + verify(mMockService, times(1)).factoryReset(); + } } diff --git a/tests/net/java/android/net/IpSecConfigTest.java b/tests/net/java/android/net/IpSecConfigTest.java index 215506c05c88..c9888b24b6da 100644 --- a/tests/net/java/android/net/IpSecConfigTest.java +++ b/tests/net/java/android/net/IpSecConfigTest.java @@ -16,12 +16,12 @@ package android.net; +import static com.android.testutils.ParcelUtilsKt.assertParcelSane; +import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; + import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import android.os.Parcel; import androidx.test.filters.SmallTest; @@ -89,23 +89,15 @@ public class IpSecConfigTest { IpSecConfig original = getSampleConfig(); IpSecConfig copy = new IpSecConfig(original); - assertTrue(IpSecConfig.equals(original, copy)); - assertFalse(original == copy); + assertEquals(original, copy); + assertNotSame(original, copy); } @Test - public void testParcelUnparcel() throws Exception { + public void testParcelUnparcel() { assertParcelingIsLossless(new IpSecConfig()); IpSecConfig c = getSampleConfig(); - assertParcelingIsLossless(c); - } - - private void assertParcelingIsLossless(IpSecConfig ci) throws Exception { - Parcel p = Parcel.obtain(); - ci.writeToParcel(p, 0); - p.setDataPosition(0); - IpSecConfig co = IpSecConfig.CREATOR.createFromParcel(p); - assertTrue(IpSecConfig.equals(co, ci)); + assertParcelSane(c, 15); } } diff --git a/tests/net/java/android/net/IpSecTransformTest.java b/tests/net/java/android/net/IpSecTransformTest.java index 2308a3c9b477..424f23dbbaf6 100644 --- a/tests/net/java/android/net/IpSecTransformTest.java +++ b/tests/net/java/android/net/IpSecTransformTest.java @@ -16,8 +16,8 @@ package android.net; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import androidx.test.filters.SmallTest; @@ -43,7 +43,7 @@ public class IpSecTransformTest { config.setSpiResourceId(1985); IpSecTransform postModification = new IpSecTransform(null, config); - assertFalse(IpSecTransform.equals(preModification, postModification)); + assertNotEquals(preModification, postModification); } @Test @@ -57,6 +57,6 @@ public class IpSecTransformTest { IpSecTransform config1 = new IpSecTransform(null, config); IpSecTransform config2 = new IpSecTransform(null, config); - assertTrue(IpSecTransform.equals(config1, config2)); + assertEquals(config1, config2); } } diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java index 583d3fd536aa..5cb0d7e7a1a9 100644 --- a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java +++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java @@ -16,14 +16,14 @@ package android.net; +import static com.android.testutils.ParcelUtilsKt.assertParcelingIsLossless; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import android.net.SocketKeepalive.InvalidPacketException; -import com.android.internal.util.TestUtils; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -79,7 +79,7 @@ public final class TcpKeepalivePacketDataTest { assertEquals(testInfo.tos, resultData.ipTos); assertEquals(testInfo.ttl, resultData.ipTtl); - TestUtils.assertParcelingIsLossless(resultData); + assertParcelingIsLossless(resultData); final byte[] packet = resultData.getPacket(); // IP version and IHL diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/tests/net/java/android/net/nsd/NsdManagerTest.java index 2d2bccba7eb9..cf7587a2039f 100644 --- a/tests/net/java/android/net/nsd/NsdManagerTest.java +++ b/tests/net/java/android/net/nsd/NsdManagerTest.java @@ -16,8 +16,6 @@ package android.net.nsd; -import static com.android.internal.util.TestUtils.waitForIdleHandler; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -40,6 +38,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.AsyncChannel; +import com.android.testutils.HandlerUtilsKt; import org.junit.After; import org.junit.Before; @@ -74,7 +73,7 @@ public class NsdManagerTest { @After public void tearDown() throws Exception { - mServiceHandler.waitForIdle(mTimeoutMs); + HandlerUtilsKt.waitForIdle(mServiceHandler, mTimeoutMs); mServiceHandler.chan.disconnect(); mServiceHandler.stop(); if (mManager != null) { @@ -334,7 +333,7 @@ public class NsdManagerTest { } int verifyRequest(int expectedMessageType) { - mServiceHandler.waitForIdle(mTimeoutMs); + HandlerUtilsKt.waitForIdle(mServiceHandler, mTimeoutMs); verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any()); reset(mServiceHandler); Message received = mServiceHandler.getLastMessage(); @@ -366,10 +365,6 @@ public class NsdManagerTest { lastMessage.copyFrom(msg); } - void waitForIdle(long timeoutMs) { - waitForIdleHandler(this, timeoutMs); - } - @Override public void handleMessage(Message msg) { setLastMessage(msg); diff --git a/tests/net/java/android/net/shared/InitialConfigurationTest.java b/tests/net/java/android/net/shared/InitialConfigurationTest.java index 2fb8b19abcd4..17f8324ed36f 100644 --- a/tests/net/java/android/net/shared/InitialConfigurationTest.java +++ b/tests/net/java/android/net/shared/InitialConfigurationTest.java @@ -18,7 +18,7 @@ package android.net.shared; import static android.net.InetAddresses.parseNumericAddress; -import static com.android.internal.util.ParcelableTestUtil.assertFieldCountEquals; +import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; diff --git a/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java index f9dbdc7fbf3e..f9873895e4aa 100644 --- a/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java +++ b/tests/net/java/android/net/shared/IpConfigurationParcelableUtilTest.java @@ -20,7 +20,7 @@ import static android.net.InetAddresses.parseNumericAddress; import static android.net.shared.IpConfigurationParcelableUtil.fromStableParcelable; import static android.net.shared.IpConfigurationParcelableUtil.toStableParcelable; -import static com.android.internal.util.ParcelableTestUtil.assertFieldCountEquals; +import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; import static org.junit.Assert.assertEquals; diff --git a/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java b/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java index 382afe0279be..7079a28dce03 100644 --- a/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java +++ b/tests/net/java/android/net/shared/ProvisioningConfigurationTest.java @@ -19,7 +19,7 @@ package android.net.shared; import static android.net.InetAddresses.parseNumericAddress; import static android.net.shared.ProvisioningConfiguration.fromStableParcelable; -import static com.android.internal.util.ParcelableTestUtil.assertFieldCountEquals; +import static com.android.testutils.MiscAssertsKt.assertFieldCountEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; diff --git a/tests/net/java/android/net/util/DnsUtilsTest.java b/tests/net/java/android/net/util/DnsUtilsTest.java index e5cb09f237be..b626db8d89e4 100644 --- a/tests/net/java/android/net/util/DnsUtilsTest.java +++ b/tests/net/java/android/net/util/DnsUtilsTest.java @@ -19,7 +19,6 @@ package android.net.util; import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_GLOBAL; import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_LINKLOCAL; import static android.net.util.DnsUtils.IPV6_ADDR_SCOPE_SITELOCAL; -import static android.net.util.DnsUtils.rfc6724Sort; import static org.junit.Assert.assertEquals; @@ -56,44 +55,40 @@ public class DnsUtilsTest { } @Test - public void testRfc6724Sort() { - final List<InetAddress> testAddresses = Arrays.asList( - stringToAddress("172.217.24.14"), - stringToAddress("216.58.200.46"), - stringToAddress("2404:6800:4008:802::200e")); - - final List<InetAddress> expected = Arrays.asList( - stringToAddress("2404:6800:4008:802::200e"), - stringToAddress("172.217.24.14"), - stringToAddress("216.58.200.46")); - - final List<InetAddress> result = rfc6724Sort(null, testAddresses); - - assertEquals(result.size(), testAddresses.size()); - assertEquals(result, expected); - } - - @Test public void testRfc6724Comparator() { final List<DnsUtils.SortableAddress> test = Arrays.asList( - makeSortableAddress("216.58.200.36"), // Ipv4 - makeSortableAddress("2404:6800:4008:801::2004"), // global - makeSortableAddress("::1"), // loop back - makeSortableAddress("fe80::c46f:1cff:fe04:39b4"), // link local - makeSortableAddress("::ffff:192.168.95.3"), // IPv4-mapped IPv6 - makeSortableAddress("2001::47c1"), // teredo tunneling - makeSortableAddress("::216.58.200.36"), // IPv4-compatible - makeSortableAddress("3ffe::1234:5678")); // 6bone + // Ipv4 + makeSortableAddress("216.58.200.36", "192.168.1.1"), + // global with different scope src + makeSortableAddress("2404:6800:4008:801::2004", "fe80::1111:2222"), + // global without src addr + makeSortableAddress("2404:6800:cafe:801::1"), + // loop back + makeSortableAddress("::1", "::1"), + // link local + makeSortableAddress("fe80::c46f:1cff:fe04:39b4", "fe80::1"), + // teredo tunneling + makeSortableAddress("2001::47c1", "2001::2"), + // 6bone without src addr + makeSortableAddress("3ffe::1234:5678"), + // IPv4-compatible + makeSortableAddress("::216.58.200.36", "::216.58.200.9"), + // 6bone + makeSortableAddress("3ffe::1234:5678", "3ffe::1234:1"), + // IPv4-mapped IPv6 + makeSortableAddress("::ffff:192.168.95.7", "::ffff:192.168.95.1")); final List<InetAddress> expected = Arrays.asList( stringToAddress("::1"), // loop back stringToAddress("fe80::c46f:1cff:fe04:39b4"), // link local - stringToAddress("2404:6800:4008:801::2004"), // global stringToAddress("216.58.200.36"), // Ipv4 - stringToAddress("::ffff:192.168.95.3"), // IPv4-mapped IPv6 + stringToAddress("::ffff:192.168.95.7"), // IPv4-mapped IPv6 stringToAddress("2001::47c1"), // teredo tunneling - stringToAddress("::216.58.200.36"), // IPv4-compatible - stringToAddress("3ffe::1234:5678")); // 6bone + stringToAddress("::216.58.200.36"), // IPv4-compatible + stringToAddress("3ffe::1234:5678"), // 6bone + stringToAddress("2404:6800:4008:801::2004"), // global with different scope src + stringToAddress("2404:6800:cafe:801::1"), // global without src addr + stringToAddress("3ffe::1234:5678")); // 6bone without src addr Collections.sort(test, new DnsUtils.Rfc6724Comparator()); diff --git a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt index 814e06e311b3..8ea226db938e 100644 --- a/tests/net/java/android/net/util/KeepaliveUtilsTest.kt +++ b/tests/net/java/android/net/util/KeepaliveUtilsTest.kt @@ -78,7 +78,6 @@ class KeepaliveUtilsTest { assertRunWithException(arrayOf("5")) // Check resource with invalid slots value. - assertRunWithException(arrayOf("2,2")) assertRunWithException(arrayOf("3,-1")) // Check resource with invalid transport type. diff --git a/tests/net/java/com/android/internal/util/RingBufferTest.java b/tests/net/java/com/android/internal/util/RingBufferTest.java index eff334f7979d..d06095a690cf 100644 --- a/tests/net/java/com/android/internal/util/RingBufferTest.java +++ b/tests/net/java/com/android/internal/util/RingBufferTest.java @@ -16,6 +16,7 @@ package com.android.internal.util; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; @@ -25,9 +26,6 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.Arrays; -import java.util.Objects; - @SmallTest @RunWith(AndroidJUnit4.class) public class RingBufferTest { @@ -36,7 +34,7 @@ public class RingBufferTest { public void testEmptyRingBuffer() { RingBuffer<String> buffer = new RingBuffer<>(String.class, 100); - assertArraysEqual(new String[0], buffer.toArray()); + assertArrayEquals(new String[0], buffer.toArray()); } @Test @@ -65,7 +63,7 @@ public class RingBufferTest { buffer.append("e"); String[] expected = {"a", "b", "c", "d", "e"}; - assertArraysEqual(expected, buffer.toArray()); + assertArrayEquals(expected, buffer.toArray()); } @Test @@ -73,19 +71,19 @@ public class RingBufferTest { RingBuffer<String> buffer = new RingBuffer<>(String.class, 1); buffer.append("a"); - assertArraysEqual(new String[]{"a"}, buffer.toArray()); + assertArrayEquals(new String[]{"a"}, buffer.toArray()); buffer.append("b"); - assertArraysEqual(new String[]{"b"}, buffer.toArray()); + assertArrayEquals(new String[]{"b"}, buffer.toArray()); buffer.append("c"); - assertArraysEqual(new String[]{"c"}, buffer.toArray()); + assertArrayEquals(new String[]{"c"}, buffer.toArray()); buffer.append("d"); - assertArraysEqual(new String[]{"d"}, buffer.toArray()); + assertArrayEquals(new String[]{"d"}, buffer.toArray()); buffer.append("e"); - assertArraysEqual(new String[]{"e"}, buffer.toArray()); + assertArrayEquals(new String[]{"e"}, buffer.toArray()); } @Test @@ -100,7 +98,7 @@ public class RingBufferTest { buffer.append("e"); String[] expected1 = {"a", "b", "c", "d", "e"}; - assertArraysEqual(expected1, buffer.toArray()); + assertArrayEquals(expected1, buffer.toArray()); String[] expected2 = new String[capacity]; int firstIndex = 0; @@ -111,22 +109,22 @@ public class RingBufferTest { buffer.append("x"); expected2[i] = "x"; } - assertArraysEqual(expected2, buffer.toArray()); + assertArrayEquals(expected2, buffer.toArray()); buffer.append("x"); expected2[firstIndex] = "x"; - assertArraysEqual(expected2, buffer.toArray()); + assertArrayEquals(expected2, buffer.toArray()); for (int i = 0; i < 10; i++) { for (String s : expected2) { buffer.append(s); } } - assertArraysEqual(expected2, buffer.toArray()); + assertArrayEquals(expected2, buffer.toArray()); buffer.append("a"); expected2[lastIndex] = "a"; - assertArraysEqual(expected2, buffer.toArray()); + assertArrayEquals(expected2, buffer.toArray()); } @Test @@ -143,7 +141,7 @@ public class RingBufferTest { expected[i] = new DummyClass1(); expected[i].x = capacity * i; } - assertArraysEqual(expected, buffer.toArray()); + assertArrayEquals(expected, buffer.toArray()); for (int i = 0; i < capacity; ++i) { if (actual[i] != buffer.getNextSlot()) { @@ -177,18 +175,4 @@ public class RingBufferTest { } private static final class DummyClass3 {} - - static <T> void assertArraysEqual(T[] expected, T[] got) { - if (expected.length != got.length) { - fail(Arrays.toString(expected) + " and " + Arrays.toString(got) - + " did not have the same length"); - } - - for (int i = 0; i < expected.length; i++) { - if (!Objects.equals(expected[i], got[i])) { - fail(Arrays.toString(expected) + " and " + Arrays.toString(got) - + " were not equal"); - } - } - } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 5934fcb6759f..7317d625a202 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -30,9 +30,12 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_MMS; import static android.net.ConnectivityManager.TYPE_NONE; import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; -import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_DNS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_FALLBACK; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTP; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL; import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; @@ -65,11 +68,15 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; import static android.net.RouteInfo.RTN_UNREACHABLE; -import static com.android.internal.util.TestUtils.waitForIdleHandler; -import static com.android.internal.util.TestUtils.waitForIdleLooper; -import static com.android.internal.util.TestUtils.waitForIdleSerialExecutor; +import static com.android.testutils.ConcurrentUtilsKt.await; +import static com.android.testutils.ConcurrentUtilsKt.durationOf; +import static com.android.testutils.HandlerUtilsKt.waitForIdleSerialExecutor; +import static com.android.testutils.MiscAssertsKt.assertContainsExactly; +import static com.android.testutils.MiscAssertsKt.assertEmpty; +import static com.android.testutils.MiscAssertsKt.assertLength; +import static com.android.testutils.MiscAssertsKt.assertRunsInAtMost; +import static com.android.testutils.MiscAssertsKt.assertThrows; -import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -193,6 +200,9 @@ import com.android.server.connectivity.Tethering; import com.android.server.connectivity.Vpn; import com.android.server.net.NetworkPinner; import com.android.server.net.NetworkPolicyManagerInternal; +import com.android.server.net.NetworkStatsFactory; +import com.android.testutils.HandlerUtilsKt; +import com.android.testutils.ThrowingConsumer; import org.junit.After; import org.junit.Before; @@ -372,19 +382,19 @@ public class ConnectivityServiceTest { public void waitForIdle(int timeoutMsAsInt) { long timeoutMs = timeoutMsAsInt; - waitForIdleHandler(mService.mHandlerThread, timeoutMs); + HandlerUtilsKt.waitForIdle(mService.mHandlerThread, timeoutMs); waitForIdle(mCellNetworkAgent, timeoutMs); waitForIdle(mWiFiNetworkAgent, timeoutMs); waitForIdle(mEthernetNetworkAgent, timeoutMs); - waitForIdleHandler(mService.mHandlerThread, timeoutMs); - waitForIdleLooper(ConnectivityThread.getInstanceLooper(), timeoutMs); + HandlerUtilsKt.waitForIdle(mService.mHandlerThread, timeoutMs); + HandlerUtilsKt.waitForIdle(ConnectivityThread.get(), timeoutMs); } public void waitForIdle(MockNetworkAgent agent, long timeoutMs) { if (agent == null) { return; } - waitForIdleHandler(agent.mHandlerThread, timeoutMs); + HandlerUtilsKt.waitForIdle(agent.mHandlerThread, timeoutMs); } private void waitForIdle() { @@ -443,6 +453,16 @@ public class ConnectivityServiceTest { } private class MockNetworkAgent { + private static final int VALIDATION_RESULT_BASE = NETWORK_VALIDATION_PROBE_DNS + | NETWORK_VALIDATION_PROBE_HTTP + | NETWORK_VALIDATION_PROBE_HTTPS; + private static final int VALIDATION_RESULT_VALID = VALIDATION_RESULT_BASE + | NETWORK_VALIDATION_RESULT_VALID; + private static final int VALIDATION_RESULT_PARTIAL = VALIDATION_RESULT_BASE + | NETWORK_VALIDATION_PROBE_FALLBACK + | NETWORK_VALIDATION_RESULT_PARTIAL; + private static final int VALIDATION_RESULT_INVALID = 0; + private final INetworkMonitor mNetworkMonitor; private final NetworkInfo mNetworkInfo; private final NetworkCapabilities mNetworkCapabilities; @@ -460,17 +480,17 @@ public class ConnectivityServiceTest { private String mRedirectUrl; private INetworkMonitorCallbacks mNmCallbacks; - private int mNmValidationResult = NETWORK_TEST_RESULT_INVALID; + private int mNmValidationResult = VALIDATION_RESULT_BASE; private String mNmValidationRedirectUrl = null; private boolean mNmProvNotificationRequested = false; void setNetworkValid() { - mNmValidationResult = NETWORK_TEST_RESULT_VALID; + mNmValidationResult = VALIDATION_RESULT_VALID; mNmValidationRedirectUrl = null; } void setNetworkInvalid() { - mNmValidationResult = NETWORK_TEST_RESULT_INVALID; + mNmValidationResult = VALIDATION_RESULT_INVALID; mNmValidationRedirectUrl = null; } @@ -480,7 +500,12 @@ public class ConnectivityServiceTest { } void setNetworkPartial() { - mNmValidationResult = NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY; + mNmValidationResult = VALIDATION_RESULT_PARTIAL; + mNmValidationRedirectUrl = null; + } + + void setNetworkPartialValid() { + mNmValidationResult = VALIDATION_RESULT_PARTIAL | VALIDATION_RESULT_VALID; mNmValidationRedirectUrl = null; } @@ -597,7 +622,7 @@ public class ConnectivityServiceTest { private void onValidationRequested() { try { if (mNmProvNotificationRequested - && mNmValidationResult == NETWORK_TEST_RESULT_VALID) { + && ((mNmValidationResult & NETWORK_VALIDATION_RESULT_VALID) != 0)) { mNmCallbacks.hideProvisioningNotification(); mNmProvNotificationRequested = false; } @@ -1202,7 +1227,7 @@ public class ConnectivityServiceTest { } public void waitForIdle(int timeoutMs) { - waitForIdleHandler(mHandlerThread, timeoutMs); + HandlerUtilsKt.waitForIdle(mHandlerThread, timeoutMs); } public void waitForIdle() { @@ -2551,10 +2576,10 @@ public class ConnectivityServiceTest { .build(); Class<IllegalArgumentException> expected = IllegalArgumentException.class; - assertException(() -> { mCm.requestNetwork(request1, new NetworkCallback()); }, expected); - assertException(() -> { mCm.requestNetwork(request1, pendingIntent); }, expected); - assertException(() -> { mCm.requestNetwork(request2, new NetworkCallback()); }, expected); - assertException(() -> { mCm.requestNetwork(request2, pendingIntent); }, expected); + assertThrows(expected, () -> mCm.requestNetwork(request1, new NetworkCallback())); + assertThrows(expected, () -> mCm.requestNetwork(request1, pendingIntent)); + assertThrows(expected, () -> mCm.requestNetwork(request2, new NetworkCallback())); + assertThrows(expected, () -> mCm.requestNetwork(request2, pendingIntent)); } @Test @@ -2651,7 +2676,7 @@ public class ConnectivityServiceTest { // With HTTPS probe disabled, NetworkMonitor should pass the network validation with http // probe. - mWiFiNetworkAgent.setNetworkValid(); + mWiFiNetworkAgent.setNetworkPartialValid(); // If the user chooses yes to use this partial connectivity wifi, switch the default // network to wifi and check if wifi becomes valid or not. mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, @@ -2749,6 +2774,55 @@ public class ConnectivityServiceTest { } @Test + public void testCaptivePortalOnPartialConnectivity() throws RemoteException { + final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); + final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL).build(); + mCm.registerNetworkCallback(captivePortalRequest, captivePortalCallback); + + final TestNetworkCallback validatedCallback = new TestNetworkCallback(); + final NetworkRequest validatedRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_VALIDATED).build(); + mCm.registerNetworkCallback(validatedRequest, validatedCallback); + + // Bring up a network with a captive portal. + // Expect onAvailable callback of listen for NET_CAPABILITY_CAPTIVE_PORTAL. + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + String redirectUrl = "http://android.com/path"; + mWiFiNetworkAgent.connectWithCaptivePortal(redirectUrl); + captivePortalCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), redirectUrl); + + // Check that startCaptivePortalApp sends the expected command to NetworkMonitor. + mCm.startCaptivePortalApp(mWiFiNetworkAgent.getNetwork()); + verify(mWiFiNetworkAgent.mNetworkMonitor, timeout(TIMEOUT_MS).times(1)) + .launchCaptivePortalApp(); + + // Report that the captive portal is dismissed with partial connectivity, and check that + // callbacks are fired. + mWiFiNetworkAgent.setNetworkPartial(); + mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); + waitForIdle(); + captivePortalCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, + mWiFiNetworkAgent); + + // Report partial connectivity is accepted. + mWiFiNetworkAgent.setNetworkPartialValid(); + mCm.setAcceptPartialConnectivity(mWiFiNetworkAgent.getNetwork(), true /* accept */, + false /* always */); + waitForIdle(); + mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true); + captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + validatedCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent); + NetworkCapabilities nc = + validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, + mWiFiNetworkAgent); + + mCm.unregisterNetworkCallback(captivePortalCallback); + mCm.unregisterNetworkCallback(validatedCallback); + } + + @Test public void testCaptivePortal() { final TestNetworkCallback captivePortalCallback = new TestNetworkCallback(); final NetworkRequest captivePortalRequest = new NetworkRequest.Builder() @@ -3417,7 +3491,7 @@ public class ConnectivityServiceTest { }; } - assertTimeLimit("Registering callbacks", REGISTER_TIME_LIMIT_MS, () -> { + assertRunsInAtMost("Registering callbacks", REGISTER_TIME_LIMIT_MS, () -> { for (NetworkCallback cb : callbacks) { mCm.registerNetworkCallback(request, cb); } @@ -3430,7 +3504,7 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(false); long onAvailableDispatchingDuration = durationOf(() -> { - awaitLatch(availableLatch, 10 * CONNECT_TIME_LIMIT_MS); + await(availableLatch, 10 * CONNECT_TIME_LIMIT_MS); }); Log.d(TAG, String.format("Dispatched %d of %d onAvailable callbacks in %dms", NUM_REQUESTS - availableLatch.getCount(), NUM_REQUESTS, @@ -3445,7 +3519,7 @@ public class ConnectivityServiceTest { mWiFiNetworkAgent.connect(false); long onLostDispatchingDuration = durationOf(() -> { - awaitLatch(losingLatch, 10 * SWITCH_TIME_LIMIT_MS); + await(losingLatch, 10 * SWITCH_TIME_LIMIT_MS); }); Log.d(TAG, String.format("Dispatched %d of %d onLosing callbacks in %dms", NUM_REQUESTS - losingLatch.getCount(), NUM_REQUESTS, onLostDispatchingDuration)); @@ -3453,33 +3527,13 @@ public class ConnectivityServiceTest { NUM_REQUESTS, onLostDispatchingDuration, SWITCH_TIME_LIMIT_MS), onLostDispatchingDuration <= SWITCH_TIME_LIMIT_MS); - assertTimeLimit("Unregistering callbacks", UNREGISTER_TIME_LIMIT_MS, () -> { + assertRunsInAtMost("Unregistering callbacks", UNREGISTER_TIME_LIMIT_MS, () -> { for (NetworkCallback cb : callbacks) { mCm.unregisterNetworkCallback(cb); } }); } - private long durationOf(Runnable fn) { - long startTime = SystemClock.elapsedRealtime(); - fn.run(); - return SystemClock.elapsedRealtime() - startTime; - } - - private void assertTimeLimit(String descr, long timeLimit, Runnable fn) { - long timeTaken = durationOf(fn); - String msg = String.format("%s: took %dms, limit was %dms", descr, timeTaken, timeLimit); - Log.d(TAG, msg); - assertTrue(msg, timeTaken <= timeLimit); - } - - private boolean awaitLatch(CountDownLatch l, long timeoutMs) { - try { - return l.await(timeoutMs, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) {} - return false; - } - @Test public void testMobileDataAlwaysOn() throws Exception { final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback(); @@ -4179,11 +4233,6 @@ public class ConnectivityServiceTest { callback3.expectStopped(); } - @FunctionalInterface - private interface ThrowingConsumer<T> { - void accept(T t) throws Exception; - } - // Helper method to prepare the executor and run test private void runTestWithSerialExecutors(ThrowingConsumer<Executor> functor) throws Exception { final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor(); @@ -4758,17 +4807,17 @@ public class ConnectivityServiceTest { assertNull(mCm.getLinkProperties(TYPE_NONE)); assertFalse(mCm.isNetworkSupported(TYPE_NONE)); - assertException(() -> { mCm.networkCapabilitiesForType(TYPE_NONE); }, - IllegalArgumentException.class); + assertThrows(IllegalArgumentException.class, + () -> { mCm.networkCapabilitiesForType(TYPE_NONE); }); Class<UnsupportedOperationException> unsupported = UnsupportedOperationException.class; - assertException(() -> { mCm.startUsingNetworkFeature(TYPE_WIFI, ""); }, unsupported); - assertException(() -> { mCm.stopUsingNetworkFeature(TYPE_WIFI, ""); }, unsupported); + assertThrows(unsupported, () -> { mCm.startUsingNetworkFeature(TYPE_WIFI, ""); }); + assertThrows(unsupported, () -> { mCm.stopUsingNetworkFeature(TYPE_WIFI, ""); }); // TODO: let test context have configuration application target sdk version // and test that pre-M requesting for TYPE_NONE sends back APN_REQUEST_FAILED - assertException(() -> { mCm.startUsingNetworkFeature(TYPE_NONE, ""); }, unsupported); - assertException(() -> { mCm.stopUsingNetworkFeature(TYPE_NONE, ""); }, unsupported); - assertException(() -> { mCm.requestRouteToHostAddress(TYPE_NONE, null); }, unsupported); + assertThrows(unsupported, () -> { mCm.startUsingNetworkFeature(TYPE_NONE, ""); }); + assertThrows(unsupported, () -> { mCm.stopUsingNetworkFeature(TYPE_NONE, ""); }); + assertThrows(unsupported, () -> { mCm.requestRouteToHostAddress(TYPE_NONE, null); }); } @Test @@ -4837,9 +4886,9 @@ public class ConnectivityServiceTest { verify(mStatsService, atLeastOnce()) .forceUpdateIfaces( eq(onlyCell), - eq(new VpnInfo[0]), any(NetworkState[].class), eq(MOBILE_IFNAME)); + assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos()); reset(mStatsService); // Default network switch should update ifaces. @@ -4850,9 +4899,9 @@ public class ConnectivityServiceTest { verify(mStatsService, atLeastOnce()) .forceUpdateIfaces( eq(onlyWifi), - eq(new VpnInfo[0]), any(NetworkState[].class), eq(WIFI_IFNAME)); + assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos()); reset(mStatsService); // Disconnect should update ifaces. @@ -4861,9 +4910,9 @@ public class ConnectivityServiceTest { verify(mStatsService, atLeastOnce()) .forceUpdateIfaces( eq(onlyCell), - eq(new VpnInfo[0]), any(NetworkState[].class), eq(MOBILE_IFNAME)); + assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos()); reset(mStatsService); // Metered change should update ifaces @@ -4872,9 +4921,9 @@ public class ConnectivityServiceTest { verify(mStatsService, atLeastOnce()) .forceUpdateIfaces( eq(onlyCell), - eq(new VpnInfo[0]), any(NetworkState[].class), eq(MOBILE_IFNAME)); + assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos()); reset(mStatsService); mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED); @@ -4882,9 +4931,9 @@ public class ConnectivityServiceTest { verify(mStatsService, atLeastOnce()) .forceUpdateIfaces( eq(onlyCell), - eq(new VpnInfo[0]), any(NetworkState[].class), eq(MOBILE_IFNAME)); + assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos()); reset(mStatsService); // Captive portal change shouldn't update ifaces @@ -4893,9 +4942,9 @@ public class ConnectivityServiceTest { verify(mStatsService, never()) .forceUpdateIfaces( eq(onlyCell), - eq(new VpnInfo[0]), any(NetworkState[].class), eq(MOBILE_IFNAME)); + assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos()); reset(mStatsService); // Roaming change should update ifaces @@ -4904,9 +4953,9 @@ public class ConnectivityServiceTest { verify(mStatsService, atLeastOnce()) .forceUpdateIfaces( eq(onlyCell), - eq(new VpnInfo[0]), any(NetworkState[].class), eq(MOBILE_IFNAME)); + assertEquals(new VpnInfo[0], NetworkStatsFactory.getVpnInfos()); reset(mStatsService); } @@ -5030,11 +5079,11 @@ public class ConnectivityServiceTest { ResolverParamsParcel resolvrParams = mResolverParamsParcelCaptor.getValue(); assertEquals(2, resolvrParams.tlsServers.length); assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, - new String[]{"2001:db8::1", "192.0.2.1"})); + new String[] { "2001:db8::1", "192.0.2.1" })); // Opportunistic mode. assertEquals(2, resolvrParams.tlsServers.length); assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, - new String[]{"2001:db8::1", "192.0.2.1"})); + new String[] { "2001:db8::1", "192.0.2.1" })); reset(mMockDnsResolver); cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent); cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, @@ -5052,7 +5101,7 @@ public class ConnectivityServiceTest { resolvrParams = mResolverParamsParcelCaptor.getValue(); assertEquals(2, resolvrParams.servers.length); assertTrue(ArrayUtils.containsAll(resolvrParams.servers, - new String[]{"2001:db8::1", "192.0.2.1"})); + new String[] { "2001:db8::1", "192.0.2.1" })); reset(mMockDnsResolver); cellNetworkCallback.assertNoCallback(); @@ -5062,10 +5111,10 @@ public class ConnectivityServiceTest { resolvrParams = mResolverParamsParcelCaptor.getValue(); assertEquals(2, resolvrParams.servers.length); assertTrue(ArrayUtils.containsAll(resolvrParams.servers, - new String[]{"2001:db8::1", "192.0.2.1"})); + new String[] { "2001:db8::1", "192.0.2.1" })); assertEquals(2, resolvrParams.tlsServers.length); assertTrue(ArrayUtils.containsAll(resolvrParams.tlsServers, - new String[]{"2001:db8::1", "192.0.2.1"})); + new String[] { "2001:db8::1", "192.0.2.1" })); reset(mMockDnsResolver); cellNetworkCallback.assertNoCallback(); @@ -5200,29 +5249,6 @@ public class ConnectivityServiceTest { assertTrue(lp.getDnsServers().containsAll(dnsServers)); } - private static <T> void assertEmpty(T[] ts) { - int length = ts.length; - assertEquals("expected empty array, but length was " + length, 0, length); - } - - private static <T> void assertLength(int expected, T[] got) { - int length = got.length; - assertEquals(String.format("expected array of length %s, but length was %s for %s", - expected, length, Arrays.toString(got)), expected, length); - } - - private static <T> void assertException(Runnable block, Class<T> expected) { - try { - block.run(); - fail("Expected exception of type " + expected); - } catch (Exception got) { - if (!got.getClass().equals(expected)) { - fail("Expected exception of type " + expected + " but got " + got); - } - return; - } - } - @Test public void testVpnNetworkActive() { final int uid = Process.myUid(); @@ -6419,14 +6445,6 @@ public class ConnectivityServiceTest { return vpnNetworkAgent; } - private void assertContainsExactly(int[] actual, int... expected) { - int[] sortedActual = Arrays.copyOf(actual, actual.length); - int[] sortedExpected = Arrays.copyOf(expected, expected.length); - Arrays.sort(sortedActual); - Arrays.sort(sortedExpected); - assertArrayEquals(sortedExpected, sortedActual); - } - private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid) { final PackageInfo packageInfo = new PackageInfo(); packageInfo.requestedPermissions = new String[0]; diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java index e4117b848a35..aef9386755d7 100644 --- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java +++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java @@ -19,8 +19,9 @@ package com.android.server.connectivity; import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO; import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME; +import static com.android.testutils.MiscAssertsKt.assertStringContains; + import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -111,15 +112,15 @@ public class NetdEventListenerServiceTest { String[] events2 = remove(listNetdEvent(), baseline); int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line assertEquals(expectedLength2, events2.length); - assertContains(events2[0], "WakeupStats"); - assertContains(events2[0], "wlan0"); - assertContains(events2[0], "0x800"); - assertContains(events2[0], "0x86dd"); + assertStringContains(events2[0], "WakeupStats"); + assertStringContains(events2[0], "wlan0"); + assertStringContains(events2[0], "0x800"); + assertStringContains(events2[0], "0x86dd"); for (int i = 0; i < uids.length; i++) { String got = events2[i+1]; - assertContains(got, "WakeupEvent"); - assertContains(got, "wlan0"); - assertContains(got, "uid: " + uids[i]); + assertStringContains(got, "WakeupEvent"); + assertStringContains(got, "wlan0"); + assertStringContains(got, "uid: " + uids[i]); } int uid = 20000; @@ -131,13 +132,13 @@ public class NetdEventListenerServiceTest { String[] events3 = remove(listNetdEvent(), baseline); int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line assertEquals(expectedLength3, events3.length); - assertContains(events2[0], "WakeupStats"); - assertContains(events2[0], "wlan0"); + assertStringContains(events2[0], "WakeupStats"); + assertStringContains(events2[0], "wlan0"); for (int i = 1; i < expectedLength3; i++) { String got = events3[i]; - assertContains(got, "WakeupEvent"); - assertContains(got, "wlan0"); - assertContains(got, "uid: " + uid); + assertStringContains(got, "WakeupEvent"); + assertStringContains(got, "wlan0"); + assertStringContains(got, "uid: " + uid); } uid = 45678; @@ -145,9 +146,9 @@ public class NetdEventListenerServiceTest { String[] events4 = remove(listNetdEvent(), baseline); String lastEvent = events4[events4.length - 1]; - assertContains(lastEvent, "WakeupEvent"); - assertContains(lastEvent, "wlan0"); - assertContains(lastEvent, "uid: " + uid); + assertStringContains(lastEvent, "WakeupEvent"); + assertStringContains(lastEvent, "wlan0"); + assertStringContains(lastEvent, "uid: " + uid); } @Test @@ -529,10 +530,6 @@ public class NetdEventListenerServiceTest { return buffer.toString().split("\\n"); } - static void assertContains(String got, String want) { - assertTrue(got + " did not contain \"" + want + "\"", got.contains(want)); - } - static <T> T[] remove(T[] array, T[] filtered) { List<T> c = Arrays.asList(filtered); int next = 0; diff --git a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java index df1f57f7a011..cd2bd26ef4bb 100644 --- a/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/PermissionMonitorTest.java @@ -65,6 +65,7 @@ import android.os.UserHandle; import android.os.UserManager; import android.util.SparseIntArray; +import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -96,6 +97,7 @@ public class PermissionMonitorTest { private static final int SYSTEM_UID1 = 1000; private static final int SYSTEM_UID2 = 1008; private static final int VPN_UID = 10002; + private static final String REAL_SYSTEM_PACKAGE_NAME = "android"; private static final String MOCK_PACKAGE1 = "appName1"; private static final String MOCK_PACKAGE2 = "appName2"; private static final String SYSTEM_PACKAGE1 = "sysName1"; @@ -188,8 +190,10 @@ public class PermissionMonitorTest { private static PackageInfo buildPackageInfo(boolean hasSystemPermission, int uid, int userId) { final PackageInfo pkgInfo; if (hasSystemPermission) { - pkgInfo = packageInfoWithPermissions(new String[] {CHANGE_NETWORK_STATE, NETWORK_STACK}, - PARTITION_SYSTEM); + final String[] systemPermissions = new String[]{ + CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS + }; + pkgInfo = packageInfoWithPermissions(systemPermissions, PARTITION_SYSTEM); } else { pkgInfo = packageInfoWithPermissions(new String[] {}, ""); } @@ -646,4 +650,16 @@ public class PermissionMonitorTest { mObserver.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1); mNetdServiceMonitor.expectPermission(INetd.PERMISSION_INTERNET, new int[]{MOCK_UID1}); } + + @Test + public void testRealSystemPermission() throws Exception { + // Use the real context as this test must ensure the *real* system package holds the + // necessary permission. + final Context realContext = InstrumentationRegistry.getContext(); + final PermissionMonitor monitor = new PermissionMonitor(realContext, mNetdService); + final PackageManager manager = realContext.getPackageManager(); + final PackageInfo systemInfo = manager.getPackageInfo(REAL_SYSTEM_PACKAGE_NAME, + GET_PERMISSIONS | MATCH_ANY_USER); + assertTrue(monitor.hasPermission(systemInfo, CONNECTIVITY_USE_RESTRICTED_NETWORKS)); + } } diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index 2cae2509026c..ce50bef53d75 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -727,94 +727,4 @@ public class VpnTest { "::/1", "8000::/2", "c000::/3", "e000::/4", "f000::/5", "f800::/6", "fe00::/8", "2605:ef80:e:af1d::/64"); } - - @Test - public void testProvidesRoutesToMostDestinations() { - final LinkProperties lp = new LinkProperties(); - - // Default route provides routes to all IPv4 destinations. - lp.addRoute(new RouteInfo(new IpPrefix("0.0.0.0/0"))); - assertTrue(Vpn.providesRoutesToMostDestinations(lp)); - - // Empty LP provides routes to no destination - lp.clear(); - assertFalse(Vpn.providesRoutesToMostDestinations(lp)); - - // All IPv4 routes except for local networks. This is the case most relevant - // to this function. It provides routes to almost the entire space. - // (clone the stream so that we can reuse it later) - publicIpV4Routes().forEach(s -> lp.addRoute(new RouteInfo(new IpPrefix(s)))); - assertTrue(Vpn.providesRoutesToMostDestinations(lp)); - - // Removing a 16-bit prefix, which is 65536 addresses. This is still enough to - // provide routes to "most" destinations. - lp.removeRoute(new RouteInfo(new IpPrefix("192.169.0.0/16"))); - assertTrue(Vpn.providesRoutesToMostDestinations(lp)); - - // Remove the /2 route, which represent a quarter of the available routing space. - // This LP does not provides routes to "most" destinations any more. - lp.removeRoute(new RouteInfo(new IpPrefix("64.0.0.0/2"))); - assertFalse(Vpn.providesRoutesToMostDestinations(lp)); - - lp.clear(); - publicIpV6Routes().forEach(s -> lp.addRoute(new RouteInfo(new IpPrefix(s)))); - assertTrue(Vpn.providesRoutesToMostDestinations(lp)); - - lp.removeRoute(new RouteInfo(new IpPrefix("::/1"))); - assertFalse(Vpn.providesRoutesToMostDestinations(lp)); - - // V6 does not provide sufficient coverage but v4 does - publicIpV4Routes().forEach(s -> lp.addRoute(new RouteInfo(new IpPrefix(s)))); - assertTrue(Vpn.providesRoutesToMostDestinations(lp)); - - // V4 still does - lp.removeRoute(new RouteInfo(new IpPrefix("192.169.0.0/16"))); - assertTrue(Vpn.providesRoutesToMostDestinations(lp)); - - // V4 does not any more - lp.removeRoute(new RouteInfo(new IpPrefix("64.0.0.0/2"))); - assertFalse(Vpn.providesRoutesToMostDestinations(lp)); - - // V4 does not, but V6 has sufficient coverage again - lp.addRoute(new RouteInfo(new IpPrefix("::/1"))); - assertTrue(Vpn.providesRoutesToMostDestinations(lp)); - - lp.clear(); - // V4-unreachable route should not be treated as sufficient coverage - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), RTN_UNREACHABLE)); - assertFalse(Vpn.providesRoutesToMostDestinations(lp)); - - lp.clear(); - // V6-unreachable route should not be treated as sufficient coverage - lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), RTN_UNREACHABLE)); - assertFalse(Vpn.providesRoutesToMostDestinations(lp)); - } - - @Test - public void testDoesNotLockUpWithTooManyRoutes() { - final LinkProperties lp = new LinkProperties(); - final byte[] ad = new byte[4]; - // Actually evaluating this many routes under 1500ms is impossible on - // current hardware and for some time, as the algorithm is O(n²). - // Make sure the system has a safeguard against this and does not - // lock up. - final int MAX_ROUTES = 4000; - final long MAX_ALLOWED_TIME_MS = 1500; - for (int i = 0; i < MAX_ROUTES; ++i) { - ad[0] = (byte)((i >> 24) & 0xFF); - ad[1] = (byte)((i >> 16) & 0xFF); - ad[2] = (byte)((i >> 8) & 0xFF); - ad[3] = (byte)(i & 0xFF); - try { - lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.getByAddress(ad), 32))); - } catch (UnknownHostException e) { - // UnknownHostException is only thrown for an address of illegal length, - // which can't happen in the case above. - } - } - final long start = SystemClock.currentThreadTimeMillis(); - assertTrue(Vpn.providesRoutesToMostDestinations(lp)); - final long end = SystemClock.currentThreadTimeMillis(); - assertTrue(end - start < MAX_ALLOWED_TIME_MS); - } } diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java index be54b1a93b5d..9931aec01487 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java @@ -25,6 +25,7 @@ import static android.net.TrafficStats.UID_TETHERING; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats; +import static com.android.testutils.MiscAssertsKt.assertContainsAll; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -51,7 +52,6 @@ import android.net.LinkProperties; import android.net.NetworkStats; import android.net.RouteInfo; import android.net.util.SharedLog; -import android.os.ConditionVariable; import android.os.Handler; import android.os.INetworkManagementService; import android.os.Looper; @@ -63,6 +63,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.FakeSettingsProvider; +import com.android.testutils.HandlerUtilsKt; import org.junit.After; import org.junit.Before; @@ -90,6 +91,7 @@ public class OffloadControllerTest { private static final String IPV6_DISCARD_PREFIX = "100::/64"; private static final String USB_PREFIX = "192.168.42.0/24"; private static final String WIFI_PREFIX = "192.168.43.0/24"; + private static final long WAIT_FOR_IDLE_TIMEOUT = 2 * 1000; @Mock private OffloadHardwareInterface mHardware; @Mock private ApplicationInfo mApplicationInfo; @@ -131,9 +133,7 @@ public class OffloadControllerTest { } private void waitForIdle() { - ConditionVariable cv = new ConditionVariable(); - new Handler(Looper.getMainLooper()).post(() -> { cv.open(); }); - cv.block(); + HandlerUtilsKt.waitForIdle(new Handler(Looper.getMainLooper()), WAIT_FOR_IDLE_TIMEOUT); } private OffloadController makeOffloadController() throws Exception { @@ -245,7 +245,7 @@ public class OffloadControllerTest { inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); ArrayList<String> localPrefixes = mStringArrayCaptor.getValue(); assertEquals(4, localPrefixes.size()); - assertArrayListContains(localPrefixes, + assertContainsAll(localPrefixes, "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"); inOrder.verifyNoMoreInteractions(); @@ -361,7 +361,7 @@ public class OffloadControllerTest { inOrder.verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); localPrefixes = mStringArrayCaptor.getValue(); assertEquals(6, localPrefixes.size()); - assertArrayListContains(localPrefixes, + assertContainsAll(localPrefixes, "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64", "2001:db8::6173:7369:676e:6564/128", "2001:db8::7261:6e64:6f6d/128"); // The relevant parts of the LinkProperties have not changed, but at the @@ -718,7 +718,7 @@ public class OffloadControllerTest { verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture()); ArrayList<String> localPrefixes = mStringArrayCaptor.getValue(); assertEquals(4, localPrefixes.size()); - assertArrayListContains(localPrefixes, + assertContainsAll(localPrefixes, // TODO: The logic to find and exclude downstream IP prefixes // is currently in Tethering's OffloadWrapper but must be moved // into OffloadController proper. After this, also check for: @@ -731,9 +731,4 @@ public class OffloadControllerTest { verifyNoMoreInteractions(mHardware); } - private static void assertArrayListContains(ArrayList<String> list, String... elems) { - for (String element : elems) { - assertTrue(element + " not in list", list.contains(element)); - } - } } diff --git a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java index 6e725dd69cb7..858358c74f80 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsAccessTest.java @@ -161,7 +161,7 @@ public class NetworkStatsAccessTest { } private void setHasCarrierPrivileges(boolean hasPrivileges) { - when(mTm.checkCarrierPrivilegesForPackage(TEST_PKG)).thenReturn( + when(mTm.checkCarrierPrivilegesForPackageAnyPhone(TEST_PKG)).thenReturn( hasPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS : TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS); } diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java new file mode 100644 index 000000000000..28785f7c9726 --- /dev/null +++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.net; + +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; +import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.DEFAULT_NETWORK_YES; +import static android.net.NetworkStats.METERED_ALL; +import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.METERED_YES; +import static android.net.NetworkStats.ROAMING_ALL; +import static android.net.NetworkStats.ROAMING_NO; +import static android.net.NetworkStats.ROAMING_YES; +import static android.net.NetworkStats.SET_ALL; +import static android.net.NetworkStats.SET_DEFAULT; +import static android.net.NetworkStats.SET_FOREGROUND; +import static android.net.NetworkStats.TAG_NONE; + +import static org.junit.Assert.assertEquals; + +import android.net.NetworkStats; + +import com.android.internal.net.VpnInfo; + +/** Superclass with utilities for NetworkStats(Service|Factory)Test */ +abstract class NetworkStatsBaseTest { + static final String TEST_IFACE = "test0"; + static final String TEST_IFACE2 = "test1"; + static final String TUN_IFACE = "test_nss_tun0"; + + static final int UID_RED = 1001; + static final int UID_BLUE = 1002; + static final int UID_GREEN = 1003; + static final int UID_VPN = 1004; + + void assertValues(NetworkStats stats, String iface, int uid, long rxBytes, + long rxPackets, long txBytes, long txPackets) { + assertValues( + stats, iface, uid, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, + rxBytes, rxPackets, txBytes, txPackets, 0); + } + + void assertValues(NetworkStats stats, String iface, int uid, int set, int tag, + int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, + long txBytes, long txPackets, long operations) { + final NetworkStats.Entry entry = new NetworkStats.Entry(); + final int[] sets; + if (set == SET_ALL) { + sets = new int[] {SET_ALL, SET_DEFAULT, SET_FOREGROUND}; + } else { + sets = new int[] {set}; + } + + final int[] roamings; + if (roaming == ROAMING_ALL) { + roamings = new int[] {ROAMING_ALL, ROAMING_YES, ROAMING_NO}; + } else { + roamings = new int[] {roaming}; + } + + final int[] meterings; + if (metered == METERED_ALL) { + meterings = new int[] {METERED_ALL, METERED_YES, METERED_NO}; + } else { + meterings = new int[] {metered}; + } + + final int[] defaultNetworks; + if (defaultNetwork == DEFAULT_NETWORK_ALL) { + defaultNetworks = + new int[] {DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES, DEFAULT_NETWORK_NO}; + } else { + defaultNetworks = new int[] {defaultNetwork}; + } + + for (int s : sets) { + for (int r : roamings) { + for (int m : meterings) { + for (int d : defaultNetworks) { + final int i = stats.findIndex(iface, uid, s, tag, m, r, d); + if (i != -1) { + entry.add(stats.getValues(i, null)); + } + } + } + } + } + + assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); + assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); + assertEquals("unexpected txBytes", txBytes, entry.txBytes); + assertEquals("unexpected txPackets", txPackets, entry.txPackets); + assertEquals("unexpected operations", operations, entry.operations); + } + + VpnInfo createVpnInfo(String[] underlyingIfaces) { + VpnInfo info = new VpnInfo(); + info.ownerUid = UID_VPN; + info.vpnIface = TUN_IFACE; + info.underlyingIfaces = underlyingIfaces; + return info; + } +} diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java index 9b4f49c6248f..8f90f13ce7ef 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java @@ -29,6 +29,7 @@ import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static com.android.server.net.NetworkStatsCollection.multiplySafe; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; @@ -43,7 +44,6 @@ import android.os.Process; import android.os.UserHandle; import android.telephony.SubscriptionPlan; import android.telephony.TelephonyManager; -import android.test.MoreAsserts; import android.text.format.DateUtils; import android.util.RecurrenceRule; @@ -240,11 +240,11 @@ public class NetworkStatsCollectionTest { 60 * MINUTE_IN_MILLIS, entry); // Verify the set of relevant UIDs for each access level. - MoreAsserts.assertEquals(new int[] { myUid }, + assertArrayEquals(new int[] { myUid }, collection.getRelevantUids(NetworkStatsAccess.Level.DEFAULT)); - MoreAsserts.assertEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser }, + assertArrayEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser }, collection.getRelevantUids(NetworkStatsAccess.Level.USER)); - MoreAsserts.assertEquals( + assertArrayEquals( new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser, uidInDifferentUser }, collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE)); diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java index 95bc7d92d538..7329474db70f 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java @@ -16,8 +16,11 @@ package com.android.server.net; +import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.DEFAULT_NETWORK_NO; +import static android.net.NetworkStats.METERED_ALL; import static android.net.NetworkStats.METERED_NO; +import static android.net.NetworkStats.ROAMING_ALL; import static android.net.NetworkStats.ROAMING_NO; import static android.net.NetworkStats.SET_ALL; import static android.net.NetworkStats.SET_DEFAULT; @@ -39,6 +42,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.tests.net.R; +import com.android.internal.net.VpnInfo; import libcore.io.IoUtils; import libcore.io.Streams; @@ -54,12 +58,12 @@ import java.io.FileWriter; import java.io.InputStream; import java.io.OutputStream; -/** - * Tests for {@link NetworkStatsFactory}. - */ +/** Tests for {@link NetworkStatsFactory}. */ @RunWith(AndroidJUnit4.class) @SmallTest -public class NetworkStatsFactoryTest { +public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { + private static final String CLAT_PREFIX = "v4-"; + private File mTestProc; private NetworkStatsFactory mFactory; @@ -75,6 +79,8 @@ public class NetworkStatsFactoryTest { // related to networkStatsFactory is compiled to a minimal native library and loaded here. System.loadLibrary("networkstatsfactorytestjni"); mFactory = new NetworkStatsFactory(mTestProc, false); + NetworkStatsFactory.updateVpnInfos(new VpnInfo[0]); + NetworkStatsFactory.clearStackedIfaces(); } @After @@ -99,6 +105,236 @@ public class NetworkStatsFactoryTest { } @Test + public void vpnRewriteTrafficThroughItself() throws Exception { + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + NetworkStatsFactory.updateVpnInfos(vpnInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // + // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED + // over VPN. + // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE + // over VPN. + // + // VPN UID rewrites packets read from TUN back to TUN, plus some of its own traffic + + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_rewrite_through_self); + + assertValues(tunStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 2000L, 200L, 1000L, 100L, 0); + assertValues(tunStats, TUN_IFACE, UID_BLUE, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 1000L, 100L, 500L, 50L, 0); + assertValues(tunStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, + DEFAULT_NETWORK_ALL, 0L, 0L, 1600L, 160L, 0); + + assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 260L, 26L); + } + + @Test + public void vpnWithClat() throws Exception { + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {CLAT_PREFIX + TEST_IFACE})}; + NetworkStatsFactory.updateVpnInfos(vpnInfos); + NetworkStatsFactory.noteStackedIface(CLAT_PREFIX + TEST_IFACE, TEST_IFACE); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED + // over VPN. + // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE + // over VPN. + // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over v4-WiFi, and clat + // added 20 bytes per packet of extra overhead + // + // For 1650 bytes sent over v4-WiFi, 4650 bytes were actually sent over WiFi, which is + // expected to be split as follows: + // UID_RED: 1000 bytes, 100 packets + // UID_BLUE: 500 bytes, 50 packets + // UID_VPN: 3150 bytes, 0 packets + // + // For 3300 bytes received over v4-WiFi, 9300 bytes were actually sent over WiFi, which is + // expected to be split as follows: + // UID_RED: 2000 bytes, 200 packets + // UID_BLUE: 1000 bytes, 100 packets + // UID_VPN: 6300 bytes, 0 packets + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_with_clat); + + assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_RED, 2000L, 200L, 1000, 100L); + assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); + assertValues(tunStats, CLAT_PREFIX + TEST_IFACE, UID_VPN, 6300L, 0L, 3150L, 0L); + } + + @Test + public void vpnWithOneUnderlyingIface() throws Exception { + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + NetworkStatsFactory.updateVpnInfos(vpnInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED + // over VPN. + // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE + // over VPN. + // VPN sent 1650 bytes (150 packets), and received 3300 (300 packets) over WiFi. + // Of 1650 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes + // attributed to UID_BLUE, and 150 bytes attributed to UID_VPN. + // Of 3300 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes + // attributed to UID_BLUE, and 300 bytes attributed to UID_VPN. + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying); + + assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 300L, 0L, 150L, 0L); + } + + @Test + public void vpnWithOneUnderlyingIfaceAndOwnTraffic() throws Exception { + // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + NetworkStatsFactory.updateVpnInfos(vpnInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent, and 2000 bytes (200 packets) were received by UID_RED + // over VPN. + // 500 bytes (50 packets) were sent, and 1000 bytes (100 packets) were received by UID_BLUE + // over VPN. + // Additionally, the VPN sends 6000 bytes (600 packets) of its own traffic into the tun + // interface (passing that traffic to the VPN endpoint), and receives 5000 bytes (500 + // packets) from it. Including overhead that is 6600/5500 bytes. + // VPN sent 8250 bytes (750 packets), and received 8800 (800 packets) over WiFi. + // Of 8250 bytes sent over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes + // attributed to UID_BLUE, and 6750 bytes attributed to UID_VPN. + // Of 8800 bytes received over WiFi, expect 2000 bytes attributed to UID_RED, 1000 bytes + // attributed to UID_BLUE, and 5800 bytes attributed to UID_VPN. + final NetworkStats tunStats = + parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_own_traffic); + + assertValues(tunStats, TEST_IFACE, UID_RED, 2000L, 200L, 1000L, 100L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 1000L, 100L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 5800L, 500L, 6750L, 600L); + } + + @Test + public void vpnWithOneUnderlyingIface_withCompression() throws Exception { + // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + NetworkStatsFactory.updateVpnInfos(vpnInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. + // 3000 bytes (300 packets) were sent/received by UID_BLUE over VPN. + // VPN sent/received 1000 bytes (100 packets) over WiFi. + // Of 1000 bytes over WiFi, expect 250 bytes attributed UID_RED and 750 bytes to UID_BLUE, + // with nothing attributed to UID_VPN for both rx/tx traffic. + final NetworkStats tunStats = + parseDetailedStats(R.raw.xt_qtaguid_vpn_one_underlying_compression); + + assertValues(tunStats, TEST_IFACE, UID_RED, 250L, 25L, 250L, 25L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 750L, 75L, 750L, 75L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L); + } + + @Test + public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception { + // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and + // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. + // Additionally, VPN is duplicating traffic across both WiFi and Cell. + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + NetworkStatsFactory.updateVpnInfos(vpnInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN. + // VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total). + // Of 8800 bytes over WiFi/Cell, expect: + // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE. + // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID. + final NetworkStats tunStats = + parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_duplication); + + assertValues(tunStats, TEST_IFACE, UID_RED, 500L, 50L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_BLUE, 500L, 50L, 500L, 50L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 1200L, 100L, 1200L, 100L); + assertValues(tunStats, TEST_IFACE2, UID_RED, 500L, 50L, 500L, 50L); + assertValues(tunStats, TEST_IFACE2, UID_BLUE, 500L, 50L, 500L, 50L); + assertValues(tunStats, TEST_IFACE2, UID_VPN, 1200L, 100L, 1200L, 100L); + } + + @Test + public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception { + // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and + // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. + // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell. + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + NetworkStatsFactory.updateVpnInfos(vpnInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent, and 500 bytes (50 packets) received by UID_RED over + // VPN. + // VPN sent 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell. + // And, it received 330 bytes (30 packets) over WiFi and 220 bytes (20 packets) over Cell. + // For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for sent (tx) + // traffic. For received (rx) traffic, expect 300 bytes over WiFi and 200 bytes over Cell. + // + // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for tx traffic. + // And, 30 bytes over WiFi and 20 bytes over Cell for rx traffic. + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split); + + assertValues(tunStats, TEST_IFACE, UID_RED, 300L, 30L, 600L, 60L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 30L, 0L, 60L, 0L); + assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 400L, 40L); + assertValues(tunStats, TEST_IFACE2, UID_VPN, 20L, 0L, 40L, 0L); + } + + @Test + public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception { + // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and + // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. + // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell. + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; + NetworkStatsFactory.updateVpnInfos(vpnInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface: + // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. + // VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell. + // For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both + // rx/tx. + // UID_VPN gets nothing attributed to it (avoiding negative stats). + final NetworkStats tunStats = + parseDetailedStats(R.raw.xt_qtaguid_vpn_two_underlying_split_compression); + + assertValues(tunStats, TEST_IFACE, UID_RED, 600L, 60L, 600L, 60L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L); + assertValues(tunStats, TEST_IFACE2, UID_RED, 200L, 20L, 200L, 20L); + assertValues(tunStats, TEST_IFACE2, UID_VPN, 0L, 0L, 0L, 0L); + } + + @Test + public void vpnWithIncorrectUnderlyingIface() throws Exception { + // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2), + // but has declared only WiFi (TEST_IFACE) in its underlying network set. + VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; + NetworkStatsFactory.updateVpnInfos(vpnInfos); + + // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption + // overhead per packet): + // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. + // VPN sent/received 1100 bytes (100 packets) over Cell. + // Of 1100 bytes over Cell, expect all of it attributed to UID_VPN for both rx/tx traffic. + final NetworkStats tunStats = parseDetailedStats(R.raw.xt_qtaguid_vpn_incorrect_iface); + + assertValues(tunStats, TEST_IFACE, UID_RED, 0L, 0L, 0L, 0L); + assertValues(tunStats, TEST_IFACE, UID_VPN, 0L, 0L, 0L, 0L); + assertValues(tunStats, TEST_IFACE2, UID_RED, 0L, 0L, 0L, 0L); + assertValues(tunStats, TEST_IFACE2, UID_VPN, 1100L, 100L, 1100L, 100L); + } + + @Test public void testKernelTags() throws Exception { assertEquals(0, kernelToTag("0x0000000000000000")); assertEquals(0x32, kernelToTag("0x0000003200000000")); @@ -146,7 +382,7 @@ public class NetworkStatsFactoryTest { } @Test - public void testDoubleClatAccounting() throws Exception { + public void testDoubleClatAccountingSimple() throws Exception { NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0"); // xt_qtaguid_with_clat_simple is a synthetic file that simulates @@ -154,12 +390,17 @@ public class NetworkStatsFactoryTest { // - 41 sent 464xlat packets of size 100 bytes // - no other traffic on base interface for root uid. NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat_simple); - assertEquals(4, stats.size()); + assertEquals(3, stats.size()); assertStatsEntry(stats, "v4-wlan0", 10060, SET_DEFAULT, 0x0, 46860L, 4920L); assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 0L, 0L); + } + + @Test + public void testDoubleClatAccounting() throws Exception { + NetworkStatsFactory.noteStackedIface("v4-wlan0", "wlan0"); - stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat); + NetworkStats stats = parseDetailedStats(R.raw.xt_qtaguid_with_clat); assertEquals(42, stats.size()); assertStatsEntry(stats, "v4-wlan0", 0, SET_DEFAULT, 0x0, 356L, 276L); @@ -272,11 +513,19 @@ public class NetworkStatsFactoryTest { private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, int tag, long rxBytes, long rxPackets, long txBytes, long txPackets) { - final int i = stats.findIndex(iface, uid, set, tag, METERED_NO, ROAMING_NO, - DEFAULT_NETWORK_NO); + assertStatsEntry(stats, iface, uid, set, tag, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, + rxBytes, rxPackets, txBytes, txPackets); + } + + private static void assertStatsEntry(NetworkStats stats, String iface, int uid, int set, + int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, + long txBytes, long txPackets) { + final int i = stats.findIndex(iface, uid, set, tag, metered, roaming, defaultNetwork); + if (i < 0) { - fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d)", - iface, uid, set, tag)); + fail(String.format("no NetworkStats for (iface: %s, uid: %d, set: %d, tag: %d, metered:" + + " %d, roaming: %d, defaultNetwork: %d)", + iface, uid, set, tag, metered, roaming, defaultNetwork)); } final NetworkStats.Entry entry = stats.getValues(i, null); assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java index 43a38039c0da..c0f9dc14869f 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java @@ -28,8 +28,6 @@ import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.TrafficStats.MB_IN_BYTES; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; -import static com.android.internal.util.TestUtils.waitForIdleHandler; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; @@ -54,8 +52,8 @@ import android.util.ArrayMap; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.net.VpnInfo; import com.android.server.net.NetworkStatsServiceTest.LatchedHandler; +import com.android.testutils.HandlerUtilsKt; import org.junit.Before; import org.junit.Test; @@ -94,8 +92,6 @@ public class NetworkStatsObserversTest { private static final long BASE_BYTES = 7 * MB_IN_BYTES; private static final int INVALID_TYPE = -1; - private static final VpnInfo[] VPN_INFO = new VpnInfo[0]; - private long mElapsedRealtime; private HandlerThread mObserverHandlerThread; @@ -248,8 +244,7 @@ public class NetworkStatsObserversTest { NetworkStats uidSnapshot = null; mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); waitForObserverToIdle(); } @@ -272,15 +267,13 @@ public class NetworkStatsObserversTest { .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); NetworkStats uidSnapshot = null; mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta xtSnapshot = new NetworkStats(TEST_START, 1 /* initialSize */) .addIfaceValues(TEST_IFACE, BASE_BYTES + 1024L, 10L, BASE_BYTES + 2048L, 20L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); waitForObserverToIdle(); } @@ -304,16 +297,14 @@ public class NetworkStatsObserversTest { .addIfaceValues(TEST_IFACE, BASE_BYTES, 8L, BASE_BYTES, 16L); NetworkStats uidSnapshot = null; mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta xtSnapshot = new NetworkStats(TEST_START + MINUTE_IN_MILLIS, 1 /* initialSize */) .addIfaceValues(TEST_IFACE, BASE_BYTES + THRESHOLD_BYTES, 12L, BASE_BYTES + THRESHOLD_BYTES, 22L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); waitForObserverToIdle(); assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); } @@ -338,8 +329,7 @@ public class NetworkStatsObserversTest { .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) @@ -347,8 +337,7 @@ public class NetworkStatsObserversTest { DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); waitForObserverToIdle(); assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); } @@ -373,8 +362,7 @@ public class NetworkStatsObserversTest { .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) @@ -382,8 +370,7 @@ public class NetworkStatsObserversTest { DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); waitForObserverToIdle(); } @@ -407,8 +394,7 @@ public class NetworkStatsObserversTest { .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) @@ -416,8 +402,7 @@ public class NetworkStatsObserversTest { DEFAULT_NETWORK_YES, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); waitForObserverToIdle(); assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType); } @@ -442,8 +427,7 @@ public class NetworkStatsObserversTest { .addValues(TEST_IFACE, UID_ANOTHER_USER, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, BASE_BYTES, 2L, BASE_BYTES, 2L, 0L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); // Delta uidSnapshot = new NetworkStats(TEST_START + 2 * MINUTE_IN_MILLIS, 2 /* initialSize */) @@ -451,13 +435,12 @@ public class NetworkStatsObserversTest { ROAMING_NO, DEFAULT_NETWORK_NO, BASE_BYTES + THRESHOLD_BYTES, 2L, BASE_BYTES + THRESHOLD_BYTES, 2L, 0L); mStatsObservers.updateStats( - xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, - VPN_INFO, TEST_START); + xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces, TEST_START); waitForObserverToIdle(); } private void waitForObserverToIdle() { - waitForIdleHandler(mObserverHandlerThread, WAIT_TIMEOUT_MS); - waitForIdleHandler(mHandler, WAIT_TIMEOUT_MS); + HandlerUtilsKt.waitForIdle(mObserverHandlerThread, WAIT_TIMEOUT_MS); + HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT_MS); } } diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java index e35c34affd1a..1682dd17c4a3 100644 --- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java @@ -23,7 +23,6 @@ import static android.net.ConnectivityManager.TYPE_VPN; import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.TYPE_WIMAX; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; -import static android.net.NetworkStats.DEFAULT_NETWORK_NO; import static android.net.NetworkStats.DEFAULT_NETWORK_YES; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.INTERFACES_ALL; @@ -42,7 +41,6 @@ import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; import static android.net.NetworkStatsHistory.FIELD_ALL; import static android.net.NetworkTemplate.buildTemplateMobileAll; -import static android.net.NetworkTemplate.buildTemplateMobileWildcard; import static android.net.NetworkTemplate.buildTemplateWifiWildcard; import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.UID_REMOVED; @@ -52,17 +50,14 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; -import static com.android.internal.util.TestUtils.waitForIdleHandler; import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL; 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -100,11 +95,11 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.net.VpnInfo; import com.android.internal.util.ArrayUtils; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.server.net.NetworkStatsService.NetworkStatsSettings; import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config; +import com.android.testutils.HandlerUtilsKt; import libcore.io.IoUtils; @@ -130,13 +125,9 @@ import java.util.Objects; */ @RunWith(AndroidJUnit4.class) @SmallTest -public class NetworkStatsServiceTest { +public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private static final String TAG = "NetworkStatsServiceTest"; - private static final String TEST_IFACE = "test0"; - private static final String TEST_IFACE2 = "test1"; - private static final String TUN_IFACE = "test_nss_tun0"; - private static final long TEST_START = 1194220800000L; private static final String IMSI_1 = "310004"; @@ -147,11 +138,6 @@ public class NetworkStatsServiceTest { private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); - private static final int UID_RED = 1001; - private static final int UID_BLUE = 1002; - private static final int UID_GREEN = 1003; - private static final int UID_VPN = 1004; - private static final Network WIFI_NETWORK = new Network(100); private static final Network MOBILE_NETWORK = new Network(101); private static final Network VPN_NETWORK = new Network(102); @@ -217,12 +203,9 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectSystemReady(); - assertNull(mService.getTunAdjustedStats()); mService.systemReady(); - // Verify that system ready fetches realtime stats and initializes tun adjusted stats. + // Verify that system ready fetches realtime stats verify(mNetManager).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL); - assertNotNull("failed to initialize TUN adjusted stats", mService.getTunAdjustedStats()); - assertEquals(0, mService.getTunAdjustedStats().size()); mSession = mService.openSession(); assertNotNull("openSession() failed", mSession); @@ -258,7 +241,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -302,7 +285,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -376,8 +359,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); - + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); // modify some number on wifi, and trigger poll event incrementCurrentTime(2 * HOUR_IN_MILLIS); @@ -418,8 +400,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); - + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states)); // create some traffic on first network incrementCurrentTime(HOUR_IN_MILLIS); @@ -454,7 +435,7 @@ public class NetworkStatsServiceTest { .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L)); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states)); forcePollAndWaitForIdle(); @@ -494,8 +475,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); - + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); // create some traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -553,8 +533,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); - + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states)); // create some traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -581,7 +560,7 @@ public class NetworkStatsServiceTest { .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states)); forcePollAndWaitForIdle(); @@ -611,8 +590,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); - + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); // create some traffic for two apps incrementCurrentTime(HOUR_IN_MILLIS); @@ -670,7 +648,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); NetworkStats.Entry entry1 = new NetworkStats.Entry( TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L); @@ -714,7 +692,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); NetworkStats.Entry uidStats = new NetworkStats.Entry( TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xF00D, 1024L, 8L, 512L, 4L, 0L); @@ -742,8 +720,12 @@ public class NetworkStatsServiceTest { // mNetManager#getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL) has following invocations: // 1) NetworkStatsService#systemReady from #setUp. // 2) mService#forceUpdateIfaces in the test above. - // 3) Finally, mService#getDetailedUidStats. - verify(mNetManager, times(3)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL); + // + // Additionally, we should have one call from the above call to mService#getDetailedUidStats + // with the augmented ifaceFilter + verify(mNetManager, times(2)).getNetworkStatsUidDetail(UID_ALL, INTERFACES_ALL); + verify(mNetManager, times(1)).getNetworkStatsUidDetail( + eq(UID_ALL), eq(NetworkStatsFactory.augmentWithStackedInterfaces(ifaceFilter))); assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), TEST_IFACE)); assertTrue(ArrayUtils.contains(stats.getUniqueIfaces(), stackedIface)); assertEquals(2, stats.size()); @@ -760,8 +742,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); - + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); // create some initial traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -818,8 +799,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); - + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); // create some initial traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -859,8 +839,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); - + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states)); // Create some traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -898,8 +877,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_MOBILE, new VpnInfo[0], states, getActiveIface(states)); - + mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states)); // create some tethering traffic incrementCurrentTime(HOUR_IN_MILLIS); @@ -931,369 +909,6 @@ public class NetworkStatsServiceTest { } @Test - public void vpnWithOneUnderlyingIface() throws Exception { - // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - expectDefaultSettings(); - NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()}; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. - // 500 bytes (50 packets) were sent/received by UID_BLUE over VPN. - // VPN sent/received 1650 bytes (150 packets) over WiFi. - // Of 1650 bytes over WiFi, expect 1000 bytes attributed to UID_RED, 500 bytes attributed to - // UID_BLUE, and 150 bytes attributed to UID_VPN for both rx/tx traffic. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L) - .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 500L, 50L, 500L, 50L, 1L) - // VPN received 1650 bytes over WiFi in background (SET_DEFAULT). - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1650L, 150L, 0L, 0L, 1L) - // VPN sent 1650 bytes over WiFi in foreground (SET_FOREGROUND). - .addValues(TEST_IFACE, UID_VPN, SET_FOREGROUND, TAG_NONE, 0L, 0L, 1650L, 150L, 1L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 1000L, 100L, 1000L, 100L, 1); - assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1); - assertUidTotal(sTemplateWifi, UID_VPN, 150L, 0L, 150L, 0L, 2); - } - - @Test - public void vpnWithOneUnderlyingIface_withCompression() throws Exception { - // WiFi network is connected and VPN is using WiFi (which has TEST_IFACE). - expectDefaultSettings(); - NetworkState[] networkStates = new NetworkState[] {buildWifiState(), buildVpnState()}; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. - // 3000 bytes (300 packets) were sent/received by UID_BLUE over VPN. - // VPN sent/received 1000 bytes (100 packets) over WiFi. - // Of 1000 bytes over WiFi, expect 250 bytes attributed UID_RED and 750 bytes to UID_BLUE, - // with nothing attributed to UID_VPN for both rx/tx traffic. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L) - .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 3000L, 300L, 3000L, 300L, 1L) - .addValues( - TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 0L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 250L, 25L, 250L, 25L, 0); - assertUidTotal(sTemplateWifi, UID_BLUE, 750L, 75L, 750L, 75L, 0); - assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0); - } - - @Test - public void vpnWithTwoUnderlyingIfaces_packetDuplication() throws Exception { - // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and - // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. - // Additionally, VPN is duplicating traffic across both WiFi and Cell. - expectDefaultSettings(); - NetworkState[] networkStates = - new NetworkState[] { - buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() - }; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent/received by UID_RED and UID_BLUE over VPN. - // VPN sent/received 4400 bytes (400 packets) over both WiFi and Cell (8800 bytes in total). - // Of 8800 bytes over WiFi/Cell, expect: - // - 500 bytes rx/tx each over WiFi/Cell attributed to both UID_RED and UID_BLUE. - // - 1200 bytes rx/tx each over WiFi/Cell for VPN_UID. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L) - .addValues(TUN_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L) - .addValues( - TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 2200L, 200L, 2L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 500L, 50L, 500L, 50L, 1); - assertUidTotal(sTemplateWifi, UID_BLUE, 500L, 50L, 500L, 50L, 1); - assertUidTotal(sTemplateWifi, UID_VPN, 1200L, 100L, 1200L, 100L, 2); - - assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 500L, 50L, 500L, 50L, 1); - assertUidTotal(buildTemplateMobileWildcard(), UID_BLUE, 500L, 50L, 500L, 50L, 1); - assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 1200L, 100L, 1200L, 100L, 2); - } - - @Test - public void vpnWithTwoUnderlyingIfaces_splitTraffic() throws Exception { - // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and - // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. - // Additionally, VPN is arbitrarily splitting traffic across WiFi and Cell. - expectDefaultSettings(); - NetworkState[] networkStates = - new NetworkState[] { - buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() - }; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. - // VPN sent/received 660 bytes (60 packets) over WiFi and 440 bytes (40 packets) over Cell. - // For UID_RED, expect 600 bytes attributed over WiFi and 400 bytes over Cell for both - // rx/tx. - // For UID_VPN, expect 60 bytes attributed over WiFi and 40 bytes over Cell for both rx/tx. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 2L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 660L, 60L, 660L, 60L, 1L) - .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 440L, 40L, 440L, 40L, 1L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 600L, 60L, 600L, 60L, 1); - assertUidTotal(sTemplateWifi, UID_VPN, 60L, 0L, 60L, 0L, 1); - - assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 400L, 40L, 400L, 40L, 1); - assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 40L, 0L, 40L, 0L, 1); - } - - @Test - public void vpnWithTwoUnderlyingIfaces_splitTrafficWithCompression() throws Exception { - // WiFi and Cell networks are connected and VPN is using WiFi (which has TEST_IFACE) and - // Cell (which has TEST_IFACE2) and has declared both of them in its underlying network set. - // Additionally, VPN is arbitrarily splitting compressed traffic across WiFi and Cell. - expectDefaultSettings(); - NetworkState[] networkStates = - new NetworkState[] { - buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() - }; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE, TEST_IFACE2})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface: - // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. - // VPN sent/received 600 bytes (60 packets) over WiFi and 200 bytes (20 packets) over Cell. - // For UID_RED, expect 600 bytes attributed over WiFi and 200 bytes over Cell for both - // rx/tx. - // UID_VPN gets nothing attributed to it (avoiding negative stats). - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 4) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 600L, 60L, 600L, 60L, 0L) - .addValues(TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 200L, 20L, 200L, 20L, 0L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 600L, 60L, 600L, 60L, 0); - assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0); - - assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 200L, 20L, 200L, 20L, 0); - assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 0L, 0L, 0L, 0L, 0); - } - - @Test - public void vpnWithIncorrectUnderlyingIface() throws Exception { - // WiFi and Cell networks are connected and VPN is using Cell (which has TEST_IFACE2), - // but has declared only WiFi (TEST_IFACE) in its underlying network set. - expectDefaultSettings(); - NetworkState[] networkStates = - new NetworkState[] { - buildWifiState(), buildMobile4gState(TEST_IFACE2), buildVpnState() - }; - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectNetworkStatsUidDetail(buildEmptyStats()); - expectBandwidthControlCheck(); - - mService.forceUpdateIfaces( - new Network[] {WIFI_NETWORK, VPN_NETWORK}, - vpnInfos, - networkStates, - getActiveIface(networkStates)); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were sent/received by UID_RED over VPN. - // VPN sent/received 1100 bytes (100 packets) over Cell. - // Of 1100 bytes over Cell, expect all of it attributed to UID_VPN for both rx/tx traffic. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 1000L, 100L, 1L) - .addValues( - TEST_IFACE2, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 1100L, 100L, 1L)); - - forcePollAndWaitForIdle(); - - assertUidTotal(sTemplateWifi, UID_RED, 0L, 0L, 0L, 0L, 0); - assertUidTotal(sTemplateWifi, UID_VPN, 0L, 0L, 0L, 0L, 0); - assertUidTotal(buildTemplateMobileWildcard(), UID_RED, 0L, 0L, 0L, 0L, 0); - assertUidTotal(buildTemplateMobileWildcard(), UID_VPN, 1100L, 100L, 1100L, 100L, 1); - } - - @Test - public void recordSnapshot_migratesTunTrafficAndUpdatesTunAdjustedStats() throws Exception { - assertEquals(0, mService.getTunAdjustedStats().size()); - // VPN using WiFi (TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectBandwidthControlCheck(); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were downloaded by UID_RED over VPN. - // VPN received 1100 bytes (100 packets) over WiFi. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L)); - - // this should lead to NSS#recordSnapshotLocked - mService.forceUpdateIfaces( - new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */); - - // Verify TUN adjusted stats have traffic migrated correctly. - // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100 - // bytes attributed to UID_VPN. - NetworkStats tunAdjStats = mService.getTunAdjustedStats(); - assertValues( - tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); - assertValues( - tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0); - } - - @Test - public void getDetailedUidStats_migratesTunTrafficAndUpdatesTunAdjustedStats() - throws Exception { - assertEquals(0, mService.getTunAdjustedStats().size()); - // VPN using WiFi (TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectBandwidthControlCheck(); - mService.forceUpdateIfaces( - new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were downloaded by UID_RED over VPN. - // VPN received 1100 bytes (100 packets) over WiFi. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L)); - - mService.getDetailedUidStats(INTERFACES_ALL); - - // Verify internally maintained TUN adjusted stats - NetworkStats tunAdjStats = mService.getTunAdjustedStats(); - // Verify stats for TEST_IFACE (WiFi): - // Of 1100 bytes VPN received over WiFi, expect 1000 bytes attributed to UID_RED and 100 - // bytes attributed to UID_VPN. - assertValues( - tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); - assertValues( - tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0); - // Verify stats for TUN_IFACE; only UID_RED should have usage on it. - assertValues( - tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); - assertValues( - tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0); - - // lets assume that since last time, VPN received another 1100 bytes (same assumptions as - // before i.e. UID_RED downloaded another 1000 bytes). - incrementCurrentTime(HOUR_IN_MILLIS); - // Note - NetworkStatsFactory returns counters that are monotonically increasing. - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 2000L, 200L, 0L, 0L, 0L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 2200L, 200L, 0L, 0L, 0L)); - - mService.getDetailedUidStats(INTERFACES_ALL); - - tunAdjStats = mService.getTunAdjustedStats(); - // verify TEST_IFACE stats: - assertValues( - tunAdjStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0); - assertValues( - tunAdjStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 200L, 0L, 0L, 0L, 0); - // verify TUN_IFACE stats: - assertValues( - tunAdjStats, TUN_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 2000L, 200L, 0L, 0L, 0); - assertValues( - tunAdjStats, TUN_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 0L, 0L, 0L, 0L, 0); - } - - @Test - public void getDetailedUidStats_returnsCorrectStatsWithVpnRunning() throws Exception { - // VPN using WiFi (TEST_IFACE). - VpnInfo[] vpnInfos = new VpnInfo[] {createVpnInfo(new String[] {TEST_IFACE})}; - expectBandwidthControlCheck(); - mService.forceUpdateIfaces( - new Network[0], vpnInfos, new NetworkState[0], null /* activeIface */); - // create some traffic (assume 10 bytes of MTU for VPN interface and 1 byte encryption - // overhead per packet): - // 1000 bytes (100 packets) were downloaded by UID_RED over VPN. - // VPN received 1100 bytes (100 packets) over WiFi. - incrementCurrentTime(HOUR_IN_MILLIS); - expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 2) - .addValues(TUN_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1000L, 100L, 0L, 0L, 0L) - .addValues(TEST_IFACE, UID_VPN, SET_DEFAULT, TAG_NONE, 1100L, 100L, 0L, 0L, 0L)); - - // Query realtime stats for TEST_IFACE. - NetworkStats queriedStats = - mService.getDetailedUidStats(new String[] {TEST_IFACE}); - - assertEquals(HOUR_IN_MILLIS, queriedStats.getElapsedRealtime()); - // verify that returned stats are only for TEST_IFACE and VPN traffic is migrated correctly. - assertEquals(new String[] {TEST_IFACE}, queriedStats.getUniqueIfaces()); - assertValues( - queriedStats, TEST_IFACE, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 1000L, 100L, 0L, 0L, 0); - assertValues( - queriedStats, TEST_IFACE, UID_VPN, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL, - DEFAULT_NETWORK_ALL, 100L, 0L, 0L, 0L, 0); - } - - @Test public void testRegisterUsageCallback() throws Exception { // pretend that wifi network comes online; service should ask about full // network state, and poll any existing interfaces before updating. @@ -1303,7 +918,7 @@ public class NetworkStatsServiceTest { expectNetworkStatsUidDetail(buildEmptyStats()); expectBandwidthControlCheck(); - mService.forceUpdateIfaces(NETWORKS_WIFI, new VpnInfo[0], states, getActiveIface(states)); + mService.forceUpdateIfaces(NETWORKS_WIFI, states, getActiveIface(states)); // verify service has empty history for wifi assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); @@ -1321,8 +936,6 @@ public class NetworkStatsServiceTest { expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); - - // Register and verify request and that binder was called DataUsageRequest request = mService.registerUsageCallback(mServiceContext.getOpPackageName(), inputRequest, @@ -1334,14 +947,11 @@ public class NetworkStatsServiceTest { // Send dummy message to make sure that any previous message has been handled mHandler.sendMessage(mHandler.obtainMessage(-1)); - waitForIdleHandler(mHandler, WAIT_TIMEOUT); - - + HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT); // Make sure that the caller binder gets connected verify(mBinder).linkToDeath(any(IBinder.DeathRecipient.class), anyInt()); - // modify some number on wifi, and trigger poll event // not enough traffic to call data usage callback incrementCurrentTime(HOUR_IN_MILLIS); @@ -1528,59 +1138,6 @@ public class NetworkStatsServiceTest { } } - private static void assertValues(NetworkStats stats, String iface, int uid, int set, - int tag, int metered, int roaming, int defaultNetwork, long rxBytes, long rxPackets, - long txBytes, long txPackets, int operations) { - final NetworkStats.Entry entry = new NetworkStats.Entry(); - final int[] sets; - if (set == SET_ALL) { - sets = new int[] { SET_ALL, SET_DEFAULT, SET_FOREGROUND }; - } else { - sets = new int[] { set }; - } - - final int[] roamings; - if (roaming == ROAMING_ALL) { - roamings = new int[] { ROAMING_ALL, ROAMING_YES, ROAMING_NO }; - } else { - roamings = new int[] { roaming }; - } - - final int[] meterings; - if (metered == METERED_ALL) { - meterings = new int[] { METERED_ALL, METERED_YES, METERED_NO }; - } else { - meterings = new int[] { metered }; - } - - final int[] defaultNetworks; - if (defaultNetwork == DEFAULT_NETWORK_ALL) { - defaultNetworks = new int[] { DEFAULT_NETWORK_ALL, DEFAULT_NETWORK_YES, - DEFAULT_NETWORK_NO }; - } else { - defaultNetworks = new int[] { defaultNetwork }; - } - - for (int s : sets) { - for (int r : roamings) { - for (int m : meterings) { - for (int d : defaultNetworks) { - final int i = stats.findIndex(iface, uid, s, tag, m, r, d); - if (i != -1) { - entry.add(stats.getValues(i, null)); - } - } - } - } - } - - assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes); - assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets); - assertEquals("unexpected txBytes", txBytes, entry.txBytes); - assertEquals("unexpected txPackets", txPackets, entry.txPackets); - assertEquals("unexpected operations", operations, entry.operations); - } - private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes, long rxPackets, long txBytes, long txPackets, int operations) { final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null); @@ -1646,14 +1203,6 @@ public class NetworkStatsServiceTest { return new NetworkState(info, prop, new NetworkCapabilities(), VPN_NETWORK, null, null); } - private static VpnInfo createVpnInfo(String[] underlyingIfaces) { - VpnInfo info = new VpnInfo(); - info.ownerUid = UID_VPN; - info.vpnIface = TUN_IFACE; - info.underlyingIfaces = underlyingIfaces; - return info; - } - private long getElapsedRealtime() { return mElapsedRealtime; } @@ -1674,7 +1223,7 @@ public class NetworkStatsServiceTest { mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL)); // Send dummy message to make sure that any previous message has been handled mHandler.sendMessage(mHandler.obtainMessage(-1)); - waitForIdleHandler(mHandler, WAIT_TIMEOUT); + HandlerUtilsKt.waitForIdle(mHandler, WAIT_TIMEOUT); } static class LatchedHandler extends Handler { diff --git a/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface b/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface new file mode 100644 index 000000000000..fc92715253ed --- /dev/null +++ b/tests/net/res/raw/xt_qtaguid_vpn_incorrect_iface @@ -0,0 +1,3 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test1 0x0 1004 0 1100 100 1100 100 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying new file mode 100644 index 000000000000..1ef18894b669 --- /dev/null +++ b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying @@ -0,0 +1,5 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 +4 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +5 test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression new file mode 100644 index 000000000000..6d6bf550bbfa --- /dev/null +++ b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_compression @@ -0,0 +1,4 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 3000 300 3000 300 0 0 0 0 0 0 0 0 0 0 0 0 +4 test0 0x0 1004 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic new file mode 100644 index 000000000000..2c2e5d2555f6 --- /dev/null +++ b/tests/net/res/raw/xt_qtaguid_vpn_one_underlying_own_traffic @@ -0,0 +1,6 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 +4 test_nss_tun0 0x0 1004 0 5000 500 6000 600 0 0 0 0 0 0 0 0 0 0 0 0 +5 test0 0x0 1004 0 8800 800 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 test0 0x0 1004 1 0 0 8250 750 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self b/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self new file mode 100644 index 000000000000..afcdd7199026 --- /dev/null +++ b/tests/net/res/raw/xt_qtaguid_vpn_rewrite_through_self @@ -0,0 +1,6 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 +4 test_nss_tun0 0x0 1004 0 0 0 1600 160 0 0 0 0 0 0 0 0 0 0 0 0 +5 test0 0x0 1004 1 0 0 1760 176 0 0 0 0 0 0 0 0 0 0 0 0 +6 test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication new file mode 100644 index 000000000000..d7c7eb9f4ae8 --- /dev/null +++ b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_duplication @@ -0,0 +1,5 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +4 test0 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0 +5 test1 0x0 1004 0 2200 200 2200 200 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split new file mode 100644 index 000000000000..38a3dce4a834 --- /dev/null +++ b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split @@ -0,0 +1,4 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 500 50 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test0 0x0 1004 0 330 30 660 60 0 0 0 0 0 0 0 0 0 0 0 0 +4 test1 0x0 1004 0 220 20 440 40 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression new file mode 100644 index 000000000000..d35244b3b4f2 --- /dev/null +++ b/tests/net/res/raw/xt_qtaguid_vpn_two_underlying_split_compression @@ -0,0 +1,4 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 1000 100 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test0 0x0 1004 0 600 60 600 60 0 0 0 0 0 0 0 0 0 0 0 0 +4 test1 0x0 1004 0 200 20 200 20 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_vpn_with_clat b/tests/net/res/raw/xt_qtaguid_vpn_with_clat new file mode 100644 index 000000000000..0d893d515ca2 --- /dev/null +++ b/tests/net/res/raw/xt_qtaguid_vpn_with_clat @@ -0,0 +1,8 @@ +idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets +2 test_nss_tun0 0x0 1001 0 2000 200 1000 100 0 0 0 0 0 0 0 0 0 0 0 0 +3 test_nss_tun0 0x0 1002 0 1000 100 500 50 0 0 0 0 0 0 0 0 0 0 0 0 +4 v4-test0 0x0 1004 0 3300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +5 v4-test0 0x0 1004 1 0 0 1650 150 0 0 0 0 0 0 0 0 0 0 0 0 +6 test0 0x0 0 0 9300 300 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +7 test0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +8 test0 0x0 1029 0 0 0 4650 150 0 0 0 0 0 0 0 0 0 0 0 0
\ No newline at end of file diff --git a/tests/net/res/raw/xt_qtaguid_with_clat_simple b/tests/net/res/raw/xt_qtaguid_with_clat_simple index 8c132e7e0a0e..b37fae6d2a3d 100644 --- a/tests/net/res/raw/xt_qtaguid_with_clat_simple +++ b/tests/net/res/raw/xt_qtaguid_with_clat_simple @@ -2,5 +2,4 @@ idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packe 2 v4-wlan0 0x0 10060 0 42600 213 4100 41 42600 213 0 0 0 0 4100 41 0 0 0 0 3 v4-wlan0 0x0 10060 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4 wlan0 0x0 0 0 46860 213 0 0 46860 213 0 0 0 0 0 0 0 0 0 0 -5 wlan0 0x0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -6 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0 +5 wlan0 0x0 1029 0 0 0 4920 41 0 0 0 0 0 0 4920 41 0 0 0 0 diff --git a/tests/net/util/java/com/android/internal/util/ParcelableTestUtil.java b/tests/net/util/java/com/android/internal/util/ParcelableTestUtil.java deleted file mode 100644 index 87537b93887b..000000000000 --- a/tests/net/util/java/com/android/internal/util/ParcelableTestUtil.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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.util; - -import static org.junit.Assert.assertEquals; - -import java.lang.reflect.Modifier; -import java.util.Arrays; - -/** - * Utility classes to write tests for stable AIDL parceling/unparceling - */ -public final class ParcelableTestUtil { - - /** - * Verifies that the number of nonstatic fields in a class equals a given count. - * - * <p>This assertion serves as a reminder to update test code around it if fields are added - * after the test is written. - * @param count Expected number of nonstatic fields in the class. - * @param clazz Class to test. - */ - public static <T> void assertFieldCountEquals(int count, Class<T> clazz) { - assertEquals(count, Arrays.stream(clazz.getDeclaredFields()) - .filter(f -> !Modifier.isStatic(f.getModifiers())).count()); - } -} diff --git a/tests/net/util/java/com/android/internal/util/TestUtils.java b/tests/net/util/java/com/android/internal/util/TestUtils.java deleted file mode 100644 index a99cd4716f9a..000000000000 --- a/tests/net/util/java/com/android/internal/util/TestUtils.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2017 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.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import android.os.ConditionVariable; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Parcel; -import android.os.Parcelable; - -import androidx.annotation.NonNull; - -import java.util.concurrent.Executor; - -public final class TestUtils { - private TestUtils() { } - - /** - * Block until the given Handler thread becomes idle, or until timeoutMs has passed. - */ - public static void waitForIdleHandler(HandlerThread handlerThread, long timeoutMs) { - waitForIdleLooper(handlerThread.getLooper(), timeoutMs); - } - - /** - * Block until the given Looper becomes idle, or until timeoutMs has passed. - */ - public static void waitForIdleLooper(Looper looper, long timeoutMs) { - waitForIdleHandler(new Handler(looper), timeoutMs); - } - - /** - * Block until the given Handler becomes idle, or until timeoutMs has passed. - */ - public static void waitForIdleHandler(Handler handler, long timeoutMs) { - final ConditionVariable cv = new ConditionVariable(); - handler.post(() -> cv.open()); - if (!cv.block(timeoutMs)) { - fail(handler.toString() + " did not become idle after " + timeoutMs + " ms"); - } - } - - /** - * Block until the given Serial Executor becomes idle, or until timeoutMs has passed. - */ - public static void waitForIdleSerialExecutor(@NonNull Executor executor, long timeoutMs) { - final ConditionVariable cv = new ConditionVariable(); - executor.execute(() -> cv.open()); - if (!cv.block(timeoutMs)) { - fail(executor.toString() + " did not become idle after " + timeoutMs + " ms"); - } - } - - /** - * Return a new instance of {@code T} after being parceled then unparceled. - */ - public static <T extends Parcelable> T parcelingRoundTrip(T source) { - final Parcelable.Creator<T> creator; - try { - creator = (Parcelable.Creator<T>) source.getClass().getField("CREATOR").get(null); - } catch (IllegalAccessException | NoSuchFieldException e) { - fail("Missing CREATOR field: " + e.getMessage()); - return null; - } - Parcel p = Parcel.obtain(); - source.writeToParcel(p, /* flags */ 0); - p.setDataPosition(0); - final byte[] marshalled = p.marshall(); - p = Parcel.obtain(); - p.unmarshall(marshalled, 0, marshalled.length); - p.setDataPosition(0); - return creator.createFromParcel(p); - } - - /** - * Assert that after being parceled then unparceled, {@code source} is equal to the original - * object. - */ - public static <T extends Parcelable> void assertParcelingIsLossless(T source) { - assertEquals(source, parcelingRoundTrip(source)); - } -} diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp index 550edd344aa3..77ecf6210a56 100644 --- a/tools/aapt2/io/ZipArchive.cpp +++ b/tools/aapt2/io/ZipArchive.cpp @@ -115,12 +115,9 @@ std::unique_ptr<ZipFileCollection> ZipFileCollection::Create( using IterationEnder = std::unique_ptr<void, decltype(EndIteration)*>; IterationEnder iteration_ender(cookie, EndIteration); - ZipString zip_entry_name; + std::string zip_entry_path; ZipEntry zip_data; - while ((result = Next(cookie, &zip_data, &zip_entry_name)) == 0) { - std::string zip_entry_path = - std::string(reinterpret_cast<const char*>(zip_entry_name.name), - zip_entry_name.name_length); + while ((result = Next(cookie, &zip_data, &zip_entry_path)) == 0) { std::string nested_path = path.to_string() + "@" + zip_entry_path; std::unique_ptr<IFile> file = util::make_unique<ZipFile>(collection->handle_, zip_data, Source(nested_path)); diff --git a/tools/lock_agent/Android.bp b/tools/lock_agent/Android.bp index c54e5b57ccc5..2125ad682bb6 100644 --- a/tools/lock_agent/Android.bp +++ b/tools/lock_agent/Android.bp @@ -51,11 +51,22 @@ java_library { installable: true, } +cc_binary { + name: "lockagent_crasher", + srcs: ["crasher.cpp"], + static_libs: ["libbase_ndk"], + shared_libs: ["liblog"], + sdk_version: "current", + stl: "c++_static", + compile_multilib: "first", +} + sh_binary { name: "start_with_lockagent", src: "start_with_lockagent.sh", required: [ "liblockagent", "lockagent", + "lockagent_crasher", ], } diff --git a/packages/ExtServices/src/android/ext/services/Version.java b/tools/lock_agent/crasher.cpp index 026cccd37334..2829d6d9447c 100644 --- a/packages/ExtServices/src/android/ext/services/Version.java +++ b/tools/lock_agent/crasher.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,23 +14,17 @@ * limitations under the License. */ -package android.ext.services; +#include <android-base/logging.h> -/** - * Class that provides the version of the library. - */ -public final class Version { - - private Version() { - /* do nothing - hide constructor */ - } - - /** - * Gets the version of the library. - * - * @return The version. - */ - public static int getVersionCode() { - return 1; +// Simple binary that will just crash with the message given as the first parameter. +// +// This is helpful in cases the caller does not want to crash itself, e.g., fork+crash +// instead, as LOG(FATAL) might not be safe (for example in a multi-threaded environment). +int main(int argc, char *argv[]) { + if (argc != 2) { + LOG(FATAL) << "Need one argument for abort message"; + __builtin_unreachable(); } -}
\ No newline at end of file + LOG(FATAL) << argv[1]; + __builtin_unreachable(); +} diff --git a/tools/lock_agent/java/com/android/lock_checker/LockHook.java b/tools/lock_agent/java/com/android/lock_checker/LockHook.java index 95b318101316..efab1d217225 100644 --- a/tools/lock_agent/java/com/android/lock_checker/LockHook.java +++ b/tools/lock_agent/java/com/android/lock_checker/LockHook.java @@ -66,7 +66,8 @@ public class LockHook { static final StatLogger sStats = new StatLogger(new String[] { "on-thread", }); - private static final ConcurrentLinkedQueue<Object> sViolations = new ConcurrentLinkedQueue<>(); + private static final ConcurrentLinkedQueue<Violation> sViolations = + new ConcurrentLinkedQueue<>(); private static final int MAX_VIOLATIONS = 50; private static final LockChecker[] sCheckers; @@ -101,8 +102,8 @@ public class LockHook { } } - static void wtf(String message) { - sHandler.wtf(message); + static void wtf(Violation v) { + sHandler.wtf(v); } static void doCheckOnThisThread(boolean check) { @@ -151,10 +152,10 @@ public class LockHook { super(looper); } - public void wtf(String msg) { + public void wtf(Violation v) { sDoCheck.set(false); SomeArgs args = SomeArgs.obtain(); - args.arg1 = msg; + args.arg1 = v; obtainMessage(MSG_WTF, args).sendToTarget(); sDoCheck.set(true); } @@ -164,13 +165,18 @@ public class LockHook { switch (msg.what) { case MSG_WTF: SomeArgs args = (SomeArgs) msg.obj; - Log.wtf(TAG, (String) args.arg1); + handleViolation((Violation) args.arg1); args.recycle(); break; } } } + private static void handleViolation(Violation v) { + String msg = v.toString(); + Log.wtf(TAG, msg); + } + /** * Generates a hash for a given stacktrace of a {@link Throwable}. */ @@ -224,8 +230,10 @@ public class LockHook { } } - static void addViolation(Object o) { - sViolations.offer(o); + static void addViolation(Violation v) { + wtf(v); + + sViolations.offer(v); while (sViolations.size() > MAX_VIOLATIONS) { sViolations.poll(); } @@ -287,4 +295,7 @@ public class LockHook { void dump(PrintWriter pw); } + + interface Violation { + } } diff --git a/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java b/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java index 0f3a28598741..e4e721156b2b 100644 --- a/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java +++ b/tools/lock_agent/java/com/android/lock_checker/OnThreadLockChecker.java @@ -220,7 +220,7 @@ class OnThreadLockChecker implements LockHook.LockChecker { heldLocks.remove(index); } - private static class Violation { + private static class Violation implements LockHook.Violation { int mSelfTid; String mSelfName; Object mAlreadyHeld; @@ -323,7 +323,6 @@ class OnThreadLockChecker implements LockHook.LockChecker { if (LockHook.shouldDumpStacktrace(mStacktraceHasher.get(), mDumpedStacktraceHashes, Boolean.TRUE, v.mStack, 0, to)) { mNumDetectedUnique.incrementAndGet(); - LockHook.wtf(v.toString()); LockHook.addViolation(v); } } |