Snap for 7550844 from cd8c863821a3fe7003950a3208660784b17d8ee7 to mainline-conscrypt-release Change-Id: Ifaf629ccbc464be9e7a407730fde52ccc1cf41be
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2732435 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,7 @@ +# Generated build files +gen/com/android/networkstack/** + +# IntelliJ project files +**/.idea +**/*.iml +**/*.ipr
diff --git a/Android.bp b/Android.bp index 802ca42..44e91bc 100644 --- a/Android.bp +++ b/Android.bp
@@ -22,12 +22,12 @@ // / \ // +NetworkStackApiStableShims --> / \ <-- +NetworkStackApiCurrentShims // +NetworkStackReleaseApiLevel / \ +NetworkStackDevApiLevel -// +jarjar apistub.api[latest].* / \ +module src/ -// to apistub.* / \ +// +jarjar apishim.api[latest].* / \ +// to apishim.* / \ // / \ -// NetworkStackApiStableDependencies \ +// / \ // / \ android libs w/ all code -// +module src/ --> / \ (also used in unit tests) +// / <- +module src/ -> \ (also used in unit tests) // / \ | // NetworkStackApiStableLib NetworkStackApiCurrentLib <--* // | | @@ -41,6 +41,10 @@ // TestNetworkStack // Common defaults to define SDK level +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + java_defaults { name: "NetworkStackDevApiLevel", min_sdk_version: "29", @@ -49,32 +53,153 @@ java_defaults { name: "NetworkStackReleaseApiLevel", - sdk_version: "system_30", + sdk_version: "module_31", min_sdk_version: "29", - target_sdk_version: "30", + target_sdk_version: "31", + libs: [ + "framework-connectivity", + "framework-statsd", + "framework-wifi", + ] } -// Filegroups for the API shims -filegroup { - name: "NetworkStackApiCurrentShims", +// Libraries for the API shims +java_defaults { + name: "NetworkStackShimsDefaults", + libs: [ + "androidx.annotation_annotation", + "networkstack-client", + ], + static_libs : [ + "modules-utils-build_system" + ], + apex_available: [ + "com.android.tethering", + "//apex_available:platform", // For InProcessNetworkStack and InProcessTethering + ], + min_sdk_version: "29", +} + +// Common shim code. This includes the shim interface definitions themselves, and things like +// ShimUtils and UnsupportedApiLevelException. Compiles against system_current because ShimUtils +// needs access to all Build.VERSION_CODES.*, which by definition are only in the newest SDK. +// TODO: consider moving ShimUtils into a library (or removing it in favour of SdkLevel) and compile +// this target against the lowest-supported SDK (currently 29). +java_library { + name: "NetworkStackShimsCommon", + defaults: ["NetworkStackShimsDefaults"], + srcs: ["apishim/common/**/*.java"], + sdk_version: "system_current", + visibility: ["//visibility:private"], +} + +// Each level of the shims (29, 30, ...) is its own java_library compiled against the corresponding +// system_X SDK. this ensures that each shim can only use SDK classes that exist in its SDK level. +java_library { + name: "NetworkStackApi29Shims", + defaults: ["NetworkStackShimsDefaults"], + srcs: ["apishim/29/**/*.java"], + libs: [ + "NetworkStackShimsCommon", + ], + sdk_version: "system_29", + visibility: ["//visibility:private"], +} + +java_library { + name: "NetworkStackApi30Shims", + defaults: ["NetworkStackShimsDefaults"], srcs: [ - "apishim/common/**/*.java", - "apishim/29/**/*.java", "apishim/30/**/*.java", + ], + libs: [ + "NetworkStackShimsCommon", + "NetworkStackApi29Shims", + ], + sdk_version: "system_30", + visibility: ["//visibility:private"], + lint: { + baseline_filename: "lint-baseline-api-30-shims.xml", + }, +} + +// Shims for APIs being added to the current development version of Android. These APIs are not +// stable and have no defined version number. These could be called 10000, but they use the next +// integer so if the next SDK release happens to use that integer, we don't need to rename them. +java_library { + name: "NetworkStackApi31Shims", + defaults: ["NetworkStackShimsDefaults"], + srcs: [ "apishim/31/**/*.java", - ":networkstack-module-utils-srcs", + ], + libs: [ + "NetworkStackShimsCommon", + "NetworkStackApi29Shims", + "NetworkStackApi30Shims", + "framework-connectivity", + ], + sdk_version: "module_31", + visibility: ["//visibility:private"], +} + + +// Shims for APIs being added to the current development version of Android. These APIs are not +// stable and have no defined version number. These could be called 10000, but they use the next +// integer so if the next SDK release happens to use that integer, we don't need to rename them. +java_library { + name: "NetworkStackApi32Shims", + defaults: ["NetworkStackShimsDefaults"], + srcs: [ + "apishim/32/**/*.java", + ], + libs: [ + "NetworkStackShimsCommon", + "NetworkStackApi29Shims", + "NetworkStackApi30Shims", + "NetworkStackApi31Shims", + "framework-connectivity", + ], + sdk_version: "module_current", + visibility: ["//visibility:private"], +} + +// API current uses the API current shims directly. +// The current (in-progress) shims are in the com.android.networkstack.apishim package and are +// called directly by the networkstack code. +java_library { + name: "NetworkStackApiCurrentShims", + defaults: ["NetworkStackShimsDefaults"], + static_libs: [ + "NetworkStackShimsCommon", + "NetworkStackApi29Shims", + "NetworkStackApi30Shims", + "NetworkStackApi31Shims", + "NetworkStackApi32Shims", + ], + sdk_version: "module_current", + visibility: [ + "//packages/modules/Connectivity/Tethering", + "//packages/modules/Connectivity/tests/cts/net", ], } -// API stable shims only include the compat package, but it is jarjared to replace the non-compat -// package -filegroup { +// API stable uses jarjar to rename the latest stable apishim package from +// com.android.networkstack.apishim.apiXX to com.android.networkstack.apishim, which is called by +// the networkstack code. +java_library { name: "NetworkStackApiStableShims", - srcs: [ - "apishim/common/**/*.java", - "apishim/29/**/*.java", - "apishim/30/**/*.java", - ":networkstack-module-utils-srcs", + defaults: ["NetworkStackShimsDefaults"], + static_libs: [ + "NetworkStackShimsCommon", + "NetworkStackApi29Shims", + "NetworkStackApi30Shims", + "NetworkStackApi31Shims", + ], + jarjar_rules: "apishim/jarjar-rules-compat.txt", + sdk_version: "module_31", + visibility: [ + "//packages/modules/Connectivity/Tethering", + "//packages/modules/Connectivity/tests/cts/net", ], } @@ -84,11 +209,13 @@ name: "NetworkStackAndroidLibraryDefaults", srcs: [ ":framework-networkstack-shared-srcs", + ":networkstack-module-utils-srcs", ], libs: ["unsupportedappusage"], static_libs: [ "androidx.annotation_annotation", - "netd_aidl_interface-java", + "modules-utils-build_system", + "netd_aidl_interface-lateststable-java", "netlink-client", "networkstack-client", "net-utils-framework-common", @@ -101,43 +228,49 @@ plugins: ["java_api_finder"], } -// The versions of the android library containing network stack code compiled for each SDK variant -// API current uses the sources of the API current shims directly. -// This allows API current code to be treated identically to code in src/ (it will be moved -// there eventually), and to use the compat shim as fallback on older devices. +// The versions of the android library containing network stack code compiled for each SDK variant. android_library { name: "NetworkStackApiCurrentLib", defaults: ["NetworkStackDevApiLevel", "NetworkStackAndroidLibraryDefaults"], srcs: [ - ":NetworkStackApiCurrentShims", "src/**/*.java", ":statslog-networkstack-java-gen-current" ], + static_libs: ["NetworkStackApiCurrentShims"], manifest: "AndroidManifestBase.xml", - enabled: false, // Disabled in mainline-prod -} - -// For API stable, first build the dependencies using jarjar compat rules, then build the sources -// linking with the dependencies. -java_library { - name: "NetworkStackApiStableDependencies", - defaults: ["NetworkStackReleaseApiLevel", "NetworkStackAndroidLibraryDefaults"], - srcs: [":NetworkStackApiStableShims"], - jarjar_rules: "apishim/jarjar-rules-compat.txt", + visibility: [ + "//frameworks/base/tests/net/integration", + "//packages/modules/Connectivity/Tethering/tests/integration", + "//packages/modules/Connectivity/tests/cts/net", + "//packages/modules/NetworkStack/tests/unit", + "//packages/modules/NetworkStack/tests/integration", + ], + lint: { + baseline_filename: "lint-baseline-current-lib.xml", + }, } android_library { name: "NetworkStackApiStableLib", - defaults: ["NetworkStackReleaseApiLevel"], + defaults: ["NetworkStackReleaseApiLevel", "NetworkStackAndroidLibraryDefaults"], srcs: [ "src/**/*.java", ":statslog-networkstack-java-gen-stable", ], - // API stable uses a jarjared version of the shims - static_libs: [ - "NetworkStackApiStableDependencies", - ], + static_libs: ["NetworkStackApiStableShims"], manifest: "AndroidManifestBase.xml", + visibility: [ + "//frameworks/base/packages/Connectivity/tests/integration", + "//frameworks/base/tests/net/integration", + "//packages/modules/Connectivity/Tethering/tests/integration", + "//packages/modules/Connectivity/tests/cts/net", + "//packages/modules/Connectivity/tests/integration", + "//packages/modules/NetworkStack/tests/unit", + "//packages/modules/NetworkStack/tests/integration", + ], + lint: { + baseline_filename: "lint-baseline-stable-lib.xml", + }, } filegroup { @@ -146,7 +279,6 @@ visibility: [ "//packages/modules/NetworkStack/tests/unit", "//packages/modules/NetworkStack/tests/integration", - "//frameworks/base/packages/Tethering/tests/integration", "//packages/modules/Connectivity/Tethering/tests/integration", ] } @@ -180,8 +312,10 @@ // The permission configuration *must* be included to ensure security of the device // The InProcessNetworkStack goes together with the PlatformCaptivePortalLogin, which replaces // the default CaptivePortalLogin. - required: ["PlatformNetworkPermissionConfig", "PlatformCaptivePortalLogin"], - enabled: false, // Disabled in mainline-prod + required: [ + "PlatformNetworkPermissionConfig", + "PlatformCaptivePortalLogin", + ], } // Pre-merge the AndroidManifest for NetworkStackNext, so that its manifest can be merged on top @@ -189,8 +323,7 @@ name: "NetworkStackNextManifestBase", defaults: ["NetworkStackAppDefaults", "NetworkStackDevApiLevel"], static_libs: ["NetworkStackApiCurrentLib"], - manifest: "AndroidManifest.xml", - enabled: false, // Disabled in mainline-prod + manifest: "AndroidManifest.xml" } // NetworkStack build targeting the current API release, for testing on in-development SDK @@ -201,8 +334,10 @@ certificate: "networkstack", manifest: "AndroidManifest_Next.xml", // The permission configuration *must* be included to ensure security of the device - required: ["NetworkPermissionConfig"], - enabled: false, // Disabled in mainline-prod + required: [ + "NetworkPermissionConfig", + "privapp_whitelist_com.android.networkstack", + ], } // Updatable network stack for finalized API @@ -213,21 +348,11 @@ certificate: "networkstack", manifest: "AndroidManifest.xml", // The permission configuration *must* be included to ensure security of the device - required: ["NetworkPermissionConfig"], - updatable: true, -} - -// Android library to derive test APKs for integration tests -android_library { - name: "TestNetworkStackLib", - defaults: ["NetworkStackAppDefaults", "NetworkStackReleaseApiLevel"], - static_libs: ["NetworkStackApiStableLib"], - manifest: "AndroidManifestBase.xml", - visibility: [ - "//frameworks/base/tests/net/integration", - "//cts/tests/tests/net", - "//packages/modules/Connectivity/tests/cts/net", + required: [ + "NetworkPermissionConfig", + "privapp_whitelist_com.android.networkstack", ], + updatable: true, } cc_library_shared { @@ -303,7 +428,10 @@ certificate: "networkstack", manifest: ":NetworkStackTestAndroidManifest", // The permission configuration *must* be included to ensure security of the device - required: ["NetworkPermissionConfig"], + required: [ + "NetworkPermissionConfig", + "privapp_whitelist_com.android.networkstack", + ], } // When adding or modifying protos, the jarjar rules and possibly proguard rules need
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 55357a8..6a11b2c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml
@@ -19,8 +19,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack" android:sharedUserId="android.uid.networkstack" - android:versionCode="309999900" - android:versionName="r_aml_309999900" + android:versionCode="319999900" + android:versionName="s_aml_319999900" > <!-- 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
diff --git a/AndroidManifest_Next.xml b/AndroidManifest_Next.xml index 02fcb64..9ad69ae 100644 --- a/AndroidManifest_Next.xml +++ b/AndroidManifest_Next.xml
@@ -17,6 +17,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack" android:sharedUserId="android.uid.networkstack" - android:versionCode="300000000" - android:versionName="R-next"> + android:versionCode="320000000" + android:versionName="T-next"> </manifest>
diff --git a/OWNERS b/OWNERS index 0e1e65d..8cb7492 100644 --- a/OWNERS +++ b/OWNERS
@@ -2,5 +2,7 @@ jchalard@google.com junyulai@google.com lorenzo@google.com +maze@google.com reminv@google.com satk@google.com +xiaom@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING index a2ed850..2bb0e61 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING
@@ -5,11 +5,31 @@ }, { "name": "NetworkStackNextTests" + }, + { + "name": "NetworkStackIntegrationTests" } ], "postsubmit": [ { "name": "NetworkStackHostTests" + } + ], + "auto-postsubmit": [ + // Test tag for automotive targets. These are only running in postsubmit so as to harden the + // automotive targets to avoid introducing additional test flake and build time. The plan for + // presubmit testing for auto is to augment the existing tests to cover auto use cases as well. + // Additionally, this tag is used in targeted test suites to limit resource usage on the test + // infra during the hardening phase. + // TODO: this tag to be removed once the above is no longer an issue. + { + "name": "NetworkStackTests" + }, + { + "name": "NetworkStackNextTests" + }, + { + "name": "NetworkStackHostTests" }, { "name": "NetworkStackIntegrationTests" @@ -20,7 +40,13 @@ // We must specify at least one module here or the tests won't run. Use the same set as CTS // so in theory the infra would not need to reinstall/reboot devices to run both. { - "name": "NetworkStackTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]" + // TODO: add back tethering when it is updatable in this branch + "name": "NetworkStackTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex]" + } + ], + "mainline-postsubmit": [ + { + "name": "NetworkStackIntegrationTests[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]" } ], "imports": [
diff --git a/apishim/29/com/android/networkstack/apishim/api29/CaptivePortalDataShimImpl.java b/apishim/29/com/android/networkstack/apishim/api29/CaptivePortalDataShimImpl.java index 42216a9..8719e83 100644 --- a/apishim/29/com/android/networkstack/apishim/api29/CaptivePortalDataShimImpl.java +++ b/apishim/29/com/android/networkstack/apishim/api29/CaptivePortalDataShimImpl.java
@@ -16,7 +16,7 @@ package com.android.networkstack.apishim.api29; -import android.net.CaptivePortalData; +import android.net.Uri; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -28,7 +28,7 @@ import org.json.JSONObject; /** - * Compatibility implementation of {@link CaptivePortalDataShim}. + * Compatibility implementation of {@link CaptivePortalData}. * * <p>Use {@link com.android.networkstack.apishim.CaptivePortalDataShimImpl} instead of this * fallback implementation. @@ -37,7 +37,7 @@ protected CaptivePortalDataShimImpl() {} /** - * Parse a {@link android.net.CaptivePortalData} from JSON. + * Parse a {@link android.net.CaptivePortalDataShim} from JSON. * * <p>Use * {@link com.android.networkstack.apishim.CaptivePortalDataShimImpl#fromJson(JSONObject)} @@ -51,24 +51,51 @@ } @Override - public String getVenueFriendlyName() { + public CharSequence getVenueFriendlyName() { // Not supported in API level 29 return null; } + @Override + public int getUserPortalUrlSource() { + // Not supported in API level 29 + return ConstantsShim.CAPTIVE_PORTAL_DATA_SOURCE_OTHER; + } + @VisibleForTesting public static boolean isSupported() { return false; } /** - * Generate a {@link CaptivePortalData} object with a friendly name set + * Generate a {@link CaptivePortalDataShim} object with a friendly name set * * @param friendlyName The friendly name to set * @return a {@link CaptivePortalData} object with a friendly name set */ - public CaptivePortalData withVenueFriendlyName(String friendlyName) { + @Override + public CaptivePortalDataShim withVenueFriendlyName(String friendlyName) + throws UnsupportedApiLevelException { // Not supported in API level 29 - return null; + throw new UnsupportedApiLevelException("CaptivePortalData not supported on API 29"); + } + + /** + * Generate a {@link CaptivePortalDataShim} object with a friendly name and Passpoint external + * URLs set + * + * @param friendlyName The friendly name to set + * @param venueInfoUrl Venue information URL + * @param termsAndConditionsUrl Terms and conditions URL + * + * @return a {@link CaptivePortalDataShim} object with friendly name, venue info URL and terms + * and conditions URL set + */ + @Override + public CaptivePortalDataShim withPasspointInfo(@NonNull String friendlyName, + @NonNull Uri venueInfoUrl, @NonNull Uri termsAndConditionsUrl) + throws UnsupportedApiLevelException { + // Not supported in API level 29 + throw new UnsupportedApiLevelException("CaptivePortalData not supported on API 29"); } }
diff --git a/apishim/29/com/android/networkstack/apishim/api29/ConnectivityManagerShimImpl.java b/apishim/29/com/android/networkstack/apishim/api29/ConnectivityManagerShimImpl.java new file mode 100644 index 0000000..07327be --- /dev/null +++ b/apishim/29/com/android/networkstack/apishim/api29/ConnectivityManagerShimImpl.java
@@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 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.apishim.api29; + +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; +import android.os.Handler; + +import androidx.annotation.NonNull; + +import com.android.networkstack.apishim.common.ConnectivityManagerShim; +import com.android.networkstack.apishim.common.UnsupportedApiLevelException; + +/** + * Implementation of {@link ConnectivityManagerShim} for API 29. + */ +public class ConnectivityManagerShimImpl implements ConnectivityManagerShim { + protected final ConnectivityManager mCm; + protected ConnectivityManagerShimImpl(Context context) { + mCm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + } + + /** + * Get a new instance of {@link ConnectivityManagerShim}. + */ + public static ConnectivityManagerShim newInstance(Context context) { + return new ConnectivityManagerShimImpl(context); + } + /** + * See android.net.ConnectivityManager#requestBackgroundNetwork + * @throws UnsupportedApiLevelException if API is not available in this API level. + */ + @Override + public void requestBackgroundNetwork(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) + throws UnsupportedApiLevelException { + // Not supported for API 29. + throw new UnsupportedApiLevelException("Not supported in API 29."); + } + + /** + * See android.net.ConnectivityManager#registerSystemDefaultNetworkCallback + */ + @Override + public void registerSystemDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, + @NonNull Handler handler) { + // defaultNetworkRequest is not really a "request", just a way of tracking the system + // default network. It's guaranteed not to actually bring up any networks because it + // should be the same request as the ConnectivityService default request, and thus + // shares fate with it. In API <= R, registerSystemDefaultNetworkCallback is not + // available, and registerDefaultNetworkCallback will not track the system default when + // a VPN applies to the UID of this process. + final NetworkRequest defaultNetworkRequest = makeEmptyCapabilitiesRequest() + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .build(); + mCm.requestNetwork(defaultNetworkRequest, networkCallback, handler); + } + + @NonNull + protected NetworkRequest.Builder makeEmptyCapabilitiesRequest() { + // Q does not have clearCapabilities(), so assume the default capabilities are as below + return new NetworkRequest.Builder() + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) + .removeCapability(NET_CAPABILITY_TRUSTED) + .removeCapability(NET_CAPABILITY_NOT_VPN); + } +}
diff --git a/apishim/29/com/android/networkstack/apishim/api29/ConstantsShim.java b/apishim/29/com/android/networkstack/apishim/api29/ConstantsShim.java index b655858..0b000a9 100644 --- a/apishim/29/com/android/networkstack/apishim/api29/ConstantsShim.java +++ b/apishim/29/com/android/networkstack/apishim/api29/ConstantsShim.java
@@ -34,4 +34,12 @@ // Constants defined in android.net.ConnectivityDiagnosticsManager. public static final int DETECTION_METHOD_DNS_EVENTS = 1; public static final int DETECTION_METHOD_TCP_METRICS = 2; + + // Constants defined in android.net.CaptivePortalData. + public static final int CAPTIVE_PORTAL_DATA_SOURCE_OTHER = 0; + public static final int CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT = 1; + + // Constants defined in android.net.NetworkCapabilities. + public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; + }
diff --git a/apishim/29/com/android/networkstack/apishim/api29/NetworkInformationShimImpl.java b/apishim/29/com/android/networkstack/apishim/api29/NetworkInformationShimImpl.java index 8dc7b5c..e68020b 100644 --- a/apishim/29/com/android/networkstack/apishim/api29/NetworkInformationShimImpl.java +++ b/apishim/29/com/android/networkstack/apishim/api29/NetworkInformationShimImpl.java
@@ -16,7 +16,6 @@ package com.android.networkstack.apishim.api29; -import android.net.CaptivePortalData; import android.net.IpPrefix; import android.net.LinkProperties; import android.net.NetworkCapabilities; @@ -121,10 +120,7 @@ * @param captivePortalData Captive portal data to be used */ public void setCaptivePortalData(@NonNull LinkProperties lp, - @Nullable CaptivePortalData captivePortalData) { - if (lp == null) { - return; - } - lp.setCaptivePortalData(captivePortalData); + @Nullable CaptivePortalDataShim captivePortalData) { + // Not supported on this API level: no-op } }
diff --git a/apishim/29/com/android/networkstack/apishim/api29/NetworkRequestShimImpl.java b/apishim/29/com/android/networkstack/apishim/api29/NetworkRequestShimImpl.java new file mode 100644 index 0000000..0c1d837 --- /dev/null +++ b/apishim/29/com/android/networkstack/apishim/api29/NetworkRequestShimImpl.java
@@ -0,0 +1,49 @@ +/* + * Copyright (C) 2021 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.apishim.api29; + +import android.net.NetworkRequest; +import android.util.Range; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.networkstack.apishim.common.NetworkRequestShim; +import com.android.networkstack.apishim.common.UnsupportedApiLevelException; + +import java.util.Set; + +/** + * Implementation of {@link NetworkRequestShim} for API 29. + */ +public class NetworkRequestShimImpl implements NetworkRequestShim { + protected NetworkRequestShimImpl() {} + + /** + * Get a new instance of {@link NetworkRequestShim}. + */ + public static NetworkRequestShim newInstance() { + return new NetworkRequestShimImpl(); + } + + @Override + public void setUids(@NonNull NetworkRequest.Builder builder, + @Nullable Set<Range<Integer>> uids) throws UnsupportedApiLevelException { + // Not supported before API 31. + throw new UnsupportedApiLevelException("Not supported before API 31."); + } +}
diff --git a/apishim/30/com/android/networkstack/apishim/api30/CaptivePortalDataShimImpl.java b/apishim/30/com/android/networkstack/apishim/api30/CaptivePortalDataShimImpl.java index 19a41db..5825021 100644 --- a/apishim/30/com/android/networkstack/apishim/api30/CaptivePortalDataShimImpl.java +++ b/apishim/30/com/android/networkstack/apishim/api30/CaptivePortalDataShimImpl.java
@@ -39,10 +39,14 @@ @NonNull protected final CaptivePortalData mData; - protected CaptivePortalDataShimImpl(@NonNull CaptivePortalData data) { + public CaptivePortalDataShimImpl(@NonNull CaptivePortalData data) { mData = data; } + public CaptivePortalData getData() { + return mData; + } + /** * Parse a {@link CaptivePortalDataShim} from a JSON object. * @throws JSONException The JSON is not a representation of correct captive portal data. @@ -116,4 +120,36 @@ public void notifyChanged(INetworkMonitorCallbacks cb) throws RemoteException { cb.notifyCaptivePortalDataChanged(mData); } + + /** + * Generate a {@link CaptivePortalDataShim} object with a friendly name set + * + * @param friendlyName The friendly name to set + * @return a {@link CaptivePortalDataShim} object with a friendly name set + */ + @Override + public CaptivePortalDataShim withVenueFriendlyName(String friendlyName) + throws UnsupportedApiLevelException { + // Not supported in API level 29 + throw new UnsupportedApiLevelException("FriendlyName not supported on API 30"); + } + + /** + * Generate a {@link CaptivePortalDataShim} object with a friendly name and Passpoint external + * URLs set + * + * @param friendlyName The friendly name to set + * @param venueInfoUrl Venue information URL + * @param termsAndConditionsUrl Terms and conditions URL + * + * @return a {@link CaptivePortalDataShim} object with friendly name, venue info URL and terms + * and conditions URL set + */ + @Override + public CaptivePortalDataShim withPasspointInfo(@NonNull String friendlyName, + @NonNull Uri venueInfoUrl, @NonNull Uri termsAndConditionsUrl) + throws UnsupportedApiLevelException { + // Not supported in API level 29 + throw new UnsupportedApiLevelException("PasspointInfo not supported on API 30"); + } }
diff --git a/apishim/30/com/android/networkstack/apishim/api30/ConnectivityManagerShimImpl.java b/apishim/30/com/android/networkstack/apishim/api30/ConnectivityManagerShimImpl.java new file mode 100644 index 0000000..7c1d786 --- /dev/null +++ b/apishim/30/com/android/networkstack/apishim/api30/ConnectivityManagerShimImpl.java
@@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 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.apishim.api30; + +import static com.android.modules.utils.build.SdkLevel.isAtLeastR; + +import android.content.Context; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.NetworkRequest; +import android.os.Build; +import android.os.Handler; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.android.networkstack.apishim.common.ConnectivityManagerShim; +import com.android.networkstack.apishim.common.UnsupportedApiLevelException; + +/** + * Implementation of {@link ConnectivityManagerShim} for API 30. + */ +@RequiresApi(Build.VERSION_CODES.R) +public class ConnectivityManagerShimImpl + extends com.android.networkstack.apishim.api29.ConnectivityManagerShimImpl { + protected ConnectivityManagerShimImpl(Context context) { + super(context); + } + + /** + * Get a new instance of {@link ConnectivityManagerShim}. + */ + @RequiresApi(Build.VERSION_CODES.Q) + public static ConnectivityManagerShim newInstance(Context context) { + if (!isAtLeastR()) { + return com.android.networkstack.apishim.api29.ConnectivityManagerShimImpl + .newInstance(context); + } + return new ConnectivityManagerShimImpl(context); + } + + /** + * See android.net.ConnectivityManager#requestBackgroundNetwork + * @throws UnsupportedApiLevelException if API is not available in this API level. + */ + @Override + public void requestBackgroundNetwork(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) + throws UnsupportedApiLevelException { + // Not supported for API 30. + throw new UnsupportedApiLevelException("Not supported in API 30."); + } + + @NonNull + @Override + protected NetworkRequest.Builder makeEmptyCapabilitiesRequest() { + return new NetworkRequest.Builder().clearCapabilities(); + } +}
diff --git a/apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java b/apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java index 27fd745..19ff9d3 100644 --- a/apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java +++ b/apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java
@@ -38,8 +38,12 @@ public static final int DETECTION_METHOD_TCP_METRICS = DataStallReport.DETECTION_METHOD_TCP_METRICS; - /** - * @see android.net.NetworkCapabilities - */ + // Constants defined in android.net.ConnectivityManager. + public static final int BLOCKED_REASON_NONE = 0; + public static final int BLOCKED_REASON_LOCKDOWN_VPN = 16; + + // Constants defined in android.net.NetworkCapabilities. + public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; + public static final int NET_CAPABILITY_ENTERPRISE = 29; public static final int TRANSPORT_TEST = 7; }
diff --git a/apishim/30/com/android/networkstack/apishim/api30/NetworkInformationShimImpl.java b/apishim/30/com/android/networkstack/apishim/api30/NetworkInformationShimImpl.java index 5d9b013..477dd42a 100644 --- a/apishim/30/com/android/networkstack/apishim/api30/NetworkInformationShimImpl.java +++ b/apishim/30/com/android/networkstack/apishim/api30/NetworkInformationShimImpl.java
@@ -21,6 +21,7 @@ import android.net.NetworkCapabilities; import android.net.Uri; import android.os.Build; +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -37,6 +38,8 @@ */ public class NetworkInformationShimImpl extends com.android.networkstack.apishim.api29.NetworkInformationShimImpl { + private static final String TAG = "api30.NetworkInformationShimImpl"; + protected NetworkInformationShimImpl() {} /** @@ -105,4 +108,20 @@ @NonNull Inet4Address serverAddress) { lp.setDhcpServerAddress(serverAddress); } + + @Override + public void setCaptivePortalData(@NonNull LinkProperties lp, + @Nullable CaptivePortalDataShim captivePortalData) { + if (lp == null) { + return; + } + if (!(captivePortalData instanceof CaptivePortalDataShimImpl)) { + // The caller passed in a subclass that is not a CaptivePortalDataShimImpl. + // This is a programming error, but don't crash with ClassCastException. + Log.wtf(TAG, "Expected CaptivePortalDataShimImpl, but got " + + captivePortalData.getClass().getName()); + return; + } + lp.setCaptivePortalData(((CaptivePortalDataShimImpl) captivePortalData).getData()); + } }
diff --git a/apishim/30/com/android/networkstack/apishim/api30/NetworkRequestShimImpl.java b/apishim/30/com/android/networkstack/apishim/api30/NetworkRequestShimImpl.java new file mode 100644 index 0000000..b65a556 --- /dev/null +++ b/apishim/30/com/android/networkstack/apishim/api30/NetworkRequestShimImpl.java
@@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 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.apishim.api30; + +import android.os.Build; + +import com.android.networkstack.apishim.common.NetworkRequestShim; +import com.android.networkstack.apishim.common.ShimUtils; + +/** + * Implementation of {@link NetworkRequestShim} for API 30. + */ +public class NetworkRequestShimImpl + extends com.android.networkstack.apishim.api29.NetworkRequestShimImpl { + protected NetworkRequestShimImpl() { + super(); + } + + /** + * Get a new instance of {@link NetworkRequestShim}. + */ + public static NetworkRequestShim newInstance() { + if (!ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)) { + return com.android.networkstack.apishim.api29.NetworkRequestShimImpl + .newInstance(); + } + return new NetworkRequestShimImpl(); + } +}
diff --git a/apishim/30/com/android/networkstack/apishim/api30/SettingsShimImpl.java b/apishim/30/com/android/networkstack/apishim/api30/SettingsShimImpl.java new file mode 100644 index 0000000..b8188c6 --- /dev/null +++ b/apishim/30/com/android/networkstack/apishim/api30/SettingsShimImpl.java
@@ -0,0 +1,50 @@ +/* + * Copyright (C) 2021 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.apishim.api30; + +import android.content.Context; +import android.provider.Settings; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.networkstack.apishim.common.SettingsShim; + +/** + * Implementation of {@link SettingsShim} for API 30. + */ +public class SettingsShimImpl implements SettingsShim { + protected SettingsShimImpl() { } + + /** + * Get a new instance of {@link SettingsShim}. + * + * Use com.android.networkstack.apishim.SeetingsShim#newInstance() + * (non-API30 version) instead, to use the correct shims depending on build SDK. + */ + public static SettingsShim newInstance() { + return new SettingsShimImpl(); + } + + @Override + public boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid, + @NonNull String callingPackage, @Nullable String callingAttributionTag, + boolean throwException) { + return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage, + throwException); + } +}
diff --git a/apishim/31/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java b/apishim/31/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java deleted file mode 100644 index 955167d..0000000 --- a/apishim/31/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java +++ /dev/null
@@ -1,50 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.networkstack.apishim; - -import android.net.CaptivePortalData; - -import androidx.annotation.NonNull; - -import com.android.networkstack.apishim.common.CaptivePortalDataShim; - -/** - * Compatibility implementation of {@link CaptivePortalDataShim}. - */ -public class CaptivePortalDataShimImpl - extends com.android.networkstack.apishim.api30.CaptivePortalDataShimImpl { - protected CaptivePortalDataShimImpl(@NonNull CaptivePortalData data) { - super(data); - } - - @Override - public String getVenueFriendlyName() { - return mData.getVenueFriendlyName(); - } - - /** - * Generate a {@link CaptivePortalData} object with a friendly name set - * - * @param friendlyName The friendly name to set - * @return a {@link CaptivePortalData} object with a friendly name set - */ - public CaptivePortalData withVenueFriendlyName(String friendlyName) { - return new CaptivePortalData.Builder(mData) - .setVenueFriendlyName(friendlyName) - .build(); - } -}
diff --git a/apishim/31/com/android/networkstack/apishim/api31/CaptivePortalDataShimImpl.java b/apishim/31/com/android/networkstack/apishim/api31/CaptivePortalDataShimImpl.java new file mode 100644 index 0000000..5ae006b --- /dev/null +++ b/apishim/31/com/android/networkstack/apishim/api31/CaptivePortalDataShimImpl.java
@@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.networkstack.apishim.api31; + +import android.net.CaptivePortalData; +import android.net.Uri; + +import androidx.annotation.NonNull; + +import com.android.networkstack.apishim.common.CaptivePortalDataShim; + +/** + * Compatibility implementation of {@link CaptivePortalDataShim}. + */ +public class CaptivePortalDataShimImpl + extends com.android.networkstack.apishim.api30.CaptivePortalDataShimImpl { + public CaptivePortalDataShimImpl(@NonNull CaptivePortalData data) { + super(data); + } + + @Override + public CharSequence getVenueFriendlyName() { + return mData.getVenueFriendlyName(); + } + + /** + * Get the information source of the User portal + * @return The source that the User portal was obtained from + */ + @Override + public int getUserPortalUrlSource() { + return mData.getUserPortalUrlSource(); + } + + /** + * Generate a {@link CaptivePortalDataShim} object with a friendly name set + * + * @param friendlyName The friendly name to set + * @return a {@link CaptivePortalDataShim} object with a friendly name set + */ + @Override + public CaptivePortalDataShim withVenueFriendlyName(String friendlyName) { + return new CaptivePortalDataShimImpl(new CaptivePortalData.Builder(mData) + .setVenueFriendlyName(friendlyName) + .build()); + } + + /** + * Generate a {@link CaptivePortalDataShim} object with a friendly name and Passpoint external + * URLs set + * + * @param friendlyName The friendly name to set + * @param venueInfoUrl Venue information URL + * @param termsAndConditionsUrl Terms and conditions URL + * + * @return a {@link CaptivePortalDataShim} object with friendly name, venue info URL and terms + * and conditions URL set + */ + @Override + public CaptivePortalDataShim withPasspointInfo(@NonNull String friendlyName, + @NonNull Uri venueInfoUrl, @NonNull Uri termsAndConditionsUrl) { + return new CaptivePortalDataShimImpl(new CaptivePortalData.Builder(mData) + .setVenueFriendlyName(friendlyName) + .setVenueInfoUrl(venueInfoUrl, ConstantsShim.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .setUserPortalUrl(termsAndConditionsUrl, + ConstantsShim.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) + .build()); + } +}
diff --git a/apishim/31/com/android/networkstack/apishim/api31/ConnectivityManagerShimImpl.java b/apishim/31/com/android/networkstack/apishim/api31/ConnectivityManagerShimImpl.java new file mode 100644 index 0000000..46de698 --- /dev/null +++ b/apishim/31/com/android/networkstack/apishim/api31/ConnectivityManagerShimImpl.java
@@ -0,0 +1,100 @@ +/* + * Copyright (C) 2021 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.apishim.api31; + +import static com.android.modules.utils.build.SdkLevel.isAtLeastS; + +import android.content.Context; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.NetworkRequest; +import android.os.Build; +import android.os.Handler; +import android.util.Range; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.android.networkstack.apishim.common.ConnectivityManagerShim; + +import java.util.Collection; + +/** + * Implementation of {@link ConnectivityManagerShim} for API 31. + */ +@RequiresApi(Build.VERSION_CODES.S) +public class ConnectivityManagerShimImpl + extends com.android.networkstack.apishim.api30.ConnectivityManagerShimImpl { + + protected ConnectivityManagerShimImpl(Context context) { + super(context); + } + + /** + * Get a new instance of {@link ConnectivityManagerShim}. + */ + @RequiresApi(Build.VERSION_CODES.Q) + public static ConnectivityManagerShim newInstance(Context context) { + if (!isAtLeastS()) { + return com.android.networkstack.apishim.api30.ConnectivityManagerShimImpl + .newInstance(context); + } + return new ConnectivityManagerShimImpl(context); + } + + /** + * See android.net.ConnectivityManager#requestBackgroundNetwork + */ + @Override + public void requestBackgroundNetwork(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { + mCm.requestBackgroundNetwork(request, networkCallback, handler); + } + + /** + * See android.net.ConnectivityManager#registerSystemDefaultNetworkCallback + */ + @Override + public void registerSystemDefaultNetworkCallback( + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { + mCm.registerSystemDefaultNetworkCallback(networkCallback, handler); + } + + /** + * See android.net.ConnectivityManager#registerDefaultNetworkCallbackAsUid + */ + @Override + public void registerDefaultNetworkCallbackForUid( + int uid, @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { + mCm.registerDefaultNetworkCallbackForUid(uid, networkCallback, handler); + } + + /** + * See android.net.ConnectivityManager#setLegacyLockdownVpnEnabled + */ + @Override + public void setLegacyLockdownVpnEnabled(boolean enabled) { + mCm.setLegacyLockdownVpnEnabled(enabled); + } + + /** + * See android.net.ConnectivityManager#setRequireVpnForUids + */ + @Override + public void setRequireVpnForUids(boolean requireVpn, Collection<Range<Integer>> ranges) { + mCm.setRequireVpnForUids(requireVpn, ranges); + } +}
diff --git a/apishim/31/com/android/networkstack/apishim/ConstantsShim.java b/apishim/31/com/android/networkstack/apishim/api31/ConstantsShim.java similarity index 96% rename from apishim/31/com/android/networkstack/apishim/ConstantsShim.java rename to apishim/31/com/android/networkstack/apishim/api31/ConstantsShim.java index 0184845..95ff072 100644 --- a/apishim/31/com/android/networkstack/apishim/ConstantsShim.java +++ b/apishim/31/com/android/networkstack/apishim/api31/ConstantsShim.java
@@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.networkstack.apishim; +package com.android.networkstack.apishim.api31; import androidx.annotation.VisibleForTesting;
diff --git a/apishim/31/com/android/networkstack/apishim/NetworkInformationShimImpl.java b/apishim/31/com/android/networkstack/apishim/api31/NetworkInformationShimImpl.java similarity index 83% rename from apishim/31/com/android/networkstack/apishim/NetworkInformationShimImpl.java rename to apishim/31/com/android/networkstack/apishim/api31/NetworkInformationShimImpl.java index d668d7e..a5c9a71 100644 --- a/apishim/31/com/android/networkstack/apishim/NetworkInformationShimImpl.java +++ b/apishim/31/com/android/networkstack/apishim/api31/NetworkInformationShimImpl.java
@@ -14,12 +14,14 @@ * limitations under the License. */ -package com.android.networkstack.apishim; +package com.android.networkstack.apishim.api31; import android.net.LinkProperties; +import android.net.NetworkCapabilities; import android.os.Build; import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import androidx.annotation.VisibleForTesting; import com.android.networkstack.apishim.common.CaptivePortalDataShim; @@ -48,7 +50,7 @@ if (!useApiAboveR()) { return com.android.networkstack.apishim.api30.NetworkInformationShimImpl.newInstance(); } - return new com.android.networkstack.apishim.NetworkInformationShimImpl(); + return new NetworkInformationShimImpl(); } @Nullable @@ -57,4 +59,11 @@ if (lp == null || lp.getCaptivePortalData() == null) return null; return new CaptivePortalDataShimImpl(lp.getCaptivePortalData()); } + + @RequiresApi(Build.VERSION_CODES.S) + @Nullable + @Override + public String getCapabilityCarrierName(int capability) { + return NetworkCapabilities.getCapabilityCarrierName(capability); + } }
diff --git a/apishim/31/com/android/networkstack/apishim/api31/NetworkRequestShimImpl.java b/apishim/31/com/android/networkstack/apishim/api31/NetworkRequestShimImpl.java new file mode 100644 index 0000000..2dc5d72 --- /dev/null +++ b/apishim/31/com/android/networkstack/apishim/api31/NetworkRequestShimImpl.java
@@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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.apishim.api31; + +import static com.android.modules.utils.build.SdkLevel.isAtLeastS; + +import android.net.NetworkRequest; +import android.os.Build; +import android.util.Range; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + +import com.android.networkstack.apishim.common.NetworkRequestShim; + +import java.util.Set; + +/** + * Implementation of {@link NetworkRequestShim} for API 31. + */ +@RequiresApi(Build.VERSION_CODES.S) +public class NetworkRequestShimImpl + extends com.android.networkstack.apishim.api30.NetworkRequestShimImpl { + protected NetworkRequestShimImpl() { + super(); + } + + /** + * Get a new instance of {@link NetworkRequestShim}. + */ + @RequiresApi(Build.VERSION_CODES.Q) + public static NetworkRequestShim newInstance() { + if (!isAtLeastS()) { + return com.android.networkstack.apishim.api30.NetworkRequestShimImpl.newInstance(); + } + return new NetworkRequestShimImpl(); + } + + @Override + public void setUids(@NonNull NetworkRequest.Builder builder, + @Nullable Set<Range<Integer>> uids) { + builder.setUids(uids); + } + + @Override + public NetworkRequest.Builder setIncludeOtherUidNetworks(NetworkRequest.Builder builder, + boolean include) { + builder.setIncludeOtherUidNetworks(include); + return builder; + } + + @Override + public NetworkRequest.Builder newBuilder(@NonNull NetworkRequest request) { + return new NetworkRequest.Builder(request); + } +}
diff --git a/apishim/31/com/android/networkstack/apishim/NetworkShimImpl.java b/apishim/31/com/android/networkstack/apishim/api31/NetworkShimImpl.java similarity index 95% rename from apishim/31/com/android/networkstack/apishim/NetworkShimImpl.java rename to apishim/31/com/android/networkstack/apishim/api31/NetworkShimImpl.java index 0c92391..eda8e27 100644 --- a/apishim/31/com/android/networkstack/apishim/NetworkShimImpl.java +++ b/apishim/31/com/android/networkstack/apishim/api31/NetworkShimImpl.java
@@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.networkstack.apishim; +package com.android.networkstack.apishim.api31; import android.net.Network;
diff --git a/apishim/31/com/android/networkstack/apishim/api31/SettingsShimImpl.java b/apishim/31/com/android/networkstack/apishim/api31/SettingsShimImpl.java new file mode 100644 index 0000000..1b5cbae --- /dev/null +++ b/apishim/31/com/android/networkstack/apishim/api31/SettingsShimImpl.java
@@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 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.apishim.api31; + +import android.content.Context; +import android.os.Build; +import android.provider.Settings; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.networkstack.apishim.common.SettingsShim; +import com.android.networkstack.apishim.common.ShimUtils; + +/** + * Implementation of {@link SettingsShim} for API 31. + */ +public class SettingsShimImpl + extends com.android.networkstack.apishim.api30.SettingsShimImpl { + protected SettingsShimImpl() { } + + /** + * Get a new instance of {@link SettingsShim}. + */ + public static SettingsShim newInstance() { + if (!ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.R)) { + return com.android.networkstack.apishim.api30.SettingsShimImpl + .newInstance(); + } + return new SettingsShimImpl(); + } + + @Override + public boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid, + @NonNull String callingPackage, @Nullable String callingAttributionTag, + boolean throwException) { + return Settings.checkAndNoteWriteSettingsOperation(context, uid, callingPackage, + callingAttributionTag, throwException); + } +}
diff --git a/apishim/31/com/android/networkstack/apishim/SocketUtilsShimImpl.java b/apishim/31/com/android/networkstack/apishim/api31/SocketUtilsShimImpl.java similarity index 94% rename from apishim/31/com/android/networkstack/apishim/SocketUtilsShimImpl.java rename to apishim/31/com/android/networkstack/apishim/api31/SocketUtilsShimImpl.java index 483bde0..f5aa80b 100644 --- a/apishim/31/com/android/networkstack/apishim/SocketUtilsShimImpl.java +++ b/apishim/31/com/android/networkstack/apishim/api31/SocketUtilsShimImpl.java
@@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.networkstack.apishim; +package com.android.networkstack.apishim.api31; /** * Implementation of {@link NetworkShim} for API 30.
diff --git a/apishim/32/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java b/apishim/32/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java new file mode 100644 index 0000000..2056b1b --- /dev/null +++ b/apishim/32/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java
@@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 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.apishim; + +import android.net.CaptivePortalData; +import android.os.Build; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import com.android.networkstack.apishim.common.CaptivePortalDataShim; + +/** + * Compatibility implementation of {@link CaptivePortalDataShim}. + */ +@RequiresApi(Build.VERSION_CODES.S) // Change to T when version code available, and adding T methods +public class CaptivePortalDataShimImpl + extends com.android.networkstack.apishim.api31.CaptivePortalDataShimImpl { + // Currently identical to the API 31 shim, so inherit everything + public CaptivePortalDataShimImpl(@NonNull CaptivePortalData data) { + super(data); + } +}
diff --git a/apishim/32/com/android/networkstack/apishim/ConnectivityManagerShimImpl.java b/apishim/32/com/android/networkstack/apishim/ConnectivityManagerShimImpl.java new file mode 100644 index 0000000..a7aa0c8 --- /dev/null +++ b/apishim/32/com/android/networkstack/apishim/ConnectivityManagerShimImpl.java
@@ -0,0 +1,36 @@ +/* + * Copyright (C) 2021 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.apishim; + +import android.content.Context; +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.android.networkstack.apishim.common.ConnectivityManagerShim; + +/** + * Compatibility implementation of {@link ConnectivityManagerShim}. + */ +@RequiresApi(Build.VERSION_CODES.S) // Change to T when version code available, and adding T methods +public class ConnectivityManagerShimImpl + extends com.android.networkstack.apishim.api31.ConnectivityManagerShimImpl { + // Currently identical to the API 31 shim, so inherit everything + protected ConnectivityManagerShimImpl(Context context) { + super(context); + } +}
diff --git a/apishim/31/com/android/networkstack/apishim/ConstantsShim.java b/apishim/32/com/android/networkstack/apishim/ConstantsShim.java similarity index 76% copy from apishim/31/com/android/networkstack/apishim/ConstantsShim.java copy to apishim/32/com/android/networkstack/apishim/ConstantsShim.java index 0184845..0a5b555 100644 --- a/apishim/31/com/android/networkstack/apishim/ConstantsShim.java +++ b/apishim/32/com/android/networkstack/apishim/ConstantsShim.java
@@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 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. @@ -21,7 +21,7 @@ /** * Utility class for defining and importing constants from the Android platform. */ -public class ConstantsShim extends com.android.networkstack.apishim.api30.ConstantsShim { +public class ConstantsShim extends com.android.networkstack.apishim.api31.ConstantsShim { /** * Constant that callers can use to determine what version of the shim they are using. * Must be the same as the version of the shims. @@ -29,9 +29,5 @@ * the shimmed objects and methods themselves. */ @VisibleForTesting - public static final int VERSION = 31; - - // When removing this shim, the version in NetworkMonitorUtils should be removed too. - // TODO: add TRANSPORT_TEST to system API in API 31 (it is only a test API as of R) - public static final int TRANSPORT_TEST = 7; + public static final int VERSION = 32; }
diff --git a/apishim/32/com/android/networkstack/apishim/NetworkInformationShimImpl.java b/apishim/32/com/android/networkstack/apishim/NetworkInformationShimImpl.java new file mode 100644 index 0000000..28aa75c --- /dev/null +++ b/apishim/32/com/android/networkstack/apishim/NetworkInformationShimImpl.java
@@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 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.apishim; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.android.networkstack.apishim.common.NetworkInformationShim; + +/** + * Compatibility implementation of {@link NetworkInformationShim}. + */ +@RequiresApi(Build.VERSION_CODES.S) // Change to T when version code available, and adding T methods +public class NetworkInformationShimImpl + extends com.android.networkstack.apishim.api31.NetworkInformationShimImpl { + // Currently identical to the API 31 shim, so inherit everything + protected NetworkInformationShimImpl() {} +}
diff --git a/apishim/32/com/android/networkstack/apishim/NetworkRequestShimImpl.java b/apishim/32/com/android/networkstack/apishim/NetworkRequestShimImpl.java new file mode 100644 index 0000000..95ae5ba --- /dev/null +++ b/apishim/32/com/android/networkstack/apishim/NetworkRequestShimImpl.java
@@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 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.apishim; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.android.networkstack.apishim.common.NetworkRequestShim; + +/** + * Implementation of {@link NetworkRequestShim} for API 31. + */ +@RequiresApi(Build.VERSION_CODES.S) // Change to T when version code available, and adding T methods +public class NetworkRequestShimImpl + extends com.android.networkstack.apishim.api31.NetworkRequestShimImpl { + // Currently identical to the API 31 shim, so inherit everything + protected NetworkRequestShimImpl() { + super(); + } +}
diff --git a/apishim/31/com/android/networkstack/apishim/NetworkShimImpl.java b/apishim/32/com/android/networkstack/apishim/NetworkShimImpl.java similarity index 70% copy from apishim/31/com/android/networkstack/apishim/NetworkShimImpl.java copy to apishim/32/com/android/networkstack/apishim/NetworkShimImpl.java index 0c92391..2e31a78 100644 --- a/apishim/31/com/android/networkstack/apishim/NetworkShimImpl.java +++ b/apishim/32/com/android/networkstack/apishim/NetworkShimImpl.java
@@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 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. @@ -17,14 +17,17 @@ package com.android.networkstack.apishim; import android.net.Network; +import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; /** - * Implementation of {@link NetworkShim} for API 30. + * Compatibility implementation of {@link com.android.networkstack.apishim.common.NetworkShim}. */ +@RequiresApi(Build.VERSION_CODES.S) // Change to T when version code available, and adding T methods public class NetworkShimImpl extends com.android.networkstack.apishim.api30.NetworkShimImpl { - // Currently, this is the same as the API 30 shim, so inherit everything from that. + // Currently, this is the same as the API 31 shim, so inherit everything from that. protected NetworkShimImpl(@NonNull Network network) { super(network); }
diff --git a/apishim/32/com/android/networkstack/apishim/SettingsShimImpl.java b/apishim/32/com/android/networkstack/apishim/SettingsShimImpl.java new file mode 100644 index 0000000..46d2102 --- /dev/null +++ b/apishim/32/com/android/networkstack/apishim/SettingsShimImpl.java
@@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 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.apishim; + +import android.os.Build; + +import androidx.annotation.RequiresApi; + +import com.android.networkstack.apishim.common.SettingsShim; + +/** + * Compatibility implementation of {@link SettingsShim} for API 31. + */ +@RequiresApi(Build.VERSION_CODES.S) // Change to T when version code available, and adding T methods +public class SettingsShimImpl + extends com.android.networkstack.apishim.api30.SettingsShimImpl { + // Currently identical to the API 31 shim, so inherit everything + protected SettingsShimImpl() { } +}
diff --git a/apishim/31/com/android/networkstack/apishim/SocketUtilsShimImpl.java b/apishim/32/com/android/networkstack/apishim/SocketUtilsShimImpl.java similarity index 68% copy from apishim/31/com/android/networkstack/apishim/SocketUtilsShimImpl.java copy to apishim/32/com/android/networkstack/apishim/SocketUtilsShimImpl.java index 483bde0..2f4e500 100644 --- a/apishim/31/com/android/networkstack/apishim/SocketUtilsShimImpl.java +++ b/apishim/32/com/android/networkstack/apishim/SocketUtilsShimImpl.java
@@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,16 @@ package com.android.networkstack.apishim; +import android.os.Build; + +import androidx.annotation.RequiresApi; + /** - * Implementation of {@link NetworkShim} for API 30. + * Implementation of {@link com.android.networkstack.apishim.common.SocketUtilsShim}. */ +@RequiresApi(Build.VERSION_CODES.S) // Change to T when version code available, and adding T methods public class SocketUtilsShimImpl extends com.android.networkstack.apishim.api30.SocketUtilsShimImpl { - // Currently, this is the same as the API 30 shim, so inherit everything from that. + // Currently, this is the same as the API 31 shim, so inherit everything from that. protected SocketUtilsShimImpl() {} }
diff --git a/apishim/common/com/android/networkstack/apishim/common/CaptivePortalDataShim.java b/apishim/common/com/android/networkstack/apishim/common/CaptivePortalDataShim.java index 4bd5532..13bf257 100644 --- a/apishim/common/com/android/networkstack/apishim/common/CaptivePortalDataShim.java +++ b/apishim/common/com/android/networkstack/apishim/common/CaptivePortalDataShim.java
@@ -16,12 +16,12 @@ package com.android.networkstack.apishim.common; -import android.annotation.NonNull; -import android.net.CaptivePortalData; import android.net.INetworkMonitorCallbacks; import android.net.Uri; import android.os.RemoteException; +import androidx.annotation.NonNull; + /** * Compatibility interface for {@link android.net.CaptivePortalData}. */ @@ -54,7 +54,12 @@ /** * @see CaptivePortalData#getVenueFriendlyName() */ - String getVenueFriendlyName(); + CharSequence getVenueFriendlyName(); + + /** + * @see CaptivePortalData#getUserPortalUrlSource() + */ + int getUserPortalUrlSource(); /** * @see INetworkMonitorCallbacks#notifyCaptivePortalDataChanged(android.net.CaptivePortalData) @@ -65,7 +70,25 @@ * Generate a {@link CaptivePortalData} object with a friendly name set * * @param friendlyName The friendly name to set + * @throws UnsupportedApiLevelException when used with API level lower than 31 * @return a {@link CaptivePortalData} object with a friendly name set */ - CaptivePortalData withVenueFriendlyName(@NonNull String friendlyName); + CaptivePortalDataShim withVenueFriendlyName(@NonNull String friendlyName) + throws UnsupportedApiLevelException; + + /** + * Generate a {@link CaptivePortalData} object with a friendly name and Passpoint external URLs + * set + * + * @param friendlyName The friendly name to set + * @param venueInfoUrl Venue information URL + * @param termsAndConditionsUrl Terms and conditions URL + * + * @throws UnsupportedApiLevelException when used with API level lower than 31 + * @return a {@link CaptivePortalData} object with friendly name, venue info URL and terms + * and conditions URL set + */ + CaptivePortalDataShim withPasspointInfo(@NonNull String friendlyName, + @NonNull Uri venueInfoUrl, @NonNull Uri termsAndConditionsUrl) + throws UnsupportedApiLevelException; }
diff --git a/apishim/common/com/android/networkstack/apishim/common/ConnectivityManagerShim.java b/apishim/common/com/android/networkstack/apishim/common/ConnectivityManagerShim.java new file mode 100644 index 0000000..86d785e --- /dev/null +++ b/apishim/common/com/android/networkstack/apishim/common/ConnectivityManagerShim.java
@@ -0,0 +1,64 @@ +/* + * Copyright (C) 2021 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.apishim.common; + +import android.net.ConnectivityManager.NetworkCallback; +import android.net.NetworkRequest; +import android.os.Handler; +import android.util.Range; + +import androidx.annotation.NonNull; + +import java.util.Collection; + +/** + * Interface used to access API methods in {@link android.net.ConnectivityManager}, with + * appropriate fallbacks if the methods are not yet part of the released API. + * + * <p>This interface makes it easier for callers to use ConnectivityManagerShimImpl, as it's more + * obvious what methods must be implemented on each API level, and it abstracts from callers the + * need to reference classes that have different implementations (which also does not work well + * with IDEs). + */ +public interface ConnectivityManagerShim { + /** See android.net.ConnectivityManager#requestBackgroundNetwork */ + void requestBackgroundNetwork(@NonNull NetworkRequest request, + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) + throws UnsupportedApiLevelException; + + /** See android.net.ConnectivityManager#registerSystemDefaultNetworkCallback */ + void registerSystemDefaultNetworkCallback( + @NonNull NetworkCallback networkCallback, @NonNull Handler handler); + + /** See android.net.ConnectivityManager#registerDefaultNetworkCallbackForUid */ + default void registerDefaultNetworkCallbackForUid( + int uid, @NonNull NetworkCallback networkCallback, @NonNull Handler handler) + throws UnsupportedApiLevelException { + throw new UnsupportedApiLevelException("Only supported starting from API 31"); + } + + /** See android.net.ConnectivityManager#setLegacyLockdownVpnEnabled */ + default void setLegacyLockdownVpnEnabled(boolean enabled) throws UnsupportedApiLevelException { + throw new UnsupportedApiLevelException("Only supported starting from API 31"); + } + + /** See android.net.ConnectivityManager#setRequireVpnForUids */ + default void setRequireVpnForUids(boolean requireVpn, Collection<Range<Integer>> ranges) + throws UnsupportedApiLevelException { + throw new UnsupportedApiLevelException("Only supported starting from API 31"); + } +}
diff --git a/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java b/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java index 6cdcf8c..7fa1777 100644 --- a/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java +++ b/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java
@@ -16,7 +16,6 @@ package com.android.networkstack.apishim.common; -import android.net.CaptivePortalData; import android.net.IpPrefix; import android.net.LinkProperties; import android.net.NetworkCapabilities; @@ -83,5 +82,18 @@ * @param captivePortalData Captive portal data to be used */ void setCaptivePortalData(@NonNull LinkProperties lp, - @Nullable CaptivePortalData captivePortalData); + @Nullable CaptivePortalDataShim captivePortalData); + + /** + * Get the name of the given capability that carriers use. + * If the capability does not have a carrier-name, returns null. + * + * @param capability The capability to get the carrier-name of. + * @return The carrier-name of the capability, or null if it doesn't exist. + * @hide + */ + @Nullable + default String getCapabilityCarrierName(int capability) { + return null; + } }
diff --git a/apishim/common/com/android/networkstack/apishim/common/NetworkRequestShim.java b/apishim/common/com/android/networkstack/apishim/common/NetworkRequestShim.java new file mode 100644 index 0000000..d07d1ae --- /dev/null +++ b/apishim/common/com/android/networkstack/apishim/common/NetworkRequestShim.java
@@ -0,0 +1,56 @@ +/* + * Copyright (C) 2021 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.apishim.common; + +import android.net.NetworkRequest; +import android.util.Range; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.Set; + +/** + * Interface used to access API methods in {@link android.net.NetworkRequest}, with + * appropriate fallbacks if the methods are not yet part of the released API. + */ +public interface NetworkRequestShim { + /** + * See android.net.NetworkRequest.Builder#setUids. + * Set the {@code uids} into {@code builder}. + */ + void setUids(@NonNull NetworkRequest.Builder builder, + @Nullable Set<Range<Integer>> uids) throws UnsupportedApiLevelException; + + /** + * See android.net.NetworkRequest.Builder#setIncludeOtherUidNetworks. + */ + default NetworkRequest.Builder setIncludeOtherUidNetworks(NetworkRequest.Builder builder, + boolean include) throws UnsupportedApiLevelException { + throw new UnsupportedApiLevelException("Not supported before API 31."); + } + + /** + * See android.net.NetworkRequest.Builder(NetworkRequest). + * @throws UnsupportedApiLevelException if API is not available in the API level. + */ + default NetworkRequest.Builder newBuilder(@NonNull NetworkRequest request) + throws UnsupportedApiLevelException { + // Not supported before API 31. + throw new UnsupportedApiLevelException("Not supported before API 31."); + } +}
diff --git a/apishim/common/com/android/networkstack/apishim/common/SettingsShim.java b/apishim/common/com/android/networkstack/apishim/common/SettingsShim.java new file mode 100644 index 0000000..2453084 --- /dev/null +++ b/apishim/common/com/android/networkstack/apishim/common/SettingsShim.java
@@ -0,0 +1,35 @@ +/* + * Copyright (C) 2021 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.apishim.common; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** + * Interce for accessing API methods in {@link android.provider.Settings} by different API level. + */ +public interface SettingsShim { + /** + * @see android.provider.Settings#checkAndNoteWriteSettingsOperation(Context, int, String, + * String, boolean) + */ + boolean checkAndNoteWriteSettingsOperation(@NonNull Context context, int uid, + @NonNull String callingPackage, @Nullable String callingAttributionTag, + boolean throwException); +}
diff --git a/apishim/jarjar-rules-compat.txt b/apishim/jarjar-rules-compat.txt index dba2b49..4f34ccb 100644 --- a/apishim/jarjar-rules-compat.txt +++ b/apishim/jarjar-rules-compat.txt
@@ -1,7 +1,7 @@ # jarjar rules to use on API stable builds. # Use the latest stable apishim package as the main apishim package, to replace and avoid building # the unstable, non-compatibility shims. -# Once API 31 is stable, apishim/31/com.android.networkstack.apishim should be moved to the -# com.android.networkstack.apishim.api31 package, a new apishim/32/com.android.networkstack.apishim -# package should be created, and this rule should reference api31. -rule com.android.networkstack.apishim.api30.** com.android.networkstack.apishim.@1 \ No newline at end of file +# Once API 32 is stable, apishim/32/com.android.networkstack.apishim should be moved to the +# com.android.networkstack.apishim.api32 package, a new apishim/33/com.android.networkstack.apishim +# package should be created, and this rule should reference api32. +rule com.android.networkstack.apishim.api31.** com.android.networkstack.apishim.@1 \ No newline at end of file
diff --git a/common/captiveportal/Android.bp b/common/captiveportal/Android.bp index 0b49eb2..876e733 100644 --- a/common/captiveportal/Android.bp +++ b/common/captiveportal/Android.bp
@@ -14,6 +14,10 @@ // limitations under the License. // +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + java_library { name: "captiveportal-lib", srcs: ["src/**/*.java"], @@ -23,4 +27,4 @@ sdk_version: "system_current", // this is part of updatable modules(NetworkStack) which targets 29(Q) min_sdk_version: "29", -} \ No newline at end of file +}
diff --git a/common/captiveportal/src/android/net/captiveportal/CaptivePortalProbeResult.java b/common/captiveportal/src/android/net/captiveportal/CaptivePortalProbeResult.java index 2ba1dcc..8b388ad 100755 --- a/common/captiveportal/src/android/net/captiveportal/CaptivePortalProbeResult.java +++ b/common/captiveportal/src/android/net/captiveportal/CaptivePortalProbeResult.java
@@ -103,11 +103,22 @@ } public boolean isSuccessful() { - return mHttpResponseCode == SUCCESS_CODE; + return isSuccessCode(mHttpResponseCode); } public boolean isPortal() { - return !isSuccessful() && (mHttpResponseCode >= 200) && (mHttpResponseCode <= 399); + return isPortalCode(mHttpResponseCode); + } + + private static boolean isSuccessCode(int responseCode) { + return responseCode == SUCCESS_CODE; + } + + /** + * @return Whether the specified HTTP return code indicates a captive portal. + */ + public static boolean isPortalCode(int responseCode) { + return !isSuccessCode(responseCode) && (responseCode >= 200) && (responseCode <= 399); } public boolean isFailed() {
diff --git a/common/moduleutils/Android.bp b/common/moduleutils/Android.bp index 644b0a4..2230549 100644 --- a/common/moduleutils/Android.bp +++ b/common/moduleutils/Android.bp
@@ -17,21 +17,18 @@ // Shared utility sources to be used by multiple network modules // TODO: remove all frameworks/base dependencies on packages/modules/NetworkStack and // frameworks/base/packages/Tethering by moving these files to frameworks/libs/net. +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +// TODO: remove this filegroup together with services.net filegroup { name: "net-module-utils-srcs", srcs: [ - "src/android/net/util/SharedLog.java", - "src/android/net/shared/InitialConfiguration.java", - "src/android/net/shared/Layer2Information.java", - "src/android/net/shared/LinkPropertiesParcelableUtil.java", - "src/android/net/shared/ParcelableUtil.java", "src/android/net/shared/NetdUtils.java", - "src/android/net/shared/NetworkMonitorUtils.java", - "src/android/net/shared/ParcelableUtil.java", - "src/android/net/shared/PrivateDnsConfig.java", - "src/android/net/shared/ProvisioningConfiguration.java", "src/android/net/shared/RouteUtils.java", "src/android/net/util/InterfaceParams.java", + "src/android/net/util/SharedLog.java", ], visibility: [ "//frameworks/base/services/net", @@ -39,6 +36,19 @@ } filegroup { + name: "connectivity-module-utils-srcs", + srcs: [ + "src/android/net/util/SharedLog.java", + "src/android/net/shared/NetdUtils.java", + "src/android/net/shared/NetworkMonitorUtils.java", + "src/android/net/shared/RouteUtils.java", + ], + visibility: [ + "//packages/modules/Connectivity/service", + ] +} + +filegroup { name: "networkstack-module-utils-srcs", srcs: ["src/**/*.java"], visibility: [
diff --git a/common/moduleutils/src/android/net/ip/ConntrackMonitor.java b/common/moduleutils/src/android/net/ip/ConntrackMonitor.java index 9189002..6c72984 100644 --- a/common/moduleutils/src/android/net/ip/ConntrackMonitor.java +++ b/common/moduleutils/src/android/net/ip/ConntrackMonitor.java
@@ -51,6 +51,12 @@ public static final int NF_NETLINK_CONNTRACK_UPDATE = 2; public static final int NF_NETLINK_CONNTRACK_DESTROY = 4; + // The socket receive buffer size in bytes. If too many conntrack messages are sent too + // quickly, the conntrack messages can overflow the socket receive buffer. This can happen + // if too many connections are disconnected by losing network and so on. Use a large-enough + // buffer to avoid the error ENOBUFS while listening to the conntrack messages. + private static final int SOCKET_RECV_BUFSIZE = 6 * 1024 * 1024; + /** * A class for describing parsed netfilter conntrack events. */ @@ -176,7 +182,7 @@ public ConntrackMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull ConntrackEventConsumer cb) { super(h, log, TAG, OsConstants.NETLINK_NETFILTER, NF_NETLINK_CONNTRACK_NEW - | NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); + | NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY, SOCKET_RECV_BUFSIZE); mConsumer = cb; }
diff --git a/common/moduleutils/src/android/net/ip/NetlinkMonitor.java b/common/moduleutils/src/android/net/ip/NetlinkMonitor.java index 3d314f1..2025967 100644 --- a/common/moduleutils/src/android/net/ip/NetlinkMonitor.java +++ b/common/moduleutils/src/android/net/ip/NetlinkMonitor.java
@@ -21,6 +21,8 @@ import static android.system.OsConstants.AF_NETLINK; 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_RCVBUF; import android.annotation.NonNull; import android.net.netlink.NetlinkErrorMessage; @@ -56,9 +58,13 @@ protected final String mTag; private final int mFamily; private final int mBindGroups; + private final int mSockRcvbufSize; private static final boolean DBG = false; + // Default socket receive buffer size. This means the specific buffer size is not set. + private static final int DEFAULT_SOCKET_RECV_BUFSIZE = -1; + /** * Constructs a new {@code NetlinkMonitor} instance. * @@ -68,14 +74,23 @@ * @param tag The log tag to use for log messages. * @param family the Netlink socket family to, e.g., {@code NETLINK_ROUTE}. * @param bindGroups the netlink groups to bind to. + * @param sockRcvbufSize the specific socket receive buffer size in bytes. -1 means that don't + * set the specific socket receive buffer size in #createFd and use the default value in + * /proc/sys/net/core/rmem_default file. See SO_RCVBUF in man-pages/socket. */ public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag, - int family, int bindGroups) { + int family, int bindGroups, int sockRcvbufSize) { super(h, NetlinkSocket.DEFAULT_RECV_BUFSIZE); mLog = log.forSubComponent(tag); mTag = tag; mFamily = family; mBindGroups = bindGroups; + mSockRcvbufSize = sockRcvbufSize; + } + + public NetlinkMonitor(@NonNull Handler h, @NonNull SharedLog log, @NonNull String tag, + int family, int bindGroups) { + this(h, log, tag, family, bindGroups, DEFAULT_SOCKET_RECV_BUFSIZE); } @Override @@ -84,6 +99,9 @@ try { fd = Os.socket(AF_NETLINK, SOCK_DGRAM | SOCK_NONBLOCK, mFamily); + if (mSockRcvbufSize != DEFAULT_SOCKET_RECV_BUFSIZE) { + Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, mSockRcvbufSize); + } Os.bind(fd, makeNetlinkSocketAddress(0, mBindGroups)); NetlinkSocket.connectToKernel(fd);
diff --git a/common/moduleutils/src/android/net/shared/NetworkMonitorUtils.java b/common/moduleutils/src/android/net/shared/NetworkMonitorUtils.java index 981a576..0cd9f65 100644 --- a/common/moduleutils/src/android/net/shared/NetworkMonitorUtils.java +++ b/common/moduleutils/src/android/net/shared/NetworkMonitorUtils.java
@@ -19,6 +19,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; @@ -27,6 +28,8 @@ import android.net.NetworkCapabilities; +import com.android.modules.utils.build.SdkLevel; + /** @hide */ public class NetworkMonitorUtils { // This class is used by both NetworkMonitor and ConnectivityService, so it cannot use @@ -36,6 +39,14 @@ // TODO: use NetworkCapabilities.TRANSPORT_TEST once NetworkStack builds against API 31. private static final int TRANSPORT_TEST = 7; + // This class is used by both NetworkMonitor and ConnectivityService, so it cannot use + // NetworkStack shims, but at the same time cannot use non-system APIs. + // NET_CAPABILITY_NOT_VCN_MANAGED is system API as of S (so it is enforced to always be 28 and + // can't be changed). + // TODO: use NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED once NetworkStack builds against + // API 31. + public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; + // Network conditions broadcast constants public static final String ACTION_NETWORK_CONDITIONS_MEASURED = "android.net.conn.NETWORK_CONDITIONS_MEASURED"; @@ -59,11 +70,16 @@ public static boolean isPrivateDnsValidationRequired(NetworkCapabilities nc) { if (nc == null) return false; + final boolean isVcnManaged = SdkLevel.isAtLeastS() + && !nc.hasCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + final boolean isOemPaid = nc.hasCapability(NET_CAPABILITY_OEM_PAID) + && nc.hasCapability(NET_CAPABILITY_TRUSTED); + final boolean isDefaultCapable = nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) + && nc.hasCapability(NET_CAPABILITY_TRUSTED); + // TODO: Consider requiring validation for DUN networks. if (nc.hasCapability(NET_CAPABILITY_INTERNET) - && nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) - && nc.hasCapability(NET_CAPABILITY_TRUSTED)) { - // Real networks + && (isVcnManaged || isOemPaid || isDefaultCapable)) { return true; }
diff --git a/common/netlinkclient/Android.bp b/common/netlinkclient/Android.bp index 2b4a2d6..9a60e57 100644 --- a/common/netlinkclient/Android.bp +++ b/common/netlinkclient/Android.bp
@@ -14,6 +14,10 @@ // limitations under the License. // +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + java_library { name: "netlink-client", srcs: [
diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp index dc91881..31e920d 100644 --- a/common/networkstackclient/Android.bp +++ b/common/networkstackclient/Android.bp
@@ -15,6 +15,10 @@ // // AIDL interfaces between the core system and the networking mainline module. +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + aidl_interface { name: "ipmemorystore-aidl-interfaces", local_include_dir: "src", @@ -68,7 +72,7 @@ // For framework parcelables. "frameworks/base/core/java", // For API parcelables in connectivity - "frameworks/base/packages/Connectivity/framework/src", + "packages/modules/Connectivity/framework/aidl-export", "frameworks/native/aidl/binder", // For PersistableBundle.aidl ], srcs: [ @@ -129,6 +133,7 @@ "8", "9", "10", + "11", ], // TODO: have tethering depend on networkstack-client and set visibility to private visibility: [ @@ -141,25 +146,38 @@ java_library { name: "networkstack-client", sdk_version: "system_current", - // this is part of updatable modules(NetworkStack) which targets 29(Q) + // this is part of updatable modules(NetworkStack) which runs on Q and above min_sdk_version: "29", srcs: [ ":framework-annotations", + "src/android/net/ip/**/*.java", + "src/android/net/IpMemoryStore.java", "src/android/net/IpMemoryStoreClient.java", "src/android/net/ipmemorystore/**/*.java", + "src/android/net/NetworkMonitorManager.java", "src/android/net/networkstack/**/*.java", "src/android/net/networkstack/aidl/quirks/**/*.java", "src/android/net/shared/**/*.java", + "src/android/net/util/**/*.java", + ], + libs: [ + "net-utils-framework-common", // XXX for IpUtils.java only ], static_libs: [ - "ipmemorystore-aidl-interfaces-java", - "networkstack-aidl-interfaces-java", + "ipmemorystore-aidl-interfaces-V10-java", + "networkstack-aidl-interfaces-V11-java", ], visibility: [ - "//frameworks/base/packages/Tethering", + "//frameworks/base/packages/Connectivity/service", "//packages/modules/Connectivity/Tethering", + "//packages/modules/Connectivity/service", "//frameworks/base/services/net", "//frameworks/opt/net/wifi/service", + "//packages/apps/Bluetooth", "//packages/modules/NetworkStack", ], + apex_available: [ + "//apex_available:platform", + "com.android.tethering", + ], }
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/.hash b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/.hash new file mode 100644 index 0000000..2914d2a --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/.hash
@@ -0,0 +1 @@ +7fecd0a7a6d978705afad88c5e492613cc46e2cb
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/DataStallReportParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/DataStallReportParcelable.aidl new file mode 100644 index 0000000..771deda --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/DataStallReportParcelable.aidl
@@ -0,0 +1,42 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable DataStallReportParcelable { + long timestampMillis = 0; + int detectionMethod = 1; + int tcpPacketFailRate = 2; + int tcpMetricsCollectionPeriodMillis = 3; + int dnsConsecutiveTimeouts = 4; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/DhcpResultsParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/DhcpResultsParcelable.aidl new file mode 100644 index 0000000..31f2194 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/DhcpResultsParcelable.aidl
@@ -0,0 +1,44 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable DhcpResultsParcelable { + android.net.StaticIpConfiguration baseConfiguration; + int leaseDuration; + int mtu; + String serverAddress; + String vendorInfo; + @nullable String serverHostName; + @nullable String captivePortalApiUrl; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkMonitor.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkMonitor.aidl new file mode 100644 index 0000000..d92196d --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkMonitor.aidl
@@ -0,0 +1,59 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +/* @hide */ +interface INetworkMonitor { + oneway void start(); + oneway void launchCaptivePortalApp(); + oneway void notifyCaptivePortalAppFinished(int response); + oneway void setAcceptPartialConnectivity(); + oneway void forceReevaluation(int uid); + oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config); + oneway void notifyDnsResponse(int returnCode); + oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc); + oneway void notifyNetworkDisconnected(); + oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp); + oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc); + const int NETWORK_TEST_RESULT_VALID = 0; + const int NETWORK_TEST_RESULT_INVALID = 1; + const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2; + const int NETWORK_VALIDATION_RESULT_VALID = 1; + const int NETWORK_VALIDATION_RESULT_PARTIAL = 2; + const int NETWORK_VALIDATION_RESULT_SKIPPED = 4; + const int NETWORK_VALIDATION_PROBE_DNS = 4; + const int NETWORK_VALIDATION_PROBE_HTTP = 8; + const int NETWORK_VALIDATION_PROBE_HTTPS = 16; + const int NETWORK_VALIDATION_PROBE_FALLBACK = 32; + const int NETWORK_VALIDATION_PROBE_PRIVDNS = 64; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkMonitorCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkMonitorCallbacks.aidl new file mode 100644 index 0000000..36eda8e --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkMonitorCallbacks.aidl
@@ -0,0 +1,46 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +/* @hide */ +interface INetworkMonitorCallbacks { + oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor) = 0; + oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl) = 1; + oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config) = 2; + oneway void showProvisioningNotification(String action, String packageName) = 3; + oneway void hideProvisioningNotification() = 4; + oneway void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) = 5; + oneway void notifyNetworkTestedWithExtras(in android.net.NetworkTestResultParcelable result) = 6; + oneway void notifyDataStallSuspected(in android.net.DataStallReportParcelable report) = 7; + oneway void notifyCaptivePortalDataChanged(in android.net.CaptivePortalData data) = 8; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkStackConnector.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkStackConnector.aidl new file mode 100644 index 0000000..8120ffc --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkStackConnector.aidl
@@ -0,0 +1,42 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +/* @hide */ +interface INetworkStackConnector { + oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb); + oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb); + oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks); + oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb); + oneway void allowTestUid(int uid, in android.net.INetworkStackStatusCallback cb); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkStackStatusCallback.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkStackStatusCallback.aidl new file mode 100644 index 0000000..0b6b778 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,38 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +/* @hide */ +interface INetworkStackStatusCallback { + oneway void onStatusAvailable(int statusCode); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/InformationElementParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/InformationElementParcelable.aidl new file mode 100644 index 0000000..6103774 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/InformationElementParcelable.aidl
@@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable InformationElementParcelable { + int id; + byte[] payload; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/InitialConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/InitialConfigurationParcelable.aidl new file mode 100644 index 0000000..6a597e6 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/InitialConfigurationParcelable.aidl
@@ -0,0 +1,41 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable InitialConfigurationParcelable { + android.net.LinkAddress[] ipAddresses; + android.net.IpPrefix[] directlyConnectedRoutes; + String[] dnsServers; + String gateway; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/Layer2InformationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/Layer2InformationParcelable.aidl new file mode 100644 index 0000000..83796ee --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/Layer2InformationParcelable.aidl
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable Layer2InformationParcelable { + String l2Key; + String cluster; + android.net.MacAddress bssid; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/Layer2PacketParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/Layer2PacketParcelable.aidl new file mode 100644 index 0000000..4b3fff5 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/Layer2PacketParcelable.aidl
@@ -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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable Layer2PacketParcelable { + android.net.MacAddress dstMacAddress; + byte[] payload; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/NattKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/NattKeepalivePacketDataParcelable.aidl new file mode 100644 index 0000000..18cf954 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/NattKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,41 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable NattKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/NetworkTestResultParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/NetworkTestResultParcelable.aidl new file mode 100644 index 0000000..4d6d5a2 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/NetworkTestResultParcelable.aidl
@@ -0,0 +1,42 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable NetworkTestResultParcelable { + long timestampMillis; + int result; + int probesSucceeded; + int probesAttempted; + String redirectUrl; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/PrivateDnsConfigParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/PrivateDnsConfigParcel.aidl new file mode 100644 index 0000000..1457caf --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,39 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable PrivateDnsConfigParcel { + String hostname; + String[] ips; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ProvisioningConfigurationParcelable.aidl new file mode 100644 index 0000000..0b7a7a1 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ProvisioningConfigurationParcelable.aidl
@@ -0,0 +1,54 @@ +/* +** +** 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. +*/ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable ProvisioningConfigurationParcelable { + boolean enableIPv4; + boolean enableIPv6; + boolean usingMultinetworkPolicyTracker; + boolean usingIpReachabilityMonitor; + int requestedPreDhcpActionMs; + android.net.InitialConfigurationParcelable initialConfig; + android.net.StaticIpConfiguration staticIpConfig; + android.net.apf.ApfCapabilities apfCapabilities; + int provisioningTimeoutMs; + int ipv6AddrGenMode; + android.net.Network network; + String displayName; + boolean enablePreconnection; + @nullable android.net.ScanResultInfoParcelable scanResultInfo; + @nullable android.net.Layer2InformationParcelable layer2Info; + @nullable List<android.net.networkstack.aidl.dhcp.DhcpOption> options; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ScanResultInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ScanResultInfoParcelable.aidl new file mode 100644 index 0000000..94fc27f --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ScanResultInfoParcelable.aidl
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable ScanResultInfoParcelable { + String ssid; + String bssid; + android.net.InformationElementParcelable[] informationElements; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/TcpKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/TcpKeepalivePacketDataParcelable.aidl new file mode 100644 index 0000000..0e1c21c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/TcpKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,47 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable TcpKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; + int seq; + int ack; + int rcvWnd; + int rcvWndScale; + int tos; + int ttl; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/DhcpLeaseParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/DhcpLeaseParcelable.aidl new file mode 100644 index 0000000..3cd8860 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/DhcpLeaseParcelable.aidl
@@ -0,0 +1,43 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable DhcpLeaseParcelable { + byte[] clientId; + byte[] hwAddr; + int netAddr; + int prefixLength; + long expTime; + String hostname; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/DhcpServingParamsParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/DhcpServingParamsParcel.aidl new file mode 100644 index 0000000..fa412cb --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -0,0 +1,48 @@ +/** + * + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +@JavaDerive(toString=true) +parcelable DhcpServingParamsParcel { + int serverAddr; + int serverAddrPrefixLength; + int[] defaultRouters; + int[] dnsServers; + int[] excludedAddrs; + long dhcpLeaseTimeSecs; + int linkMtu; + boolean metered; + int singleClientAddr = 0; + boolean changePrefixOnDecline = false; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/IDhcpEventCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/IDhcpEventCallbacks.aidl new file mode 100644 index 0000000..9312f47 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/IDhcpEventCallbacks.aidl
@@ -0,0 +1,38 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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 IDhcpEventCallbacks { + oneway void onLeasesChanged(in List<android.net.dhcp.DhcpLeaseParcelable> newLeases); + oneway void onNewPrefixRequest(in android.net.IpPrefix currentPrefix); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/IDhcpServer.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/IDhcpServer.aidl new file mode 100644 index 0000000..1109f35 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/IDhcpServer.aidl
@@ -0,0 +1,45 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +/* @hide */ +interface IDhcpServer { + oneway void start(in android.net.INetworkStackStatusCallback cb) = 0; + oneway void startWithCallbacks(in android.net.INetworkStackStatusCallback statusCb, in android.net.dhcp.IDhcpEventCallbacks eventCb) = 3; + oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb) = 1; + oneway void stop(in android.net.INetworkStackStatusCallback cb) = 2; + const int STATUS_UNKNOWN = 0; + const int STATUS_SUCCESS = 1; + const int STATUS_INVALID_ARGUMENT = 2; + const int STATUS_UNKNOWN_ERROR = 3; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/IDhcpServerCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/IDhcpServerCallbacks.aidl new file mode 100644 index 0000000..ab8577c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -0,0 +1,38 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +/* @hide */ +interface IDhcpServerCallbacks { + oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ip/IIpClient.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ip/IIpClient.aidl new file mode 100644 index 0000000..1fe4c4c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ip/IIpClient.aidl
@@ -0,0 +1,52 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +/* @hide */ +interface IIpClient { + oneway void completedPreDhcpAction(); + oneway void confirmConfiguration(); + oneway void readPacketFilterComplete(in byte[] data); + oneway void shutdown(); + oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req); + oneway void stop(); + oneway void setTcpBufferSizes(in String tcpBufferSizes); + oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo); + oneway void setMulticastFilter(boolean enabled); + oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt); + oneway void removeKeepalivePacketFilter(int slot); + oneway void setL2KeyAndGroupHint(in String l2Key, in String cluster); + oneway void addNattKeepalivePacketFilter(int slot, in android.net.NattKeepalivePacketDataParcelable pkt); + oneway void notifyPreconnectionComplete(boolean success); + oneway void updateLayer2Information(in android.net.Layer2InformationParcelable info); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ip/IIpClientCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ip/IIpClientCallbacks.aidl new file mode 100644 index 0000000..488510d --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/ip/IIpClientCallbacks.aidl
@@ -0,0 +1,51 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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; +/* @hide */ +interface IIpClientCallbacks { + oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient); + oneway void onPreDhcpAction(); + oneway void onPostDhcpAction(); + oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults); + oneway void onProvisioningSuccess(in android.net.LinkProperties newLp); + oneway void onProvisioningFailure(in android.net.LinkProperties newLp); + oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp); + oneway void onReachabilityLost(in String logMsg); + oneway void onQuit(); + oneway void installPacketFilter(in byte[] filter); + oneway void startReadPacketFilter(); + oneway void setFallbackMulticastFilter(boolean enabled); + oneway void setNeighborDiscoveryOffload(boolean enable); + oneway void onPreconnectionStart(in List<android.net.Layer2PacketParcelable> packets); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/networkstack/aidl/dhcp/DhcpOption.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/networkstack/aidl/dhcp/DhcpOption.aidl new file mode 100644 index 0000000..eea3e0d --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/11/android/net/networkstack/aidl/dhcp/DhcpOption.aidl
@@ -0,0 +1,39 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file 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.networkstack.aidl.dhcp; +@JavaDerive(toString=true) +parcelable DhcpOption { + byte type; + @nullable byte[] value; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkMonitor.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkMonitor.aidl index db9145f..9e7b40d 100644 --- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkMonitor.aidl +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/INetworkMonitor.aidl
@@ -35,6 +35,7 @@ const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2; const int NETWORK_VALIDATION_RESULT_VALID = 1; const int NETWORK_VALIDATION_RESULT_PARTIAL = 2; + const int NETWORK_VALIDATION_RESULT_SKIPPED = 4; const int NETWORK_VALIDATION_PROBE_DNS = 4; const int NETWORK_VALIDATION_PROBE_HTTP = 8; const int NETWORK_VALIDATION_PROBE_HTTPS = 16;
diff --git a/common/networkstackclient/src/android/net/INetworkMonitor.aidl b/common/networkstackclient/src/android/net/INetworkMonitor.aidl index 3fc81a3..b124734 100644 --- a/common/networkstackclient/src/android/net/INetworkMonitor.aidl +++ b/common/networkstackclient/src/android/net/INetworkMonitor.aidl
@@ -44,10 +44,16 @@ // are set, then it's equal to NETWORK_TEST_RESULT_INVALID. If NETWORK_VALIDATION_RESULT_VALID // is set, then the network validates and equal to NETWORK_TEST_RESULT_VALID. If // NETWORK_VALIDATION_RESULT_PARTIAL is set, then the network has partial connectivity which - // is equal to NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY. NETWORK_VALIDATION_PROBE_* is set - // when the specific probe result of the network is resolved. + // is equal to NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY. Networks receiving validation that both + // do not require validation and are not validated will have NETWORK_VALIDATION_RESULT_SKIPPED + // set. NETWORK_VALIDATION_PROBE_* is set when the specific probe result of the network is + // resolved. const int NETWORK_VALIDATION_RESULT_VALID = 0x01; const int NETWORK_VALIDATION_RESULT_PARTIAL = 0x02; + const int NETWORK_VALIDATION_RESULT_SKIPPED = 0x04; + + // NETWORK_VALIDATION_RESULT_* and NETWORK_VALIDATION_PROBE_* are independent values sent in + // different ints. const int NETWORK_VALIDATION_PROBE_DNS = 0x04; const int NETWORK_VALIDATION_PROBE_HTTP = 0x08; const int NETWORK_VALIDATION_PROBE_HTTPS = 0x10;
diff --git a/common/networkstackclient/src/android/net/IpMemoryStore.java b/common/networkstackclient/src/android/net/IpMemoryStore.java new file mode 100644 index 0000000..f2c1d35 --- /dev/null +++ b/common/networkstackclient/src/android/net/IpMemoryStore.java
@@ -0,0 +1,98 @@ +/* + * 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 android.net.networkstack.ModuleNetworkStackClient; +import android.util.Log; + +import com.android.internal.annotations.VisibleForTesting; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +/** + * Manager class used to communicate with the ip memory store service in the network stack, + * which is running in a separate module. + * @hide +*/ +public class IpMemoryStore extends IpMemoryStoreClient { + private static final String TAG = IpMemoryStore.class.getSimpleName(); + @NonNull private final CompletableFuture<IIpMemoryStore> mService; + @NonNull private final AtomicReference<CompletableFuture<IIpMemoryStore>> mTailNode; + + public IpMemoryStore(@NonNull final Context context) { + super(context); + mService = new CompletableFuture<>(); + mTailNode = new AtomicReference<CompletableFuture<IIpMemoryStore>>(mService); + getModuleNetworkStackClient(context).fetchIpMemoryStore( + new IIpMemoryStoreCallbacks.Stub() { + @Override + public void onIpMemoryStoreFetched(@NonNull final IIpMemoryStore memoryStore) { + mService.complete(memoryStore); + } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } + + @Override + public String getInterfaceHash() { + return this.HASH; + } + }); + } + + /* + * If the IpMemoryStore is ready, this function will run the request synchronously. + * Otherwise, it will enqueue the requests for execution immediately after the + * service becomes ready. The requests are guaranteed to be executed in the order + * they are sumbitted. + */ + @Override + protected void runWhenServiceReady(Consumer<IIpMemoryStore> cb) throws ExecutionException { + mTailNode.getAndUpdate(future -> future.handle((store, exception) -> { + if (exception != null) { + // this should never happens since we also catch the exception below + Log.wtf(TAG, "Error fetching IpMemoryStore", exception); + return store; + } + + try { + cb.accept(store); + } catch (Exception e) { + Log.wtf(TAG, "Exception occurred: " + e.getMessage()); + } + return store; + })); + } + + @VisibleForTesting + protected ModuleNetworkStackClient getModuleNetworkStackClient(Context context) { + return ModuleNetworkStackClient.getInstance(context); + } + + /** Gets an instance of the memory store */ + @NonNull + public static IpMemoryStore getMemoryStore(final Context context) { + return new IpMemoryStore(context); + } +}
diff --git a/common/networkstackclient/src/android/net/NetworkMonitorManager.java b/common/networkstackclient/src/android/net/NetworkMonitorManager.java new file mode 100644 index 0000000..0f66981 --- /dev/null +++ b/common/networkstackclient/src/android/net/NetworkMonitorManager.java
@@ -0,0 +1,203 @@ +/* + * 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.Hide; +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. + */ +@Hide +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/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java b/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java new file mode 100644 index 0000000..b17fcaa --- /dev/null +++ b/common/networkstackclient/src/android/net/ip/IpClientCallbacks.java
@@ -0,0 +1,136 @@ +/* + * 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.ip; + +import android.net.DhcpResultsParcelable; +import android.net.Layer2PacketParcelable; +import android.net.LinkProperties; + +import java.util.List; + +/** + * Callbacks for handling IpClient events. + * + * This is a convenience class to allow clients not to override all methods of IIpClientCallbacks, + * and avoid unparceling arguments. + * These methods are called asynchronously on a Binder thread, as IpClient lives in a different + * process. + * @hide + */ +public class IpClientCallbacks { + + /** + * Callback called upon IpClient creation. + * + * @param ipClient The Binder token to communicate with IpClient. + */ + public void onIpClientCreated(IIpClient ipClient) {} + + /** + * Callback called prior to DHCP discovery/renewal. + * + * <p>In order to receive onPreDhcpAction(), call #withPreDhcpAction() when constructing a + * ProvisioningConfiguration. + * + * <p>Implementations of onPreDhcpAction() must call IpClient#completedPreDhcpAction() to + * indicate that DHCP is clear to proceed. + */ + public void onPreDhcpAction() {} + + /** + * Callback called after DHCP discovery/renewal. + */ + public void onPostDhcpAction() {} + + /** + * Callback called when new DHCP results are available. + * + * <p>This is purely advisory and not an indication of provisioning success or failure. This is + * only here for callers that want to expose DHCPv4 results to other APIs + * (e.g., WifiInfo#setInetAddress). + * + * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not + * the passed-in DhcpResults object is null. + */ + public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) { + // In general callbacks would not use a parcelable directly (DhcpResultsParcelable), and + // would use a wrapper instead, because of the lack of safety of stable parcelables. But + // there are already two classes in the tree for DHCP information: DhcpInfo and DhcpResults, + // and neither of them exposes an appropriate API (they are bags of mutable fields and can't + // be changed because they are public API and @UnsupportedAppUsage, being no better than the + // stable parcelable). Adding a third class would cost more than the gain considering that + // the only client of this callback is WiFi, which will end up converting the results to + // DhcpInfo anyway. + } + + /** + * Indicates that provisioning was successful. + */ + public void onProvisioningSuccess(LinkProperties newLp) {} + + /** + * Indicates that provisioning failed. + */ + public void onProvisioningFailure(LinkProperties newLp) {} + + /** + * Invoked on LinkProperties changes. + */ + public void onLinkPropertiesChange(LinkProperties newLp) {} + + /**Called when the internal IpReachabilityMonitor (if enabled) has + * detected the loss of a critical number of required neighbors. + */ + public void onReachabilityLost(String logMsg) {} + + /** + * Called when the IpClient state machine terminates. + */ + public void onQuit() {} + + /** + * Called to indicate that a new APF program must be installed to filter incoming packets. + */ + public void installPacketFilter(byte[] filter) {} + + /** + * Called to indicate that the APF Program & data buffer must be read asynchronously from the + * wifi driver. + * + * <p>Due to Wifi HAL limitations, the current implementation only supports dumping the entire + * buffer. In response to this request, the driver returns the data buffer asynchronously + * by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message. + */ + public void startReadPacketFilter() {} + + /** + * If multicast filtering cannot be accomplished with APF, this function will be called to + * actuate multicast filtering using another means. + */ + public void setFallbackMulticastFilter(boolean enabled) {} + + /** + * Enabled/disable Neighbor Discover offload functionality. This is called, for example, + * whenever 464xlat is being started or stopped. + */ + public void setNeighborDiscoveryOffload(boolean enable) {} + + /** + * Invoked on starting preconnection process. + */ + public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {} +}
diff --git a/common/networkstackclient/src/android/net/ip/IpClientManager.java b/common/networkstackclient/src/android/net/ip/IpClientManager.java new file mode 100644 index 0000000..b45405f --- /dev/null +++ b/common/networkstackclient/src/android/net/ip/IpClientManager.java
@@ -0,0 +1,326 @@ +/* + * 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.ip; + +import android.annotation.Hide; +import android.annotation.NonNull; +import android.net.NattKeepalivePacketData; +import android.net.ProxyInfo; +import android.net.TcpKeepalivePacketData; +import android.net.TcpKeepalivePacketDataParcelable; +import android.net.shared.Layer2Information; +import android.net.shared.ProvisioningConfiguration; +import android.net.util.KeepalivePacketDataUtil; +import android.os.Binder; +import android.os.RemoteException; +import android.util.Log; + +/** + * A convenience wrapper for IpClient. + * + * Wraps IIpClient 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 IIpClient 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. + */ +@Hide +public class IpClientManager { + @NonNull private final IIpClient mIpClient; + @NonNull private final String mTag; + + public IpClientManager(@NonNull IIpClient ipClient, @NonNull String tag) { + mIpClient = ipClient; + mTag = tag; + } + + public IpClientManager(@NonNull IIpClient ipClient) { + this(ipClient, IpClientManager.class.getSimpleName()); + } + + private void log(String s, Throwable e) { + Log.e(mTag, s, e); + } + + /** + * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be + * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to + * proceed. + */ + public boolean completedPreDhcpAction() { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.completedPreDhcpAction(); + return true; + } catch (RemoteException e) { + log("Error completing PreDhcpAction", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Confirm the provisioning configuration. + */ + public boolean confirmConfiguration() { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.confirmConfiguration(); + return true; + } catch (RemoteException e) { + log("Error confirming IpClient configuration", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Indicate that packet filter read is complete. + */ + public boolean readPacketFilterComplete(byte[] data) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.readPacketFilterComplete(data); + return true; + } catch (RemoteException e) { + log("Error notifying IpClient of packet filter read", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Shut down this IpClient instance altogether. + */ + public boolean shutdown() { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.shutdown(); + return true; + } catch (RemoteException e) { + log("Error shutting down IpClient", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Start provisioning with the provided parameters. + */ + public boolean startProvisioning(ProvisioningConfiguration prov) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.startProvisioning(prov.toStableParcelable()); + return true; + } catch (RemoteException e) { + log("Error starting IpClient provisioning", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Stop this IpClient. + * + * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}. + */ + public boolean stop() { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.stop(); + return true; + } catch (RemoteException e) { + log("Error stopping IpClient", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * 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 boolean setTcpBufferSizes(String tcpBufferSizes) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.setTcpBufferSizes(tcpBufferSizes); + return true; + } catch (RemoteException e) { + log("Error setting IpClient TCP buffer sizes", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * 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 boolean setHttpProxy(ProxyInfo proxyInfo) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.setHttpProxy(proxyInfo); + return true; + } catch (RemoteException e) { + log("Error setting IpClient proxy", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Enable or disable the multicast filter. Attempts to use APF to accomplish the filtering, + * if not, Callback.setFallbackMulticastFilter() is called. + */ + public boolean setMulticastFilter(boolean enabled) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.setMulticastFilter(enabled); + return true; + } catch (RemoteException e) { + log("Error setting multicast filter", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Add a TCP keepalive packet filter before setting up keepalive offload. + */ + public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketData pkt) { + return addKeepalivePacketFilter(slot, KeepalivePacketDataUtil.toStableParcelable(pkt)); + } + + /** + * Add a TCP keepalive packet filter before setting up keepalive offload. + * @deprecated This method is for use on pre-S platforms where TcpKeepalivePacketData is not + * system API. On newer platforms use + * addKeepalivePacketFilter(int, TcpKeepalivePacketData) instead. + */ + @Deprecated + public boolean addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.addKeepalivePacketFilter(slot, pkt); + return true; + } catch (RemoteException e) { + log("Error adding Keepalive Packet Filter ", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Add a NAT-T keepalive packet filter before setting up keepalive offload. + */ + public boolean addKeepalivePacketFilter(int slot, NattKeepalivePacketData pkt) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.addNattKeepalivePacketFilter( + slot, KeepalivePacketDataUtil.toStableParcelable(pkt)); + return true; + } catch (RemoteException e) { + log("Error adding NAT-T Keepalive Packet Filter ", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Remove a keepalive packet filter after stopping keepalive offload. + */ + public boolean removeKeepalivePacketFilter(int slot) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.removeKeepalivePacketFilter(slot); + return true; + } catch (RemoteException e) { + log("Error removing Keepalive Packet Filter ", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Set the L2 key and group hint for storing info into the memory store. + */ + public boolean setL2KeyAndGroupHint(String l2Key, String groupHint) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.setL2KeyAndGroupHint(l2Key, groupHint); + return true; + } catch (RemoteException e) { + log("Failed setL2KeyAndGroupHint", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Notify IpClient that preconnection is complete and that the link is ready for use. + * The success parameter indicates whether the packets passed in by 'onPreconnectionStart' + * were successfully sent to the network or not. + */ + public boolean notifyPreconnectionComplete(boolean success) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.notifyPreconnectionComplete(success); + return true; + } catch (RemoteException e) { + log("Error notifying IpClient Preconnection completed", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } + + /** + * Update the bssid, L2 key and group hint layer2 information. + */ + public boolean updateLayer2Information(Layer2Information info) { + final long token = Binder.clearCallingIdentity(); + try { + mIpClient.updateLayer2Information(info.toStableParcelable()); + return true; + } catch (RemoteException e) { + log("Error updating layer2 information", e); + return false; + } finally { + Binder.restoreCallingIdentity(token); + } + } +}
diff --git a/common/networkstackclient/src/android/net/ip/IpClientUtil.java b/common/networkstackclient/src/android/net/ip/IpClientUtil.java new file mode 100644 index 0000000..1b55776 --- /dev/null +++ b/common/networkstackclient/src/android/net/ip/IpClientUtil.java
@@ -0,0 +1,204 @@ +/* + * 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.ip; + +import android.content.Context; +import android.net.DhcpResultsParcelable; +import android.net.Layer2PacketParcelable; +import android.net.LinkProperties; +import android.net.networkstack.ModuleNetworkStackClient; +import android.os.ConditionVariable; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; + + +/** + * Utilities and wrappers to simplify communication with IpClient, which lives in the NetworkStack + * process. + * + * @hide + */ +public class IpClientUtil { + // TODO: remove with its callers + public static final String DUMP_ARG = "ipclient"; + + /** + * Subclass of {@link IpClientCallbacks} allowing clients to block until provisioning is + * complete with {@link WaitForProvisioningCallbacks#waitForProvisioning()}. + */ + public static class WaitForProvisioningCallbacks extends IpClientCallbacks { + private final ConditionVariable mCV = new ConditionVariable(); + private LinkProperties mCallbackLinkProperties; + + /** + * Block until either {@link #onProvisioningSuccess(LinkProperties)} or + * {@link #onProvisioningFailure(LinkProperties)} is called. + */ + public LinkProperties waitForProvisioning() { + mCV.block(); + return mCallbackLinkProperties; + } + + @Override + public void onProvisioningSuccess(LinkProperties newLp) { + mCallbackLinkProperties = newLp; + mCV.open(); + } + + @Override + public void onProvisioningFailure(LinkProperties newLp) { + mCallbackLinkProperties = null; + mCV.open(); + } + } + + /** + * Create a new IpClient. + * + * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of + * {@link IIpClientCallbacks}. + * @see {@link ModuleNetworkStackClient#makeIpClient(String, IIpClientCallbacks)} + */ + public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) { + ModuleNetworkStackClient.getInstance(context) + .makeIpClient(ifName, new IpClientCallbacksProxy(callback)); + } + + /** + * Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}. + */ + private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub { + protected final IpClientCallbacks mCb; + + /** + * Create a new IpClientCallbacksProxy. + */ + IpClientCallbacksProxy(IpClientCallbacks cb) { + mCb = cb; + } + + @Override + public void onIpClientCreated(IIpClient ipClient) { + mCb.onIpClientCreated(ipClient); + } + + @Override + public void onPreDhcpAction() { + mCb.onPreDhcpAction(); + } + + @Override + public void onPostDhcpAction() { + mCb.onPostDhcpAction(); + } + + // This is purely advisory and not an indication of provisioning + // success or failure. This is only here for callers that want to + // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress). + // DHCPv4 or static IPv4 configuration failure or success can be + // determined by whether or not the passed-in DhcpResults object is + // null or not. + @Override + public void onNewDhcpResults(DhcpResultsParcelable dhcpResults) { + mCb.onNewDhcpResults(dhcpResults); + } + + @Override + public void onProvisioningSuccess(LinkProperties newLp) { + mCb.onProvisioningSuccess(newLp); + } + @Override + public void onProvisioningFailure(LinkProperties newLp) { + mCb.onProvisioningFailure(newLp); + } + + // Invoked on LinkProperties changes. + @Override + public void onLinkPropertiesChange(LinkProperties newLp) { + mCb.onLinkPropertiesChange(newLp); + } + + // Called when the internal IpReachabilityMonitor (if enabled) has + // detected the loss of a critical number of required neighbors. + @Override + public void onReachabilityLost(String logMsg) { + mCb.onReachabilityLost(logMsg); + } + + // Called when the IpClient state machine terminates. + @Override + public void onQuit() { + mCb.onQuit(); + } + + // Install an APF program to filter incoming packets. + @Override + public void installPacketFilter(byte[] filter) { + mCb.installPacketFilter(filter); + } + + // Asynchronously read back the APF program & data buffer from the wifi driver. + // Due to Wifi HAL limitations, the current implementation only supports dumping the entire + // buffer. In response to this request, the driver returns the data buffer asynchronously + // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message. + @Override + public void startReadPacketFilter() { + mCb.startReadPacketFilter(); + } + + // If multicast filtering cannot be accomplished with APF, this function will be called to + // actuate multicast filtering using another means. + @Override + public void setFallbackMulticastFilter(boolean enabled) { + mCb.setFallbackMulticastFilter(enabled); + } + + // Enabled/disable Neighbor Discover offload functionality. This is + // called, for example, whenever 464xlat is being started or stopped. + @Override + public void setNeighborDiscoveryOffload(boolean enable) { + mCb.setNeighborDiscoveryOffload(enable); + } + + // Invoked on starting preconnection process. + @Override + public void onPreconnectionStart(List<Layer2PacketParcelable> packets) { + mCb.onPreconnectionStart(packets); + } + + @Override + public int getInterfaceVersion() { + return this.VERSION; + } + + @Override + public String getInterfaceHash() { + return this.HASH; + } + } + + /** + * Dump logs for the specified IpClient. + * TODO: remove callers and delete + */ + public static void dumpIpClient( + IIpClient connector, FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("IpClient logs have moved to dumpsys network_stack"); + } +}
diff --git a/common/moduleutils/src/android/net/shared/InitialConfiguration.java b/common/networkstackclient/src/android/net/shared/InitialConfiguration.java similarity index 100% rename from common/moduleutils/src/android/net/shared/InitialConfiguration.java rename to common/networkstackclient/src/android/net/shared/InitialConfiguration.java
diff --git a/common/moduleutils/src/android/net/shared/Layer2Information.java b/common/networkstackclient/src/android/net/shared/Layer2Information.java similarity index 100% rename from common/moduleutils/src/android/net/shared/Layer2Information.java rename to common/networkstackclient/src/android/net/shared/Layer2Information.java
diff --git a/common/moduleutils/src/android/net/shared/ParcelableUtil.java b/common/networkstackclient/src/android/net/shared/ParcelableUtil.java similarity index 100% rename from common/moduleutils/src/android/net/shared/ParcelableUtil.java rename to common/networkstackclient/src/android/net/shared/ParcelableUtil.java
diff --git a/common/moduleutils/src/android/net/shared/PrivateDnsConfig.java b/common/networkstackclient/src/android/net/shared/PrivateDnsConfig.java similarity index 100% rename from common/moduleutils/src/android/net/shared/PrivateDnsConfig.java rename to common/networkstackclient/src/android/net/shared/PrivateDnsConfig.java
diff --git a/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java b/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java similarity index 96% rename from common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java rename to common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java index d3bc04d..6ee9b73 100644 --- a/common/moduleutils/src/android/net/shared/ProvisioningConfiguration.java +++ b/common/networkstackclient/src/android/net/shared/ProvisioningConfiguration.java
@@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; -import android.net.INetd; import android.net.InformationElementParcelable; import android.net.Network; import android.net.ProvisioningConfigurationParcelable; @@ -76,6 +75,13 @@ // allowing for 10% jitter. private static final int DEFAULT_TIMEOUT_MS = 18 * 1000; + // TODO: These cannot be imported from INetd.aidl, because networkstack-client cannot depend on + // INetd, as there are users of IpClient that depend on INetd directly (potentially at a + // different version, which is not allowed by the build system). + // Find a better way to express these constants. + public static final int IPV6_ADDR_GEN_MODE_EUI64 = 0; + public static final int IPV6_ADDR_GEN_MODE_STABLE_PRIVACY = 2; + /** * Builder to create a {@link ProvisioningConfiguration}. */ @@ -180,7 +186,7 @@ * Specify that IPv6 address generation should use a random MAC address. */ public Builder withRandomMacAddress() { - mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64; + mConfig.mIPv6AddrGenMode = IPV6_ADDR_GEN_MODE_EUI64; return this; } @@ -188,7 +194,7 @@ * Specify that IPv6 address generation should use a stable MAC address. */ public Builder withStableMacAddress() { - mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; + mConfig.mIPv6AddrGenMode = IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; return this; } @@ -437,7 +443,7 @@ public StaticIpConfiguration mStaticIpConfig; public ApfCapabilities mApfCapabilities; public int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS; - public int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; + public int mIPv6AddrGenMode = IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; public Network mNetwork = null; public String mDisplayName = null; public ScanResultInfo mScanResultInfo;
diff --git a/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java b/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java new file mode 100644 index 0000000..5666985 --- /dev/null +++ b/common/networkstackclient/src/android/net/util/KeepalivePacketDataUtil.java
@@ -0,0 +1,223 @@ +/* + * 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 static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.InvalidPacketException; +import android.net.KeepalivePacketData; +import android.net.NattKeepalivePacketData; +import android.net.NattKeepalivePacketDataParcelable; +import android.net.TcpKeepalivePacketData; +import android.net.TcpKeepalivePacketDataParcelable; +import android.os.Build; +import android.system.OsConstants; +import android.util.Log; + +import com.android.net.module.util.IpUtils; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Utility class to convert to/from keepalive data parcelables. + * + * TODO: move to networkstack-client library when it is moved to frameworks/libs/net. + * This class cannot go into other shared libraries as it depends on NetworkStack AIDLs. + * @hide + */ +public final class KeepalivePacketDataUtil { + private static final int IPV4_HEADER_LENGTH = 20; + private static final int IPV6_HEADER_LENGTH = 40; + private static final int TCP_HEADER_LENGTH = 20; + + private static final String TAG = KeepalivePacketDataUtil.class.getSimpleName(); + + /** + * Convert a NattKeepalivePacketData to a NattKeepalivePacketDataParcelable. + */ + @NonNull + public static NattKeepalivePacketDataParcelable toStableParcelable( + @NonNull NattKeepalivePacketData pkt) { + final NattKeepalivePacketDataParcelable parcel = new NattKeepalivePacketDataParcelable(); + final InetAddress srcAddress = pkt.getSrcAddress(); + final InetAddress dstAddress = pkt.getDstAddress(); + parcel.srcAddress = srcAddress.getAddress(); + parcel.srcPort = pkt.getSrcPort(); + parcel.dstAddress = dstAddress.getAddress(); + parcel.dstPort = pkt.getDstPort(); + return parcel; + } + + /** + * Convert a TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable. + */ + @NonNull + public static TcpKeepalivePacketDataParcelable toStableParcelable( + @NonNull TcpKeepalivePacketData pkt) { + final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable(); + final InetAddress srcAddress = pkt.getSrcAddress(); + final InetAddress dstAddress = pkt.getDstAddress(); + parcel.srcAddress = srcAddress.getAddress(); + parcel.srcPort = pkt.getSrcPort(); + parcel.dstAddress = dstAddress.getAddress(); + parcel.dstPort = pkt.getDstPort(); + parcel.seq = pkt.getTcpSeq(); + parcel.ack = pkt.getTcpAck(); + parcel.rcvWnd = pkt.getTcpWindow(); + parcel.rcvWndScale = pkt.getTcpWindowScale(); + parcel.tos = pkt.getIpTos(); + parcel.ttl = pkt.getIpTtl(); + return parcel; + } + + /** + * Factory method to create tcp keepalive packet structure. + * @hide + */ + public static TcpKeepalivePacketData fromStableParcelable( + TcpKeepalivePacketDataParcelable tcpDetails) throws InvalidPacketException { + final byte[] packet; + try { + if ((tcpDetails.srcAddress != null) && (tcpDetails.dstAddress != null) + && (tcpDetails.srcAddress.length == 4 /* V4 IP length */) + && (tcpDetails.dstAddress.length == 4 /* V4 IP length */)) { + packet = buildV4Packet(tcpDetails); + } else { + // TODO: support ipv6 + throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); + } + return new TcpKeepalivePacketData( + InetAddress.getByAddress(tcpDetails.srcAddress), + tcpDetails.srcPort, + InetAddress.getByAddress(tcpDetails.dstAddress), + tcpDetails.dstPort, + packet, + tcpDetails.seq, tcpDetails.ack, tcpDetails.rcvWnd, tcpDetails.rcvWndScale, + tcpDetails.tos, tcpDetails.ttl); + } catch (UnknownHostException e) { + throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS); + } + + } + + /** + * Build ipv4 tcp keepalive packet, not including the link-layer header. + */ + // TODO : if this code is ever moved to the network stack, factorize constants with the ones + // over there. + private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) { + final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH; + ByteBuffer buf = ByteBuffer.allocate(length); + buf.order(ByteOrder.BIG_ENDIAN); + buf.put((byte) 0x45); // IP version and IHL + buf.put((byte) tcpDetails.tos); // TOS + buf.putShort((short) length); + buf.putInt(0x00004000); // ID, flags=DF, offset + buf.put((byte) tcpDetails.ttl); // TTL + buf.put((byte) OsConstants.IPPROTO_TCP); + final int ipChecksumOffset = buf.position(); + buf.putShort((short) 0); // IP checksum + buf.put(tcpDetails.srcAddress); + buf.put(tcpDetails.dstAddress); + buf.putShort((short) tcpDetails.srcPort); + buf.putShort((short) tcpDetails.dstPort); + buf.putInt(tcpDetails.seq); // Sequence Number + buf.putInt(tcpDetails.ack); // ACK + buf.putShort((short) 0x5010); // TCP length=5, flags=ACK + buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size + final int tcpChecksumOffset = buf.position(); + buf.putShort((short) 0); // TCP checksum + // URG is not set therefore the urgent pointer is zero. + buf.putShort((short) 0); // Urgent pointer + + buf.putShort(ipChecksumOffset, com.android.net.module.util.IpUtils.ipChecksum(buf, 0)); + buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum( + buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH)); + + return buf.array(); + } + + // TODO: add buildV6Packet. + + /** + * Get a {@link TcpKeepalivePacketDataParcelable} from {@link KeepalivePacketData}, if the + * generic class actually contains TCP keepalive data. + * + * @deprecated This method is used on R platforms where android.net.TcpKeepalivePacketData was + * not yet system API. Newer platforms should use android.net.TcpKeepalivePacketData directly. + * + * @param data A {@link KeepalivePacketData} that may contain TCP keepalive data. + * @return A parcelable containing TCP keepalive data, or null if the input data does not + * contain TCP keepalive data. + */ + @Deprecated + @SuppressWarnings("AndroidFrameworkCompatChange") // API version check used to Log.wtf + @Nullable + public static TcpKeepalivePacketDataParcelable parseTcpKeepalivePacketData( + @Nullable KeepalivePacketData data) { + if (data == null) return null; + + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) { + Log.wtf(TAG, "parseTcpKeepalivePacketData should not be used after R, use " + + "TcpKeepalivePacketData instead."); + } + + // Reconstruct TcpKeepalivePacketData from the packet contained in KeepalivePacketData + final ByteBuffer buffer = ByteBuffer.wrap(data.getPacket()); + buffer.order(ByteOrder.BIG_ENDIAN); + + // Most of the fields are accessible from the KeepalivePacketData superclass: instead of + // using Struct to parse everything, just extract the extra fields necessary for + // TcpKeepalivePacketData. + final int tcpSeq; + final int tcpAck; + final int wndSize; + final int ipTos; + final int ttl; + try { + // This only support IPv4, because TcpKeepalivePacketData only supports IPv4 for R and + // below, and this method should not be used on newer platforms. + tcpSeq = buffer.getInt(IPV4_HEADER_LENGTH + 4); + tcpAck = buffer.getInt(IPV4_HEADER_LENGTH + 8); + wndSize = buffer.getShort(IPV4_HEADER_LENGTH + 14); + ipTos = buffer.get(1); + ttl = buffer.get(8); + } catch (IndexOutOfBoundsException e) { + return null; + } + + final TcpKeepalivePacketDataParcelable p = new TcpKeepalivePacketDataParcelable(); + p.srcAddress = data.getSrcAddress().getAddress(); + p.srcPort = data.getSrcPort(); + p.dstAddress = data.getDstAddress().getAddress(); + p.dstPort = data.getDstPort(); + p.seq = tcpSeq; + p.ack = tcpAck; + // TcpKeepalivePacketData could actually use non-zero wndScale, but this does not affect + // actual functionality as generated packets will be the same (no wndScale option added) + p.rcvWnd = wndSize; + p.rcvWndScale = 0; + p.tos = ipTos; + p.ttl = ttl; + return p; + } +}
diff --git a/jarjar-rules-shared.txt b/jarjar-rules-shared.txt index bb2acd4..e8c9c19 100644 --- a/jarjar-rules-shared.txt +++ b/jarjar-rules-shared.txt
@@ -11,3 +11,6 @@ rule android.util.LocalLog* android.net.networkstack.util.LocalLog@1 rule android.util.IndentingPrintWriter* android.net.networkstack.util.AndroidUtilIndentingPrintWriter@1 + +# Classes from modules-utils-build_system +rule com.android.modules.utils.build.** com.android.networkstack.utils.build.@1 \ No newline at end of file
diff --git a/lint-baseline-api-30-shims.xml b/lint-baseline-api-30-shims.xml new file mode 100644 index 0000000..da541cd --- /dev/null +++ b/lint-baseline-api-30-shims.xml
@@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#getNat64Prefix`" + errorLine1=" return lp.getNat64Prefix();" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/apishim/30/com/android/networkstack/apishim/api30/NetworkInformationShimImpl.java" + line="85" + column="19"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#setNat64Prefix`" + errorLine1=" lp.setNat64Prefix(prefix);" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/apishim/30/com/android/networkstack/apishim/api30/NetworkInformationShimImpl.java" + line="90" + column="12"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#setDhcpServerAddress`" + errorLine1=" lp.setDhcpServerAddress(serverAddress);" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/apishim/30/com/android/networkstack/apishim/api30/NetworkInformationShimImpl.java" + line="109" + column="12"/> + </issue> + +</issues>
diff --git a/lint-baseline-current-lib.xml b/lint-baseline-current-lib.xml new file mode 100644 index 0000000..e8cfe3e --- /dev/null +++ b/lint-baseline-current-lib.xml
@@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#getNat64Prefix`" + errorLine1=" newLp.setNat64Prefix(netlinkLinkProperties.getNat64Prefix());" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/src/android/net/ip/IpClient.java" + line="1337" + column="52"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#setNat64Prefix`" + errorLine1=" newLp.setNat64Prefix(netlinkLinkProperties.getNat64Prefix());" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/src/android/net/ip/IpClient.java" + line="1337" + column="15"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.telephony.NetworkRegistrationInfo#getCellIdentity`" + errorLine1=" nri == null ? null : nri.getCellIdentity());" + errorLine2=" ~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java" + line="3088" + column="46"/> + </issue> + +</issues>
diff --git a/lint-baseline-stable-lib.xml b/lint-baseline-stable-lib.xml new file mode 100644 index 0000000..e8cfe3e --- /dev/null +++ b/lint-baseline-stable-lib.xml
@@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#getNat64Prefix`" + errorLine1=" newLp.setNat64Prefix(netlinkLinkProperties.getNat64Prefix());" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/src/android/net/ip/IpClient.java" + line="1337" + column="52"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#setNat64Prefix`" + errorLine1=" newLp.setNat64Prefix(netlinkLinkProperties.getNat64Prefix());" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/src/android/net/ip/IpClient.java" + line="1337" + column="15"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.telephony.NetworkRegistrationInfo#getCellIdentity`" + errorLine1=" nri == null ? null : nri.getCellIdentity());" + errorLine2=" ~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java" + line="3088" + column="46"/> + </issue> + +</issues>
diff --git a/proguard.flags b/proguard.flags index af4262a..7f8f207 100644 --- a/proguard.flags +++ b/proguard.flags
@@ -8,6 +8,10 @@ static final int EVENT_*; } +-keepclassmembers public class * extends com.android.networkstack.util.Struct { + *; +} + # The lite proto runtime uses reflection to access fields based on the names in # the schema, keep all the fields. # This replicates the base proguard rule used by the build by default
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index 4772691..2267cd4 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml
@@ -18,9 +18,9 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="notification_channel_name_connected" msgid="1795068343200033922">"אימות של פורטל שבוי"</string> <string name="notification_channel_description_connected" msgid="7239184168268014518">"התראות המוצגות כשהמכשיר אומת בהצלחה וחובר לרשת של פורטל שבוי"</string> - <string name="notification_channel_name_network_venue_info" msgid="6526543187249265733">"מידע על מקום רשת"</string> + <string name="notification_channel_name_network_venue_info" msgid="6526543187249265733">"מידע על מקום הרשת"</string> <string name="notification_channel_description_network_venue_info" msgid="5131499595382733605">"התראות המוצגות כדי לציין שלרשת יש דף מידע על מקום"</string> - <string name="connected" msgid="4563643884927480998">"מחובר"</string> + <string name="connected" msgid="4563643884927480998">"המכשיר מחובר"</string> <string name="tap_for_info" msgid="6849746325626883711">"מחוברת / יש להקיש כדי להציג את האתר"</string> <string name="application_label" msgid="1322847171305285454">"ניהול רשתות"</string> </resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 500d584..b7ff1bc 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml
@@ -20,7 +20,7 @@ <string name="notification_channel_description_connected" msgid="7239184168268014518">"यन्त्र क्याप्टिभ पोर्टल नेटवर्कमा सफलतापूर्वक जोडिएको कुरा प्रमाणित भएपछि देखाइने सूचनाहरू"</string> <string name="notification_channel_name_network_venue_info" msgid="6526543187249265733">"नेटवर्कको स्थानसम्बन्धी जानकारी"</string> <string name="notification_channel_description_network_venue_info" msgid="5131499595382733605">"नेटवर्कको स्थानसम्बन्धी जानकारी भएको पृष्ठ रहेको सङ्केत गर्न देखाइने सूचनाहरू"</string> - <string name="connected" msgid="4563643884927480998">"जोडिएको छ"</string> + <string name="connected" msgid="4563643884927480998">"कनेक्ट गरिएको छ"</string> <string name="tap_for_info" msgid="6849746325626883711">"जोडियो / वेबसाइट हेर्न ट्याप गर्नुहोस्"</string> <string name="application_label" msgid="1322847171305285454">"नेटवर्क व्यवस्थापक"</string> </resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 527d895..7b54302 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml
@@ -17,9 +17,9 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="notification_channel_name_connected" msgid="1795068343200033922">"Verificatie van captive portal"</string> - <string name="notification_channel_description_connected" msgid="7239184168268014518">"Er worden meldingen weergegeven als het apparaat is geverifieerd voor een captive portal-netwerk"</string> + <string name="notification_channel_description_connected" msgid="7239184168268014518">"Er worden meldingen getoond als het apparaat is geverifieerd voor een captive portal-netwerk"</string> <string name="notification_channel_name_network_venue_info" msgid="6526543187249265733">"Netwerklocatie-informatie"</string> - <string name="notification_channel_description_network_venue_info" msgid="5131499595382733605">"Er worden meldingen weergegeven om aan te geven dat het netwerk een locatie-informatiepagina heeft"</string> + <string name="notification_channel_description_network_venue_info" msgid="5131499595382733605">"Er worden meldingen getoond om aan te geven dat het netwerk een locatie-informatiepagina heeft"</string> <string name="connected" msgid="4563643884927480998">"Verbonden"</string> <string name="tap_for_info" msgid="6849746325626883711">"Verbonden / Tik om de website te bekijken"</string> <string name="application_label" msgid="1322847171305285454">"Netwerkbeheer"</string>
diff --git a/res/values/config.xml b/res/values/config.xml index d6a11ab..805ca04 100644 --- a/res/values/config.xml +++ b/res/values/config.xml
@@ -105,4 +105,20 @@ increased until reaching the config_max_retry_timer. --> <integer name="config_evaluating_bandwidth_min_retry_timer_ms"></integer> <integer name="config_evaluating_bandwidth_max_retry_timer_ms"></integer> + + <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames + Those frames are identified by the field Eth-type having values + less than 0x600 --> + <bool name="config_apfDrop802_3Frames">true</bool> + + <!-- An array of Denylisted EtherType, packets with EtherTypes within this array + will be dropped + TODO: need to put proper values, these are for testing purposes only --> + <integer-array name="config_apfEthTypeDenyList"> + <item>0x88A2</item> + <item>0x88A4</item> + <item>0x88B8</item> + <item>0x88CD</item> + <item>0x88E3</item> + </integer-array> </resources>
diff --git a/res/values/overlayable.xml b/res/values/overlayable.xml index b2967b9..bfb450e 100644 --- a/res/values/overlayable.xml +++ b/res/values/overlayable.xml
@@ -77,6 +77,13 @@ <item type="integer" name="config_evaluating_bandwidth_timeout_ms"/> <item type="integer" name="config_evaluating_bandwidth_min_retry_timer_ms"/> <item type="integer" name="config_evaluating_bandwidth_max_retry_timer_ms"/> + + <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames + Those frames are identified by the field Eth-type having values less than 0x600 --> + <item type="bool" name="config_apfDrop802_3Frames"/> + <!-- An array of Denylisted EtherType, packets with EtherTypes within this array + will be dropped --> + <item type="array" name="config_apfEthTypeDenyList"/> </policy> </overlayable> </resources>
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java index 0bb4094..7a13392 100644 --- a/src/android/net/apf/ApfFilter.java +++ b/src/android/net/apf/ApfFilter.java
@@ -27,6 +27,7 @@ import static android.system.OsConstants.IPPROTO_UDP; import static android.system.OsConstants.SOCK_RAW; +import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST; import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE; import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT; import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; @@ -285,8 +286,6 @@ 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; @@ -1254,7 +1253,7 @@ // 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); + gen.addJumpIfBytesNotEqual(Register.R0, ETHER_BROADCAST, mCountAndPassLabel); // Either a unicast request, a unicast reply, or a broadcast reply. gen.defineLabel(checkTargetIPv4); @@ -1350,7 +1349,7 @@ // 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); + gen.addJumpIfBytesNotEqual(Register.R0, ETHER_BROADCAST, mCountAndPassLabel); maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST); gen.addJump(mCountAndDropLabel); } else { @@ -1412,7 +1411,7 @@ // pass // if it's ICMPv6 RS to any: // drop - // if it's ICMPv6 NA to ff02::1: + // if it's ICMPv6 NA to anything in ff02::/120 // drop // if keepalive ack // drop @@ -1466,11 +1465,14 @@ gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel); // If not neighbor announcements, skip filter. gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel); - // If to ff02::1, drop. + // Drop all multicast NA to ff02::/120. + // This is a way to cover ff02::1 and ff02::2 with a single JNEBS. // TODO: Drop only if they don't contain the address of on-link neighbours. + final byte[] unsolicitedNaDropPrefix = Arrays.copyOf(IPV6_ALL_NODES_ADDRESS, 15); gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET); - gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS, + gen.addJumpIfBytesNotEqual(Register.R0, unsolicitedNaDropPrefix, skipUnsolicitedMulticastNALabel); + maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA); gen.addJump(mCountAndDropLabel); gen.defineLabel(skipUnsolicitedMulticastNALabel); @@ -1493,7 +1495,7 @@ * <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 NAs to anything in ff02::/120. * <li>Drop IPv6 ICMPv6 RSs. * <li>Filter IPv4 packets (see generateIPv4FilterLocked()) * <li>Filter IPv6 packets (see generateIPv6FilterLocked()) @@ -1569,7 +1571,7 @@ // 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); + gen.addJumpIfBytesNotEqual(Register.R0, ETHER_BROADCAST, mCountAndPassLabel); maybeSetupCounter(gen, Counter.DROPPED_ETH_BROADCAST); gen.addJump(mCountAndDropLabel);
diff --git a/src/android/net/apf/ApfGenerator.java b/src/android/net/apf/ApfGenerator.java index 44ce2db..bf4d910 100644 --- a/src/android/net/apf/ApfGenerator.java +++ b/src/android/net/apf/ApfGenerator.java
@@ -752,7 +752,8 @@ /** * 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}. + * packet at an offset specified by {@code register} don't match {@code bytes}, {@code register} + * must be R0. */ public ApfGenerator addJumpIfBytesNotEqual(Register register, byte[] bytes, String target) throws IllegalInstructionException {
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java index 5f77128..8e0e9d3 100644 --- a/src/android/net/dhcp/DhcpClient.java +++ b/src/android/net/dhcp/DhcpClient.java
@@ -97,9 +97,9 @@ 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.net.module.util.DeviceConfigUtils; +import com.android.net.module.util.NetworkStackConstants; import com.android.net.module.util.PacketReader; import com.android.networkstack.R; import com.android.networkstack.apishim.CaptivePortalDataShimImpl; @@ -611,7 +611,7 @@ private boolean initUdpSocket() { final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_DHCP); + NetworkStackConstants.TAG_SYSTEM_DHCP); try { mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); SocketUtils.bindSocketToInterface(mUdpSock, mIfaceName);
diff --git a/src/android/net/dhcp/DhcpPacket.java b/src/android/net/dhcp/DhcpPacket.java index f70f3ec..1331a24 100644 --- a/src/android/net/dhcp/DhcpPacket.java +++ b/src/android/net/dhcp/DhcpPacket.java
@@ -200,7 +200,8 @@ * DHCP Optional Type: DHCP Interface MTU */ public static final byte DHCP_MTU = 26; - protected Short mMtu; + @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) + public Short mMtu; /** * DHCP Optional Type: DHCP BROADCAST ADDRESS
diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java index 3acd76e..3465e72 100644 --- a/src/android/net/dhcp/DhcpServer.java +++ b/src/android/net/dhcp/DhcpServer.java
@@ -33,12 +33,12 @@ 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.net.module.util.Inet4AddressUtils.getBroadcastAddress; import static com.android.net.module.util.Inet4AddressUtils.getPrefixMaskAsInet4Address; import static com.android.net.module.util.NetworkStackConstants.INFINITE_LEASE; import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ALL; import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY; +import static com.android.net.module.util.NetworkStackConstants.TAG_SYSTEM_DHCP_SERVER; import static com.android.server.util.PermissionUtil.enforceNetworkStackCallingPermission; import static java.lang.Integer.toUnsignedLong;
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java index a73e997..c0ae611 100644 --- a/src/android/net/ip/IpClient.java +++ b/src/android/net/ip/IpClient.java
@@ -18,12 +18,25 @@ import static android.net.RouteInfo.RTN_UNICAST; import static android.net.dhcp.DhcpResultsParcelableUtil.toStableParcelable; +import static android.net.util.NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION; +import static android.net.util.NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION; +import static android.net.util.NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION; +import static android.net.util.SocketUtils.makePacketSocketAddress; import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; +import static android.system.OsConstants.AF_PACKET; +import static android.system.OsConstants.ETH_P_ARP; +import static android.system.OsConstants.ETH_P_IPV6; +import static android.system.OsConstants.SOCK_NONBLOCK; +import static android.system.OsConstants.SOCK_RAW; +import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY; +import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST; +import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST; import static com.android.net.module.util.NetworkStackConstants.VENDOR_SPECIFIC_IE_ID; import static com.android.server.util.PermissionUtil.enforceNetworkStackCallingPermission; import android.content.Context; +import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.DhcpResults; import android.net.INetd; @@ -48,19 +61,25 @@ import android.net.metrics.IpManagerEvent; import android.net.networkstack.aidl.dhcp.DhcpOption; import android.net.shared.InitialConfiguration; +import android.net.shared.Layer2Information; import android.net.shared.ProvisioningConfiguration; import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import android.net.shared.ProvisioningConfiguration.ScanResultInfo.InformationElement; import android.net.util.InterfaceParams; +import android.net.util.NetworkStackUtils; import android.net.util.SharedLog; import android.os.Build; import android.os.ConditionVariable; +import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; import android.stats.connectivity.DisconnectCode; +import android.stats.connectivity.NetworkQuirkEvent; +import android.system.ErrnoException; +import android.system.Os; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; @@ -78,17 +97,26 @@ import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; import com.android.net.module.util.DeviceConfigUtils; +import com.android.networkstack.R; import com.android.networkstack.apishim.NetworkInformationShimImpl; +import com.android.networkstack.apishim.SocketUtilsShimImpl; import com.android.networkstack.apishim.common.NetworkInformationShim; import com.android.networkstack.apishim.common.ShimUtils; +import com.android.networkstack.arp.ArpPacket; import com.android.networkstack.metrics.IpProvisioningMetrics; +import com.android.networkstack.metrics.NetworkQuirkMetrics; +import com.android.networkstack.packets.NeighborAdvertisement; import com.android.server.NetworkObserverRegistry; import com.android.server.NetworkStackService.NetworkStackServiceManager; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.MalformedURLException; +import java.net.SocketAddress; +import java.net.SocketException; import java.net.URL; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; @@ -121,6 +149,7 @@ * @hide */ public class IpClient extends StateMachine { + private static final String TAG = IpClient.class.getSimpleName(); private static final boolean DBG = false; // For message logging. @@ -134,6 +163,7 @@ private final NetworkStackIpMemoryStore mIpMemoryStore; private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance(); private final IpProvisioningMetrics mIpProvisioningMetrics = new IpProvisioningMetrics(); + private final NetworkQuirkMetrics mNetworkQuirkMetrics; /** * Dump all state machine and connectivity packet logs to the specified writer. @@ -479,6 +509,8 @@ private final MessageHandlingLogger mMsgStateLogger; private final IpConnectivityLog mMetricsLog; private final InterfaceController mInterfaceCtrl; + // Set of IPv6 addresses for which unsolicited gratuitous NA packets have been sent. + private final Set<Inet6Address> mGratuitousNaTargetAddresses = new HashSet<>(); // Ignore nonzero RDNSS option lifetimes below this value. 0 = disabled. private final int mMinRdnssLifetimeSec; @@ -501,7 +533,7 @@ private boolean mMulticastFiltering; private long mStartTimeMillis; private MacAddress mCurrentBssid; - private boolean mHasDisabledIPv6OnProvLoss; + private boolean mHasDisabledIpv6OrAcceptRaOnProvLoss; /** * Reading the snapshot is an asynchronous operation initiated by invoking @@ -564,6 +596,52 @@ public IpConnectivityLog getIpConnectivityLog() { return new IpConnectivityLog(); } + + /** + * Get a NetworkQuirkMetrics instance. + */ + public NetworkQuirkMetrics getNetworkQuirkMetrics() { + return new NetworkQuirkMetrics(); + } + + /** + * Get a IpReachabilityMonitor instance. + */ + public IpReachabilityMonitor getIpReachabilityMonitor(Context context, + InterfaceParams ifParams, Handler h, SharedLog log, + IpReachabilityMonitor.Callback callback, boolean usingMultinetworkPolicyTracker, + IpReachabilityMonitor.Dependencies deps, final INetd netd) { + return new IpReachabilityMonitor(context, ifParams, h, log, callback, + usingMultinetworkPolicyTracker, deps, netd); + } + + /** + * Get a IpReachabilityMonitor dependencies instance. + */ + public IpReachabilityMonitor.Dependencies getIpReachabilityMonitorDeps(Context context, + String name) { + return IpReachabilityMonitor.Dependencies.makeDefault(context, name); + } + + /** + * Return whether a feature guarded by a feature flag is enabled. + * @see NetworkStackUtils#isFeatureEnabled(Context, String, String) + */ + public boolean isFeatureEnabled(final Context context, final String name, + boolean defaultEnabled) { + return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name, + defaultEnabled); + } + + /** + * Create an APF filter if apfCapabilities indicates support for packet filtering using + * APF programs. + * @see ApfFilter#maybeCreate + */ + public ApfFilter maybeCreateApfFilter(Context context, ApfFilter.ApfConfiguration config, + InterfaceParams ifParams, IpClientCallbacksWrapper cb) { + return ApfFilter.maybeCreate(context, config, ifParams, cb); + } } public IpClient(Context context, String ifName, IIpClientCallbacks callback, @@ -586,6 +664,7 @@ mClatInterfaceName = CLAT_PREFIX + ifName; mDependencies = deps; mMetricsLog = deps.getIpConnectivityLog(); + mNetworkQuirkMetrics = deps.getNetworkQuirkMetrics(); mShutdownLatch = new CountDownLatch(1); mCm = mContext.getSystemService(ConnectivityManager.class); mObserverRegistry = observerRegistry; @@ -647,6 +726,21 @@ logMsg(msg); } + @Override + public void onInterfaceAddressRemoved(LinkAddress address, String iface) { + super.onInterfaceAddressRemoved(address, iface); + if (!mInterfaceName.equals(iface)) return; + if (!address.isIpv6()) return; + final Inet6Address targetIp = (Inet6Address) address.getAddress(); + if (mGratuitousNaTargetAddresses.contains(targetIp)) { + mGratuitousNaTargetAddresses.remove(targetIp); + + final String msg = "Global IPv6 address: " + targetIp + + " has removed from the set of gratuitous NA target address."; + logMsg(msg); + } + } + private void logMsg(String msg) { Log.d(mTag, msg); getHandler().post(() -> mLog.log("OBSERVED " + msg)); @@ -793,6 +887,45 @@ mLinkObserver.shutdown(); } + private boolean isGratuitousNaEnabled() { + return mDependencies.isFeatureEnabled(mContext, IPCLIENT_GRATUITOUS_NA_VERSION, + false /* defaultEnabled */); + } + + private boolean isGratuitousArpNaRoamingEnabled() { + return mDependencies.isFeatureEnabled(mContext, IPCLIENT_GARP_NA_ROAMING_VERSION, + false /* defaultEnabled */); + } + + @VisibleForTesting + static MacAddress getInitialBssid(final Layer2Information layer2Info, + final ScanResultInfo scanResultInfo, boolean isAtLeastS) { + MacAddress bssid = null; + // http://b/185202634 + // ScanResultInfo is not populated in some situations. + // On S and above, prefer getting the BSSID from the Layer2Info. + // On R and below, get the BSSID from the ScanResultInfo and fall back to + // getting it from the Layer2Info. This ensures no regressions if any R + // devices pass in a null or meaningless BSSID in the Layer2Info. + if (!isAtLeastS && scanResultInfo != null) { + try { + bssid = MacAddress.fromString(scanResultInfo.getBssid()); + } catch (IllegalArgumentException e) { + Log.wtf(TAG, "Invalid BSSID: " + scanResultInfo.getBssid() + + " in provisioning configuration", e); + } + } + if (bssid == null && layer2Info != null) { + bssid = layer2Info.mBssid; + } + return bssid; + } + + private boolean shouldDisableAcceptRaOnProvisioningLoss() { + return mDependencies.isFeatureEnabled(mContext, IPCLIENT_DISABLE_ACCEPT_RA_VERSION, + true /* defaultEnabled */); + } + @Override protected void onQuitting() { mCallback.onQuit(); @@ -816,17 +949,8 @@ return; } - final ScanResultInfo scanResultInfo = req.mScanResultInfo; - mCurrentBssid = null; - if (scanResultInfo != null) { - try { - mCurrentBssid = MacAddress.fromString(scanResultInfo.getBssid()); - } catch (IllegalArgumentException e) { - Log.wtf(mTag, "Invalid BSSID: " + scanResultInfo.getBssid() - + " in provisioning configuration", e); - } - } - + mCurrentBssid = getInitialBssid(req.mLayer2Info, req.mScanResultInfo, + ShimUtils.isAtLeastS()); if (req.mLayer2Info != null) { mL2Key = req.mLayer2Info.mL2Key; mCluster = req.mLayer2Info.mCluster; @@ -1120,6 +1244,21 @@ return config.isProvisionedBy(lp.getLinkAddresses(), lp.getRoutes()); } + private void setIpv6AcceptRa(int acceptRa) { + try { + mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mInterfaceParams.name, "accept_ra", + Integer.toString(acceptRa)); + } catch (Exception e) { + Log.e(mTag, "Failed to set accept_ra to " + acceptRa); + } + } + + private void restartIpv6WithAcceptRaDisabled() { + mInterfaceCtrl.disableIPv6(); + setIpv6AcceptRa(0 /* accept_ra */); + startIPv6(); + } + // 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 @@ -1169,7 +1308,7 @@ // 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 = mHasDisabledIPv6OnProvLoss + final boolean ignoreIPv6ProvisioningLoss = mHasDisabledIpv6OrAcceptRaOnProvLoss || (mConfiguration != null && mConfiguration.mUsingMultinetworkPolicyTracker && !mCm.shouldAvoidBadWifi()); @@ -1197,18 +1336,31 @@ if (oldLp.hasGlobalIpv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) { // Although link properties have lost IPv6 default route in this case, if IPv4 is still // working with appropriate routes and DNS servers, we can keep the current connection - // without disconnecting from the network, just disable IPv6 on that given network until - // to the next provisioning. Disabling IPv6 will result in all IPv6 connectivity torn - // down and all IPv6 sockets being closed, the non-routable IPv6 DNS servers will be - // stripped out, so applications will be able to reconnect immediately over IPv4. See - // b/131781810. + // without disconnecting from the network, just disable IPv6 or accept_ra parameter on + // that given network until to the next provisioning. + // + // Disabling IPv6 stack will result in all IPv6 connectivity torn down and all IPv6 + // sockets being closed, the non-routable IPv6 DNS servers will be stripped out, so + // applications will be able to reconnect immediately over IPv4. See b/131781810. + // + // Sometimes disabling IPv6 stack might introduce other issues(see b/179222860), + // instead disabling accept_ra will result in only IPv4 provisioning and IPv6 link + // local address left on the interface, so applications will be able to reconnect + // immediately over IPv4 and keep IPv6 link-local capable. if (newLp.isIpv4Provisioned()) { - mInterfaceCtrl.disableIPv6(); - mHasDisabledIPv6OnProvLoss = true; - delta = PROV_CHANGE_STILL_PROVISIONED; - if (DBG) { - mLog.log("Disable IPv6 stack completely when the default router has gone"); + if (shouldDisableAcceptRaOnProvisioningLoss()) { + restartIpv6WithAcceptRaDisabled(); + } else { + mInterfaceCtrl.disableIPv6(); } + mNetworkQuirkMetrics.setEvent(NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST); + mNetworkQuirkMetrics.statsWrite(); + mHasDisabledIpv6OrAcceptRaOnProvLoss = true; + delta = PROV_CHANGE_STILL_PROVISIONED; + mLog.log(shouldDisableAcceptRaOnProvisioningLoss() + ? "Disabled accept_ra parameter " + : "Disabled IPv6 stack completely " + + "when the IPv6 default router has gone"); } else { delta = PROV_CHANGE_LOST_PROVISIONING; } @@ -1377,6 +1529,92 @@ } } + private void transmitPacket(final ByteBuffer packet, final SocketAddress sockAddress, + final String msg) { + FileDescriptor sock = null; + try { + sock = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0 /* protocol */); + Os.sendto(sock, packet.array(), 0 /* byteOffset */, packet.limit() /* byteCount */, + 0 /* flags */, sockAddress); + } catch (SocketException | ErrnoException e) { + logError(msg, e); + } finally { + NetworkStackUtils.closeSocketQuietly(sock); + } + } + + private void sendGratuitousNA(final Inet6Address srcIp, final Inet6Address targetIp) { + final int flags = 0; // R=0, S=0, O=0 + final Inet6Address dstIp = IPV6_ADDR_ALL_ROUTERS_MULTICAST; + // Ethernet multicast destination address: 33:33:00:00:00:02. + final MacAddress dstMac = NetworkStackUtils.ipv6MulticastToEthernetMulticast(dstIp); + final ByteBuffer packet = NeighborAdvertisement.build(mInterfaceParams.macAddr, dstMac, + srcIp, dstIp, flags, targetIp); + final SocketAddress sockAddress = + SocketUtilsShimImpl.newInstance().makePacketSocketAddress(ETH_P_IPV6, + mInterfaceParams.index, dstMac.toByteArray()); + + transmitPacket(packet, sockAddress, "Failed to send Gratuitous Neighbor Advertisement"); + } + + private void sendGratuitousARP(final Inet4Address srcIp) { + final ByteBuffer packet = ArpPacket.buildArpPacket(ETHER_BROADCAST /* dstMac */, + mInterfaceParams.macAddr.toByteArray() /* srcMac */, + srcIp.getAddress() /* targetIp */, + ETHER_BROADCAST /* targetHwAddress */, + srcIp.getAddress() /* senderIp */, (short) ARP_REPLY); + final SocketAddress sockAddress = + makePacketSocketAddress(ETH_P_ARP, mInterfaceParams.index); + + transmitPacket(packet, sockAddress, "Failed to send GARP"); + } + + private static Inet6Address getIpv6LinkLocalAddress(final LinkProperties newLp) { + for (LinkAddress la : newLp.getLinkAddresses()) { + if (!la.isIpv6()) continue; + final Inet6Address ip = (Inet6Address) la.getAddress(); + if (ip.isLinkLocalAddress()) return ip; + } + return null; + } + + private void maybeSendGratuitousNAs(final LinkProperties lp, boolean afterRoaming) { + if (!lp.hasGlobalIpv6Address()) return; + + final Inet6Address srcIp = getIpv6LinkLocalAddress(lp); + if (srcIp == null) return; + + // TODO: add experiment with sending only one gratuitous NA packet instead of one + // packet per address. + for (LinkAddress la : lp.getLinkAddresses()) { + if (!la.isIpv6() || !la.isGlobalPreferred()) continue; + final Inet6Address targetIp = (Inet6Address) la.getAddress(); + // Already sent gratuitous NA with this target global IPv6 address. But for + // the L2 roaming case, device should always (re)transmit Gratuitous NA for + // each IPv6 global unicast address respectively after roaming. + if (!afterRoaming && mGratuitousNaTargetAddresses.contains(targetIp)) continue; + if (DBG) { + mLog.log("send Gratuitous NA from " + srcIp.getHostAddress() + " for " + + targetIp.getHostAddress() + (afterRoaming ? " after roaming" : "")); + } + sendGratuitousNA(srcIp, targetIp); + if (!afterRoaming) mGratuitousNaTargetAddresses.add(targetIp); + } + } + + private void maybeSendGratuitousARP(final LinkProperties lp) { + for (LinkAddress address : lp.getLinkAddresses()) { + if (address.getAddress() instanceof Inet4Address) { + final Inet4Address srcIp = (Inet4Address) address.getAddress(); + if (DBG) { + mLog.log("send GARP for " + srcIp.getHostAddress() + " HW address: " + + mInterfaceParams.macAddr); + } + sendGratuitousARP(srcIp); + } + } + } + // Returns false if we have lost provisioning, true otherwise. private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) { final LinkProperties newLp = assembleLinkProperties(); @@ -1384,6 +1622,13 @@ return true; } + // Check if new assigned IPv6 GUA is available in the LinkProperties now. If so, initiate + // gratuitous multicast unsolicited Neighbor Advertisements as soon as possible to inform + // first-hop routers that the new GUA host is goning to use. + if (isGratuitousNaEnabled()) { + maybeSendGratuitousNAs(newLp, false /* isGratuitousNaAfterRoaming */); + } + // Either success IPv4 or IPv6 provisioning triggers new LinkProperties update, // wait for the provisioning completion and record the latency. mIpProvisioningMetrics.setIPv4ProvisionedLatencyOnFirstTime(newLp.isIpv4Provisioned()); @@ -1554,7 +1799,7 @@ private boolean startIpReachabilityMonitor() { try { - mIpReachabilityMonitor = new IpReachabilityMonitor( + mIpReachabilityMonitor = mDependencies.getIpReachabilityMonitor( mContext, mInterfaceParams, getHandler(), @@ -1566,6 +1811,7 @@ } }, mConfiguration.mUsingMultinetworkPolicyTracker, + mDependencies.getIpReachabilityMonitorDeps(mContext, mInterfaceParams.name), mNetd); } catch (IllegalArgumentException iae) { // Failed to start IpReachabilityMonitor. Log it and call @@ -1639,6 +1885,13 @@ // If the BSSID has not changed, there is nothing to do. if (info.bssid.equals(mCurrentBssid)) return; + // Before trigger probing to the interesting neighbors, send Gratuitous ARP + // and Neighbor Advertisment in advance to propgate host's IPv4/v6 addresses. + if (isGratuitousArpNaRoamingEnabled()) { + maybeSendGratuitousARP(mLinkProperties); + maybeSendGratuitousNAs(mLinkProperties, true /* isGratuitousNaAfterRoaming */); + } + if (mIpReachabilityMonitor != null) { mIpReachabilityMonitor.probeAll(); } @@ -1661,7 +1914,9 @@ @Override public void enter() { stopAllIP(); - mHasDisabledIPv6OnProvLoss = false; + setIpv6AcceptRa(2 /* accept_ra */); + mHasDisabledIpv6OrAcceptRaOnProvLoss = false; + mGratuitousNaTargetAddresses.clear(); mLinkObserver.clearInterfaceParams(); resetLinkProperties(); @@ -1981,10 +2236,19 @@ apfConfig.apfCapabilities = mConfiguration.mApfCapabilities; apfConfig.multicastFilter = mMulticastFiltering; // Get the Configuration for ApfFilter from Context - apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames(); - apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList(); + // Resource settings were moved from ApfCapabilities APIs to NetworkStack resources in S + if (ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.R)) { + final Resources res = mContext.getResources(); + apfConfig.ieee802_3Filter = res.getBoolean(R.bool.config_apfDrop802_3Frames); + apfConfig.ethTypeBlackList = res.getIntArray(R.array.config_apfEthTypeDenyList); + } else { + apfConfig.ieee802_3Filter = ApfCapabilities.getApfDrop8023Frames(); + apfConfig.ethTypeBlackList = ApfCapabilities.getApfEtherTypeBlackList(); + } + apfConfig.minRdnssLifetimeSec = mMinRdnssLifetimeSec; - mApfFilter = ApfFilter.maybeCreate(mContext, apfConfig, mInterfaceParams, mCallback); + mApfFilter = mDependencies.maybeCreateApfFilter(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) {
diff --git a/src/android/net/ip/IpReachabilityMonitor.java b/src/android/net/ip/IpReachabilityMonitor.java index 3dbe662..89c70a9 100644 --- a/src/android/net/ip/IpReachabilityMonitor.java +++ b/src/android/net/ip/IpReachabilityMonitor.java
@@ -20,6 +20,8 @@ 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 static android.net.util.NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION; +import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import android.content.Context; import android.net.ConnectivityManager; @@ -43,8 +45,12 @@ import android.text.TextUtils; import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; +import com.android.net.module.util.DeviceConfigUtils; import com.android.networkstack.R; import java.io.PrintWriter; @@ -143,6 +149,8 @@ protected static final int MIN_NUD_SOLICIT_NUM = 5; protected static final int MAX_NUD_SOLICIT_INTERVAL_MS = 1000; protected static final int MIN_NUD_SOLICIT_INTERVAL_MS = 750; + protected static final int NUD_MCAST_RESOLICIT_NUM = 3; + private static final int INVALID_NUD_MCAST_RESOLICIT_NUM = -1; public interface Callback { /** @@ -161,6 +169,7 @@ interface Dependencies { void acquireWakeLock(long durationMs); IpNeighborMonitor makeIpNeighborMonitor(Handler h, SharedLog log, NeighborEventConsumer cb); + boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled); static Dependencies makeDefault(Context context, String iface) { final String lockName = TAG + "." + iface; @@ -176,6 +185,12 @@ NeighborEventConsumer cb) { return new IpNeighborMonitor(h, log, cb); } + + public boolean isFeatureEnabled(final Context context, final String name, + boolean defaultEnabled) { + return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name, + defaultEnabled); + } }; } } @@ -183,7 +198,6 @@ 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; @@ -196,12 +210,14 @@ private volatile long mLastProbeTimeMs; private int mNumSolicits; private int mInterSolicitIntervalMs; + @NonNull + private final Callback mCallback; public IpReachabilityMonitor( Context context, InterfaceParams ifParams, Handler h, SharedLog log, Callback callback, - boolean usingMultinetworkPolicyTracker, final INetd netd) { - this(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker, - Dependencies.makeDefault(context, ifParams.name), new IpConnectivityLog(), netd); + boolean usingMultinetworkPolicyTracker, Dependencies dependencies, final INetd netd) { + this(context, ifParams, h, log, callback, usingMultinetworkPolicyTracker, dependencies, + new IpConnectivityLog(), netd); } @VisibleForTesting @@ -225,7 +241,10 @@ // In case the overylaid parameters specify an invalid configuration, set the parameters // to the hardcoded defaults first, then set them to the values used in the steady state. try { - setNeighborParameters(MIN_NUD_SOLICIT_NUM, MIN_NUD_SOLICIT_INTERVAL_MS); + int numResolicits = isMulticastResolicitEnabled() + ? NUD_MCAST_RESOLICIT_NUM + : INVALID_NUD_MCAST_RESOLICIT_NUM; + setNeighborParameters(MIN_NUD_SOLICIT_NUM, MIN_NUD_SOLICIT_INTERVAL_MS, numResolicits); } catch (Exception e) { Log.e(TAG, "Failed to adjust neighbor parameters with hardcoded defaults"); } @@ -241,10 +260,12 @@ // 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) { + // After both unicast probe and multicast probe(if mcast_resolicit is not 0) + // attempts fail, trigger the neighbor lost event and disconnect. mLog.w("ALERT neighbor went from: " + prev + " to: " + event); handleNeighborLost(event); } else if (event.nudState == StructNdMsg.NUD_REACHABLE) { - maybeRestoreNeighborParameters(); + handleNeighborReachable(prev, event); } }); mIpNeighborMonitor.start(); @@ -296,6 +317,26 @@ return false; } + private boolean hasDefaultRouterNeighborMacAddressChanged( + @Nullable final NeighborEvent prev, @NonNull final NeighborEvent event) { + if (prev == null || !isNeighborDefaultRouter(event)) return false; + return !event.macAddr.equals(prev.macAddr); + } + + private boolean isNeighborDefaultRouter(@NonNull final NeighborEvent event) { + // For the IPv6 link-local scoped address, equals() works because the NeighborEvent.ip + // doesn't have a scope id and Inet6Address#equals doesn't consider scope id neither. + for (RouteInfo route : mLinkProperties.getRoutes()) { + if (route.isDefaultRoute() && event.ip.equals(route.getGateway())) return true; + } + return false; + } + + private boolean isMulticastResolicitEnabled() { + return mDependencies.isFeatureEnabled(mContext, IP_REACHABILITY_MCAST_RESOLICIT_VERSION, + false /* defaultEnabled */); + } + public void updateLinkProperties(LinkProperties lp) { if (!mInterfaceParams.name.equals(lp.getInterfaceName())) { // TODO: figure out whether / how to cope with interface changes. @@ -333,6 +374,24 @@ if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } } + private void handleNeighborReachable(@Nullable final NeighborEvent prev, + @NonNull final NeighborEvent event) { + if (isMulticastResolicitEnabled() + && hasDefaultRouterNeighborMacAddressChanged(prev, event)) { + // This implies device has confirmed the neighbor's reachability from + // other states(e.g., NUD_PROBE or NUD_STALE), checking if the mac + // address hasn't changed is required. If Mac address does change, then + // trigger a new neighbor lost event and disconnect. + final String logMsg = "ALERT neighbor: " + event.ip + + " MAC address changed from: " + prev.macAddr + + " to: " + event.macAddr; + mLog.w(logMsg); + mCallback.notifyLost(event.ip, logMsg); + return; + } + maybeRestoreNeighborParameters(); + } + private void handleNeighborLost(NeighborEvent event) { final LinkProperties whatIfLp = new LinkProperties(mLinkProperties); @@ -370,11 +429,9 @@ 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); - } + // TODO: remove |ip| when the callback signature no longer has + // an InetAddress argument. + mCallback.notifyLost(ip, logMsg); } logNudFailed(lostProvisioning); } @@ -450,6 +507,12 @@ private void setNeighborParameters(int numSolicits, int interSolicitIntervalMs) throws RemoteException, IllegalArgumentException { + // Do not set mcast_resolicit param by default. + setNeighborParameters(numSolicits, interSolicitIntervalMs, INVALID_NUD_MCAST_RESOLICIT_NUM); + } + + private void setNeighborParameters(int numSolicits, int interSolicitIntervalMs, + int numResolicits) throws RemoteException, IllegalArgumentException { Preconditions.checkArgument(numSolicits >= MIN_NUD_SOLICIT_NUM, "numSolicits must be at least " + MIN_NUD_SOLICIT_NUM); Preconditions.checkArgument(numSolicits <= MAX_NUD_SOLICIT_NUM, @@ -464,6 +527,10 @@ Integer.toString(interSolicitIntervalMs)); mNetd.setProcSysNet(family, INetd.NEIGH, mInterfaceParams.name, "ucast_solicit", Integer.toString(numSolicits)); + if (numResolicits != INVALID_NUD_MCAST_RESOLICIT_NUM) { + mNetd.setProcSysNet(family, INetd.NEIGH, mInterfaceParams.name, "mcast_resolicit", + Integer.toString(numResolicits)); + } } mNumSolicits = numSolicits;
diff --git a/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java index 5c11733..e06cdca 100755 --- a/src/android/net/util/NetworkStackUtils.java +++ b/src/android/net/util/NetworkStackUtils.java
@@ -17,12 +17,16 @@ package android.net.util; import android.content.Context; +import android.net.MacAddress; + +import androidx.annotation.NonNull; import com.android.net.module.util.DeviceConfigUtils; import java.io.FileDescriptor; import java.io.IOException; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.SocketException; /** @@ -232,6 +236,32 @@ */ public static final String VALIDATION_METRICS_VERSION = "validation_metrics_version"; + /** + * Experiment flag to enable sending gratuitous multicast unsolicited Neighbor Advertisements + * to propagate new assigned IPv6 GUA as quickly as possible. + */ + public static final String IPCLIENT_GRATUITOUS_NA_VERSION = "ipclient_gratuitous_na_version"; + + /** + * Experiment flag to enable sending Gratuitous APR and Gratuitous Neighbor Advertisement for + * all assigned IPv4 and IPv6 GUAs after completing L2 roaming. + */ + public static final String IPCLIENT_GARP_NA_ROAMING_VERSION = + "ipclient_garp_na_roaming_version"; + + /** + * Experiment flag to disable accept_ra parameter when IPv6 provisioning loss happens due to + * the default route has gone. + */ + public static final String IPCLIENT_DISABLE_ACCEPT_RA_VERSION = "ipclient_disable_accept_ra"; + + /** + * Experiment flag to enable "mcast_resolicit" neighbor parameter in IpReachabilityMonitor, + * set it to 3 by default. + */ + public static final String IP_REACHABILITY_MCAST_RESOLICIT_VERSION = + "ip_reachability_mcast_resolicit_version"; + static { System.loadLibrary("networkstackutilsjni"); } @@ -247,6 +277,21 @@ } /** + * Convert IPv6 multicast address to ethernet multicast address in network order. + */ + public static MacAddress ipv6MulticastToEthernetMulticast(@NonNull final Inet6Address addr) { + final byte[] etherMulticast = new byte[6]; + final byte[] ipv6Multicast = addr.getAddress(); + etherMulticast[0] = (byte) 0x33; + etherMulticast[1] = (byte) 0x33; + etherMulticast[2] = ipv6Multicast[12]; + etherMulticast[3] = ipv6Multicast[13]; + etherMulticast[4] = ipv6Multicast[14]; + etherMulticast[5] = ipv6Multicast[15]; + return MacAddress.fromBytes(etherMulticast); + } + + /** * Attaches a socket filter that accepts DHCP packets to the given socket. */ public static native void attachDhcpFilter(FileDescriptor fd) throws SocketException;
diff --git a/src/com/android/networkstack/NetworkStackNotifier.java b/src/com/android/networkstack/NetworkStackNotifier.java index 0558d3a..acf3c95 100644 --- a/src/com/android/networkstack/NetworkStackNotifier.java +++ b/src/com/android/networkstack/NetworkStackNotifier.java
@@ -237,8 +237,8 @@ // If the venue friendly name is available (in Passpoint use-case), display it. // Otherwise, display the SSID. - final String friendlyName = capportData.getVenueFriendlyName(); - final String venueDisplayName = TextUtils.isEmpty(friendlyName) + final CharSequence friendlyName = capportData.getVenueFriendlyName(); + final CharSequence venueDisplayName = TextUtils.isEmpty(friendlyName) ? getSsid(networkStatus) : friendlyName; builder = getNotificationBuilder(channel, networkStatus, res, venueDisplayName) @@ -284,9 +284,9 @@ private Notification.Builder getNotificationBuilder(@NonNull String channelId, @NonNull TrackedNetworkStatus networkStatus, @NonNull Resources res, - @NonNull String ssid) { + @NonNull CharSequence networkIdentifier) { return new Notification.Builder(mContext, channelId) - .setContentTitle(ssid) + .setContentTitle(networkIdentifier) .setSmallIcon(R.drawable.icon_wifi); }
diff --git a/src/com/android/networkstack/metrics/NetworkQuirkMetrics.java b/src/com/android/networkstack/metrics/NetworkQuirkMetrics.java new file mode 100644 index 0000000..dee4504 --- /dev/null +++ b/src/com/android/networkstack/metrics/NetworkQuirkMetrics.java
@@ -0,0 +1,72 @@ +/* + * Copyright (C) 2021 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.stats.connectivity.NetworkQuirkEvent; + +import androidx.annotation.VisibleForTesting; + +/** + * Class to record the network Quirk event into statsd. + * @hide + */ +public class NetworkQuirkMetrics { + private final Dependencies mDependencies; + private final NetworkStackQuirkReported.Builder mStatsBuilder = + NetworkStackQuirkReported.newBuilder(); + /** + * Dependencies of {@link NetworkQuirkMetrics}, useful for testing. + */ + @VisibleForTesting + public static class Dependencies { + /** + * @see NetworkStackStatsLog#write. + */ + public void writeStats(int event) { + NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_STACK_QUIRK_REPORTED, + 0, event); + } + } + + /** + * Get a NetworkQuirkMetrics instance. + */ + public NetworkQuirkMetrics() { + this(new Dependencies()); + } + + @VisibleForTesting + public NetworkQuirkMetrics(Dependencies deps) { + mDependencies = deps; + } + + /** + * Write the network Quirk Event into mStatsBuilder. + */ + public void setEvent(NetworkQuirkEvent event) { + mStatsBuilder.setEvent(event); + } + + /** + * Write the NetworkStackQuirkReported proto into statsd. + */ + public NetworkStackQuirkReported statsWrite() { + final NetworkStackQuirkReported stats = mStatsBuilder.build(); + mDependencies.writeStats(stats.getEvent().getNumber()); + return stats; + } +}
diff --git a/src/com/android/networkstack/packets/NeighborAdvertisement.java b/src/com/android/networkstack/packets/NeighborAdvertisement.java new file mode 100644 index 0000000..ef38314 --- /dev/null +++ b/src/com/android/networkstack/packets/NeighborAdvertisement.java
@@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 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.packets; + +import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NA_HEADER_LEN; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA; +import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN; + +import android.net.MacAddress; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.net.module.util.Ipv6Utils; +import com.android.net.module.util.Struct; +import com.android.net.module.util.structs.EthernetHeader; +import com.android.net.module.util.structs.Icmpv6Header; +import com.android.net.module.util.structs.Ipv6Header; +import com.android.net.module.util.structs.LlaOption; +import com.android.net.module.util.structs.NaHeader; + +import java.net.Inet6Address; +import java.nio.ByteBuffer; + +/** + * Defines basic data and operations needed to build and parse Neighbor Advertisement packet. + * + * @hide + */ +public class NeighborAdvertisement { + @NonNull + public final EthernetHeader ethHdr; + @NonNull + public final Ipv6Header ipv6Hdr; + @NonNull + public final Icmpv6Header icmpv6Hdr; + @NonNull + public final NaHeader naHdr; + @Nullable + public final LlaOption tlla; + + public NeighborAdvertisement(@NonNull final EthernetHeader ethHdr, + @NonNull final Ipv6Header ipv6Hdr, @NonNull final Icmpv6Header icmpv6Hdr, + @NonNull final NaHeader naHdr, @Nullable final LlaOption tlla) { + this.ethHdr = ethHdr; + this.ipv6Hdr = ipv6Hdr; + this.icmpv6Hdr = icmpv6Hdr; + this.naHdr = naHdr; + this.tlla = tlla; + } + + /** + * Convert a Neighbor Advertisement instance to ByteBuffer. + */ + public ByteBuffer toByteBuffer() { + final int etherHeaderLen = Struct.getSize(EthernetHeader.class); + final int ipv6HeaderLen = Struct.getSize(Ipv6Header.class); + final int icmpv6HeaderLen = Struct.getSize(Icmpv6Header.class); + final int naHeaderLen = Struct.getSize(NaHeader.class); + final int tllaOptionLen = (tlla == null) ? 0 : Struct.getSize(LlaOption.class); + final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + ipv6HeaderLen + + icmpv6HeaderLen + naHeaderLen + tllaOptionLen); + + ethHdr.writeToByteBuffer(packet); + ipv6Hdr.writeToByteBuffer(packet); + icmpv6Hdr.writeToByteBuffer(packet); + naHdr.writeToByteBuffer(packet); + if (tlla != null) { + tlla.writeToByteBuffer(packet); + } + packet.flip(); + + return packet; + } + + /** + * Build a Neighbor Advertisement packet from the required specified parameters. + */ + public static ByteBuffer build(@NonNull final MacAddress srcMac, + @NonNull final MacAddress dstMac, @NonNull final Inet6Address srcIp, + @NonNull final Inet6Address dstIp, int flags, @NonNull final Inet6Address target) { + final ByteBuffer tlla = LlaOption.build((byte) ICMPV6_ND_OPTION_TLLA, srcMac); + return Ipv6Utils.buildNaPacket(srcMac, dstMac, srcIp, dstIp, flags, target, tlla); + } + + /** + * Parse a Neighbor Advertisement packet from ByteBuffer. + */ + public static NeighborAdvertisement parse(@NonNull final byte[] recvbuf, final int length) + throws ParseException { + if (length < ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_NA_HEADER_LEN + || recvbuf.length < length) { + throw new ParseException("Invalid packet length: " + length); + } + final ByteBuffer packet = ByteBuffer.wrap(recvbuf, 0, length); + + // Parse each header and option in Neighbor Advertisement packet in order. + final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, packet); + final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, packet); + final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, packet); + final NaHeader naHdr = Struct.parse(NaHeader.class, packet); + final LlaOption tlla = (packet.remaining() == 0) + ? null + : Struct.parse(LlaOption.class, packet); + + return new NeighborAdvertisement(ethHdr, ipv6Hdr, icmpv6Hdr, naHdr, tlla); + } + + /** + * Thrown when parsing Neighbor Advertisement packet failed. + */ + public static class ParseException extends Exception { + ParseException(String message) { + super(message); + } + } +}
diff --git a/src/com/android/networkstack/packets/NeighborSolicitation.java b/src/com/android/networkstack/packets/NeighborSolicitation.java new file mode 100644 index 0000000..5c3e40a --- /dev/null +++ b/src/com/android/networkstack/packets/NeighborSolicitation.java
@@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 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.packets; + +import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NS_HEADER_LEN; +import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN; + +import android.net.MacAddress; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.net.module.util.Ipv6Utils; +import com.android.net.module.util.Struct; +import com.android.net.module.util.structs.EthernetHeader; +import com.android.net.module.util.structs.Icmpv6Header; +import com.android.net.module.util.structs.Ipv6Header; +import com.android.net.module.util.structs.LlaOption; +import com.android.net.module.util.structs.NsHeader; + +import java.net.Inet6Address; +import java.nio.ByteBuffer; + +/** + * Defines basic data and operations needed to build and parse Neighbor Solicitation packet. + * + * @hide + */ +public class NeighborSolicitation { + @NonNull + public final EthernetHeader ethHdr; + @NonNull + public final Ipv6Header ipv6Hdr; + @NonNull + public final Icmpv6Header icmpv6Hdr; + @NonNull + public final NsHeader nsHdr; + @Nullable + public final LlaOption slla; + + public NeighborSolicitation(@NonNull final EthernetHeader ethHdr, + @NonNull final Ipv6Header ipv6Hdr, @NonNull final Icmpv6Header icmpv6Hdr, + @NonNull final NsHeader nsHdr, @Nullable final LlaOption slla) { + this.ethHdr = ethHdr; + this.ipv6Hdr = ipv6Hdr; + this.icmpv6Hdr = icmpv6Hdr; + this.nsHdr = nsHdr; + this.slla = slla; + } + + /** + * Convert a Neighbor Solicitation instance to ByteBuffer. + */ + public ByteBuffer toByteBuffer() { + final int etherHeaderLen = Struct.getSize(EthernetHeader.class); + final int ipv6HeaderLen = Struct.getSize(Ipv6Header.class); + final int icmpv6HeaderLen = Struct.getSize(Icmpv6Header.class); + final int nsHeaderLen = Struct.getSize(NsHeader.class); + final int sllaOptionLen = (slla == null) ? 0 : Struct.getSize(LlaOption.class); + final ByteBuffer packet = ByteBuffer.allocate(etherHeaderLen + ipv6HeaderLen + + icmpv6HeaderLen + nsHeaderLen + sllaOptionLen); + + ethHdr.writeToByteBuffer(packet); + ipv6Hdr.writeToByteBuffer(packet); + icmpv6Hdr.writeToByteBuffer(packet); + nsHdr.writeToByteBuffer(packet); + if (slla != null) { + slla.writeToByteBuffer(packet); + } + packet.flip(); + + return packet; + } + + /** + * Build a Neighbor Solicitation packet from the required specified parameters. + */ + public static ByteBuffer build(@NonNull final MacAddress srcMac, + @NonNull final MacAddress dstMac, @NonNull final Inet6Address srcIp, + @NonNull final Inet6Address dstIp, @NonNull final Inet6Address target) { + final ByteBuffer slla = LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, srcMac); + return Ipv6Utils.buildNsPacket(srcMac, dstMac, srcIp, dstIp, target, slla); + } + + /** + * Parse a Neighbor Solicitation packet from ByteBuffer. + */ + public static NeighborSolicitation parse(@NonNull final byte[] recvbuf, final int length) + throws ParseException { + if (length < ETHER_HEADER_LEN + IPV6_HEADER_LEN + ICMPV6_NS_HEADER_LEN + || recvbuf.length < length) { + throw new ParseException("Invalid packet length: " + length); + } + final ByteBuffer packet = ByteBuffer.wrap(recvbuf, 0, length); + + // Parse each header and option in Neighbor Solicitation packet in order. + final EthernetHeader ethHdr = Struct.parse(EthernetHeader.class, packet); + final Ipv6Header ipv6Hdr = Struct.parse(Ipv6Header.class, packet); + final Icmpv6Header icmpv6Hdr = Struct.parse(Icmpv6Header.class, packet); + final NsHeader nsHdr = Struct.parse(NsHeader.class, packet); + final LlaOption slla = (packet.remaining() == 0) + ? null + : Struct.parse(LlaOption.class, packet); + + return new NeighborSolicitation(ethHdr, ipv6Hdr, icmpv6Hdr, nsHdr, slla); + } + + /** + * Thrown when parsing Neighbor Solicitation packet failed. + */ + public static class ParseException extends Exception { + ParseException(String message) { + super(message); + } + } +}
diff --git a/src/com/android/networkstack/util/DnsUtils.java b/src/com/android/networkstack/util/DnsUtils.java index 83f2daf..622f56a 100644 --- a/src/com/android/networkstack/util/DnsUtils.java +++ b/src/com/android/networkstack/util/DnsUtils.java
@@ -29,7 +29,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.internal.util.TrafficStatsConstants; +import com.android.net.module.util.NetworkStackConstants; import com.android.server.connectivity.NetworkMonitor.DnsLogFunc; import java.net.InetAddress; @@ -126,7 +126,7 @@ // look at the tag at all. Given that this is a library, the tag should be passed in by the // caller. final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_PROBE); + NetworkStackConstants.TAG_SYSTEM_PROBE); if (type == TYPE_ADDRCONFIG) { dnsResolver.query(network, host, flag, r -> r.run(), null /* cancellationSignal */,
diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java index f8a9bab..ce0374d 100755 --- a/src/com/android/server/connectivity/NetworkMonitor.java +++ b/src/com/android/server/connectivity/NetworkMonitor.java
@@ -21,8 +21,6 @@ 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; @@ -33,10 +31,10 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_SKIPPED; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; 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.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; import static android.net.captiveportal.CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs; import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE; import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS; @@ -127,12 +125,11 @@ import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; -import android.os.UserHandle; +import android.os.SystemProperties; import android.provider.DeviceConfig; import android.provider.Settings; import android.stats.connectivity.ProbeResult; import android.stats.connectivity.ProbeType; -import android.telephony.AccessNetworkConstants; import android.telephony.CellIdentityNr; import android.telephony.CellInfo; import android.telephony.CellInfoGsm; @@ -141,8 +138,6 @@ import android.telephony.CellInfoTdscdma; import android.telephony.CellInfoWcdma; import android.telephony.CellSignalStrength; -import android.telephony.NetworkRegistrationInfo; -import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -161,13 +156,15 @@ 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.net.module.util.DeviceConfigUtils; +import com.android.net.module.util.NetworkStackConstants; import com.android.networkstack.NetworkStackNotifier; import com.android.networkstack.R; import com.android.networkstack.apishim.CaptivePortalDataShimImpl; import com.android.networkstack.apishim.NetworkInformationShimImpl; +import com.android.networkstack.apishim.api29.ConstantsShim; import com.android.networkstack.apishim.common.CaptivePortalDataShim; +import com.android.networkstack.apishim.common.NetworkInformationShim; import com.android.networkstack.apishim.common.ShimUtils; import com.android.networkstack.apishim.common.UnsupportedApiLevelException; import com.android.networkstack.metrics.DataStallDetectionStats; @@ -224,6 +221,9 @@ private static final String TAG = NetworkMonitor.class.getSimpleName(); private static final boolean DBG = true; private static final boolean VDBG = false; + // TODO(b/185082309): For flaky test debug only, remove it after fixing. + private static final boolean DDBG_STALL = "cf_x86_auto-userdebug".equals( + SystemProperties.get("ro.build.flavor", "")); 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) " @@ -515,6 +515,8 @@ private final boolean mPrivateIpNoInternetEnabled; private final boolean mMetricsEnabled; + @NonNull + private final NetworkInformationShim mInfoShim = NetworkInformationShimImpl.newInstance(); // The validation metrics are accessed by individual probe threads, and by the StateMachine // thread. All accesses must be synchronized to make sure the StateMachine thread can see @@ -671,6 +673,7 @@ (Pair<LinkProperties, NetworkCapabilities>) connectedMsg.obj; mLinkProperties = attrs.first; mNetworkCapabilities = attrs.second; + suppressNotificationIfNetworkRestricted(); } /** @@ -735,6 +738,12 @@ return NetworkMonitorUtils.isPrivateDnsValidationRequired(mNetworkCapabilities); } + private void suppressNotificationIfNetworkRestricted() { + if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) { + mDontDisplaySigninNotification = true; + } + } + private void notifyNetworkTested(NetworkTestResultParcelable result) { try { if (mCallbackVersion <= 5) { @@ -984,6 +993,7 @@ break; case EVENT_NETWORK_CAPABILITIES_CHANGED: mNetworkCapabilities = (NetworkCapabilities) message.obj; + suppressNotificationIfNetworkRestricted(); break; default: break; @@ -1227,6 +1237,22 @@ if (!mEvaluationTimer.isStarted()) { mEvaluationTimer.start(); } + + // Check if the network is captive with Terms & Conditions page. The first network + // evaluation for captive networks with T&Cs returns early but NetworkMonitor will then + // keep checking for connectivity to determine when the T&Cs are cleared. + if (isTermsAndConditionsCaptive(mInfoShim.getCaptivePortalData(mLinkProperties)) + && mValidations == 0) { + mLastPortalProbeResult = new CaptivePortalProbeResult( + CaptivePortalProbeResult.PORTAL_CODE, + mLinkProperties.getCaptivePortalData().getUserPortalUrl() + .toString(), null, + CaptivePortalProbeResult.PROBE_UNKNOWN); + mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID, + mLastPortalProbeResult.redirectUrl); + transitionTo(mCaptivePortalState); + return; + } sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0); if (mUidResponsibleForReeval != INVALID_UID) { TrafficStats.setThreadStatsUid(mUidResponsibleForReeval); @@ -1562,6 +1588,16 @@ // Transit EvaluatingPrivateDnsState to get to Validated // state (even if no Private DNS validation required). transitionTo(mEvaluatingPrivateDnsState); + } else if (isTermsAndConditionsCaptive( + mInfoShim.getCaptivePortalData(mLinkProperties))) { + mLastPortalProbeResult = new CaptivePortalProbeResult( + CaptivePortalProbeResult.PORTAL_CODE, + mLinkProperties.getCaptivePortalData().getUserPortalUrl() + .toString(), null, + CaptivePortalProbeResult.PROBE_UNKNOWN); + mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID, + mLastPortalProbeResult.redirectUrl); + transitionTo(mCaptivePortalState); } else if (probeResult.isPortal()) { mEvaluationState.reportEvaluationResult(NETWORK_VALIDATION_RESULT_INVALID, probeResult.redirectUrl); @@ -2333,10 +2369,6 @@ long endTime = SystemClock.elapsedRealtime(); - sendNetworkConditionsBroadcast(true /* response received */, - result.isPortal() /* isCaptivePortal */, - startTime, endTime); - log("isCaptivePortal: isSuccessful()=" + result.isSuccessful() + " isPortal()=" + result.isPortal() + " RedirectUrl=" + result.redirectUrl @@ -2433,7 +2465,7 @@ String redirectUrl = null; final Stopwatch probeTimer = new Stopwatch().start(); final int oldTag = TrafficStats.getAndSetThreadStatsTag( - TrafficStatsConstants.TAG_SYSTEM_PROBE); + NetworkStackConstants.TAG_SYSTEM_PROBE); try { // Follow redirects for PAC probes as such probes verify connectivity by fetching the // PAC proxy file, which may be configured behind a redirect. @@ -2519,8 +2551,17 @@ final CaptivePortalProbeResult probeResult; if (probeSpec == null) { + if (CaptivePortalProbeResult.isPortalCode(httpResponseCode) + && TextUtils.isEmpty(redirectUrl) + && ShimUtils.isAtLeastS()) { + // If a portal is a non-redirect portal (often portals that return HTTP 200 with a + // login page for all HTTP requests), report the probe URL as the login URL starting + // from S (b/172048052). This avoids breaking assumptions that + // [is a portal] is equivalent to [there is a login URL]. + redirectUrl = url.toString(); + } probeResult = new CaptivePortalProbeResult(httpResponseCode, redirectUrl, - url.toString(), 1 << probeType); + url.toString(), 1 << probeType); } else { probeResult = probeSpec.getResult(httpResponseCode, redirectUrl); } @@ -3006,74 +3047,6 @@ 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); - } - mDependencies.sendNetworkConditionsBroadcast(mContext, latencyBroadcast); - } - private void logNetworkEvent(int evtype) { int[] transports = mNetworkCapabilities.getTransportTypes(); mMetricsLog.log(mCleartextDnsNetwork, transports, new NetworkEvent(evtype)); @@ -3186,15 +3159,6 @@ } /** - * Send a broadcast indicating network conditions. - */ - public void sendNetworkConditionsBroadcast(@NonNull Context context, - @NonNull Intent broadcast) { - context.sendBroadcastAsUser(broadcast, UserHandle.CURRENT, - NetworkMonitorUtils.PERMISSION_ACCESS_NETWORK_CONDITIONS); - } - - /** * Check whether or not one specific experimental feature for a particular namespace from * {@link DeviceConfig} is enabled by comparing NetworkStack module version * {@link NetworkStack} with current version of property. If this property version is valid, @@ -3277,6 +3241,10 @@ // considered in the evaluation happened in defined threshold time. final long now = SystemClock.elapsedRealtime(); final long firstTimeoutTime = now - mDnsEvents[firstConsecutiveTimeoutIndex].mTimeStamp; + if (DDBG_STALL) { + Log.d(TAG, "DSD.isDataStallSuspected, first=" + + firstTimeoutTime + ", valid=" + validTime); + } return (firstTimeoutTime < validTime); } @@ -3331,12 +3299,17 @@ int typeToCollect = 0; final int notStall = -1; - final StringJoiner msg = (DBG || VDBG_STALL) ? new StringJoiner(", ") : null; + final StringJoiner msg = (DBG || VDBG_STALL || DDBG_STALL) ? new StringJoiner(", ") : null; // Reevaluation will generate traffic. Thus, set a minimal reevaluation timer to limit the // possible traffic cost in metered network. + final long currentTime = SystemClock.elapsedRealtime(); if (!mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED) - && (SystemClock.elapsedRealtime() - getLastProbeTime() - < mDataStallMinEvaluateTime)) { + && (currentTime - getLastProbeTime() < mDataStallMinEvaluateTime)) { + if (DDBG_STALL) { + Log.d(TAG, "isDataStall: false, currentTime=" + currentTime + + ", lastProbeTime=" + getLastProbeTime() + + ", MinEvaluateTime=" + mDataStallMinEvaluateTime); + } return false; } // Check TCP signal. Suspect it may be a data stall if : @@ -3349,7 +3322,7 @@ } else if (tst.isDataStallSuspected()) { typeToCollect |= DATA_STALL_EVALUATION_TYPE_TCP; } - if (DBG || VDBG_STALL) { + if (DBG || VDBG_STALL || DDBG_STALL) { msg.add("tcp packets received=" + tst.getLatestReceivedCount()) .add("latest tcp fail rate=" + tst.getLatestPacketFailPercentage()); } @@ -3366,7 +3339,7 @@ typeToCollect |= DATA_STALL_EVALUATION_TYPE_DNS; logNetworkEvent(NetworkEvent.NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND); } - if (DBG || VDBG_STALL) { + if (DBG || VDBG_STALL || DDBG_STALL) { msg.add("consecutive dns timeout count=" + dsd.getConsecutiveTimeoutCount()); } } @@ -3391,7 +3364,7 @@ } // log only data stall suspected. - if ((DBG && (typeToCollect > 0)) || VDBG_STALL) { + if ((DBG && (typeToCollect > 0)) || VDBG_STALL || DDBG_STALL) { log("isDataStall: result=" + typeToCollect + ", " + msg); } @@ -3463,6 +3436,14 @@ } protected void reportEvaluationResult(int result, @Nullable String redirectUrl) { + if (!isValidationRequired() && mProbeCompleted == 0 && ShimUtils.isAtLeastS()) { + // If validation is not required AND no probes were attempted, the validation was + // skipped. Report this to ConnectivityService for ConnectivityDiagnostics, but only + // if the platform is Android S+, as ConnectivityService must also know how to + // understand this bit. + result |= NETWORK_VALIDATION_RESULT_SKIPPED; + } + mEvaluationResult = result; mRedirectUrl = redirectUrl; final NetworkTestResultParcelable p = new NetworkTestResultParcelable(); @@ -3566,4 +3547,17 @@ private static Uri getCaptivePortalApiUrl(LinkProperties lp) { return NetworkInformationShimImpl.newInstance().getCaptivePortalApiUrl(lp); } + + /** + * Check if the network is captive with terms and conditions page + * @return true if network is captive with T&C page, false otherwise + */ + private boolean isTermsAndConditionsCaptive(CaptivePortalDataShim captivePortalDataShim) { + return captivePortalDataShim != null + && captivePortalDataShim.getUserPortalUrl() != null + && !TextUtils.isEmpty(captivePortalDataShim.getUserPortalUrl().toString()) + && captivePortalDataShim.isCaptive() + && captivePortalDataShim.getUserPortalUrlSource() + == ConstantsShim.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT; + } }
diff --git a/tests/hostdriven/Android.bp b/tests/hostdriven/Android.bp index 3509f89..6c3de45 100644 --- a/tests/hostdriven/Android.bp +++ b/tests/hostdriven/Android.bp
@@ -14,6 +14,10 @@ // limitations under the License. // +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + java_test_host { name: "NetworkStackHostTests", srcs: ["host/src/**/*.kt"],
diff --git a/tests/hostlib/Android.bp b/tests/hostlib/Android.bp index 9a88634..189a88c 100644 --- a/tests/hostlib/Android.bp +++ b/tests/hostlib/Android.bp
@@ -14,6 +14,10 @@ // limitations under the License. // +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + java_library_host { name: "net-host-tests-utils", srcs: [ @@ -26,4 +30,4 @@ "kotlin-test", "cts-install-lib-host", ], -} \ No newline at end of file +}
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp index 2c30b3c..3842b50 100644 --- a/tests/integration/Android.bp +++ b/tests/integration/Android.bp
@@ -14,6 +14,10 @@ // limitations under the License. // +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + java_defaults { name: "NetworkStackIntegrationTestsJniDefaults", defaults: ["libnetworkstackutilsjni_deps"], @@ -68,6 +72,7 @@ platform_apis: true, test_suites: ["device-tests"], min_sdk_version: "29", + target_sdk_version: "30", } // Network stack next integration tests. @@ -83,7 +88,6 @@ certificate: "networkstack", platform_apis: true, test_suites: ["device-tests"], - enabled: false, // Disabled in mainline-prod } // The static lib needs to be jarjared by each module so they do not conflict with each other @@ -108,10 +112,12 @@ certificate: "networkstack", platform_apis: true, min_sdk_version: "29", + target_sdk_version: "30", test_suites: ["device-tests", "mts"], test_config: "AndroidTest_Coverage.xml", defaults: ["NetworkStackIntegrationTestsJniDefaults"], static_libs: [ + "modules-utils-native-coverage-listener", "NetworkStackTestsLib", "NetworkStackIntegrationTestsLib", "NetworkStackStaticLibTestsLib",
diff --git a/tests/integration/AndroidManifest.xml b/tests/integration/AndroidManifest.xml index 12f5d7d..bfd3735 100644 --- a/tests/integration/AndroidManifest.xml +++ b/tests/integration/AndroidManifest.xml
@@ -16,7 +16,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.server.networkstack.integrationtests" android:sharedUserId="android.uid.networkstack"> - <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> <!-- Note: do not add any privileged or signature permissions that are granted to the network stack app. Otherwise, the test APK will install, but when the device is
diff --git a/tests/integration/AndroidManifest_coverage.xml b/tests/integration/AndroidManifest_coverage.xml index 660e42d..fc91e59 100644 --- a/tests/integration/AndroidManifest_coverage.xml +++ b/tests/integration/AndroidManifest_coverage.xml
@@ -16,7 +16,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.server.networkstack.coverage" android:sharedUserId="android.uid.networkstack"> - <uses-sdk android:minSdkVersion="29" android:targetSdkVersion="29" /> <!-- Note: do not add any privileged or signature permissions that are granted to the network stack app. Otherwise, the test APK will install, but when the device is
diff --git a/tests/integration/AndroidTest_Coverage.xml b/tests/integration/AndroidTest_Coverage.xml index e33fa87..3e7361b 100644 --- a/tests/integration/AndroidTest_Coverage.xml +++ b/tests/integration/AndroidTest_Coverage.xml
@@ -23,5 +23,6 @@ <option name="package" value="com.android.server.networkstack.coverage" /> <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> + <option name="device-listeners" value="com.android.modules.utils.testing.NativeCoverageHackInstrumentationListener" /> </test> </configuration>
diff --git a/tests/integration/lint-baseline.xml b/tests/integration/lint-baseline.xml new file mode 100644 index 0000000..eadec6f --- /dev/null +++ b/tests/integration/lint-baseline.xml
@@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#getDhcpServerAddress`" + errorLine1=" assertEquals(SERVER_ADDR, captor.getValue().getDhcpServerAddress());" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java" + line="1327" + column="53"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#getNat64Prefix`" + errorLine1=" argThat(lp -> Objects.equals(expected, lp.getNat64Prefix())));" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java" + line="1623" + column="59"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#getNat64Prefix`" + errorLine1=" lp -> !Objects.equals(unchanged, lp.getNat64Prefix())));" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java" + line="1629" + column="53"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#getNat64Prefix`" + errorLine1=" if (lp.getNat64Prefix() != null) {" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java" + line="1660" + column="16"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.LinkProperties#getNat64Prefix`" + errorLine1=" assertEquals(prefix, lp.getNat64Prefix());" + errorLine2=" ~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java" + line="1661" + column="37"/> + </issue> + +</issues>
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt b/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt index 9f29a2e..b217ebb 100644 --- a/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt +++ b/tests/integration/src/android/net/ip/IpClientIntegrationTest.kt
@@ -17,6 +17,9 @@ package android.net.ip import android.net.ipmemorystore.NetworkAttributes +import android.util.ArrayMap +import java.net.Inet6Address +import kotlin.test.assertEquals import org.mockito.Mockito.any import org.mockito.ArgumentCaptor import org.mockito.Mockito.eq @@ -28,22 +31,20 @@ * Tests for IpClient, run with signature permissions. */ class IpClientIntegrationTest : IpClientIntegrationTestCommon() { + private val mEnabledFeatures = ArrayMap<String, Boolean>() + override fun makeIIpClient(ifaceName: String, cb: IIpClientCallbacks): IIpClient { return mIpc.makeConnector() } override fun useNetworkStackSignature() = true - override fun setDhcpFeatures( - isDhcpLeaseCacheEnabled: Boolean, - isRapidCommitEnabled: Boolean, - isDhcpIpConflictDetectEnabled: Boolean, - isIPv6OnlyPreferredEnabled: Boolean - ) { - mDependencies.setDhcpLeaseCacheEnabled(isDhcpLeaseCacheEnabled) - mDependencies.setDhcpRapidCommitEnabled(isRapidCommitEnabled) - mDependencies.setDhcpIpConflictDetectEnabled(isDhcpIpConflictDetectEnabled) - mDependencies.setIPv6OnlyPreferredEnabled(isIPv6OnlyPreferredEnabled) + override fun isFeatureEnabled(name: String, defaultEnabled: Boolean): Boolean { + return mEnabledFeatures.get(name) ?: defaultEnabled + } + + override fun setFeatureEnabled(name: String, enabled: Boolean) { + mEnabledFeatures.put(name, enabled) } override fun getStoredNetworkAttributes(l2Key: String, timeout: Long): NetworkAttributes { @@ -57,4 +58,15 @@ override fun assertIpMemoryNeverStoreNetworkAttributes(l2Key: String, timeout: Long) { verify(mIpMemoryStore, never()).storeNetworkAttributes(eq(l2Key), any(), any()) } + + override fun assertNotifyNeighborLost(targetIp: Inet6Address) { + val target = ArgumentCaptor.forClass(Inet6Address::class.java) + + verify(mCallback, timeout(TEST_TIMEOUT_MS)).notifyLost(target.capture(), any()) + assertEquals(targetIp, target.getValue()) + } + + override fun assertNeverNotifyNeighborLost() { + verify(mCallback, never()).notifyLost(any(), any()) + } }
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java index 1b5660c..2b00fb1 100644 --- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java +++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -27,31 +27,35 @@ import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS; import static android.net.dhcp.DhcpResultsParcelableUtil.fromStableParcelable; +import static android.net.ip.IpReachabilityMonitor.MIN_NUD_SOLICIT_NUM; +import static android.net.ip.IpReachabilityMonitor.NUD_MCAST_RESOLICIT_NUM; import static android.net.ipmemorystore.Status.SUCCESS; import static android.system.OsConstants.ETH_P_IPV6; import static android.system.OsConstants.IFA_F_TEMPORARY; import static android.system.OsConstants.IPPROTO_ICMPV6; -import static android.system.OsConstants.IPPROTO_TCP; import static com.android.net.module.util.Inet4AddressUtils.getBroadcastAddress; import static com.android.net.module.util.Inet4AddressUtils.getPrefixMaskAsInet4Address; import static com.android.net.module.util.NetworkStackConstants.ARP_REPLY; import static com.android.net.module.util.NetworkStackConstants.ARP_REQUEST; import static com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN; +import static com.android.net.module.util.NetworkStackConstants.ETHER_BROADCAST; import static com.android.net.module.util.NetworkStackConstants.ETHER_HEADER_LEN; -import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6; import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_OFFSET; -import static com.android.net.module.util.NetworkStackConstants.ICMPV6_CHECKSUM_OFFSET; -import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR; -import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO; -import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_RDNSS; -import static com.android.net.module.util.NetworkStackConstants.ICMPV6_RA_HEADER_LEN; -import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION; import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_SOLICITATION; import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY; +import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST; +import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST; import static com.android.net.module.util.NetworkStackConstants.IPV6_HEADER_LEN; -import static com.android.net.module.util.NetworkStackConstants.IPV6_LEN_OFFSET; import static com.android.net.module.util.NetworkStackConstants.IPV6_PROTOCOL_OFFSET; +import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE; +import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER; +import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED; +import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS; +import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK; import static junit.framework.Assert.fail; @@ -111,6 +115,7 @@ import android.net.dhcp.DhcpPacket; import android.net.dhcp.DhcpPacket.ParseException; import android.net.dhcp.DhcpRequestPacket; +import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; import android.net.ipmemorystore.NetworkAttributes; import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener; import android.net.ipmemorystore.Status; @@ -122,6 +127,7 @@ import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import android.net.util.InterfaceParams; import android.net.util.NetworkStackUtils; +import android.net.util.SharedLog; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; @@ -131,6 +137,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; +import android.stats.connectivity.NetworkQuirkEvent; import android.system.ErrnoException; import android.system.Os; @@ -139,14 +146,21 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.util.HexDump; import com.android.internal.util.StateMachine; import com.android.net.module.util.ArrayTrackRecord; -import com.android.net.module.util.IpUtils; +import com.android.net.module.util.Ipv6Utils; +import com.android.net.module.util.structs.LlaOption; +import com.android.net.module.util.structs.PrefixInformationOption; +import com.android.net.module.util.structs.RdnssOption; import com.android.networkstack.apishim.CaptivePortalDataShimImpl; import com.android.networkstack.apishim.ConstantsShim; import com.android.networkstack.apishim.common.ShimUtils; import com.android.networkstack.arp.ArpPacket; import com.android.networkstack.metrics.IpProvisioningMetrics; +import com.android.networkstack.metrics.NetworkQuirkMetrics; +import com.android.networkstack.packets.NeighborAdvertisement; +import com.android.networkstack.packets.NeighborSolicitation; import com.android.server.NetworkObserver; import com.android.server.NetworkObserverRegistry; import com.android.server.NetworkStackService.NetworkStackServiceManager; @@ -180,6 +194,7 @@ import java.lang.annotation.Target; import java.lang.reflect.Method; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.nio.ByteBuffer; @@ -258,6 +273,8 @@ @Mock private IpMemoryStoreService mIpMemoryStoreService; @Mock private PowerManager.WakeLock mTimeoutWakeLock; @Mock protected NetworkStackIpMemoryStore mIpMemoryStore; + @Mock private NetworkQuirkMetrics.Dependencies mNetworkQuirkMetricsDeps; + @Mock protected IpReachabilityMonitor.Callback mCallback; @Spy private INetd mNetd; private NetworkObserverRegistry mNetworkObserverRegistry; @@ -267,7 +284,7 @@ /***** END signature required test members *****/ - private IIpClientCallbacks mCb; + protected IIpClientCallbacks mCb; private IIpClient mIIpClient; private String mIfaceName; private HandlerThread mPacketReaderThread; @@ -322,7 +339,10 @@ private static final String HOSTNAME = "testhostname"; private static final int TEST_DEFAULT_MTU = 1500; private static final int TEST_MIN_MTU = 1280; - private static final byte[] SERVER_MAC = new byte[] { 0x00, 0x1A, 0x11, 0x22, 0x33, 0x44 }; + private static final MacAddress ROUTER_MAC = MacAddress.fromString("00:1A:11:22:33:44"); + private static final byte[] ROUTER_MAC_BYTES = ROUTER_MAC.toByteArray(); + private static final Inet6Address ROUTER_LINK_LOCAL = + (Inet6Address) InetAddresses.parseNumericAddress("fe80::1"); private static final String TEST_HOST_NAME = "AOSP on Crosshatch"; private static final String TEST_HOST_NAME_TRANSLITERATION = "AOSP-on-Crosshatch"; private static final String TEST_CAPTIVE_PORTAL_URL = "https://example.com/capportapi"; @@ -348,32 +368,12 @@ }; protected class Dependencies extends IpClient.Dependencies { - private boolean mIsDhcpLeaseCacheEnabled; - private boolean mIsDhcpRapidCommitEnabled; - private boolean mIsDhcpIpConflictDetectEnabled; // Can't use SparseIntArray, it doesn't have an easy way to know if a key is not present. private HashMap<String, Integer> mIntConfigProperties = new HashMap<>(); private DhcpClient mDhcpClient; private boolean mIsHostnameConfigurationEnabled; private String mHostname; private boolean mIsInterfaceRecovered; - private boolean mIsIPv6OnlyPreferredEnabled; - - public void setDhcpLeaseCacheEnabled(final boolean enable) { - mIsDhcpLeaseCacheEnabled = enable; - } - - public void setDhcpRapidCommitEnabled(final boolean enable) { - mIsDhcpRapidCommitEnabled = enable; - } - - public void setDhcpIpConflictDetectEnabled(final boolean enable) { - mIsDhcpIpConflictDetectEnabled = enable; - } - - public void setIPv6OnlyPreferredEnabled(final boolean enable) { - mIsIPv6OnlyPreferredEnabled = enable; - } public void setHostnameConfiguration(final boolean enable, final String hostname) { mIsHostnameConfigurationEnabled = enable; @@ -416,25 +416,28 @@ } @Override + public IpReachabilityMonitor getIpReachabilityMonitor(Context context, + InterfaceParams ifParams, Handler h, SharedLog log, + IpReachabilityMonitor.Callback callback, boolean usingMultinetworkPolicyTracker, + IpReachabilityMonitor.Dependencies deps, final INetd netd) { + return new IpReachabilityMonitor(context, ifParams, h, log, mCallback, + usingMultinetworkPolicyTracker, deps, netd); + } + + @Override + public boolean isFeatureEnabled(final Context context, final String name, + final boolean defaultEnabled) { + return IpClientIntegrationTestCommon.this.isFeatureEnabled(name, defaultEnabled); + } + + @Override public DhcpClient.Dependencies getDhcpClientDependencies( NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) { return new DhcpClient.Dependencies(ipMemoryStore, metrics) { @Override public boolean isFeatureEnabled(final Context context, final String name, final boolean defaultEnabled) { - switch (name) { - case NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION: - return mIsDhcpRapidCommitEnabled; - case NetworkStackUtils.DHCP_INIT_REBOOT_VERSION: - return mIsDhcpLeaseCacheEnabled; - case NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION: - return mIsDhcpIpConflictDetectEnabled; - case NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION: - return mIsIPv6OnlyPreferredEnabled; - default: - fail("Invalid experiment flag: " + name); - return false; - } + return Dependencies.this.isFeatureEnabled(context, name, defaultEnabled); } @Override @@ -461,6 +464,28 @@ } @Override + public IpReachabilityMonitor.Dependencies getIpReachabilityMonitorDeps(Context context, + String name) { + return new IpReachabilityMonitor.Dependencies() { + public void acquireWakeLock(long durationMs) { + // It doesn't matter for the integration test app on whether the wake lock + // is acquired or not. + return; + } + + public IpNeighborMonitor makeIpNeighborMonitor(Handler h, SharedLog log, + NeighborEventConsumer cb) { + return new IpNeighborMonitor(h, log, cb); + } + + public boolean isFeatureEnabled(final Context context, final String name, + boolean defaultEnabled) { + return Dependencies.this.isFeatureEnabled(context, name, defaultEnabled); + } + }; + } + + @Override public int getDeviceConfigPropertyInt(String name, int defaultValue) { Integer value = mIntConfigProperties.get(name); if (value == null) { @@ -472,15 +497,20 @@ public void setDeviceConfigProperty(String name, int value) { mIntConfigProperties.put(name, value); } + + @Override + public NetworkQuirkMetrics getNetworkQuirkMetrics() { + return new NetworkQuirkMetrics(mNetworkQuirkMetricsDeps); + } } @NonNull protected abstract IIpClient makeIIpClient( @NonNull String ifaceName, @NonNull IIpClientCallbacks cb); - protected abstract void setDhcpFeatures(boolean isDhcpLeaseCacheEnabled, - boolean isRapidCommitEnabled, boolean isDhcpIpConflictDetectEnabled, - boolean isIPv6OnlyPreferredEnabled); + protected abstract void setFeatureEnabled(String name, boolean enabled); + + protected abstract boolean isFeatureEnabled(String name, boolean defaultEnabled); protected abstract boolean useNetworkStackSignature(); @@ -488,6 +518,10 @@ protected abstract void assertIpMemoryNeverStoreNetworkAttributes(String l2Key, long timeout); + protected abstract void assertNotifyNeighborLost(Inet6Address targetIp); + + protected abstract void assertNeverNotifyNeighborLost(); + protected final boolean testSkipped() { // TODO: split out a test suite for root tests, and fail hard instead of skipping the test // if it is run on devices where TestNetworkStackServiceClient is not supported @@ -495,6 +529,17 @@ && (mIsSignatureRequiredTest || !TestNetworkStackServiceClient.isSupported()); } + protected void setDhcpFeatures(final boolean isDhcpLeaseCacheEnabled, + final boolean isRapidCommitEnabled, final boolean isDhcpIpConflictDetectEnabled, + final boolean isIPv6OnlyPreferredEnabled) { + setFeatureEnabled(NetworkStackUtils.DHCP_INIT_REBOOT_VERSION, isDhcpLeaseCacheEnabled); + setFeatureEnabled(NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION, isRapidCommitEnabled); + setFeatureEnabled(NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION, + isDhcpIpConflictDetectEnabled); + setFeatureEnabled(NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION, + isIPv6OnlyPreferredEnabled); + } + @Before public void setUp() throws Exception { final Method testMethod = IpClientIntegrationTestCommon.class.getMethod( @@ -709,6 +754,22 @@ } } + private NeighborAdvertisement parseNeighborAdvertisementOrNull(final byte[] packet) { + try { + return NeighborAdvertisement.parse(packet, packet.length); + } catch (NeighborAdvertisement.ParseException e) { + return null; + } + } + + private NeighborSolicitation parseNeighborSolicitationOrNull(final byte[] packet) { + try { + return NeighborSolicitation.parse(packet, packet.length); + } catch (NeighborSolicitation.ParseException e) { + return null; + } + } + private static ByteBuffer buildDhcpOfferPacket(final DhcpPacket packet, final Inet4Address clientAddress, final Integer leaseTimeSec, final short mtu, final String captivePortalUrl, final Integer ipv6OnlyWaitTime) { @@ -758,7 +819,7 @@ private void sendArpReply(final byte[] clientMac) throws IOException { final ByteBuffer packet = ArpPacket.buildArpPacket(clientMac /* dst */, - SERVER_MAC /* src */, INADDR_ANY.getAddress() /* target IP */, + ROUTER_MAC_BYTES /* srcMac */, INADDR_ANY.getAddress() /* target IP */, clientMac /* target HW address */, CLIENT_ADDR.getAddress() /* sender IP */, (short) ARP_REPLY); mPacketReader.sendResponse(packet); @@ -766,7 +827,7 @@ private void sendArpProbe() throws IOException { final ByteBuffer packet = ArpPacket.buildArpPacket(DhcpPacket.ETHER_BROADCAST /* dst */, - SERVER_MAC /* src */, CLIENT_ADDR.getAddress() /* target IP */, + ROUTER_MAC_BYTES /* srcMac */, CLIENT_ADDR.getAddress() /* target IP */, new byte[ETHER_ADDR_LEN] /* target HW address */, INADDR_ANY.getAddress() /* sender IP */, (short) ARP_REQUEST); mPacketReader.sendResponse(packet); @@ -779,11 +840,14 @@ private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled, final boolean shouldReplyRapidCommitAck, final boolean isPreconnectionEnabled, final boolean isDhcpIpConflictDetectEnabled, final boolean isIPv6OnlyPreferredEnabled, - final String displayName, final ScanResultInfo scanResultInfo) throws Exception { + final String displayName, final ScanResultInfo scanResultInfo, + final Layer2Information layer2Info) throws Exception { ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() - .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER, - MacAddress.fromString(TEST_DEFAULT_BSSID))) + .withLayer2Information(layer2Info == null + ? new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_DEFAULT_BSSID)) + : layer2Info) .withoutIPv6(); if (isPreconnectionEnabled) prov.withPreconnection(); if (displayName != null) prov.withDisplayName(displayName); @@ -805,7 +869,7 @@ throws Exception { startIpClientProvisioning(isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled, isPreconnectionEnabled, isDhcpIpConflictDetectEnabled, isIPv6OnlyPreferredEnabled, - null /* displayName */, null /* ScanResultInfo */); + null /* displayName */, null /* ScanResultInfo */, null /* layer2Info */); } private void assertIpMemoryStoreNetworkAttributes(final Integer leaseTimeSec, @@ -860,10 +924,11 @@ final boolean isDhcpIpConflictDetectEnabled, final boolean isIPv6OnlyPreferredEnabled, final String captivePortalApiUrl, final String displayName, - final ScanResultInfo scanResultInfo) throws Exception { + final ScanResultInfo scanResultInfo, final Layer2Information layer2Info) + throws Exception { startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck, false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled, - isIPv6OnlyPreferredEnabled, displayName, scanResultInfo); + isIPv6OnlyPreferredEnabled, displayName, scanResultInfo, layer2Info); return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu, captivePortalApiUrl); } @@ -909,7 +974,8 @@ return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled, false /* isIPv6OnlyPreferredEnabled */, - null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */); + null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */, + null /* layer2Info */); } private List<DhcpPacket> performDhcpHandshake() throws Exception { @@ -918,10 +984,21 @@ TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */); } - private DhcpPacket getNextDhcpPacket() throws ParseException { - byte[] packet = mDhcpPacketReadHead.getValue().poll(PACKET_TIMEOUT_MS, this::isDhcpPacket); + private DhcpPacket getNextDhcpPacket(final long timeout) throws Exception { + byte[] packet; + while ((packet = mDhcpPacketReadHead.getValue() + .poll(timeout, this::isDhcpPacket)) != null) { + final DhcpPacket dhcpPacket = DhcpPacket.decodeFullPacket(packet, packet.length, + ENCAP_L2); + if (dhcpPacket != null) return dhcpPacket; + } + return null; + } + + private DhcpPacket getNextDhcpPacket() throws Exception { + final DhcpPacket packet = getNextDhcpPacket(PACKET_TIMEOUT_MS); assertNotNull("No expected DHCP packet received on interface within timeout", packet); - return DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L2); + return packet; } private DhcpPacket getReplyFromDhcpLease(final NetworkAttributes na, boolean timeout) @@ -1127,6 +1204,14 @@ assertEquals(packet.senderIp, CLIENT_ADDR); } + private void assertGratuitousARP(final ArpPacket packet) { + assertEquals(packet.opCode, ARP_REPLY); + assertEquals(packet.senderIp, CLIENT_ADDR); + assertEquals(packet.targetIp, CLIENT_ADDR); + assertTrue(Arrays.equals(packet.senderHwAddress.toByteArray(), mClientMac)); + assertTrue(Arrays.equals(packet.targetHwAddress.toByteArray(), ETHER_BROADCAST)); + } + private void doIpAddressConflictDetectionTest(final boolean causeIpAddressConflict, final boolean shouldReplyRapidCommitAck, final boolean isDhcpIpConflictDetectEnabled, final boolean shouldResponseArpReply) throws Exception { @@ -1431,12 +1516,43 @@ HandlerUtils.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); } - private boolean isRouterSolicitation(final byte[] packetBytes) { + private boolean isIcmpv6PacketOfType(final byte[] packetBytes, int type) { ByteBuffer packet = ByteBuffer.wrap(packetBytes); return packet.getShort(ETHER_TYPE_OFFSET) == (short) ETH_P_IPV6 && packet.get(ETHER_HEADER_LEN + IPV6_PROTOCOL_OFFSET) == (byte) IPPROTO_ICMPV6 - && packet.get(ETHER_HEADER_LEN + IPV6_HEADER_LEN) - == (byte) ICMPV6_ROUTER_SOLICITATION; + && packet.get(ETHER_HEADER_LEN + IPV6_HEADER_LEN) == (byte) type; + } + + private boolean isRouterSolicitation(final byte[] packetBytes) { + return isIcmpv6PacketOfType(packetBytes, ICMPV6_ROUTER_SOLICITATION); + } + + private boolean isNeighborAdvertisement(final byte[] packetBytes) { + return isIcmpv6PacketOfType(packetBytes, ICMPV6_NEIGHBOR_ADVERTISEMENT); + } + + private boolean isNeighborSolicitation(final byte[] packetBytes) { + return isIcmpv6PacketOfType(packetBytes, ICMPV6_NEIGHBOR_SOLICITATION); + } + + private NeighborAdvertisement getNextNeighborAdvertisement() throws ParseException { + final byte[] packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS, + this::isNeighborAdvertisement); + if (packet == null) return null; + + final NeighborAdvertisement na = parseNeighborAdvertisementOrNull(packet); + assertNotNull("Invalid neighbour advertisement received", na); + return na; + } + + private NeighborSolicitation getNextNeighborSolicitation() throws ParseException { + final byte[] packet = mPacketReader.popPacket(PACKET_TIMEOUT_MS, + this::isNeighborSolicitation); + if (packet == null) return null; + + final NeighborSolicitation ns = parseNeighborSolicitationOrNull(packet); + assertNotNull("Invalid neighbour solicitation received", ns); + return ns; } private void waitForRouterSolicitation() throws ParseException { @@ -1468,111 +1584,26 @@ // TODO: move this and the following method to a common location and use them in ApfTest. private static ByteBuffer buildPioOption(int valid, int preferred, String prefixString) throws Exception { - final int optLen = 4; - IpPrefix prefix = new IpPrefix(prefixString); - ByteBuffer option = ByteBuffer.allocate(optLen * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR); - option.put((byte) ICMPV6_ND_OPTION_PIO); // Type - option.put((byte) optLen); // Length in 8-byte units - option.put((byte) prefix.getPrefixLength()); // Prefix length - option.put((byte) 0b11000000); // L = 1, A = 1 - option.putInt(valid); - option.putInt(preferred); - option.putInt(0); // Reserved - option.put(prefix.getRawAddress()); - option.flip(); - return option; + return PrefixInformationOption.build(new IpPrefix(prefixString), + (byte) (PIO_FLAG_ON_LINK | PIO_FLAG_AUTONOMOUS), valid, preferred); } private static ByteBuffer buildRdnssOption(int lifetime, String... servers) throws Exception { - final int optLen = 1 + 2 * servers.length; - ByteBuffer option = ByteBuffer.allocate(optLen * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR); - option.put((byte) ICMPV6_ND_OPTION_RDNSS); // Type - option.put((byte) optLen); // Length in 8-byte units - option.putShort((short) 0); // Reserved - option.putInt(lifetime); // Lifetime - for (String server : servers) { - option.put(InetAddress.getByName(server).getAddress()); - } - option.flip(); - return option; + return RdnssOption.build(lifetime, servers); } - // HACK: these functions are here because IpUtils#transportChecksum is private. Even if we made - // that public, it won't be available on Q devices, and this test needs to run on Q devices. - // TODO: move the IpUtils code to frameworks/lib/net and link it statically. - private static int checksumFold(int sum) { - while (sum > 0xffff) { - sum = (sum >> 16) + (sum & 0xffff); - } - return sum; - } - - private static short checksumAdjust(short checksum, short oldWord, short newWord) { - checksum = (short) ~checksum; - int tempSum = checksumFold(uint16(checksum) + uint16(newWord) + 0xffff - uint16(oldWord)); - return (short) ~tempSum; - } - - public static int uint16(short s) { - return s & 0xffff; - } - - private static short icmpv6Checksum(ByteBuffer buf, int ipOffset, int transportOffset, - int transportLen) { - // The ICMPv6 checksum is the same as the TCP checksum, except the pseudo-header uses - // 58 (ICMPv6) instead of 6 (TCP). Calculate the TCP checksum, and then do an incremental - // checksum adjustment for the change in the next header byte. - short checksum = IpUtils.tcpChecksum(buf, ipOffset, transportOffset, transportLen); - return checksumAdjust(checksum, (short) IPPROTO_TCP, (short) IPPROTO_ICMPV6); + private static ByteBuffer buildSllaOption() throws Exception { + return LlaOption.build((byte) ICMPV6_ND_OPTION_SLLA, ROUTER_MAC); } private static ByteBuffer buildRaPacket(short lifetime, ByteBuffer... options) throws Exception { - final MacAddress srcMac = MacAddress.fromString("33:33:00:00:00:01"); - final MacAddress dstMac = MacAddress.fromString("01:02:03:04:05:06"); - final byte[] routerLinkLocal = InetAddresses.parseNumericAddress("fe80::1").getAddress(); - final byte[] allNodes = InetAddresses.parseNumericAddress("ff02::1").getAddress(); - - final ByteBuffer packet = ByteBuffer.allocate(TEST_DEFAULT_MTU); - int icmpLen = ICMPV6_RA_HEADER_LEN; - - // Ethernet header. - packet.put(srcMac.toByteArray()); - packet.put(dstMac.toByteArray()); - packet.putShort((short) ETHER_TYPE_IPV6); - - // IPv6 header. - packet.putInt(0x600abcde); // Version, traffic class, flowlabel - packet.putShort((short) 0); // Length, TBD - packet.put((byte) IPPROTO_ICMPV6); // Next header - packet.put((byte) 0xff); // Hop limit - packet.put(routerLinkLocal); // Source address - packet.put(allNodes); // Destination address - - // Router advertisement. - packet.put((byte) ICMPV6_ROUTER_ADVERTISEMENT); // ICMP type - packet.put((byte) 0); // ICMP code - packet.putShort((short) 0); // Checksum, TBD - packet.put((byte) 0); // Hop limit, unspecified - packet.put((byte) 0); // M=0, O=0 - packet.putShort(lifetime); // Router lifetime - packet.putInt(0); // Reachable time, unspecified - packet.putInt(100); // Retrans time 100ms. - - for (ByteBuffer option : options) { - packet.put(option); - option.clear(); // So we can reuse it in a future packet. - icmpLen += option.capacity(); - } - - // Populate length and checksum fields. - final int transportOffset = ETHER_HEADER_LEN + IPV6_HEADER_LEN; - final short checksum = icmpv6Checksum(packet, ETHER_HEADER_LEN, transportOffset, icmpLen); - packet.putShort(ETHER_HEADER_LEN + IPV6_LEN_OFFSET, (short) icmpLen); - packet.putShort(transportOffset + ICMPV6_CHECKSUM_OFFSET, checksum); - - packet.flip(); - return packet; + final MacAddress dstMac = + NetworkStackUtils.ipv6MulticastToEthernetMulticast(IPV6_ADDR_ALL_ROUTERS_MULTICAST); + return Ipv6Utils.buildRaPacket(ROUTER_MAC /* srcMac */, dstMac, + ROUTER_LINK_LOCAL /* srcIp */, IPV6_ADDR_ALL_NODES_MULTICAST /* dstIp */, + (byte) 0 /* M=0, O=0 */, lifetime, 0 /* Reachable time, unspecified */, + 100 /* Retrans time 100ms */, options); } private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception { @@ -1611,6 +1642,17 @@ return addr.isGlobalPreferred() && hasFlag(addr, flag); } + private LinkProperties doIpv6OnlyProvisioning() throws Exception { + final InOrder inOrder = inOrder(mCb); + final String dnsServer = "2001:4860:4860::64"; + final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); + final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); + final ByteBuffer slla = buildSllaOption(); + final ByteBuffer ra = buildRaPacket(pio, rdnss, slla); + + return doIpv6OnlyProvisioning(inOrder, ra); + } + private LinkProperties doIpv6OnlyProvisioning(InOrder inOrder, ByteBuffer ra) throws Exception { waitForRouterSolicitation(); mPacketReader.sendResponse(ra); @@ -2095,9 +2137,8 @@ final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, - false /* isDhcpIpConflictDetectEnabled */, - false /* isIPv6OnlyPreferredEnabled */, - null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */); + false /* isDhcpIpConflictDetectEnabled */); + assertEquals(2, sentPackets.size()); verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); assertHostname(true, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets); @@ -2113,9 +2154,8 @@ final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, - false /* isDhcpIpConflictDetectEnabled */, - false /* isIPv6OnlyPreferredEnabled */, - null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */); + false /* isDhcpIpConflictDetectEnabled */); + assertEquals(2, sentPackets.size()); verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); assertHostname(false, TEST_HOST_NAME, TEST_HOST_NAME_TRANSLITERATION, sentPackets); @@ -2131,9 +2171,8 @@ final List<DhcpPacket> sentPackets = performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, - false /* isDhcpIpConflictDetectEnabled */, - false /* isIPv6OnlyPreferredEnabled */, - null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */); + false /* isDhcpIpConflictDetectEnabled */); + assertEquals(2, sentPackets.size()); verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); assertHostname(true, null /* hostname */, null /* hostnameAfterTransliteration */, @@ -2230,7 +2269,8 @@ false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */, - null /* captivePortalApiUrl */, displayName, info /* scanResultInfo */); + null /* captivePortalApiUrl */, displayName, info /* scanResultInfo */, + null /* layer2Info */); assertEquals(2, sentPackets.size()); verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); @@ -2308,11 +2348,18 @@ true /* expectMetered */); } + private void forceLayer2Roaming() throws Exception { + final Layer2InformationParcelable roamingInfo = new Layer2InformationParcelable(); + roamingInfo.bssid = MacAddress.fromString(TEST_DHCP_ROAM_BSSID); + roamingInfo.l2Key = TEST_DHCP_ROAM_L2KEY; + roamingInfo.cluster = TEST_DHCP_ROAM_CLUSTER; + mIIpClient.updateLayer2Information(roamingInfo); + } + private void doDhcpRoamingTest(final boolean hasMismatchedIpAddress, final String displayName, - final String ssid, final String bssid, final boolean expectRoaming) throws Exception { + final MacAddress bssid, final boolean expectRoaming) throws Exception { long currentTime = System.currentTimeMillis(); - final ScanResultInfo scanResultInfo = (ssid == null || bssid == null) - ? null : makeScanResultInfo(ssid, bssid); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, bssid); doAnswer(invocation -> { // we don't rely on the Init-Reboot state to renew previous cached IP lease. @@ -2329,16 +2376,13 @@ true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */, - null /* captivePortalApiUrl */, displayName, scanResultInfo); + null /* captivePortalApiUrl */, displayName, null /* scanResultInfo */, + layer2Info); verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); assertIpMemoryStoreNetworkAttributes(TEST_LEASE_DURATION_S, currentTime, TEST_DEFAULT_MTU); // simulate the roaming by updating bssid. - final Layer2InformationParcelable roamingInfo = new Layer2InformationParcelable(); - roamingInfo.bssid = MacAddress.fromString(TEST_DHCP_ROAM_BSSID); - roamingInfo.l2Key = TEST_DHCP_ROAM_L2KEY; - roamingInfo.cluster = TEST_DHCP_ROAM_CLUSTER; - mIpc.updateLayer2Information(roamingInfo); + forceLayer2Roaming(); currentTime = System.currentTimeMillis(); reset(mIpMemoryStore); @@ -2379,57 +2423,41 @@ @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") public void testDhcpRoaming() throws Exception { doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, - TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */); + MacAddress.fromString(TEST_DEFAULT_BSSID), true /* expectRoaming */); } @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") public void testDhcpRoaming_invalidBssid() throws Exception { doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, - TEST_DHCP_ROAM_SSID, TEST_DHCP_ROAM_BSSID, false /* expectRoaming */); + MacAddress.fromString(TEST_DHCP_ROAM_BSSID), false /* expectRoaming */); } @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") - public void testDhcpRoaming_nullScanResultInfo() throws Exception { + public void testDhcpRoaming_nullBssid() throws Exception { doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, - null /* SSID */, null /* BSSID */, false /* expectRoaming */); - } - - @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") - public void testDhcpRoaming_invalidSsid() throws Exception { - doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, - TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID, false /* expectRoaming */); + null /* BSSID */, false /* expectRoaming */); } @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") public void testDhcpRoaming_invalidDisplayName() throws Exception { doDhcpRoamingTest(false /* hasMismatchedIpAddress */, "\"test-ssid\"" /* display name */, - TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, false /* expectRoaming */); + MacAddress.fromString(TEST_DEFAULT_BSSID), false /* expectRoaming */); } @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") public void testDhcpRoaming_mismatchedLeasedIpAddress() throws Exception { doDhcpRoamingTest(true /* hasMismatchedIpAddress */, "\"0001docomo\"" /* display name */, - TEST_DHCP_ROAM_SSID, TEST_DEFAULT_BSSID, true /* expectRoaming */); + MacAddress.fromString(TEST_DEFAULT_BSSID), true /* expectRoaming */); } - private void doDualStackProvisioning() throws Exception { - when(mCm.shouldAvoidBadWifi()).thenReturn(true); - - final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() - .withoutIpReachabilityMonitor() - .build(); - // Enable rapid commit to accelerate DHCP handshake to shorten test duration, - // not strictly necessary. - setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); - mIpc.startProvisioning(config); - + private void performDualStackProvisioning() throws Exception { final InOrder inOrder = inOrder(mCb); final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); final String dnsServer = "2001:4860:4860::64"; final ByteBuffer pio = buildPioOption(3600, 1800, "2001:db8:1::/64"); final ByteBuffer rdnss = buildRdnssOption(3600, dnsServer); - final ByteBuffer ra = buildRaPacket(pio, rdnss); + final ByteBuffer slla = buildSllaOption(); + final ByteBuffer ra = buildRaPacket(pio, rdnss, slla); doIpv6OnlyProvisioning(inOrder, ra); @@ -2450,9 +2478,27 @@ reset(mCb); } - @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") - public void testIgnoreIpv6ProvisioningLoss() throws Exception { - doDualStackProvisioning(); + private void doDualStackProvisioning(boolean shouldDisableAcceptRa) throws Exception { + when(mCm.shouldAvoidBadWifi()).thenReturn(true); + + final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withoutIpReachabilityMonitor() + .build(); + + setFeatureEnabled(NetworkStackUtils.IPCLIENT_DISABLE_ACCEPT_RA_VERSION, + shouldDisableAcceptRa); + // Enable rapid commit to accelerate DHCP handshake to shorten test duration, + // not strictly necessary. + setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, + false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + mIpc.startProvisioning(config); + + performDualStackProvisioning(); + } + + @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck") + public void testIgnoreIpv6ProvisioningLoss_disableIPv6Stack() throws Exception { + doDualStackProvisioning(false /* shouldDisableAcceptRa */); final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); @@ -2474,11 +2520,52 @@ assertNotNull(lp); assertEquals(lp.getAddresses().get(0), CLIENT_ADDR); assertEquals(lp.getDnsServers().get(0), SERVER_ADDR); + + final ArgumentCaptor<Integer> quirkEvent = ArgumentCaptor.forClass(Integer.class); + verify(mNetworkQuirkMetricsDeps, timeout(TEST_TIMEOUT_MS)).writeStats(quirkEvent.capture()); + assertEquals((long) quirkEvent.getValue(), + (long) NetworkQuirkEvent.QE_IPV6_PROVISIONING_ROUTER_LOST.ordinal()); + } + + @Test @SignatureRequiredTest(reason = "signature perms are required due to mocked callabck") + public void testIgnoreIpv6ProvisioningLoss_disableAcceptRa() throws Exception { + doDualStackProvisioning(true /* shouldDisableAcceptRa */); + + final CompletableFuture<LinkProperties> lpFuture = new CompletableFuture<>(); + + // Send RA with 0-lifetime and wait until all global IPv6 addresses, IPv6-related default + // route and DNS servers have been removed, then verify if there is IPv4-only, IPv6 link + // local address and route to fe80::/64 info left in the LinkProperties. + sendRouterAdvertisementWithZeroLifetime(); + verify(mCb, timeout(TEST_TIMEOUT_MS).atLeastOnce()).onLinkPropertiesChange( + argThat(x -> { + // Only IPv4 provisioned and IPv6 link-local address + final boolean isIPv6LinkLocalAndIPv4OnlyProvisioned = + (x.getLinkAddresses().size() == 2 + && x.getDnsServers().size() == 1 + && x.getAddresses().get(0) instanceof Inet4Address + && x.getDnsServers().get(0) instanceof Inet4Address); + + if (!isIPv6LinkLocalAndIPv4OnlyProvisioned) return false; + lpFuture.complete(x); + return true; + })); + final LinkProperties lp = lpFuture.get(TEST_TIMEOUT_MS, TimeUnit.MILLISECONDS); + assertNotNull(lp); + assertEquals(lp.getAddresses().get(0), CLIENT_ADDR); + assertEquals(lp.getDnsServers().get(0), SERVER_ADDR); + assertTrue(lp.getAddresses().get(1).isLinkLocalAddress()); + + reset(mCb); + + // Send an RA to verify that global IPv6 addresses won't be configured on the interface. + sendBasicRouterAdvertisement(false /* waitForRs */); + verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(any()); } @Test @SignatureRequiredTest(reason = "TODO: evaluate whether signature perms are required") public void testDualStackProvisioning() throws Exception { - doDualStackProvisioning(); + doDualStackProvisioning(false /* shouldDisableAcceptRa */); verify(mCb, never()).onProvisioningFailure(any()); } @@ -2698,7 +2785,7 @@ public void testNoFdLeaks() throws Exception { // Shut down and restart IpClient once to ensure that any fds that are opened the first // time it runs do not cause the test to fail. - doDualStackProvisioning(); + doDualStackProvisioning(false /* shouldDisableAcceptRa */); shutdownAndRecreateIpClient(); // Unfortunately we cannot use a large number of iterations as it would make the test run @@ -2706,7 +2793,7 @@ final int iterations = 10; final int before = getNumOpenFds(); for (int i = 0; i < iterations; i++) { - doDualStackProvisioning(); + doDualStackProvisioning(false /* shouldDisableAcceptRa */); shutdownAndRecreateIpClient(); // The last time this loop runs, mIpc will be shut down in tearDown. } @@ -2823,8 +2910,8 @@ final List<DhcpOption> options = Arrays.asList( makeDhcpOption((byte) 60, TEST_OEM_VENDOR_ID.getBytes()), makeDhcpOption((byte) 77, TEST_OEM_USER_CLASS_INFO), - // DHCP_HOST_NAME - makeDhcpOption((byte) 12, new String("Pixel 3 XL").getBytes())); + // Option 26: MTU + makeDhcpOption((byte) 26, HexDump.toByteArray(TEST_DEFAULT_MTU))); final ScanResultInfo info = makeScanResultInfo(0xdd /* vendor-specificIE */, TEST_OEM_OUI, (byte) 0x17 /* vendor-specific IE type */); final DhcpPacket packet = doCustomizedDhcpOptionsTest(options, info); @@ -2832,7 +2919,7 @@ assertTrue(packet instanceof DhcpDiscoverPacket); assertEquals(packet.mVendorId, TEST_OEM_VENDOR_ID); assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO); - assertNull(packet.mHostName); + assertNull(packet.mMtu); } @Test @@ -2851,4 +2938,315 @@ assertArrayEquals(packet.mUserClass, TEST_OEM_USER_CLASS_INFO); assertFalse(packet.hasRequestedParam((byte) 42 /* NTP_SERVER */)); } + + private void assertGratuitousNa(final NeighborAdvertisement na) throws Exception { + final MacAddress etherMulticast = + NetworkStackUtils.ipv6MulticastToEthernetMulticast(IPV6_ADDR_ALL_ROUTERS_MULTICAST); + final LinkAddress target = new LinkAddress(na.naHdr.target, 64); + + assertEquals(etherMulticast, na.ethHdr.dstMac); + assertEquals(ETH_P_IPV6, na.ethHdr.etherType); + assertEquals(IPPROTO_ICMPV6, na.ipv6Hdr.nextHeader); + assertEquals(0xff, na.ipv6Hdr.hopLimit); + assertTrue(na.ipv6Hdr.srcIp.isLinkLocalAddress()); + assertEquals(IPV6_ADDR_ALL_ROUTERS_MULTICAST, na.ipv6Hdr.dstIp); + assertEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, na.icmpv6Hdr.type); + assertEquals(0, na.icmpv6Hdr.code); + assertEquals(0, na.naHdr.flags); + assertTrue(target.isGlobalPreferred()); + } + + @Test + public void testGratuitousNaForNewGlobalUnicastAddresses() throws Exception { + final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withoutIpReachabilityMonitor() + .withoutIPv4() + .build(); + + setFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION, + true /* isGratuitousNaEnabled */); + assertTrue(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION, false)); + startIpClientProvisioning(config); + + doIpv6OnlyProvisioning(); + + final List<NeighborAdvertisement> naList = new ArrayList<>(); + NeighborAdvertisement packet; + while ((packet = getNextNeighborAdvertisement()) != null) { + assertGratuitousNa(packet); + naList.add(packet); + } + assertEquals(2, naList.size()); // privacy address and stable privacy address + } + + private void startGratuitousArpAndNaAfterRoamingTest(boolean isGratuitousArpNaRoamingEnabled, + boolean hasIpv4, boolean hasIpv6) throws Exception { + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_DEFAULT_BSSID)); + final ScanResultInfo scanResultInfo = + makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID); + final ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() + .withoutIpReachabilityMonitor() + .withLayer2Information(layer2Info) + .withScanResultInfo(scanResultInfo) + .withDisplayName("ssid"); + if (!hasIpv4) prov.withoutIPv4(); + if (!hasIpv6) prov.withoutIPv6(); + + // Enable rapid commit to accelerate DHCP handshake to shorten test duration, + // not strictly necessary. + setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, + false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + if (isGratuitousArpNaRoamingEnabled) { + setFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, true); + assertTrue(isFeatureEnabled(NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION, false)); + } + startIpClientProvisioning(prov.build()); + } + + private void waitForGratuitousArpAndNaPacket(final List<ArpPacket> arpList, + final List<NeighborAdvertisement> naList) throws Exception { + NeighborAdvertisement na; + ArpPacket garp; + do { + na = getNextNeighborAdvertisement(); + if (na != null) { + assertGratuitousNa(na); + naList.add(na); + } + garp = getNextArpPacket(TEST_TIMEOUT_MS); + if (garp != null) { + assertGratuitousARP(garp); + arpList.add(garp); + } + } while (na != null || garp != null); + } + + @Test + public void testGratuitousArpAndNaAfterRoaming() throws Exception { + startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */, + true /* hasIpv4 */, true /* hasIpv6 */); + performDualStackProvisioning(); + forceLayer2Roaming(); + + final List<ArpPacket> arpList = new ArrayList<>(); + final List<NeighborAdvertisement> naList = new ArrayList<>(); + waitForGratuitousArpAndNaPacket(arpList, naList); + assertEquals(2, naList.size()); // privacy address and stable privacy address + assertEquals(1, arpList.size()); // IPv4 address + } + + @Test + public void testGratuitousArpAndNaAfterRoaming_disableExpFlag() throws Exception { + startGratuitousArpAndNaAfterRoamingTest(false /* isGratuitousArpNaRoamingEnabled */, + true /* hasIpv4 */, true /* hasIpv6 */); + performDualStackProvisioning(); + forceLayer2Roaming(); + + final List<ArpPacket> arpList = new ArrayList<>(); + final List<NeighborAdvertisement> naList = new ArrayList<>(); + waitForGratuitousArpAndNaPacket(arpList, naList); + assertEquals(0, naList.size()); + assertEquals(0, arpList.size()); + } + + @Test + public void testGratuitousArpAndNaAfterRoaming_IPv6OnlyNetwork() throws Exception { + startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */, + false /* hasIpv4 */, true /* hasIpv6 */); + doIpv6OnlyProvisioning(); + forceLayer2Roaming(); + + final List<ArpPacket> arpList = new ArrayList<>(); + final List<NeighborAdvertisement> naList = new ArrayList<>(); + waitForGratuitousArpAndNaPacket(arpList, naList); + assertEquals(2, naList.size()); + assertEquals(0, arpList.size()); + } + + @Test + public void testGratuitousArpAndNaAfterRoaming_IPv4OnlyNetwork() throws Exception { + startGratuitousArpAndNaAfterRoamingTest(true /* isGratuitousArpNaRoamingEnabled */, + true /* hasIpv4 */, false /* hasIpv6 */); + + // Start IPv4 provisioning and wait until entire provisioning completes. + handleDhcpPackets(true /* isSuccessLease */, TEST_LEASE_DURATION_S, + true /* shouldReplyRapidCommitAck */, TEST_DEFAULT_MTU, null /* serverSentUrl */); + verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); + forceLayer2Roaming(); + + final List<ArpPacket> arpList = new ArrayList<>(); + final List<NeighborAdvertisement> naList = new ArrayList<>(); + waitForGratuitousArpAndNaPacket(arpList, naList); + assertEquals(0, naList.size()); + assertEquals(1, arpList.size()); + } + + private void assertNeighborSolicitation(final NeighborSolicitation ns, + final Inet6Address target) { + assertEquals(ETH_P_IPV6, ns.ethHdr.etherType); + assertEquals(IPPROTO_ICMPV6, ns.ipv6Hdr.nextHeader); + assertEquals(0xff, ns.ipv6Hdr.hopLimit); + assertTrue(ns.ipv6Hdr.srcIp.isLinkLocalAddress()); + assertEquals(ICMPV6_NEIGHBOR_SOLICITATION, ns.icmpv6Hdr.type); + assertEquals(0, ns.icmpv6Hdr.code); + assertEquals(0, ns.nsHdr.reserved); + assertEquals(target, ns.nsHdr.target); + assertEquals(ns.slla.linkLayerAddress, ns.ethHdr.srcMac); + } + + private void assertUnicastNeighborSolicitation(final NeighborSolicitation ns, + final MacAddress dstMac, final Inet6Address dstIp, final Inet6Address target) { + assertEquals(dstMac, ns.ethHdr.dstMac); + assertEquals(dstIp, ns.ipv6Hdr.dstIp); + assertNeighborSolicitation(ns, target); + } + + private void assertMulticastNeighborSolicitation(final NeighborSolicitation ns, + final Inet6Address target) { + final MacAddress etherMulticast = + NetworkStackUtils.ipv6MulticastToEthernetMulticast(ns.ipv6Hdr.dstIp); + assertEquals(etherMulticast, ns.ethHdr.dstMac); + assertTrue(ns.ipv6Hdr.dstIp.isMulticastAddress()); + assertNeighborSolicitation(ns, target); + } + + private NeighborSolicitation waitForUnicastNeighborSolicitation(final MacAddress dstMac, + final Inet6Address dstIp, final Inet6Address targetIp) throws Exception { + NeighborSolicitation ns; + while ((ns = getNextNeighborSolicitation()) != null) { + // Filter out the NSes used for duplicate address detetction, the target address + // is the global IPv6 address inside these NSes. + if (ns.nsHdr.target.isLinkLocalAddress()) break; + } + assertNotNull("No unicast Neighbor solicitation received on interface within timeout", ns); + assertUnicastNeighborSolicitation(ns, dstMac, dstIp, targetIp); + return ns; + } + + private List<NeighborSolicitation> waitForMultipleNeighborSolicitations() throws Exception { + NeighborSolicitation ns; + final List<NeighborSolicitation> nsList = new ArrayList<NeighborSolicitation>(); + while ((ns = getNextNeighborSolicitation()) != null) { + // Filter out the NSes used for duplicate address detetction, the target address + // is the global IPv6 address inside these NSes. + if (ns.nsHdr.target.isLinkLocalAddress()) { + nsList.add(ns); + } + } + assertFalse(nsList.isEmpty()); + return nsList; + } + + // Override this function with disabled experiment flag by default, in order not to + // affect those tests which are just related to basic IpReachabilityMonitor infra. + private void prepareIpReachabilityMonitorTest() throws Exception { + prepareIpReachabilityMonitorTest(false /* isMulticastResolicitEnabled */); + } + + private void prepareIpReachabilityMonitorTest(boolean isMulticastResolicitEnabled) + throws Exception { + final ScanResultInfo info = makeScanResultInfo(TEST_DEFAULT_SSID, TEST_DEFAULT_BSSID); + ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withLayer2Information(new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_DEFAULT_BSSID))) + .withScanResultInfo(info) + .withDisplayName(TEST_DEFAULT_SSID) + .withoutIPv4() + .build(); + setFeatureEnabled(NetworkStackUtils.IP_REACHABILITY_MCAST_RESOLICIT_VERSION, + isMulticastResolicitEnabled); + startIpClientProvisioning(config); + verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(false); + doIpv6OnlyProvisioning(); + + // Simulate the roaming. + forceLayer2Roaming(); + } + + @Test + public void testIpReachabilityMonitor_probeFailed() throws Exception { + prepareIpReachabilityMonitorTest(); + + final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations(); + assertEquals(MIN_NUD_SOLICIT_NUM, nsList.size()); + for (NeighborSolicitation ns : nsList) { + assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */, + ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */); + } + assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */); + } + + @Test + public void testIpReachabilityMonitor_probeReachable() throws Exception { + prepareIpReachabilityMonitorTest(); + + final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */, + ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */); + + // Reply Neighbor Advertisement and check notifyLost callback won't be triggered. + int flag = NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED; + final ByteBuffer na = NeighborAdvertisement.build(ROUTER_MAC /* srcMac */, + ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */, + ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */); + mPacketReader.sendResponse(na); + assertNeverNotifyNeighborLost(); + } + + @Test + public void testIpReachabilityMonitor_mcastResoclicitProbeFailed() throws Exception { + prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */); + + final List<NeighborSolicitation> nsList = waitForMultipleNeighborSolicitations(); + int expectedSize = MIN_NUD_SOLICIT_NUM + NUD_MCAST_RESOLICIT_NUM; + assertEquals(expectedSize, nsList.size()); // 5 unicast NSes + 3 multicast NSes + for (NeighborSolicitation ns : nsList.subList(0, MIN_NUD_SOLICIT_NUM)) { + assertUnicastNeighborSolicitation(ns, ROUTER_MAC /* dstMac */, + ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */); + } + for (NeighborSolicitation ns : nsList.subList(MIN_NUD_SOLICIT_NUM, nsList.size())) { + assertMulticastNeighborSolicitation(ns, ROUTER_LINK_LOCAL /* targetIp */); + } + assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */); + } + + @Test + public void testIpReachabilityMonitor_mcastResoclicitProbeReachableWithSameLinkLayerAddress() + throws Exception { + prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */); + + final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */, + ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */); + + // Reply Neighbor Advertisement and check notifyLost callback won't be triggered. + int flag = NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED; + final ByteBuffer na = NeighborAdvertisement.build(ROUTER_MAC /* srcMac */, + ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */, + ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */); + mPacketReader.sendResponse(na); + assertNeverNotifyNeighborLost(); + } + + @Test + public void testIpReachabilityMonitor_mcastResoclicitProbeReachableWithDiffLinkLayerAddress() + throws Exception { + prepareIpReachabilityMonitorTest(true /* isMulticastResolicitEnabled */); + + final NeighborSolicitation ns = waitForUnicastNeighborSolicitation(ROUTER_MAC /* dstMac */, + ROUTER_LINK_LOCAL /* dstIp */, ROUTER_LINK_LOCAL /* targetIp */); + + // Reply Neighbor Advertisement with a different link-layer address and check notifyLost + // callback will be triggered. Override flag must be set, which indicates that the + // advertisement should override an existing cache entry and update the cached link-layer + // address, otherwise, kernel won't transit to REACHABLE state with a different link-layer + // address. + int flag = NEIGHBOR_ADVERTISEMENT_FLAG_ROUTER | NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED + | NEIGHBOR_ADVERTISEMENT_FLAG_OVERRIDE; + final MacAddress newMac = MacAddress.fromString("00:1a:11:22:33:55"); + final ByteBuffer na = NeighborAdvertisement.build(newMac /* srcMac */, + ns.ethHdr.srcMac /* dstMac */, ROUTER_LINK_LOCAL /* srcIp */, + ns.ipv6Hdr.srcIp /* dstIp */, flag, ROUTER_LINK_LOCAL /* target */); + mPacketReader.sendResponse(na); + assertNotifyNeighborLost(ROUTER_LINK_LOCAL /* targetIp */); + } }
diff --git a/tests/integration/src/android/net/ip/IpClientRootTest.kt b/tests/integration/src/android/net/ip/IpClientRootTest.kt index ea2ec11..8a99e4f 100644 --- a/tests/integration/src/android/net/ip/IpClientRootTest.kt +++ b/tests/integration/src/android/net/ip/IpClientRootTest.kt
@@ -26,13 +26,14 @@ import android.net.ipmemorystore.NetworkAttributes import android.net.ipmemorystore.Status import android.net.networkstack.TestNetworkStackServiceClient -import android.net.util.NetworkStackUtils import android.os.Process import android.provider.DeviceConfig import android.util.ArrayMap import android.util.Log import androidx.test.platform.app.InstrumentationRegistry +import com.android.net.module.util.DeviceConfigUtils import java.lang.System.currentTimeMillis +import java.net.Inet6Address import java.util.concurrent.CompletableFuture import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit @@ -45,6 +46,8 @@ import org.junit.AfterClass import org.junit.BeforeClass import org.mockito.ArgumentCaptor +import org.mockito.Mockito.anyString +import org.mockito.Mockito.never import org.mockito.Mockito.timeout import org.mockito.Mockito.verify @@ -194,37 +197,33 @@ return ipClientCaptor.value } - override fun setDhcpFeatures( - isDhcpLeaseCacheEnabled: Boolean, - isRapidCommitEnabled: Boolean, - isDhcpIpConflictDetectEnabled: Boolean, - isIPv6OnlyPreferredEnabled: Boolean - ) { + override fun setFeatureEnabled(feature: String, enabled: Boolean) { automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG) try { - setFeatureEnabled(NetworkStackUtils.DHCP_INIT_REBOOT_VERSION, isDhcpLeaseCacheEnabled) - setFeatureEnabled(NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION, isRapidCommitEnabled) - setFeatureEnabled(NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION, - isDhcpIpConflictDetectEnabled) - setFeatureEnabled(NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION, - isIPv6OnlyPreferredEnabled) + // Do not use computeIfAbsent as it would overwrite null values (flag originally unset) + if (!originalFlagValues.containsKey(feature)) { + originalFlagValues[feature] = + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature) + } + // The feature is enabled if the flag is lower than the package version. + // Package versions follow a standard format with 9 digits. + // TODO: consider resetting flag values on reboot when set to special values like "1" or + // "999999999" + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature, + if (enabled) "1" else "999999999", false) } finally { automation.dropShellPermissionIdentity() } } - private fun setFeatureEnabled(feature: String, enabled: Boolean) { - // Do not use computeIfAbsent as it would overwrite null values (flag originally unset) - if (!originalFlagValues.containsKey(feature)) { - originalFlagValues[feature] = - DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature) + override fun isFeatureEnabled(name: String, defaultEnabled: Boolean): Boolean { + automation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG) + try { + return DeviceConfigUtils.isFeatureEnabled(mContext, DeviceConfig.NAMESPACE_CONNECTIVITY, + name, defaultEnabled) + } finally { + automation.dropShellPermissionIdentity() } - // The feature is enabled if the flag is lower than the package version. - // Package versions follow a standard format with 9 digits. - // TODO: consider resetting flag values on reboot when set to special values like "1" or - // "999999999" - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_CONNECTIVITY, feature, - if (enabled) "1" else "999999999", false) } private class TestAttributesRetrievedListener : OnNetworkAttributesRetrievedListener { @@ -264,4 +263,12 @@ mStore.retrieveNetworkAttributes(l2Key, listener) assertNull(listener.getBlockingNetworkAttributes(timeout)) } + + override fun assertNotifyNeighborLost(targetIp: Inet6Address) { + verify(mCb, timeout(TEST_TIMEOUT_MS)).onReachabilityLost(anyString()) + } + + override fun assertNeverNotifyNeighborLost() { + verify(mCb, never()).onReachabilityLost(anyString()) + } }
diff --git a/tests/integration/src/android/net/util/NetworkStackUtilsIntegrationTest.kt b/tests/integration/src/android/net/util/NetworkStackUtilsIntegrationTest.kt index 7e544ea..0ec43a5 100644 --- a/tests/integration/src/android/net/util/NetworkStackUtilsIntegrationTest.kt +++ b/tests/integration/src/android/net/util/NetworkStackUtilsIntegrationTest.kt
@@ -19,6 +19,7 @@ import android.Manifest.permission.MANAGE_TEST_NETWORKS import android.content.Context import android.net.InetAddresses.parseNumericAddress +import android.net.IpPrefix import android.net.MacAddress import android.net.TestNetworkInterface import android.net.TestNetworkManager @@ -26,12 +27,22 @@ import android.os.HandlerThread import android.system.Os import android.system.OsConstants.AF_INET +import android.system.OsConstants.AF_PACKET +import android.system.OsConstants.ARPHRD_ETHER +import android.system.OsConstants.ETH_P_IPV6 import android.system.OsConstants.IPPROTO_UDP import android.system.OsConstants.SOCK_DGRAM import android.system.OsConstants.SOCK_NONBLOCK import androidx.test.platform.app.InstrumentationRegistry +import android.system.OsConstants.SOCK_RAW +import android.system.OsConstants.SOL_SOCKET +import android.system.OsConstants.SO_RCVTIMEO +import android.system.StructTimeval +import com.android.net.module.util.Ipv6Utils import com.android.net.module.util.NetworkStackConstants.ETHER_ADDR_LEN import com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY +import com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_NODES_MULTICAST +import com.android.net.module.util.structs.PrefixInformationOption import com.android.testutils.ArpRequestFilter import com.android.testutils.ETHER_HEADER_LENGTH import com.android.testutils.IPV4_HEADER_LENGTH @@ -42,9 +53,13 @@ import org.junit.Assert.assertArrayEquals import org.junit.Before import org.junit.Test +import java.io.FileDescriptor import java.net.Inet4Address import kotlin.reflect.KClass +import java.net.Inet6Address +import java.nio.ByteBuffer import kotlin.test.assertEquals +import kotlin.test.assertTrue import kotlin.test.fail class NetworkStackUtilsIntegrationTest { @@ -52,8 +67,12 @@ private val context by lazy { inst.context } private val TEST_TIMEOUT_MS = 10_000L + private val TEST_MTU = 1500 private val TEST_TARGET_IPV4_ADDR = parseNumericAddress("192.0.2.42") as Inet4Address + private val TEST_SRC_MAC = MacAddress.fromString("BA:98:76:54:32:10") private val TEST_TARGET_MAC = MacAddress.fromString("01:23:45:67:89:0A") + private val TEST_INET6ADDR_1 = parseNumericAddress("2001:db8::1") as Inet6Address + private val TEST_INET6ADDR_2 = parseNumericAddress("2001:db8::2") as Inet6Address private val readerHandler = HandlerThread( NetworkStackUtilsIntegrationTest::class.java.simpleName) @@ -103,8 +122,7 @@ null /* hostname */, false /* metered */, 1500 /* mtu */, null /* captivePortalUrl */) // Not using .array as per errorprone "ByteBufferBackingArray" recommendation - val originalPacket = ByteArray(buffer.limit()) - buffer.get(originalPacket) + val originalPacket = buffer.readAsArray() Os.sendto(socket, originalPacket, 0 /* bytesOffset */, originalPacket.size /* bytesCount */, 0 /* flags */, TEST_TARGET_IPV4_ADDR, DhcpPacket.DHCP_CLIENT.toInt() /* port */) @@ -112,7 +130,7 @@ // Verify the packet was sent to the mac address specified in the ARP entry // Also accept ARP requests, but expect that none is sent before the UDP packet // IPv6 NS may be sent on the interface but will be filtered out - val sentPacket = reader.popPacket(TEST_TIMEOUT_MS, IPv4UdpFilter().or(ArpRequestFilter())) + val sentPacket = reader.poll(TEST_TIMEOUT_MS, IPv4UdpFilter().or(ArpRequestFilter())) ?: fail("Packet was not sent on the interface") val sentTargetAddr = MacAddress.fromBytes(sentPacket.copyOfRange(0, ETHER_ADDR_LEN)) @@ -123,7 +141,55 @@ assertArrayEquals("Sent packet != original packet", originalPacket, sentDhcpPacket) } + + @Test + fun testAttachRaFilter() { + val socket = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IPV6) + val ifParams = InterfaceParams.getByName(iface.interfaceName) + ?: fail("Could not obtain interface params for ${iface.interfaceName}") + val socketAddr = SocketUtils.makePacketSocketAddress(ETH_P_IPV6, ifParams.index) + Os.bind(socket, socketAddr) + Os.setsockoptTimeval(socket, SOL_SOCKET, SO_RCVTIMEO, + StructTimeval.fromMillis(TEST_TIMEOUT_MS)) + + // Verify that before setting any filter, the socket receives pings + val echo = Ipv6Utils.buildEchoRequestPacket(TEST_SRC_MAC, TEST_TARGET_MAC, TEST_INET6ADDR_1, + TEST_INET6ADDR_2) + reader.sendResponse(echo) + echo.rewind() + assertNextPacketEquals(socket, echo.readAsArray(), "ICMPv6 echo") + + NetworkStackUtils.attachRaFilter(socket, ARPHRD_ETHER) + // Send another echo, then an RA. After setting the filter expect only the RA. + echo.rewind() + reader.sendResponse(echo) + val pio = PrefixInformationOption.build(IpPrefix("2001:db8:1::/64"), + 0.toByte() /* flags */, 3600 /* validLifetime */, 1800 /* preferredLifetime */) + val ra = Ipv6Utils.buildRaPacket(TEST_SRC_MAC, TEST_TARGET_MAC, + TEST_INET6ADDR_1 /* routerAddr */, IPV6_ADDR_ALL_NODES_MULTICAST, + 0.toByte() /* flags */, 1800 /* lifetime */, 0 /* reachableTime */, + 0 /* retransTimer */, pio) + reader.sendResponse(ra) + ra.rewind() + + assertNextPacketEquals(socket, ra.readAsArray(), "ICMPv6 RA") + } + + private fun assertNextPacketEquals(socket: FileDescriptor, expected: ByteArray, descr: String) { + val buffer = ByteArray(TEST_MTU) + val readPacket = Os.read(socket, buffer, 0 /* byteOffset */, buffer.size) + assertTrue(readPacket > 0, "$descr not received") + assertEquals(expected.size, readPacket, "Received packet size does not match for $descr") + assertArrayEquals("Received packet != expected $descr", + expected, buffer.copyOfRange(0, readPacket)) + } +} + +private fun ByteBuffer.readAsArray(): ByteArray { + val out = ByteArray(remaining()) + get(out) + return out } private fun <T : Any> Context.assertHasService(manager: KClass<T>) = getSystemService(manager.java) - ?: fail("Could not find service $manager") \ No newline at end of file + ?: fail("Could not find service $manager")
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp index eb52f92..64da600 100644 --- a/tests/unit/Android.bp +++ b/tests/unit/Android.bp
@@ -14,6 +14,10 @@ // limitations under the License. // +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + java_defaults { name: "NetworkStackTestsDefaults", platform_apis: true, @@ -32,7 +36,10 @@ "android.test.base", "android.test.mock", ], - defaults: ["libnetworkstackutilsjni_deps"], + defaults: [ + "framework-connectivity-test-defaults", + "libnetworkstackutilsjni_deps" + ], jni_libs: [ // For mockito extended "libdexmakerjvmtiagent", @@ -50,11 +57,13 @@ min_sdk_version: "29", srcs: [], // TODO: tests that only apply to the current, non-stable API can be added here test_suites: ["general-tests"], - test_mainline_modules: ["CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex"], + test_mainline_modules: [ + "CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex", + "CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" + ], defaults: ["NetworkStackTestsDefaults"], static_libs: ["NetworkStackApiCurrentLib"], compile_multilib: "both", // Workaround for b/147785146 for mainline-presubmit - enabled: false, // Disabled in mainline-prod } // Library containing the unit tests. This is used by the coverage test target to pull in the @@ -67,8 +76,8 @@ static_libs: ["NetworkStackApiStableLib"], visibility: [ "//packages/modules/NetworkStack/tests/integration", - "//frameworks/base/packages/Tethering/tests/integration", - "//packages/modules/Connectivity/Tethering/tests/integration", + "//packages/modules/Connectivity/tests:__subpackages__", + "//packages/modules/Connectivity/Tethering/tests:__subpackages__", ] } @@ -77,7 +86,10 @@ min_sdk_version: "29", target_sdk_version: "30", test_suites: ["general-tests", "mts"], - test_mainline_modules: ["CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex"], + test_mainline_modules: [ + "CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex", + "CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" + ], defaults: ["NetworkStackTestsDefaults"], static_libs: ["NetworkStackApiStableLib"], compile_multilib: "both",
diff --git a/tests/unit/jni/Android.bp b/tests/unit/jni/Android.bp index fa1f420..0c9087f 100644 --- a/tests/unit/jni/Android.bp +++ b/tests/unit/jni/Android.bp
@@ -14,6 +14,10 @@ // limitations under the License. // +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + cc_library_shared { name: "libnetworkstacktestsjni", srcs: [
diff --git a/tests/unit/lint-baseline.xml b/tests/unit/lint-baseline.xml new file mode 100644 index 0000000..0bfcaa9 --- /dev/null +++ b/tests/unit/lint-baseline.xml
@@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="5" by="lint 4.1.0" client="cli" variant="all" version="4.1.0"> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.NetworkCapabilities()`" + errorLine1=" private val EMPTY_CAPABILITIES = NetworkCapabilities()" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/NetworkStackNotifierTest.kt" + line="134" + column="38"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `android.net.NetworkCapabilities()`" + errorLine1=" private val VALIDATED_CAPABILITIES = NetworkCapabilities()" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/NetworkStackNotifierTest.kt" + line="135" + column="42"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `new android.net.NetworkCapabilities`" + errorLine1=" new NetworkCapabilities()" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" + line="57" + column="13"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `new android.net.NetworkCapabilities`" + errorLine1=" NetworkCapabilities nc = new NetworkCapabilities();" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" + line="109" + column="34"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `new android.net.NetworkCapabilities`" + errorLine1=" nc = new NetworkCapabilities();" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" + line="117" + column="14"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `new android.net.NetworkCapabilities`" + errorLine1=" nc = new NetworkCapabilities();" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" + line="123" + column="14"/> + </issue> + + <issue + id="NewApi" + message="Call requires API level R (current min is 29): `new android.net.NetworkCapabilities`" + errorLine1=" nc = new NetworkCapabilities();" + errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~"> + <location + file="packages/modules/NetworkStack/tests/unit/src/com/android/networkstack/metrics/NetworkValidationMetricsTest.java" + line="129" + column="14"/> + </issue> + +</issues>
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java index 416a6c3..b6de3a1 100644 --- a/tests/unit/src/android/net/apf/ApfTest.java +++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -1056,6 +1056,10 @@ { (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 byte[] IPV6_SOLICITED_NODE_MULTICAST_ADDRESS = { + (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, + (byte) 0xff, (byte) 0xab, (byte) 0xcd, (byte) 0xef, + }; private static final int ICMP6_TYPE_OFFSET = IP_HEADER_OFFSET + IPV6_HEADER_LEN; private static final int ICMP6_ROUTER_SOLICITATION = 133; @@ -1241,6 +1245,14 @@ put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_NODES_ADDRESS); assertDrop(program, packet.array()); + // Verify ICMPv6 NA to ff02::2 is dropped + put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_ALL_ROUTERS_ADDRESS); + assertDrop(program, packet.array()); + + // Verify ICMPv6 NA to Solicited-Node Multicast is passed + put(packet, IPV6_DEST_ADDR_OFFSET, IPV6_SOLICITED_NODE_MULTICAST_ADDRESS); + assertPass(program, packet.array()); + // Verify ICMPv6 RS to any is dropped packet.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_SOLICITATION); assertDrop(program, packet.array());
diff --git a/tests/unit/src/android/net/ip/IpClientTest.java b/tests/unit/src/android/net/ip/IpClientTest.java index e991ea7..d86d0bb 100644 --- a/tests/unit/src/android/net/ip/IpClientTest.java +++ b/tests/unit/src/android/net/ip/IpClientTest.java
@@ -18,12 +18,15 @@ import static android.system.OsConstants.RT_SCOPE_UNIVERSE; +import static org.junit.Assert.assertArrayEquals; 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.Mockito.any; import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -48,22 +51,32 @@ import android.net.MacAddress; import android.net.NetworkStackIpMemoryStore; import android.net.RouteInfo; +import android.net.apf.ApfCapabilities; +import android.net.apf.ApfFilter.ApfConfiguration; import android.net.ipmemorystore.NetworkAttributes; import android.net.metrics.IpConnectivityLog; import android.net.shared.InitialConfiguration; +import android.net.shared.Layer2Information; import android.net.shared.ProvisioningConfiguration; +import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import android.net.util.InterfaceParams; +import android.os.Build; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.networkstack.R; import com.android.server.NetworkObserver; import com.android.server.NetworkObserverRegistry; import com.android.server.NetworkStackService; import com.android.server.connectivity.ipmemorystore.IpMemoryStoreService; +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; import com.android.testutils.HandlerUtils; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; @@ -73,9 +86,12 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; +import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Random; import java.util.Set; @@ -85,6 +101,9 @@ @RunWith(AndroidJUnit4.class) @SmallTest public class IpClientTest { + @Rule + public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); + private static final String VALID = "VALID"; private static final String INVALID = "INVALID"; private static final String TEST_IFNAME = "test_wlan0"; @@ -94,6 +113,9 @@ private static final int TEST_TIMEOUT_MS = 400; private static final String TEST_L2KEY = "some l2key"; private static final String TEST_CLUSTER = "some cluster"; + private static final String TEST_SSID = "test_ssid"; + private static final String TEST_BSSID = "00:11:22:33:44:55"; + private static final String TEST_BSSID2 = "00:1A:11:22:33:44"; private static final String TEST_GLOBAL_ADDRESS = "1234:4321::548d:2db2:4fcf:ef75/64"; private static final String[] TEST_LOCAL_ADDRESSES = { @@ -631,6 +653,183 @@ return out; } + private ApfConfiguration verifyApfFilterCreatedOnStart(IpClient ipc) { + ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() + .withoutIPv4() + .withoutIpReachabilityMonitor() + .withInitialConfiguration( + conf(links(TEST_LOCAL_ADDRESSES), prefixes(TEST_PREFIXES), ips())) + .withApfCapabilities(new ApfCapabilities( + 4 /* version */, 4096 /* maxProgramSize */, 4 /* format */)) + .build(); + + ipc.startProvisioning(config); + final ArgumentCaptor<ApfConfiguration> configCaptor = ArgumentCaptor.forClass( + ApfConfiguration.class); + verify(mDependencies, timeout(TEST_TIMEOUT_MS)).maybeCreateApfFilter( + any(), configCaptor.capture(), any(), any()); + + return configCaptor.getValue(); + } + + @Test @IgnoreAfter(Build.VERSION_CODES.R) + public void testApfConfiguration_R() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc); + + assertEquals(ApfCapabilities.getApfDrop8023Frames(), config.ieee802_3Filter); + assertArrayEquals(ApfCapabilities.getApfEtherTypeBlackList(), config.ethTypeBlackList); + + verify(mResources, never()).getBoolean(R.bool.config_apfDrop802_3Frames); + verify(mResources, never()).getIntArray(R.array.config_apfEthTypeDenyList); + + verifyShutdown(ipc); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testApfConfiguration() throws Exception { + doReturn(true).when(mResources).getBoolean(R.bool.config_apfDrop802_3Frames); + final int[] ethTypeDenyList = new int[] { 0x88A2, 0x88A4 }; + doReturn(ethTypeDenyList).when(mResources).getIntArray( + R.array.config_apfEthTypeDenyList); + + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc); + + assertTrue(config.ieee802_3Filter); + assertArrayEquals(ethTypeDenyList, config.ethTypeBlackList); + + verifyShutdown(ipc); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testApfConfiguration_NoApfDrop8023Frames() throws Exception { + doReturn(false).when(mResources).getBoolean(R.bool.config_apfDrop802_3Frames); + final int[] ethTypeDenyList = new int[] { 0x88A3, 0x88A5 }; + doReturn(ethTypeDenyList).when(mResources).getIntArray( + R.array.config_apfEthTypeDenyList); + + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc); + + assertFalse(config.ieee802_3Filter); + assertArrayEquals(ethTypeDenyList, config.ethTypeBlackList); + + verifyShutdown(ipc); + } + + private ScanResultInfo makeScanResultInfo(final String ssid, final String bssid) { + final ByteBuffer payload = ByteBuffer.allocate(14 /* oui + type + data */); + final byte[] data = new byte[10]; + new Random().nextBytes(data); + payload.put(new byte[] { 0x00, 0x1A, 0x11 }); + payload.put((byte) 0x06); + payload.put(data); + + final ScanResultInfo.InformationElement ie = + new ScanResultInfo.InformationElement(0xdd /* IE id */, payload); + return new ScanResultInfo(ssid, bssid, Collections.singletonList(ie)); + } + + @Test + public void testGetInitialBssidOnSOrAbove() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_BSSID)); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, TEST_BSSID2); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, scanResultInfo, + true /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidOnSOrAbove_NullScanReqsultInfo() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_BSSID)); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, null /* ScanResultInfo */, + true /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidOnSOrAbove_NullBssid() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + null /* bssid */); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, TEST_BSSID); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, scanResultInfo, + true /* isAtLeastS */); + assertNull(bssid); + } + + @Test + public void testGetInitialBssidOnSOrAbove_NullLayer2Info() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, TEST_BSSID); + final MacAddress bssid = ipc.getInitialBssid(null /* layer2Info */, scanResultInfo, + true /* isAtLeastS */); + assertNull(bssid); + } + + @Test + public void testGetInitialBssidBeforeS() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_BSSID2)); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, TEST_BSSID); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, scanResultInfo, + false /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidBeforeS_NullLayer2Info() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, TEST_BSSID); + final MacAddress bssid = ipc.getInitialBssid(null /* layer2Info */, scanResultInfo, + false /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidBeforeS_BrokenInitialBssid() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, "00:11:22:33:44:"); + final MacAddress bssid = ipc.getInitialBssid(null /* layer2Info */, scanResultInfo, + false /* isAtLeastS */); + assertNull(bssid); + } + + @Test + public void testGetInitialBssidBeforeS_BrokenInitialBssidFallback() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_BSSID)); + final ScanResultInfo scanResultInfo = makeScanResultInfo(TEST_SSID, "00:11:22:33:44:"); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, scanResultInfo, + false /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidBeforeS_NullScanResultInfoFallback() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final Layer2Information layer2Info = new Layer2Information(TEST_L2KEY, TEST_CLUSTER, + MacAddress.fromString(TEST_BSSID)); + final MacAddress bssid = ipc.getInitialBssid(layer2Info, null /* scanResultInfo */, + false /* isAtLeastS */); + assertEquals(bssid, MacAddress.fromString(TEST_BSSID)); + } + + @Test + public void testGetInitialBssidBeforeS_NullScanResultInfoAndLayer2Info() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final MacAddress bssid = ipc.getInitialBssid(null /* layer2Info */, + null /* scanResultInfo */, false /* isAtLeastS */); + assertNull(bssid); + } + interface Fn<A,B> { B call(A a) throws Exception; }
diff --git a/tests/unit/src/android/net/netlink/NetlinkSocketTest.java b/tests/unit/src/android/net/netlink/NetlinkSocketTest.java index 5716803..6a84a85 100644 --- a/tests/unit/src/android/net/netlink/NetlinkSocketTest.java +++ b/tests/unit/src/android/net/netlink/NetlinkSocketTest.java
@@ -17,21 +17,36 @@ package android.net.netlink; import static android.net.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE; +import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; +import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; +import static android.system.OsConstants.AF_INET; +import static android.system.OsConstants.AF_INET6; +import static android.system.OsConstants.AF_UNSPEC; +import static android.system.OsConstants.EACCES; import static android.system.OsConstants.NETLINK_ROUTE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import android.content.Context; import android.net.netlink.NetlinkSocket; import android.net.netlink.RtNetlinkNeighborMessage; import android.net.netlink.StructNlMsgHdr; +import android.system.ErrnoException; import android.system.NetlinkSocketAddress; import android.system.Os; import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.modules.utils.build.SdkLevel; +import com.android.net.module.util.Struct; +import com.android.net.module.util.Struct.Field; +import com.android.net.module.util.Struct.Type; + import libcore.io.IoUtils; import org.junit.Test; @@ -47,7 +62,7 @@ private final String TAG = "NetlinkSocketTest"; @Test - public void testBasicWorkingGetNeighborsQuery() throws Exception { + public void testGetNeighborsQuery() throws Exception { final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_ROUTE); assertNotNull(fd); @@ -63,6 +78,25 @@ assertNotNull(req); final long TIMEOUT = 500; + final Context ctx = InstrumentationRegistry.getInstrumentation().getContext(); + final int targetSdk = + ctx.getPackageManager() + .getApplicationInfo(ctx.getPackageName(), 0) + .targetSdkVersion; + + // Apps targeting an SDK version > S are not allowed to send RTM_GETNEIGH{TBL} messages + if (SdkLevel.isAtLeastT() && targetSdk > 31) { + try { + NetlinkSocket.sendMessage(fd, req, 0, req.length, TIMEOUT); + fail("RTM_GETNEIGH is not allowed for apps targeting SDK > 31 on T+ platforms"); + } catch (ErrnoException e) { + // Expected + assertEquals(e.errno, EACCES); + return; + } + } + + // Check that apps targeting lower API levels / running on older platforms succeed assertEquals(req.length, NetlinkSocket.sendMessage(fd, req, 0, req.length, TIMEOUT)); int neighMessageCount = 0; @@ -103,4 +137,105 @@ IoUtils.closeQuietly(fd); } + + @Test + public void testBasicWorkingGetAddrQuery() throws Exception { + final FileDescriptor fd = NetlinkSocket.forProto(NETLINK_ROUTE); + assertNotNull(fd); + + NetlinkSocket.connectToKernel(fd); + + final NetlinkSocketAddress localAddr = (NetlinkSocketAddress) Os.getsockname(fd); + assertNotNull(localAddr); + assertEquals(0, localAddr.getGroupsMask()); + assertTrue(0 != localAddr.getPortId()); + + final int testSeqno = 8; + final byte[] req = newGetAddrRequest(testSeqno); + assertNotNull(req); + + final long timeout = 500; + assertEquals(req.length, NetlinkSocket.sendMessage(fd, req, 0, req.length, timeout)); + + int addrMessageCount = 0; + + while (true) { + ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, timeout); + assertNotNull(response); + assertTrue(StructNlMsgHdr.STRUCT_SIZE <= response.limit()); + assertEquals(0, response.position()); + assertEquals(ByteOrder.nativeOrder(), response.order()); + + final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(response); + assertNotNull(nlmsghdr); + + if (nlmsghdr.nlmsg_type == NetlinkConstants.NLMSG_DONE) { + break; + } + + assertEquals(NetlinkConstants.RTM_NEWADDR, nlmsghdr.nlmsg_type); + assertTrue((nlmsghdr.nlmsg_flags & StructNlMsgHdr.NLM_F_MULTI) != 0); + assertEquals(testSeqno, nlmsghdr.nlmsg_seq); + assertEquals(localAddr.getPortId(), nlmsghdr.nlmsg_pid); + addrMessageCount++; + + final IfaddrMsg ifaMsg = Struct.parse(IfaddrMsg.class, response); + assertTrue( + "Non-IP address family: " + ifaMsg.family, + ifaMsg.family == AF_INET || ifaMsg.family == AF_INET6); + } + + assertTrue(addrMessageCount > 0); + + IoUtils.closeQuietly(fd); + } + + /** A convenience method to create an RTM_GETADDR request message. */ + private static byte[] newGetAddrRequest(int seqNo) { + final int length = StructNlMsgHdr.STRUCT_SIZE + Struct.getSize(RtgenMsg.class); + final byte[] bytes = new byte[length]; + final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes); + byteBuffer.order(ByteOrder.nativeOrder()); + + final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr(); + nlmsghdr.nlmsg_len = length; + nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETADDR; + nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + nlmsghdr.nlmsg_seq = seqNo; + nlmsghdr.pack(byteBuffer); + + final RtgenMsg rtgenMsg = new RtgenMsg(); + rtgenMsg.family = (byte) AF_UNSPEC; + rtgenMsg.writeToByteBuffer(byteBuffer); + + return bytes; + } + + /** From uapi/linux/rtnetlink.h */ + private static class RtgenMsg extends Struct { + @Field(order = 0, type = Type.U8) + public short family; + } + + /** + * From uapi/linux/ifaddr.h + * + * Public ensures visibility to Struct class + */ + public static class IfaddrMsg extends Struct { + @Field(order = 0, type = Type.U8) + public short family; + + @Field(order = 1, type = Type.U8) + public short prefixlen; + + @Field(order = 2, type = Type.U8) + public short flags; + + @Field(order = 3, type = Type.U8) + public short scope; + + @Field(order = 4, type = Type.U32) + public long index; + } }
diff --git a/tests/unit/src/android/net/testutils/TestableNetworkCallbackTest.kt b/tests/unit/src/android/net/testutils/TestableNetworkCallbackTest.kt index 5b91985..6f495e7 100644 --- a/tests/unit/src/android/net/testutils/TestableNetworkCallbackTest.kt +++ b/tests/unit/src/android/net/testutils/TestableNetworkCallbackTest.kt
@@ -1,5 +1,6 @@ package android.net.testutils +import android.annotation.SuppressLint import android.net.LinkAddress import android.net.LinkProperties import android.net.Network @@ -7,6 +8,15 @@ import com.android.testutils.ConcurrentInterpreter import com.android.testutils.InterpretMatcher import com.android.testutils.RecorderCallback.CallbackEntry +import com.android.testutils.RecorderCallback.CallbackEntry.Companion.AVAILABLE +import com.android.testutils.RecorderCallback.CallbackEntry.Companion.BLOCKED_STATUS +import com.android.testutils.RecorderCallback.CallbackEntry.Companion.LINK_PROPERTIES_CHANGED +import com.android.testutils.RecorderCallback.CallbackEntry.Companion.LOSING +import com.android.testutils.RecorderCallback.CallbackEntry.Companion.NETWORK_CAPS_UPDATED +import com.android.testutils.RecorderCallback.CallbackEntry.Companion.LOST +import com.android.testutils.RecorderCallback.CallbackEntry.Companion.RESUMED +import com.android.testutils.RecorderCallback.CallbackEntry.Companion.SUSPENDED +import com.android.testutils.RecorderCallback.CallbackEntry.Companion.UNAVAILABLE import com.android.testutils.RecorderCallback.CallbackEntry.Available import com.android.testutils.RecorderCallback.CallbackEntry.BlockedStatus import com.android.testutils.RecorderCallback.CallbackEntry.CapabilitiesChanged @@ -33,6 +43,7 @@ const val TEST_INTERFACE_NAME = "testInterfaceName" @RunWith(JUnit4::class) +@SuppressLint("NewApi") // Uses hidden APIs, which the linter would identify as missing APIs. class TestableNetworkCallbackTest { private lateinit var mCallback: TestableNetworkCallback @@ -245,6 +256,41 @@ onBlockedStatus(199) | poll(1) = BlockedStatus(199) time 0..3 """) } + + @Test + fun testEventuallyExpect() { + // TODO: Current test does not verify the inline one. Also verify the behavior after + // aligning two eventuallyExpect() + val net1 = Network(100) + val net2 = Network(101) + mCallback.onAvailable(net1) + mCallback.onCapabilitiesChanged(net1, NetworkCapabilities()) + mCallback.onLinkPropertiesChanged(net1, LinkProperties()) + mCallback.eventuallyExpect(LINK_PROPERTIES_CHANGED) { + net1.equals(it.network) + } + // No further new callback. Expect no callback. + assertFails { mCallback.eventuallyExpect(LINK_PROPERTIES_CHANGED) } + + // Verify no predicate set. + mCallback.onAvailable(net2) + mCallback.onLinkPropertiesChanged(net2, LinkProperties()) + mCallback.onBlockedStatusChanged(net1, false) + mCallback.eventuallyExpect(BLOCKED_STATUS) { net1.equals(it.network) } + // Verify no callback received if the callback does not happen. + assertFails { mCallback.eventuallyExpect(LOSING) } + } + + @Test + fun testEventuallyExpectOnMultiThreads() { + TNCInterpreter.interpretTestSpec(initial = mCallback, lineShift = 1, + threadTransform = { cb -> cb.createLinkedCopy() }, spec = """ + onAvailable(100) | eventually(CapabilitiesChanged(100), 1) fails + sleep ; onCapabilitiesChanged(100) | eventually(CapabilitiesChanged(100), 2) + onAvailable(101) ; onBlockedStatus(101) | eventually(BlockedStatus(100), 2) fails + onSuspended(100) ; sleep ; onLost(100) | eventually(Lost(100), 2) + """) + } } private object TNCInterpreter : ConcurrentInterpreter<TestableNetworkCallback>(interpretTable) @@ -254,6 +300,7 @@ return CallbackEntry::class.sealedSubclasses.first { it.simpleName == name } } +@SuppressLint("NewApi") // Uses hidden APIs, which the linter would identify as missing APIs. private val interpretTable = listOf<InterpretMatcher<TestableNetworkCallback>>( // Interpret "Available(xx)" as "call to onAvailable with netId xx", and likewise for // all callback types. This is implemented above by enumerating the subclasses of @@ -289,5 +336,26 @@ }, Regex("""poll\((\d+)\)""") to { i, cb, t -> cb.pollForNextCallback(t.timeArg(1)) + }, + // Interpret "eventually(Available(xx), timeout)" as calling eventuallyExpect that expects + // CallbackEntry.AVAILABLE with netId of xx within timeout*INTERPRET_TIME_UNIT timeout, and + // likewise for all callback types. + Regex("""eventually\(($EntryList)\((\d+)\),\s+(\d+)\)""") to { i, cb, t -> + val net = Network(t.intArg(2)) + val timeout = t.timeArg(3) + when (t.strArg(1)) { + "Available" -> cb.eventuallyExpect(AVAILABLE, timeout) { net == it.network } + "Suspended" -> cb.eventuallyExpect(SUSPENDED, timeout) { net == it.network } + "Resumed" -> cb.eventuallyExpect(RESUMED, timeout) { net == it.network } + "Losing" -> cb.eventuallyExpect(LOSING, timeout) { net == it.network } + "Lost" -> cb.eventuallyExpect(LOST, timeout) { net == it.network } + "Unavailable" -> cb.eventuallyExpect(UNAVAILABLE, timeout) { net == it.network } + "BlockedStatus" -> cb.eventuallyExpect(BLOCKED_STATUS, timeout) { net == it.network } + "CapabilitiesChanged" -> + cb.eventuallyExpect(NETWORK_CAPS_UPDATED, timeout) { net == it.network } + "LinkPropertiesChanged" -> + cb.eventuallyExpect(LINK_PROPERTIES_CHANGED, timeout) { net == it.network } + else -> fail("Unknown callback type") + } } )
diff --git a/tests/unit/src/com/android/net/module/util/TrackRecordTest.kt b/tests/unit/src/com/android/net/module/util/TrackRecordTest.kt deleted file mode 100644 index 9fb4d8c..0000000 --- a/tests/unit/src/com/android/net/module/util/TrackRecordTest.kt +++ /dev/null
@@ -1,446 +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.net.module.util - -import com.android.testutils.ConcurrentInterpreter -import com.android.testutils.InterpretException -import com.android.testutils.InterpretMatcher -import com.android.testutils.SyntaxException -import com.android.testutils.__FILE__ -import com.android.testutils.__LINE__ -import com.android.testutils.intArg -import com.android.testutils.strArg -import com.android.testutils.timeArg -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import java.util.concurrent.CyclicBarrier -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicInteger -import kotlin.system.measureTimeMillis -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertFalse -import kotlin.test.assertNotEquals -import kotlin.test.assertNull -import kotlin.test.assertTrue -import kotlin.test.fail - -val TEST_VALUES = listOf(4, 13, 52, 94, 41, 68, 11, 13, 51, 0, 91, 94, 33, 98, 14) -const val ABSENT_VALUE = 2 -// Caution in changing these : some tests rely on the fact that TEST_TIMEOUT > 2 * SHORT_TIMEOUT -// and LONG_TIMEOUT > 2 * TEST_TIMEOUT -const val SHORT_TIMEOUT = 40L // ms -const val TEST_TIMEOUT = 200L // ms -const val LONG_TIMEOUT = 5000L // ms - -@RunWith(JUnit4::class) -class TrackRecordTest { - @Test - fun testAddAndSizeAndGet() { - val repeats = 22 // arbitrary - val record = ArrayTrackRecord<Int>() - assertEquals(0, record.size) - repeat(repeats) { i -> record.add(i + 2) } - assertEquals(repeats, record.size) - record.add(2) - assertEquals(repeats + 1, record.size) - - assertEquals(11, record[9]) - assertEquals(11, record.getOrNull(9)) - assertEquals(2, record[record.size - 1]) - assertEquals(2, record.getOrNull(record.size - 1)) - - assertFailsWith<IndexOutOfBoundsException> { record[800] } - assertFailsWith<IndexOutOfBoundsException> { record[-1] } - assertFailsWith<IndexOutOfBoundsException> { record[repeats + 1] } - assertNull(record.getOrNull(800)) - assertNull(record.getOrNull(-1)) - assertNull(record.getOrNull(repeats + 1)) - assertNull(record.getOrNull(800) { true }) - assertNull(record.getOrNull(-1) { true }) - assertNull(record.getOrNull(repeats + 1) { true }) - } - - @Test - fun testIndexOf() { - val record = ArrayTrackRecord<Int>() - TEST_VALUES.forEach { record.add(it) } - with(record) { - assertEquals(9, indexOf(0)) - assertEquals(9, lastIndexOf(0)) - assertEquals(1, indexOf(13)) - assertEquals(7, lastIndexOf(13)) - assertEquals(3, indexOf(94)) - assertEquals(11, lastIndexOf(94)) - assertEquals(-1, indexOf(ABSENT_VALUE)) - assertEquals(-1, lastIndexOf(ABSENT_VALUE)) - } - } - - @Test - fun testContains() { - val record = ArrayTrackRecord<Int>() - TEST_VALUES.forEach { record.add(it) } - TEST_VALUES.forEach { assertTrue(record.contains(it)) } - assertFalse(record.contains(ABSENT_VALUE)) - assertTrue(record.containsAll(TEST_VALUES)) - assertTrue(record.containsAll(TEST_VALUES.sorted())) - assertTrue(record.containsAll(TEST_VALUES.sortedDescending())) - assertTrue(record.containsAll(TEST_VALUES.distinct())) - assertTrue(record.containsAll(TEST_VALUES.subList(0, TEST_VALUES.size / 2))) - assertTrue(record.containsAll(TEST_VALUES.subList(0, TEST_VALUES.size / 2).sorted())) - assertTrue(record.containsAll(listOf())) - assertFalse(record.containsAll(listOf(ABSENT_VALUE))) - assertFalse(record.containsAll(TEST_VALUES + listOf(ABSENT_VALUE))) - } - - @Test - fun testEmpty() { - val record = ArrayTrackRecord<Int>() - assertTrue(record.isEmpty()) - record.add(1) - assertFalse(record.isEmpty()) - } - - @Test - fun testIterate() { - val record = ArrayTrackRecord<Int>() - record.forEach { fail("Expected nothing to iterate") } - TEST_VALUES.forEach { record.add(it) } - // zip relies on the iterator (this calls extension function Iterable#zip(Iterable)) - record.zip(TEST_VALUES).forEach { assertEquals(it.first, it.second) } - // Also test reverse iteration (to test hasPrevious() and friends) - record.reversed().zip(TEST_VALUES.reversed()).forEach { assertEquals(it.first, it.second) } - } - - @Test - fun testIteratorIsSnapshot() { - val record = ArrayTrackRecord<Int>() - TEST_VALUES.forEach { record.add(it) } - val iterator = record.iterator() - val expectedSize = record.size - record.add(ABSENT_VALUE) - record.add(ABSENT_VALUE) - var measuredSize = 0 - iterator.forEach { - ++measuredSize - assertNotEquals(ABSENT_VALUE, it) - } - assertEquals(expectedSize, measuredSize) - } - - @Test - fun testSublist() { - val record = ArrayTrackRecord<Int>() - TEST_VALUES.forEach { record.add(it) } - assertEquals(record.subList(3, record.size - 3), - TEST_VALUES.subList(3, TEST_VALUES.size - 3)) - } - - fun testPollReturnsImmediately(record: TrackRecord<Int>) { - record.add(4) - val elapsed = measureTimeMillis { assertEquals(4, record.poll(LONG_TIMEOUT, 0)) } - // Should not have waited at all, in fact. - assertTrue(elapsed < LONG_TIMEOUT) - record.add(7) - record.add(9) - // Can poll multiple times for the same position, in whatever order - assertEquals(9, record.poll(0, 2)) - assertEquals(7, record.poll(Long.MAX_VALUE, 1)) - assertEquals(9, record.poll(0, 2)) - assertEquals(4, record.poll(0, 0)) - assertEquals(9, record.poll(0, 2) { it > 5 }) - assertEquals(7, record.poll(0, 0) { it > 5 }) - } - - @Test - fun testPollReturnsImmediately() { - testPollReturnsImmediately(ArrayTrackRecord()) - testPollReturnsImmediately(ArrayTrackRecord<Int>().newReadHead()) - } - - @Test - fun testPollTimesOut() { - val record = ArrayTrackRecord<Int>() - var delay = measureTimeMillis { assertNull(record.poll(SHORT_TIMEOUT, 0)) } - assertTrue(delay >= SHORT_TIMEOUT, "Delay $delay < $SHORT_TIMEOUT") - delay = measureTimeMillis { assertNull(record.poll(SHORT_TIMEOUT, 0) { it < 10 }) } - assertTrue(delay >= SHORT_TIMEOUT) - } - - @Test - fun testConcurrentPollDisallowed() { - val failures = AtomicInteger(0) - val readHead = ArrayTrackRecord<Int>().newReadHead() - val barrier = CyclicBarrier(2) - Thread { - barrier.await(LONG_TIMEOUT, TimeUnit.MILLISECONDS) // barrier 1 - try { - readHead.poll(LONG_TIMEOUT) - } catch (e: ConcurrentModificationException) { - failures.incrementAndGet() - // Unblock the other thread - readHead.add(0) - } - }.start() - barrier.await() // barrier 1 - try { - readHead.poll(LONG_TIMEOUT) - } catch (e: ConcurrentModificationException) { - failures.incrementAndGet() - // Unblock the other thread - readHead.add(0) - } - // One of the threads must have gotten an exception. - assertEquals(failures.get(), 1) - } - - @Test - fun testPollWakesUp() { - val record = ArrayTrackRecord<Int>() - val barrier = CyclicBarrier(2) - Thread { - barrier.await(LONG_TIMEOUT, TimeUnit.MILLISECONDS) // barrier 1 - barrier.await() // barrier 2 - Thread.sleep(SHORT_TIMEOUT * 2) - record.add(31) - }.start() - barrier.await() // barrier 1 - // Should find the element in more than SHORT_TIMEOUT but less than TEST_TIMEOUT - var delay = measureTimeMillis { - barrier.await() // barrier 2 - assertEquals(31, record.poll(TEST_TIMEOUT, 0)) - } - assertTrue(delay in SHORT_TIMEOUT..TEST_TIMEOUT) - // Polling for an element already added in anothe thread (pos 0) : should return immediately - delay = measureTimeMillis { assertEquals(31, record.poll(TEST_TIMEOUT, 0)) } - assertTrue(delay < TEST_TIMEOUT, "Delay $delay > $TEST_TIMEOUT") - // Waiting for an element that never comes - delay = measureTimeMillis { assertNull(record.poll(SHORT_TIMEOUT, 1)) } - assertTrue(delay >= SHORT_TIMEOUT, "Delay $delay < $SHORT_TIMEOUT") - // Polling for an element that doesn't match what is already there - delay = measureTimeMillis { assertNull(record.poll(SHORT_TIMEOUT, 0) { it < 10 }) } - assertTrue(delay >= SHORT_TIMEOUT) - } - - // Just make sure the interpreter actually throws an exception when the spec - // does not conform to the behavior. The interpreter is just a tool to test a - // tool used for a tool for test, let's not have hundreds of tests for it ; - // if it's broken one of the tests using it will break. - @Test - fun testInterpreter() { - val interpretLine = __LINE__ + 2 - try { - TRTInterpreter.interpretTestSpec(useReadHeads = true, spec = """ - add(4) | poll(1, 0) = 5 - """) - fail("This spec should have thrown") - } catch (e: InterpretException) { - assertTrue(e.cause is AssertionError) - assertEquals(interpretLine + 1, e.stackTrace[0].lineNumber) - assertTrue(e.stackTrace[0].fileName.contains(__FILE__)) - assertTrue(e.stackTrace[0].methodName.contains("testInterpreter")) - assertTrue(e.stackTrace[0].methodName.contains("thread1")) - } - } - - @Test - fun testMultipleAdds() { - TRTInterpreter.interpretTestSpec(useReadHeads = false, spec = """ - add(2) | | | - | add(4) | | - | | add(6) | - | | | add(8) - poll(0, 0) = 2 time 0..1 | poll(0, 0) = 2 | poll(0, 0) = 2 | poll(0, 0) = 2 - poll(0, 1) = 4 time 0..1 | poll(0, 1) = 4 | poll(0, 1) = 4 | poll(0, 1) = 4 - poll(0, 2) = 6 time 0..1 | poll(0, 2) = 6 | poll(0, 2) = 6 | poll(0, 2) = 6 - poll(0, 3) = 8 time 0..1 | poll(0, 3) = 8 | poll(0, 3) = 8 | poll(0, 3) = 8 - """) - } - - @Test - fun testConcurrentAdds() { - TRTInterpreter.interpretTestSpec(useReadHeads = false, spec = """ - add(2) | add(4) | add(6) | add(8) - add(1) | add(3) | add(5) | add(7) - poll(0, 1) is even | poll(0, 0) is even | poll(0, 3) is even | poll(0, 2) is even - poll(0, 5) is odd | poll(0, 4) is odd | poll(0, 7) is odd | poll(0, 6) is odd - """) - } - - @Test - fun testMultiplePoll() { - TRTInterpreter.interpretTestSpec(useReadHeads = false, spec = """ - add(4) | poll(1, 0) = 4 - | poll(0, 1) = null time 0..1 - | poll(1, 1) = null time 1..2 - sleep; add(7) | poll(2, 1) = 7 time 1..2 - sleep; add(18) | poll(2, 2) = 18 time 1..2 - """) - } - - @Test - fun testMultiplePollWithPredicate() { - TRTInterpreter.interpretTestSpec(useReadHeads = false, spec = """ - | poll(1, 0) = null | poll(1, 0) = null - add(6) | poll(1, 0) = 6 | - add(11) | poll(1, 0) { > 20 } = null | poll(1, 0) { = 11 } = 11 - | poll(1, 0) { > 8 } = 11 | - """) - } - - @Test - fun testMultipleReadHeads() { - TRTInterpreter.interpretTestSpec(useReadHeads = true, spec = """ - | poll() = null | poll() = null | poll() = null - add(5) | | poll() = 5 | - | poll() = 5 | | - add(8) | poll() = 8 | poll() = 8 | - | | | poll() = 5 - | | | poll() = 8 - | | | poll() = null - | | poll() = null | - """) - } - - @Test - fun testReadHeadPollWithPredicate() { - TRTInterpreter.interpretTestSpec(useReadHeads = true, spec = """ - add(5) | poll() { < 0 } = null - | poll() { > 5 } = null - add(10) | - | poll() { = 5 } = null // The "5" was skipped in the previous line - add(15) | poll() { > 8 } = 15 // The "10" was skipped in the previous line - | poll(1, 0) { > 8 } = 10 // 10 is the first element after pos 0 matching > 8 - """) - } - - @Test - fun testPollImmediatelyAdvancesReadhead() { - TRTInterpreter.interpretTestSpec(useReadHeads = true, spec = """ - add(1) | add(2) | add(3) | add(4) - mark = 0 | poll(0) { > 3 } = 4 | | - poll(0) { > 10 } = null | | | - mark = 4 | | | - poll() = null | | | - """) - } - - @Test - fun testParallelReadHeads() { - TRTInterpreter.interpretTestSpec(useReadHeads = true, spec = """ - mark = 0 | mark = 0 | mark = 0 | mark = 0 - add(2) | | | - | add(4) | | - | | add(6) | - | | | add(8) - poll() = 2 | poll() = 2 | poll() = 2 | poll() = 2 - poll() = 4 | poll() = 4 | poll() = 4 | poll() = 4 - poll() = 6 | poll() = 6 | poll() = 6 | mark = 2 - poll() = 8 | poll() = 8 | mark = 3 | poll() = 6 - mark = 4 | mark = 4 | poll() = 8 | poll() = 8 - """) - } - - @Test - fun testPeek() { - TRTInterpreter.interpretTestSpec(useReadHeads = true, spec = """ - add(2) | | | - | add(4) | | - | | add(6) | - | | | add(8) - peek() = 2 | poll() = 2 | poll() = 2 | peek() = 2 - peek() = 2 | peek() = 4 | poll() = 4 | peek() = 2 - peek() = 2 | peek() = 4 | peek() = 6 | poll() = 2 - peek() = 2 | mark = 1 | mark = 2 | poll() = 4 - mark = 0 | peek() = 4 | peek() = 6 | peek() = 6 - poll() = 2 | poll() = 4 | poll() = 6 | poll() = 6 - poll() = 4 | mark = 2 | poll() = 8 | peek() = 8 - peek() = 6 | peek() = 6 | peek() = null | mark = 3 - """) - } -} - -private object TRTInterpreter : ConcurrentInterpreter<TrackRecord<Int>>(interpretTable) { - fun interpretTestSpec(spec: String, useReadHeads: Boolean) = if (useReadHeads) { - interpretTestSpec(spec, initial = ArrayTrackRecord(), - threadTransform = { (it as ArrayTrackRecord).newReadHead() }) - } else { - interpretTestSpec(spec, ArrayTrackRecord()) - } -} - -/* - * Quick ref of supported expressions : - * sleep(x) : sleeps for x time units and returns Unit ; sleep alone means sleep(1) - * add(x) : calls and returns TrackRecord#add. - * poll(time, pos) [{ predicate }] : calls and returns TrackRecord#poll(x time units, pos). - * Optionally, a predicate may be specified. - * poll() [{ predicate }] : calls and returns ReadHead#poll(1 time unit). Optionally, a predicate - * may be specified. - * EXPR = VALUE : asserts that EXPR equals VALUE. EXPR is interpreted. VALUE can either be the - * string "null" or an int. Returns Unit. - * EXPR time x..y : measures the time taken by EXPR and asserts it took at least x and at most - * y time units. - * predicate must be one of "= x", "< x" or "> x". - */ -private val interpretTable = listOf<InterpretMatcher<TrackRecord<Int>>>( - // Interpret "XXX is odd" : run XXX and assert its return value is odd ("even" works too) - Regex("(.*)\\s+is\\s+(even|odd)") to { i, t, r -> - i.interpret(r.strArg(1), t).also { - assertEquals((it as Int) % 2, if ("even" == r.strArg(2)) 0 else 1) - } - }, - // Interpret "add(XXX)" as TrackRecord#add(int) - Regex("""add\((\d+)\)""") to { i, t, r -> - t.add(r.intArg(1)) - }, - // Interpret "poll(x, y)" as TrackRecord#poll(timeout = x * INTERPRET_TIME_UNIT, pos = y) - // Accepts an optional {} argument for the predicate (see makePredicate for syntax) - Regex("""poll\((\d+),\s*(\d+)\)\s*(\{.*\})?""") to { i, t, r -> - t.poll(r.timeArg(1), r.intArg(2), makePredicate(r.strArg(3))) - }, - // ReadHead#poll. If this throws in the cast, the code is malformed and has passed "poll()" - // in a test that takes a TrackRecord that is not a ReadHead. It's technically possible to get - // the test code to not compile instead of throw, but it's vastly more complex and this will - // fail 100% at runtime any test that would not have compiled. - Regex("""poll\((\d+)?\)\s*(\{.*\})?""") to { i, t, r -> - (if (r.strArg(1).isEmpty()) i.interpretTimeUnit else r.timeArg(1)).let { time -> - (t as ArrayTrackRecord<Int>.ReadHead).poll(time, makePredicate(r.strArg(2))) - } - }, - // ReadHead#mark. The same remarks apply as with ReadHead#poll. - Regex("mark") to { i, t, _ -> (t as ArrayTrackRecord<Int>.ReadHead).mark }, - // ReadHead#peek. The same remarks apply as with ReadHead#poll. - Regex("peek\\(\\)") to { i, t, _ -> (t as ArrayTrackRecord<Int>.ReadHead).peek() } -) - -// Parses a { = x } or { < x } or { > x } string and returns the corresponding predicate -// Returns an always-true predicate for empty and null arguments -private fun makePredicate(spec: String?): (Int) -> Boolean { - if (spec.isNullOrEmpty()) return { true } - val match = Regex("""\{\s*([<>=])\s*(\d+)\s*\}""").matchEntire(spec) - ?: throw SyntaxException("Predicate \"${spec}\"") - val arg = match.intArg(2) - return when (match.strArg(1)) { - ">" -> { i -> i > arg } - "<" -> { i -> i < arg } - "=" -> { i -> i == arg } - else -> throw RuntimeException("How did \"${spec}\" match this regexp ?") - } -}
diff --git a/tests/unit/src/com/android/networkstack/packets/NeighborAdvertisementTest.java b/tests/unit/src/com/android/networkstack/packets/NeighborAdvertisementTest.java new file mode 100644 index 0000000..c689d7b --- /dev/null +++ b/tests/unit/src/com/android/networkstack/packets/NeighborAdvertisementTest.java
@@ -0,0 +1,273 @@ +/* + * Copyright (C) 2021 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.packets; + +import static android.system.OsConstants.ETH_P_IPV6; +import static android.system.OsConstants.IPPROTO_ICMPV6; + +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_TLLA; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT; +import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST; +import static com.android.testutils.MiscAsserts.assertThrows; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import android.net.InetAddresses; +import android.net.MacAddress; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet6Address; +import java.nio.ByteBuffer; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class NeighborAdvertisementTest { + private static final Inet6Address TEST_SRC_ADDR = + (Inet6Address) InetAddresses.parseNumericAddress("fe80::dfd9:50a0:cc7b:7d6d"); + private static final Inet6Address TEST_TARGET_ADDR = + (Inet6Address) InetAddresses.parseNumericAddress("2001:db8:1:0:c928:250d:b90c:3178"); + private static final byte[] TEST_SOURCE_MAC_ADDR = new byte[] { + (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25, (byte) 0xc1, (byte) 0x25 + }; + private static final byte[] TEST_DST_MAC_ADDR = new byte[] { + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + }; + private static final byte[] TEST_GRATUITOUS_NA = new byte[] { + // dst mac address + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // src mac address + (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25, (byte) 0xc1, (byte) 0x25, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xdf, (byte) 0xd9, (byte) 0x50, (byte) 0xa0, + (byte) 0xcc, (byte) 0x7b, (byte) 0x7d, (byte) 0x6d, + // destination address + (byte) 0xff, (byte) 0x02, (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) 0x02, + // ICMP type, code, checksum + (byte) 0x88, (byte) 0x00, (byte) 0x3a, (byte) 0x3c, + // flags + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target address + (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0xc9, (byte) 0x28, (byte) 0x25, (byte) 0x0d, + (byte) 0xb9, (byte) 0x0c, (byte) 0x31, (byte) 0x78, + // TLLA option + (byte) 0x02, (byte) 0x01, + // Link-Layer address + (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25, (byte) 0xc1, (byte) 0x25, + }; + private static final byte[] TEST_GRATUITOUS_NA_WITHOUT_TLLA = new byte[] { + // dst mac address + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // src mac address + (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25, (byte) 0xc1, (byte) 0x25, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xdf, (byte) 0xd9, (byte) 0x50, (byte) 0xa0, + (byte) 0xcc, (byte) 0x7b, (byte) 0x7d, (byte) 0x6d, + // destination address + (byte) 0xff, (byte) 0x02, (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) 0x02, + // ICMP type, code, checksum + (byte) 0x88, (byte) 0x00, (byte) 0x3a, (byte) 0x3c, + // flags + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target address + (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0xc9, (byte) 0x28, (byte) 0x25, (byte) 0x0d, + (byte) 0xb9, (byte) 0x0c, (byte) 0x31, (byte) 0x78, + }; + private static final byte[] TEST_GRATUITOUS_NA_LESS_LENGTH = new byte[] { + // dst mac address + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // src mac address + (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25, (byte) 0xc1, (byte) 0x25, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xdf, (byte) 0xd9, (byte) 0x50, (byte) 0xa0, + (byte) 0xcc, (byte) 0x7b, (byte) 0x7d, (byte) 0x6d, + // destination address + (byte) 0xff, (byte) 0x02, (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) 0x02, + // ICMP type, code, checksum + (byte) 0x88, (byte) 0x00, (byte) 0x3a, (byte) 0x3c, + // flags + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + private static final byte[] TEST_GRATUITOUS_NA_TRUNCATED = new byte[] { + // dst mac address + (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, + // src mac address + (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25, (byte) 0xc1, (byte) 0x25, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xdf, (byte) 0xd9, (byte) 0x50, (byte) 0xa0, + (byte) 0xcc, (byte) 0x7b, (byte) 0x7d, (byte) 0x6d, + // destination address + (byte) 0xff, (byte) 0x02, (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) 0x02, + // ICMP type, code, checksum + (byte) 0x88, (byte) 0x00, (byte) 0x3a, (byte) 0x3c, + // flags + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target address + (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, + (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, + (byte) 0xc9, (byte) 0x28, (byte) 0x25, (byte) 0x0d, + (byte) 0xb9, (byte) 0x0c, (byte) 0x31, (byte) 0x78, + // TLLA option + (byte) 0x02, (byte) 0x01, + // truncatd Link-Layer address: 4bytes + (byte) 0xea, (byte) 0xbe, (byte) 0x11, (byte) 0x25, + }; + + @Test + public void testGratuitousNa_build() throws Exception { + final ByteBuffer na = NeighborAdvertisement.build( + MacAddress.fromBytes(TEST_SOURCE_MAC_ADDR), + MacAddress.fromBytes(TEST_DST_MAC_ADDR), + TEST_SRC_ADDR, IPV6_ADDR_ALL_ROUTERS_MULTICAST, 0 /* flags */, TEST_TARGET_ADDR); + assertArrayEquals(na.array(), TEST_GRATUITOUS_NA); + } + + private void assertNeighborAdvertisement(final NeighborAdvertisement na, + boolean hasTllaOption) { + assertArrayEquals(TEST_SOURCE_MAC_ADDR, na.ethHdr.srcMac.toByteArray()); + assertArrayEquals(TEST_DST_MAC_ADDR, na.ethHdr.dstMac.toByteArray()); + assertEquals(ETH_P_IPV6, na.ethHdr.etherType); + assertEquals(IPPROTO_ICMPV6, na.ipv6Hdr.nextHeader); + assertEquals(0xff, na.ipv6Hdr.hopLimit); + assertEquals(IPV6_ADDR_ALL_ROUTERS_MULTICAST, na.ipv6Hdr.dstIp); + assertEquals(TEST_SRC_ADDR, na.ipv6Hdr.srcIp); + assertEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, na.icmpv6Hdr.type); + assertEquals(0, na.icmpv6Hdr.code); + assertEquals(0, na.naHdr.flags); + assertEquals(TEST_TARGET_ADDR, na.naHdr.target); + if (hasTllaOption) { + assertEquals(ICMPV6_ND_OPTION_TLLA, na.tlla.type); + assertEquals(1, na.tlla.length); + assertArrayEquals(TEST_SOURCE_MAC_ADDR, na.tlla.linkLayerAddress.toByteArray()); + } + } + + @Test + public void testGratuitousNa_parse() throws Exception { + final NeighborAdvertisement na = NeighborAdvertisement.parse(TEST_GRATUITOUS_NA, + TEST_GRATUITOUS_NA.length); + + assertNeighborAdvertisement(na, true /* hasTllaOption */); + assertArrayEquals(TEST_GRATUITOUS_NA, na.toByteBuffer().array()); + } + + @Test + public void testGratuitousNa_parseWithoutTllaOption() throws Exception { + final NeighborAdvertisement na = + NeighborAdvertisement.parse(TEST_GRATUITOUS_NA_WITHOUT_TLLA, + TEST_GRATUITOUS_NA_WITHOUT_TLLA.length); + + assertNeighborAdvertisement(na, false /* hasTllaOption */); + assertArrayEquals(TEST_GRATUITOUS_NA_WITHOUT_TLLA, na.toByteBuffer().array()); + } + + @Test + public void testGratuitousNa_zeroPacketLength() throws Exception { + assertThrows(NeighborAdvertisement.ParseException.class, + () -> NeighborAdvertisement.parse(TEST_GRATUITOUS_NA, 0)); + } + + @Test + public void testGratuitousNa_invalidByteBufferLength() throws Exception { + assertThrows(NeighborAdvertisement.ParseException.class, + () -> NeighborAdvertisement.parse(TEST_GRATUITOUS_NA_TRUNCATED, + TEST_GRATUITOUS_NA.length)); + } + + @Test + public void testGratuitousNa_lessPacketLength() throws Exception { + assertThrows(NeighborAdvertisement.ParseException.class, + () -> NeighborAdvertisement.parse(TEST_GRATUITOUS_NA_LESS_LENGTH, + TEST_GRATUITOUS_NA_LESS_LENGTH.length)); + } + + @Test + public void testGratuitousNa_truncatedPacket() throws Exception { + assertThrows(IllegalArgumentException.class, + () -> NeighborAdvertisement.parse(TEST_GRATUITOUS_NA_TRUNCATED, + TEST_GRATUITOUS_NA_TRUNCATED.length)); + } +}
diff --git a/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java b/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java new file mode 100644 index 0000000..18a3ef3 --- /dev/null +++ b/tests/unit/src/com/android/networkstack/packets/NeighborSolicitationTest.java
@@ -0,0 +1,273 @@ +/* + * Copyright (C) 2021 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.packets; + +import static android.system.OsConstants.ETH_P_IPV6; +import static android.system.OsConstants.IPPROTO_ICMPV6; + +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_SLLA; +import static com.android.net.module.util.NetworkStackConstants.ICMPV6_NEIGHBOR_SOLICITATION; +import static com.android.testutils.MiscAsserts.assertThrows; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import android.net.InetAddresses; +import android.net.MacAddress; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.Inet6Address; +import java.nio.ByteBuffer; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class NeighborSolicitationTest { + private static final Inet6Address TEST_SRC_ADDR = + (Inet6Address) InetAddresses.parseNumericAddress("fe80::d419:d664:df38:2f65"); + private static final Inet6Address TEST_DST_ADDR = + (Inet6Address) InetAddresses.parseNumericAddress("fe80::200:1a:1122:3344"); + private static final Inet6Address TEST_TARGET_ADDR = + (Inet6Address) InetAddresses.parseNumericAddress("fe80::200:1a:1122:3344"); + private static final byte[] TEST_SOURCE_MAC_ADDR = new byte[] { + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + }; + private static final byte[] TEST_DST_MAC_ADDR = new byte[] { + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + }; + private static final byte[] TEST_NEIGHBOR_SOLICITATION = new byte[] { + // dst mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // src mac address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xd4, (byte) 0x19, (byte) 0xd6, (byte) 0x64, + (byte) 0xdf, (byte) 0x38, (byte) 0x2f, (byte) 0x65, + // destination address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // ICMP type, code, checksum + (byte) 0x87, (byte) 0x00, (byte) 0x22, (byte) 0x96, + // reserved + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // slla option + (byte) 0x01, (byte) 0x01, + // link-layer address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, + (byte) 0x61, (byte) 0x11, + }; + private static final byte[] TEST_NEIGHBOR_SOLICITATION_WITHOUT_SLLA = new byte[] { + // dst mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // src mac address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xd4, (byte) 0x19, (byte) 0xd6, (byte) 0x64, + (byte) 0xdf, (byte) 0x38, (byte) 0x2f, (byte) 0x65, + // destination address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // ICMP type, code, checksum + (byte) 0x87, (byte) 0x00, (byte) 0x22, (byte) 0x96, + // reserved + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + }; + private static final byte[] TEST_NEIGHBOR_SOLICITATION_LESS_LENGTH = new byte[] { + // dst mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // src mac address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xd4, (byte) 0x19, (byte) 0xd6, (byte) 0x64, + (byte) 0xdf, (byte) 0x38, (byte) 0x2f, (byte) 0x65, + // destination address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // ICMP type, code, checksum + (byte) 0x87, (byte) 0x00, (byte) 0x22, (byte) 0x96, + // reserved + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + }; + private static final byte[] TEST_NEIGHBOR_SOLICITATION_TRUNCATED = new byte[] { + // dst mac address + (byte) 0x00, (byte) 0x1a, (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // src mac address + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, (byte) 0x61, (byte) 0x11, + // ether type + (byte) 0x86, (byte) 0xdd, + // version, priority and flow label + (byte) 0x60, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // length + (byte) 0x00, (byte) 0x20, + // next header + (byte) 0x3a, + // hop limit + (byte) 0xff, + // source address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0xd4, (byte) 0x19, (byte) 0xd6, (byte) 0x64, + (byte) 0xdf, (byte) 0x38, (byte) 0x2f, (byte) 0x65, + // destination address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // ICMP type, code, checksum + (byte) 0x87, (byte) 0x00, (byte) 0x22, (byte) 0x96, + // reserved + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + // target address + (byte) 0xfe, (byte) 0x80, (byte) 0x00, (byte) 0x00, + (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, + (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x1a, + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, + // slla option + (byte) 0x01, (byte) 0x01, + // truncatd link-layer address: 4bytes + (byte) 0x06, (byte) 0x5a, (byte) 0xac, (byte) 0x02, + }; + + @Test + public void testNeighborSolicitation_build() throws Exception { + final ByteBuffer ns = NeighborSolicitation.build( + MacAddress.fromBytes(TEST_SOURCE_MAC_ADDR), + MacAddress.fromBytes(TEST_DST_MAC_ADDR), + TEST_SRC_ADDR, TEST_DST_ADDR, TEST_TARGET_ADDR); + assertArrayEquals(ns.array(), TEST_NEIGHBOR_SOLICITATION); + } + + private void assertNeighborSolicitation(final NeighborSolicitation ns, boolean hasSllaOption) { + assertArrayEquals(TEST_SOURCE_MAC_ADDR, ns.ethHdr.srcMac.toByteArray()); + assertArrayEquals(TEST_DST_MAC_ADDR, ns.ethHdr.dstMac.toByteArray()); + assertEquals(ETH_P_IPV6, ns.ethHdr.etherType); + assertEquals(IPPROTO_ICMPV6, ns.ipv6Hdr.nextHeader); + assertEquals(0xff, ns.ipv6Hdr.hopLimit); + assertEquals(TEST_DST_ADDR, ns.ipv6Hdr.dstIp); + assertEquals(TEST_SRC_ADDR, ns.ipv6Hdr.srcIp); + assertEquals(ICMPV6_NEIGHBOR_SOLICITATION, ns.icmpv6Hdr.type); + assertEquals(0, ns.icmpv6Hdr.code); + assertEquals(TEST_TARGET_ADDR, ns.nsHdr.target); + if (hasSllaOption) { + assertEquals(ICMPV6_ND_OPTION_SLLA, ns.slla.type); + assertEquals(1, ns.slla.length); + assertEquals(MacAddress.fromBytes(TEST_SOURCE_MAC_ADDR), ns.slla.linkLayerAddress); + } + } + + @Test + public void testNeighborSolicitation_parse() throws Exception { + final NeighborSolicitation ns = NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION, + TEST_NEIGHBOR_SOLICITATION.length); + + assertNeighborSolicitation(ns, true /* hasSllaOption */); + assertArrayEquals(TEST_NEIGHBOR_SOLICITATION, ns.toByteBuffer().array()); + } + + @Test + public void testNeighborSolicitation_parseWithoutSllaOption() throws Exception { + final NeighborSolicitation ns = + NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION_WITHOUT_SLLA, + TEST_NEIGHBOR_SOLICITATION_WITHOUT_SLLA.length); + + assertNeighborSolicitation(ns, false /* hasSllaOption */); + assertArrayEquals(TEST_NEIGHBOR_SOLICITATION_WITHOUT_SLLA, ns.toByteBuffer().array()); + } + + @Test + public void testNeighborSolicitation_invalidPacketLength() throws Exception { + assertThrows(NeighborSolicitation.ParseException.class, + () -> NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION, 0)); + } + + @Test + public void testNeighborSolicitation_invalidByteBufferLength() throws Exception { + assertThrows(NeighborSolicitation.ParseException.class, + () -> NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION_TRUNCATED, + TEST_NEIGHBOR_SOLICITATION.length)); + } + + @Test + public void testNeighborSolicitation_lessPacketLength() throws Exception { + assertThrows(NeighborSolicitation.ParseException.class, + () -> NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION_LESS_LENGTH, + TEST_NEIGHBOR_SOLICITATION_LESS_LENGTH.length)); + } + + @Test + public void testNeighborSolicitation_truncatedPacket() throws Exception { + assertThrows(IllegalArgumentException.class, + () -> NeighborSolicitation.parse(TEST_NEIGHBOR_SOLICITATION_TRUNCATED, + TEST_NEIGHBOR_SOLICITATION_TRUNCATED.length)); + } +}
diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java index 8f42a61..66d1c71 100644 --- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -25,9 +25,14 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_HTTPS; import static android.net.INetworkMonitor.NETWORK_VALIDATION_PROBE_PRIVDNS; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_PARTIAL; +import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_SKIPPED; import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_VPN; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; @@ -91,6 +96,7 @@ import static java.util.stream.Collectors.toList; import android.annotation.NonNull; +import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -142,7 +148,11 @@ import com.android.networkstack.R; import com.android.networkstack.apishim.CaptivePortalDataShimImpl; import com.android.networkstack.apishim.ConstantsShim; +import com.android.networkstack.apishim.NetworkInformationShimImpl; +import com.android.networkstack.apishim.common.CaptivePortalDataShim; +import com.android.networkstack.apishim.common.NetworkInformationShim; import com.android.networkstack.apishim.common.ShimUtils; +import com.android.networkstack.apishim.common.UnsupportedApiLevelException; import com.android.networkstack.metrics.DataStallDetectionStats; import com.android.networkstack.metrics.DataStallStatsUtils; import com.android.networkstack.netlink.TcpSocketTracker; @@ -151,6 +161,7 @@ import com.android.server.connectivity.nano.DnsEvent; import com.android.server.connectivity.nano.WifiData; import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; import com.android.testutils.HandlerUtils; @@ -197,6 +208,7 @@ @RunWith(AndroidJUnit4.class) @SmallTest +@SuppressLint("NewApi") // Uses hidden APIs, which the linter would identify as missing APIs. public class NetworkMonitorTest { private static final String LOCATION_HEADER = "location"; private static final String CONTENT_TYPE_HEADER = "Content-Type"; @@ -261,6 +273,7 @@ private static final String TEST_SPEED_TEST_URL = "https://speedtest.example.com"; private static final String TEST_RELATIVE_URL = "/test/relative/gen_204"; private static final String TEST_MCCMNC = "123456"; + private static final String TEST_FRIENDLY_NAME = "Friendly Name"; private static final String[] TEST_HTTP_URLS = {TEST_HTTP_OTHER_URL1, TEST_HTTP_OTHER_URL2}; private static final String[] TEST_HTTPS_URLS = {TEST_HTTPS_OTHER_URL1, TEST_HTTPS_OTHER_URL2}; private static final int TEST_TCP_FAIL_RATE = 99; @@ -278,7 +291,9 @@ private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5; private static final int HANDLER_TIMEOUT_MS = 1000; - + private static final int TEST_MIN_STALL_EVALUATE_INTERVAL_MS = 500; + private static final int STALL_EXPECTED_LAST_PROBE_TIME_MS = + TEST_MIN_STALL_EVALUATE_INTERVAL_MS + HANDLER_TIMEOUT_MS; private static final LinkProperties TEST_LINK_PROPERTIES = new LinkProperties(); // Cannot have a static member for the LinkProperties with captive portal API information, as @@ -308,6 +323,14 @@ private static final NetworkCapabilities CELL_NO_INTERNET_CAPABILITIES = new NetworkCapabilities().addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); + private static final NetworkCapabilities WIFI_OEM_PAID_CAPABILITIES = + new NetworkCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_METERED) + .addCapability(NET_CAPABILITY_OEM_PAID) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED); + /** * Fakes DNS responses. * @@ -538,7 +561,7 @@ resetCallbacks(); - setMinDataStallEvaluateInterval(500); + setMinDataStallEvaluateInterval(TEST_MIN_STALL_EVALUATE_INTERVAL_MS); setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); setValidDataStallDnsTimeThreshold(500); setConsecutiveDnsTimeoutThreshold(5); @@ -1061,6 +1084,34 @@ runPortalNetworkTest(); } + @Test + public void testIsCaptivePortal_Http200EmptyResponse() throws Exception { + setSslException(mHttpsConnection); + setStatus(mHttpConnection, 200); + // Invalid if there is no content (can't login to an empty page) + runNetworkTest(VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null); + verify(mCallbacks, never()).showProvisioningNotification(any(), any()); + } + + private void doCaptivePortal200ResponseTest(String expectedRedirectUrl) throws Exception { + setSslException(mHttpsConnection); + setStatus(mHttpConnection, 200); + doReturn(100L).when(mHttpConnection).getContentLengthLong(); + // Redirect URL was null before S + runNetworkTest(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, expectedRedirectUrl); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS)).showProvisioningNotification(any(), any()); + } + + @Test @IgnoreAfter(Build.VERSION_CODES.R) + public void testIsCaptivePortal_HttpProbeIs200Portal_R() throws Exception { + doCaptivePortal200ResponseTest(null); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testIsCaptivePortal_HttpProbeIs200Portal() throws Exception { + doCaptivePortal200ResponseTest(TEST_HTTP_URL); + } + private void setupPrivateIpResponse(String privateAddr) throws Exception { setSslException(mHttpsConnection); setPortal302(mHttpConnection); @@ -1598,7 +1649,8 @@ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); assertFalse(wrappedMonitor.isDataStall()); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); + wrappedMonitor.setLastProbeTime( + SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertTrue(wrappedMonitor.isDataStall()); verify(mCallbacks).notifyDataStallSuspected( @@ -1608,7 +1660,8 @@ @Test public void testIsDataStall_EvaluationDnsWithDnsTimeoutCount() throws Exception { WrappedNetworkMonitor wrappedMonitor = makeCellMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); + wrappedMonitor.setLastProbeTime( + SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); makeDnsTimeoutEvent(wrappedMonitor, 3); assertFalse(wrappedMonitor.isDataStall()); // Reset consecutive timeout counts. @@ -1626,7 +1679,8 @@ // Set the value to larger than the default dns log size. setConsecutiveDnsTimeoutThreshold(51); wrappedMonitor = makeCellMeteredNetworkMonitor(); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); + wrappedMonitor.setLastProbeTime( + SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); makeDnsTimeoutEvent(wrappedMonitor, 50); assertFalse(wrappedMonitor.isDataStall()); @@ -1658,7 +1712,8 @@ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertFalse(wrappedMonitor.isDataStall()); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); + wrappedMonitor.setLastProbeTime( + SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); assertTrue(wrappedMonitor.isDataStall()); verify(mCallbacks).notifyDataStallSuspected( matchDnsDataStallParcelable(DEFAULT_DNS_TIMEOUT_THRESHOLD)); @@ -1669,7 +1724,8 @@ wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertFalse(wrappedMonitor.isDataStall()); - wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); + wrappedMonitor.setLastProbeTime( + SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); assertFalse(wrappedMonitor.isDataStall()); } @@ -1703,7 +1759,7 @@ setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS | DATA_STALL_EVALUATION_TYPE_TCP); setupTcpDataStall(); final WrappedNetworkMonitor nm = makeMonitor(CELL_METERED_CAPABILITIES); - nm.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); + nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); makeDnsTimeoutEvent(nm, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertTrue(nm.isDataStall()); verify(mCallbacks).notifyDataStallSuspected( @@ -1739,11 +1795,85 @@ runFailedNetworkTest(); } + private void doValidationSkippedTest(NetworkCapabilities nc) throws Exception { + // For S+, the RESULT_SKIPPED bit will be included on networks that both do not require + // validation and for which validation is not performed. + final int validationResult = ShimUtils.isAtLeastS() + ? NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED + : NETWORK_VALIDATION_RESULT_VALID; + runNetworkTest(TEST_LINK_PROPERTIES, nc, validationResult, + 0 /* probesSucceeded */, null /* redirectUrl */); + verify(mCleartextDnsNetwork, never()).openConnection(any()); + } + @Test public void testNoInternetCapabilityValidated() throws Exception { - runNetworkTest(TEST_LINK_PROPERTIES, CELL_NO_INTERNET_CAPABILITIES, - NETWORK_VALIDATION_RESULT_VALID, 0 /* probesSucceeded */, null /* redirectUrl */); - verify(mCleartextDnsNetwork, never()).openConnection(any()); + doValidationSkippedTest(CELL_NO_INTERNET_CAPABILITIES); + } + + @Test + public void testNoTrustedCapabilityValidated() throws Exception { + final NetworkCapabilities.Builder nc = new NetworkCapabilities.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .removeCapability(NET_CAPABILITY_TRUSTED) + .addTransportType(TRANSPORT_CELLULAR); + if (ShimUtils.isAtLeastS()) { + nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + } + doValidationSkippedTest(nc.build()); + } + + @Test + public void testRestrictedCapabilityValidated() throws Exception { + final NetworkCapabilities.Builder nc = new NetworkCapabilities.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .removeCapability(NET_CAPABILITY_NOT_RESTRICTED) + .addTransportType(TRANSPORT_CELLULAR); + if (ShimUtils.isAtLeastS()) { + nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED); + } + doValidationSkippedTest(nc.build()); + } + + private NetworkCapabilities getVcnUnderlyingCarrierWifiCaps() { + // Must be called from within the test because NOT_VCN_MANAGED is an invalid capability + // value up to Android R. Thus, this must be guarded by an SDK check in tests that use this. + return new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) + .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED) + .addCapability(NET_CAPABILITY_INTERNET) + .build(); + } + + @Test + public void testVcnUnderlyingNetwork() throws Exception { + assumeTrue(ShimUtils.isAtLeastS()); + setStatus(mHttpsConnection, 204); + setStatus(mHttpConnection, 204); + + final NetworkMonitor nm = runNetworkTest( + TEST_LINK_PROPERTIES, getVcnUnderlyingCarrierWifiCaps(), + NETWORK_VALIDATION_RESULT_VALID, + NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS, + null /* redirectUrl */); + assertEquals(NETWORK_VALIDATION_RESULT_VALID, + nm.getEvaluationState().getEvaluationResult()); + } + + @Test + public void testVcnUnderlyingNetworkBadNetwork() throws Exception { + assumeTrue(ShimUtils.isAtLeastS()); + setSslException(mHttpsConnection); + setStatus(mHttpConnection, 500); + setStatus(mFallbackConnection, 404); + + final NetworkMonitor nm = runNetworkTest( + TEST_LINK_PROPERTIES, getVcnUnderlyingCarrierWifiCaps(), + VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null /* redirectUrl */); + assertEquals(VALIDATION_RESULT_INVALID, + nm.getEvaluationState().getEvaluationResult()); } @Test @@ -1996,7 +2126,7 @@ nm.notifyNetworkConnected(TEST_LINK_PROPERTIES, nc); verifyNetworkTested(NETWORK_VALIDATION_RESULT_VALID, NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS); - nm.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); + nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); return nm; } @@ -2018,6 +2148,8 @@ ArgumentCaptor.forClass(DataStallDetectionStats.class); verify(mDependencies, timeout(HANDLER_TIMEOUT_MS).times(1)) .writeDataStallDetectionStats(statsCaptor.capture(), probeResultCaptor.capture()); + // Ensure probe will not stop due to rate-limiting mechanism. + nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); assertTrue(nm.isDataStall()); assertTrue(probeResultCaptor.getValue().isSuccessful()); verifyTestDataStallDetectionStats(evalType, transport, statsCaptor.getValue()); @@ -2537,6 +2669,118 @@ verify(mCleartextDnsNetwork, times(4)).openConnection(any()); } + @Test + public void testIsCaptivePortal_FromExternalSource() throws Exception { + assumeTrue(CaptivePortalDataShimImpl.isSupported()); + assumeTrue(ShimUtils.isAtLeastS()); + when(mDependencies.isFeatureEnabled(any(), eq(NAMESPACE_CONNECTIVITY), + eq(DISMISS_PORTAL_IN_VALIDATED_NETWORK), anyBoolean())).thenReturn(true); + final NetworkMonitor monitor = makeMonitor(WIFI_NOT_METERED_CAPABILITIES); + + NetworkInformationShim networkShim = NetworkInformationShimImpl.newInstance(); + CaptivePortalDataShim captivePortalData = new CaptivePortalDataShimImpl( + new CaptivePortalData.Builder().setCaptive(true).build()); + final LinkProperties linkProperties = new LinkProperties(TEST_LINK_PROPERTIES); + networkShim.setCaptivePortalData(linkProperties, captivePortalData); + CaptivePortalDataShim captivePortalDataShim = + networkShim.getCaptivePortalData(linkProperties); + + try { + // Set up T&C captive portal info from Passpoint + captivePortalData = captivePortalDataShim.withPasspointInfo(TEST_FRIENDLY_NAME, + Uri.parse(TEST_VENUE_INFO_URL), Uri.parse(TEST_LOGIN_URL)); + } catch (UnsupportedApiLevelException e) { + // Minimum API level for this test is 31 + return; + } + + networkShim.setCaptivePortalData(linkProperties, captivePortalData); + monitor.notifyLinkPropertiesChanged(linkProperties); + final NetworkCapabilities networkCapabilities = + new NetworkCapabilities(WIFI_NOT_METERED_CAPABILITIES); + monitor.notifyNetworkConnected(linkProperties, networkCapabilities); + verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1)) + .showProvisioningNotification(any(), any()); + assertEquals(1, mRegisteredReceivers.size()); + verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL); + + // Force reevaluation and confirm that the network is still captive + HandlerUtils.waitForIdle(monitor.getHandler(), HANDLER_TIMEOUT_MS); + resetCallbacks(); + monitor.forceReevaluation(Process.myUid()); + assertEquals(monitor.getEvaluationState().getProbeCompletedResult(), 0); + verifyNetworkTested(VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, TEST_LOGIN_URL); + + // Check that startCaptivePortalApp sends the expected intent. + monitor.launchCaptivePortalApp(); + + verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1)).startCaptivePortalApp( + argThat(network -> TEST_NETID == network.netId), + argThat(bundle -> bundle.getString( + ConnectivityManager.EXTRA_CAPTIVE_PORTAL_URL).equals(TEST_LOGIN_URL) + && TEST_NETID == ((Network) bundle.getParcelable( + ConnectivityManager.EXTRA_NETWORK)).netId)); + } + + @Test + public void testOemPaidNetworkValidated() throws Exception { + setValidProbes(); + + final NetworkMonitor nm = runNetworkTest(TEST_LINK_PROPERTIES, + WIFI_OEM_PAID_CAPABILITIES, + NETWORK_VALIDATION_RESULT_VALID, + NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTPS, + null /* redirectUrl */); + assertEquals(NETWORK_VALIDATION_RESULT_VALID, + nm.getEvaluationState().getEvaluationResult()); + } + + @Test + public void testOemPaidNetwork_AllProbesFailed() throws Exception { + setSslException(mHttpsConnection); + setStatus(mHttpConnection, 500); + setStatus(mFallbackConnection, 404); + + runNetworkTest(TEST_LINK_PROPERTIES, + WIFI_OEM_PAID_CAPABILITIES, + VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, null /* redirectUrl */); + } + + @Test + public void testOemPaidNetworkNoInternetCapabilityValidated() throws Exception { + setSslException(mHttpsConnection); + setStatus(mHttpConnection, 500); + setStatus(mFallbackConnection, 404); + + final NetworkCapabilities networkCapabilities = + new NetworkCapabilities(WIFI_OEM_PAID_CAPABILITIES); + networkCapabilities.removeCapability(NET_CAPABILITY_INTERNET); + + final int validationResult = ShimUtils.isAtLeastS() + ? NETWORK_VALIDATION_RESULT_VALID | NETWORK_VALIDATION_RESULT_SKIPPED + : NETWORK_VALIDATION_RESULT_VALID; + runNetworkTest(TEST_LINK_PROPERTIES, networkCapabilities, + validationResult, 0 /* probesSucceeded */, null /* redirectUrl */); + + verify(mCleartextDnsNetwork, never()).openConnection(any()); + verify(mHttpsConnection, never()).getResponseCode(); + verify(mHttpConnection, never()).getResponseCode(); + verify(mFallbackConnection, never()).getResponseCode(); + } + + @Test + public void testOemPaidNetwork_CaptivePortalNotLaunched() throws Exception { + setSslException(mHttpsConnection); + setStatus(mFallbackConnection, 404); + setPortal302(mHttpConnection); + + runNetworkTest(TEST_LINK_PROPERTIES, WIFI_OEM_PAID_CAPABILITIES, + VALIDATION_RESULT_PORTAL, 0 /* probesSucceeded */, + TEST_LOGIN_URL); + + verify(mCallbacks, never()).showProvisioningNotification(any(), any()); + } + private void setupResourceForMultipleProbes() { // Configure the resource to send multiple probe. when(mResources.getStringArray(R.array.config_captive_portal_https_urls))