Snap for 7195589 from 99b0cbb34d15235f25dca9a288d4e5bbf1cff34b to mainline-media-swcodec-release Change-Id: Ie7b5ff3b0ec30502938409bc4b080017a7e92ff1
diff --git a/Android.bp b/Android.bp index 802ca42..2daa1d4 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,14 @@ // TestNetworkStack // Common defaults to define SDK level +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +// Whether to enable the targets in this file that target current SDKs. +// Set to false in branches like mainline-prod where API classes are too old to build current code. +enable_current_sdk_targets = false + java_defaults { name: "NetworkStackDevApiLevel", min_sdk_version: "29", @@ -54,27 +62,116 @@ target_sdk_version: "30", } -// Filegroups for the API shims -filegroup { - name: "NetworkStackApiCurrentShims", +// Libraries for the API shims +java_defaults { + name: "NetworkStackShimsDefaults", + libs: [ + "androidx.annotation_annotation", + "networkstack-client", + ], + 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"], +} + +// 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", + enabled: enable_current_sdk_targets, + defaults: ["NetworkStackShimsDefaults"], + srcs: [ "apishim/31/**/*.java", - ":networkstack-module-utils-srcs", + ], + libs: [ + "NetworkStackShimsCommon", + "NetworkStackApi29Shims", + "NetworkStackApi30Shims", + "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", + enabled: enable_current_sdk_targets, + defaults: ["NetworkStackShimsDefaults"], + static_libs: [ + "NetworkStackShimsCommon", + "NetworkStackApi29Shims", + "NetworkStackApi30Shims", + "NetworkStackApi31Shims", + ], + 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", + ], + jarjar_rules: "apishim/jarjar-rules-compat.txt", + sdk_version: "system_30", + visibility: [ + "//packages/modules/Connectivity/Tethering", + "//packages/modules/Connectivity/tests/cts/net", ], } @@ -84,11 +181,12 @@ name: "NetworkStackAndroidLibraryDefaults", srcs: [ ":framework-networkstack-shared-srcs", + ":networkstack-module-utils-srcs", ], libs: ["unsupportedappusage"], static_libs: [ "androidx.annotation_annotation", - "netd_aidl_interface-java", + "netd_aidl_interface-lateststable-java", "netlink-client", "networkstack-client", "net-utils-framework-common", @@ -101,43 +199,42 @@ 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", + enabled: enable_current_sdk_targets, + 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", + ], } 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/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", + ], } filegroup { @@ -146,7 +243,6 @@ visibility: [ "//packages/modules/NetworkStack/tests/unit", "//packages/modules/NetworkStack/tests/integration", - "//frameworks/base/packages/Tethering/tests/integration", "//packages/modules/Connectivity/Tethering/tests/integration", ] } @@ -181,7 +277,7 @@ // The InProcessNetworkStack goes together with the PlatformCaptivePortalLogin, which replaces // the default CaptivePortalLogin. required: ["PlatformNetworkPermissionConfig", "PlatformCaptivePortalLogin"], - enabled: false, // Disabled in mainline-prod + enabled: enable_current_sdk_targets, } // Pre-merge the AndroidManifest for NetworkStackNext, so that its manifest can be merged on top @@ -190,7 +286,7 @@ defaults: ["NetworkStackAppDefaults", "NetworkStackDevApiLevel"], static_libs: ["NetworkStackApiCurrentLib"], manifest: "AndroidManifest.xml", - enabled: false, // Disabled in mainline-prod + enabled: enable_current_sdk_targets, } // NetworkStack build targeting the current API release, for testing on in-development SDK @@ -202,7 +298,7 @@ manifest: "AndroidManifest_Next.xml", // The permission configuration *must* be included to ensure security of the device required: ["NetworkPermissionConfig"], - enabled: false, // Disabled in mainline-prod + enabled: enable_current_sdk_targets, } // Updatable network stack for finalized API @@ -217,19 +313,6 @@ 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", - ], -} - cc_library_shared { name: "libnetworkstackutilsjni", srcs: [
diff --git a/apishim/29/com/android/networkstack/apishim/api29/CaptivePortalDataShimImpl.java b/apishim/29/com/android/networkstack/apishim/api29/CaptivePortalDataShimImpl.java index 42216a9..9e27666 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)} @@ -56,19 +56,46 @@ 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..e504495 --- /dev/null +++ b/apishim/29/com/android/networkstack/apishim/api29/ConnectivityManagerShimImpl.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.api29; + +import android.content.Context; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.NetworkRequest; +import android.os.Handler; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +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 ConnectivityManagerShimImpl(Context context) {} + + /** + * 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, + @Nullable Handler handler, @NonNull NetworkCallback networkCallback) + throws UnsupportedApiLevelException { + // Not supported for API 29. + throw new UnsupportedApiLevelException("Not supported in API 29."); + } + + /** + * See android.net.ConnectivityManager#registerSystemDefaultNetworkCallback + * @throws UnsupportedApiLevelException if API is not available in this API level. + */ + @Override + public void registerSystemDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, + @NonNull Handler handler) throws UnsupportedApiLevelException { + // Not supported for API 29. + throw new UnsupportedApiLevelException("Not supported in API 29."); + } +}
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/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..ac62522 --- /dev/null +++ b/apishim/30/com/android/networkstack/apishim/api30/ConnectivityManagerShimImpl.java
@@ -0,0 +1,74 @@ +/* + * 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.net.ConnectivityManager.NetworkCallback; +import android.net.NetworkRequest; +import android.os.Build; +import android.os.Handler; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.networkstack.apishim.common.ConnectivityManagerShim; +import com.android.networkstack.apishim.common.ShimUtils; +import com.android.networkstack.apishim.common.UnsupportedApiLevelException; + +/** + * Implementation of {@link ConnectivityManagerShim} for API 30. + */ +public class ConnectivityManagerShimImpl + extends com.android.networkstack.apishim.api29.ConnectivityManagerShimImpl { + protected ConnectivityManagerShimImpl(Context context) { + super(context); + } + + /** + * Get a new instance of {@link ConnectivityManagerShim}. + */ + public static ConnectivityManagerShim newInstance(Context context) { + if (!ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)) { + 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, + @Nullable Handler handler, @NonNull NetworkCallback networkCallback) + throws UnsupportedApiLevelException { + // Not supported for API 30. + throw new UnsupportedApiLevelException("Not supported in API 30."); + } + + /** + * See android.net.ConnectivityManager#registerSystemDefaultNetworkCallback + * @throws UnsupportedApiLevelException if API is not available in this API level. + */ + @Override + public void registerSystemDefaultNetworkCallback(@NonNull NetworkCallback networkCallback, + @NonNull Handler handler) throws UnsupportedApiLevelException { + // Not supported for API 30. + throw new UnsupportedApiLevelException("Not supported in API 30."); + } +}
diff --git a/apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java b/apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java index 27fd745..d5012f8 100644 --- a/apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java +++ b/apishim/30/com/android/networkstack/apishim/api30/ConstantsShim.java
@@ -38,8 +38,8 @@ public static final int DETECTION_METHOD_TCP_METRICS = DataStallReport.DETECTION_METHOD_TCP_METRICS; - /** - * @see android.net.NetworkCapabilities - */ + // Constants defined in android.net.NetworkCapabilities. + public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28; 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/31/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java b/apishim/31/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java index 955167d..6e3eb19 100644 --- a/apishim/31/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java +++ b/apishim/31/com/android/networkstack/apishim/CaptivePortalDataShimImpl.java
@@ -17,6 +17,7 @@ package com.android.networkstack.apishim; import android.net.CaptivePortalData; +import android.net.Uri; import androidx.annotation.NonNull; @@ -27,7 +28,7 @@ */ public class CaptivePortalDataShimImpl extends com.android.networkstack.apishim.api30.CaptivePortalDataShimImpl { - protected CaptivePortalDataShimImpl(@NonNull CaptivePortalData data) { + public CaptivePortalDataShimImpl(@NonNull CaptivePortalData data) { super(data); } @@ -37,14 +38,46 @@ } /** - * Generate a {@link CaptivePortalData} object with a friendly name set + * 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 CaptivePortalData} object with a friendly name set + * @return a {@link CaptivePortalDataShim} object with a friendly name set */ - public CaptivePortalData withVenueFriendlyName(String friendlyName) { - return new CaptivePortalData.Builder(mData) + @Override + public CaptivePortalDataShim withVenueFriendlyName(String friendlyName) { + return new CaptivePortalDataShimImpl(new CaptivePortalData.Builder(mData) .setVenueFriendlyName(friendlyName) - .build(); + .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/ConnectivityManagerShimImpl.java b/apishim/31/com/android/networkstack/apishim/ConnectivityManagerShimImpl.java new file mode 100644 index 0000000..0c3fa3e --- /dev/null +++ b/apishim/31/com/android/networkstack/apishim/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; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.ConnectivityManager.NetworkCallback; +import android.net.NetworkRequest; +import android.os.Build; +import android.os.Handler; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.networkstack.apishim.common.ConnectivityManagerShim; +import com.android.networkstack.apishim.common.ShimUtils; + +/** + * Implementation of {@link ConnectivityManagerShim} for API 31. + */ +public class ConnectivityManagerShimImpl + extends com.android.networkstack.apishim.api30.ConnectivityManagerShimImpl { + private final ConnectivityManager mCm; + + protected ConnectivityManagerShimImpl(Context context) { + super(context); + mCm = context.getSystemService(ConnectivityManager.class); + } + + /** + * Get a new instance of {@link ConnectivityManagerShim}. + */ + public static ConnectivityManagerShim newInstance(Context context) { + if (!ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.R)) { + 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, + @Nullable Handler handler, @NonNull NetworkCallback networkCallback) { + mCm.requestBackgroundNetwork(request, handler, networkCallback); + } + + /** + * See android.net.ConnectivityManager#registerSystemDefaultNetworkCallback + */ + @Override + public void registerSystemDefaultNetworkCallback( + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) { + mCm.registerSystemDefaultNetworkCallback(networkCallback, handler); + } +}
diff --git a/apishim/common/com/android/networkstack/apishim/common/CaptivePortalDataShim.java b/apishim/common/com/android/networkstack/apishim/common/CaptivePortalDataShim.java index 4bd5532..26ac9d4 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}. */ @@ -57,6 +57,11 @@ String getVenueFriendlyName(); /** + * @see CaptivePortalData#getUserPortalUrlSource() + */ + int getUserPortalUrlSource(); + + /** * @see INetworkMonitorCallbacks#notifyCaptivePortalDataChanged(android.net.CaptivePortalData) */ void notifyChanged(INetworkMonitorCallbacks cb) throws RemoteException; @@ -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..2d60d97 --- /dev/null +++ b/apishim/common/com/android/networkstack/apishim/common/ConnectivityManagerShim.java
@@ -0,0 +1,46 @@ +/* + * 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 androidx.annotation.NonNull; +import androidx.annotation.Nullable; + + +/** + * 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, + @Nullable Handler handler, @NonNull NetworkCallback networkCallback) + throws UnsupportedApiLevelException; + + /** See android.net.ConnectivityManager#registerSystemDefaultNetworkCallback */ + void registerSystemDefaultNetworkCallback( + @NonNull NetworkCallback networkCallback, @NonNull Handler handler) + throws UnsupportedApiLevelException; +}
diff --git a/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java b/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java index 6cdcf8c..b2ed53e 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,5 @@ * @param captivePortalData Captive portal data to be used */ void setCaptivePortalData(@NonNull LinkProperties lp, - @Nullable CaptivePortalData captivePortalData); + @Nullable CaptivePortalDataShim captivePortalData); }
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/moduleutils/Android.bp b/common/moduleutils/Android.bp index 644b0a4..5d1c9e3 100644 --- a/common/moduleutils/Android.bp +++ b/common/moduleutils/Android.bp
@@ -17,6 +17,10 @@ // 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"], +} + filegroup { name: "net-module-utils-srcs", srcs: [
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..96ff315 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", + "frameworks/base/packages/Connectivity/framework/aidl-export", "frameworks/native/aidl/binder", // For PersistableBundle.aidl ], srcs: [ @@ -152,8 +156,8 @@ "src/android/net/shared/**/*.java", ], static_libs: [ - "ipmemorystore-aidl-interfaces-java", - "networkstack-aidl-interfaces-java", + "ipmemorystore-aidl-interfaces-V10-java", + "networkstack-aidl-interfaces-V10-java", ], visibility: [ "//frameworks/base/packages/Tethering", @@ -162,4 +166,8 @@ "//frameworks/opt/net/wifi/service", "//packages/modules/NetworkStack", ], + apex_available: [ + "//apex_available:platform", + "com.android.tethering", + ], }
diff --git a/proguard.flags b/proguard.flags index af4262a..13425ce 100644 --- a/proguard.flags +++ b/proguard.flags
@@ -8,6 +8,10 @@ static final int EVENT_*; } +-keepclassmembers public class * extends com.android.networkstack.util.Struct { + public <init>(...); +} + # 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/src/com/android/networkstack/packets/NeighborAdvertisement.java b/src/com/android/networkstack/packets/NeighborAdvertisement.java new file mode 100644 index 0000000..e6cdfc8 --- /dev/null +++ b/src/com/android/networkstack/packets/NeighborAdvertisement.java
@@ -0,0 +1,127 @@ +/* + * 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_HEADER_MIN_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 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; + @NonNull + public final LlaOption tlla; + + public NeighborAdvertisement(@NonNull final EthernetHeader ethHdr, + @NonNull final Ipv6Header ipv6Hdr, @NonNull final Icmpv6Header icmpv6Hdr, + @NonNull final NaHeader naHdr, @NonNull 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 = 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); + 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_HEADER_MIN_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 = 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/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java index f8a9bab..b4a3ae9 100755 --- a/src/com/android/server/connectivity/NetworkMonitor.java +++ b/src/com/android/server/connectivity/NetworkMonitor.java
@@ -167,7 +167,9 @@ 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; @@ -515,6 +517,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 @@ -1227,6 +1231,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 +1582,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); @@ -3566,4 +3596,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..2cffc3d 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"],
diff --git a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java index 1b5660c..451437e 100644 --- a/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java +++ b/tests/integration/src/android/net/ip/IpClientIntegrationTestCommon.java
@@ -31,7 +31,6 @@ 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; @@ -39,18 +38,11 @@ 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_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_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_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 junit.framework.Assert.fail; @@ -141,7 +133,9 @@ 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.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; @@ -180,6 +174,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; @@ -1468,111 +1463,25 @@ // 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) 0b11000000 /* L = 1, A = 1 */, 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; - } - - // 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); + return RdnssOption.build(lifetime, servers); } 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 MacAddress dstMac = MacAddress.fromString("33:33:00:00:00:01"); + final MacAddress srcMac = MacAddress.fromString("01:02:03:04:05:06"); + final Inet6Address routerLinkLocal = + (Inet6Address) InetAddresses.parseNumericAddress("fe80::1"); - 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; + return Ipv6Utils.buildRaPacket(srcMac, dstMac, routerLinkLocal, + IPV6_ADDR_ALL_NODES_MULTICAST, (byte) 0 /* M=0, O=0 */, lifetime, + 0 /* Reachable time, unspecified */, 100 /* Retrans time 100ms */, + options); } private static ByteBuffer buildRaPacket(ByteBuffer... options) throws Exception {
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp index eb52f92..e308b96 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,
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/src/com/android/networkstack/packets/NeighborAdvertisementTest.java b/tests/unit/src/com/android/networkstack/packets/NeighborAdvertisementTest.java new file mode 100644 index 0000000..3317b2b --- /dev/null +++ b/tests/unit/src/com/android/networkstack/packets/NeighborAdvertisementTest.java
@@ -0,0 +1,210 @@ +/* + * 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_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_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, + }; + 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, + // Link-Layer address + (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); + } + + @Test + public void testGratuitousNa_parse() throws Exception { + final NeighborAdvertisement na = NeighborAdvertisement.parse(TEST_GRATUITOUS_NA, + TEST_GRATUITOUS_NA.length); + + 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); + assertEquals(2, na.tlla.type); + assertEquals(1, na.tlla.length); + assertArrayEquals(TEST_SOURCE_MAC_ADDR, na.tlla.linkLayerAddress.toByteArray()); + + assertArrayEquals(TEST_GRATUITOUS_NA, na.toByteBuffer().array()); + } + + @Test + public void testGratuitousNa_invalidByteBufferParameters() throws Exception { + assertThrows(NeighborAdvertisement.ParseException.class, + () -> NeighborAdvertisement.parse(TEST_GRATUITOUS_NA, 0)); + } + + @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/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java index 8f42a61..2735fb6 100644 --- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -142,7 +142,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; @@ -261,6 +265,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; @@ -2537,6 +2542,59 @@ 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)); + } + private void setupResourceForMultipleProbes() { // Configure the resource to send multiple probe. when(mResources.getStringArray(R.array.config_captive_portal_https_urls))