Merge "DO NOT MERGE Rename libbpf to libbpf_bcc" into sc-mainline-prod
diff --git a/OWNERS b/OWNERS
index 22b5561..07a775e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,7 +1,4 @@
-codewiz@google.com
-jchalard@google.com
-junyulai@google.com
-lorenzo@google.com
-maze@google.com
-reminv@google.com
-satk@google.com
+set noparent
+file:platform/packages/modules/Connectivity:master:/OWNERS_core_networking
+
+per-file **IpSec* = file:platform/frameworks/base:master:/services/core/java/com/android/server/vcn/OWNERS
\ No newline at end of file
diff --git a/OWNERS_core_networking b/OWNERS_core_networking
new file mode 100644
index 0000000..6847c74
--- /dev/null
+++ b/OWNERS_core_networking
@@ -0,0 +1,19 @@
+chenbruce@google.com
+chiachangwang@google.com
+cken@google.com
+huangaaron@google.com
+jchalard@google.com
+junyulai@google.com
+lifr@google.com
+lorenzo@google.com
+lucaslin@google.com
+markchien@google.com
+martinwu@google.com
+maze@google.com
+nuccachen@google.com
+paulhu@google.com
+reminv@google.com
+satk@google.com
+waynema@google.com
+xiaom@google.com
+yumike@google.com
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 0ac09cc..90312a4 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -12,6 +12,9 @@
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
}
]
},
@@ -29,11 +32,13 @@
],
"mainline-presubmit": [
{
- // TODO: add back the tethering modules when updatable in this branch
- "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex]",
+ "name": "CtsNetTestCasesLatestSdk[CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex]",
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
}
]
},
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index b5054cf..0b54783 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -19,9 +19,13 @@
}
java_defaults {
- name: "TetheringAndroidLibraryDefaults",
+ name: "TetheringApiLevel",
sdk_version: "module_current",
min_sdk_version: "30",
+}
+
+java_defaults {
+ name: "TetheringAndroidLibraryDefaults",
srcs: [
"apishim/**/*.java",
"src/**/*.java",
@@ -30,18 +34,18 @@
":services-tethering-shared-srcs",
],
static_libs: [
- "NetworkStackApiStableShims",
"androidx.annotation_annotation",
"modules-utils-build",
- "netlink-client",
+ "modules-utils-statemachine",
"networkstack-client",
"android.hardware.tetheroffload.config-V1.0-java",
"android.hardware.tetheroffload.control-V1.0-java",
"android.hardware.tetheroffload.control-V1.1-java",
"net-utils-framework-common",
"net-utils-device-common",
+ "net-utils-device-common-bpf",
+ "net-utils-device-common-netlink",
"netd-client",
- "NetworkStackApiCurrentShims",
],
libs: [
"framework-connectivity",
@@ -52,18 +56,40 @@
],
plugins: ["java_api_finder"],
manifest: "AndroidManifestBase.xml",
+ lint: { strict_updatability_linting: true },
}
-// Build tethering static library, used to compile both variants of the tethering.
+// build tethering static library, used to compile both variants of the tethering.
android_library {
name: "TetheringApiCurrentLib",
- defaults: ["TetheringAndroidLibraryDefaults"],
+ defaults: [
+ "TetheringAndroidLibraryDefaults",
+ "TetheringApiLevel"
+ ],
+ static_libs: [
+ "NetworkStackApiCurrentShims",
+ ],
+ apex_available: ["com.android.tethering"],
+ lint: { strict_updatability_linting: true },
+}
+
+android_library {
+ name: "TetheringApiStableLib",
+ defaults: [
+ "TetheringAndroidLibraryDefaults",
+ "TetheringApiLevel"
+ ],
+ static_libs: [
+ "NetworkStackApiStableShims",
+ ],
+ apex_available: ["com.android.tethering"],
+ lint: { strict_updatability_linting: true },
}
// Due to b/143733063, APK can't access a jni lib that is in APEX (but not in the APK).
cc_library {
- name: "libtetherutilsjni",
- sdk_version: "current",
+ name: "libcom_android_networkstack_tethering_util_jni",
+ sdk_version: "30",
apex_available: [
"//apex_available:platform", // Used by InProcessTethering
"com.android.tethering",
@@ -71,7 +97,7 @@
min_sdk_version: "30",
header_libs: [
"bpf_syscall_wrappers",
- "bpf_tethering_headers",
+ "bpf_connectivity_headers",
],
srcs: [
"jni/*.cpp",
@@ -81,6 +107,7 @@
"libnativehelper_compat_libc++",
],
static_libs: [
+ "libnet_utils_device_common_bpfjni",
"libnetjniutils",
],
@@ -108,10 +135,9 @@
// Common defaults for compiling the actual APK.
java_defaults {
name: "TetheringAppDefaults",
- sdk_version: "module_current",
privileged: true,
jni_libs: [
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
],
resource_dirs: [
"res",
@@ -124,25 +150,46 @@
optimize: {
proguard_flags_files: ["proguard.flags"],
},
+ lint: { strict_updatability_linting: true },
}
// Non-updatable tethering running in the system server process for devices not using the module
android_app {
name: "InProcessTethering",
- defaults: ["TetheringAppDefaults"],
+ defaults: ["TetheringAppDefaults", "TetheringApiLevel"],
static_libs: ["TetheringApiCurrentLib"],
certificate: "platform",
manifest: "AndroidManifest_InProcess.xml",
// InProcessTethering is a replacement for Tethering
overrides: ["Tethering"],
apex_available: ["com.android.tethering"],
- min_sdk_version: "30",
+ lint: { strict_updatability_linting: true },
}
-// Updatable tethering packaged as an application
+// Updatable tethering packaged for finalized API
android_app {
name: "Tethering",
- defaults: ["TetheringAppDefaults"],
+ defaults: ["TetheringAppDefaults", "TetheringApiLevel"],
+ static_libs: ["TetheringApiStableLib"],
+ certificate: "networkstack",
+ manifest: "AndroidManifest.xml",
+ use_embedded_native_libs: true,
+ // The permission configuration *must* be included to ensure security of the device
+ required: [
+ "NetworkPermissionConfig",
+ "privapp_whitelist_com.android.networkstack.tethering",
+ ],
+ apex_available: ["com.android.tethering"],
+ lint: { strict_updatability_linting: true },
+}
+
+android_app {
+ name: "TetheringNext",
+ defaults: [
+ "TetheringAppDefaults",
+ "TetheringApiLevel",
+ "ConnectivityNextEnableDefaults",
+ ],
static_libs: ["TetheringApiCurrentLib"],
certificate: "networkstack",
manifest: "AndroidManifest.xml",
@@ -153,7 +200,7 @@
"privapp_whitelist_com.android.networkstack.tethering",
],
apex_available: ["com.android.tethering"],
- min_sdk_version: "30",
+ lint: { strict_updatability_linting: true },
}
sdk {
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index f86a79d..d43cf5b 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -18,8 +18,26 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// Defaults to enable/disable java targets which uses development APIs. "enabled" may have a
+// different value depending on the branch.
+java_defaults {
+ name: "ConnectivityNextEnableDefaults",
+ enabled: false,
+}
+apex_defaults {
+ name: "ConnectivityApexDefaults",
+ // Tethering app to include in the AOSP apex. Branches that disable the "next" targets may use
+ // a stable tethering app instead, but will generally override the AOSP apex to use updatable
+ // package names and keys, so that apex will be unused anyway.
+ apps: ["Tethering"], // Replace to "Tethering" if ConnectivityNextEnableDefaults is false.
+}
+// This is a placeholder comment to avoid merge conflicts
+// as the above target may have different "enabled" values
+// depending on the branch
+
apex {
name: "com.android.tethering",
+ defaults: ["ConnectivityApexDefaults"],
compile_multilib: "both",
updatable: true,
min_sdk_version: "30",
@@ -43,7 +61,6 @@
],
apps: [
"ServiceConnectivityResources",
- "Tethering",
],
prebuilts: ["current_sdkinfo"],
manifest: "manifest.json",
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 611c828..7189933 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -29,9 +29,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.net.module.util.BpfMap;
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.networkstack.tethering.BpfMap;
import com.android.networkstack.tethering.BpfUtils;
import com.android.networkstack.tethering.Tether4Key;
import com.android.networkstack.tethering.Tether4Value;
diff --git a/Tethering/common/TetheringLib/Android.bp b/Tethering/common/TetheringLib/Android.bp
index f652772..c82a993 100644
--- a/Tethering/common/TetheringLib/Android.bp
+++ b/Tethering/common/TetheringLib/Android.bp
@@ -22,7 +22,19 @@
defaults: ["framework-module-defaults"],
impl_library_visibility: [
"//packages/modules/Connectivity/Tethering:__subpackages__",
+
+ // Using for test only
+ "//cts/tests/netlegacy22.api",
+ "//external/sl4a:__subpackages__",
+ "//frameworks/base/packages/Connectivity/tests:__subpackages__",
+ "//frameworks/libs/net/common/testutils",
+ "//frameworks/libs/net/common/tests:__subpackages__",
+ "//frameworks/opt/telephony/tests/telephonytests",
+ "//packages/modules/CaptivePortalLogin/tests",
+ "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
"//packages/modules/Connectivity/tests:__subpackages__",
+ "//packages/modules/NetworkStack/tests:__subpackages__",
+ "//packages/modules/Wifi/service/tests/wifitests",
],
srcs: [":framework-tethering-srcs"],
@@ -41,6 +53,7 @@
apex_available: ["com.android.tethering"],
permitted_packages: ["android.net"],
min_sdk_version: "30",
+ lint: { strict_updatability_linting: true },
}
filegroup {
diff --git a/Tethering/common/TetheringLib/api/module-lib-current.txt b/Tethering/common/TetheringLib/api/module-lib-current.txt
index 0566040..460c216 100644
--- a/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ b/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -27,6 +27,15 @@
method @Deprecated public int untether(@NonNull String);
}
+ public static interface TetheringManager.TetheredInterfaceCallback {
+ method public void onAvailable(@NonNull String);
+ method public void onUnavailable();
+ }
+
+ public static interface TetheringManager.TetheredInterfaceRequest {
+ method public void release();
+ }
+
public static interface TetheringManager.TetheringEventCallback {
method @Deprecated public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
}
diff --git a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index cf094aa..77e78bd 100644
--- a/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -49,4 +49,6 @@
void stopAllTethering(String callerPkg, String callingAttributionTag,
IIntResultListener receiver);
+
+ void setPreferTestNetworks(boolean prefer, IIntResultListener listener);
}
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index edd141d..6f9b33e 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -22,6 +22,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.Bundle;
@@ -37,6 +38,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -265,7 +267,7 @@
public TetheringManager(@NonNull final Context context,
@NonNull Supplier<IBinder> connectorSupplier) {
mContext = context;
- mCallback = new TetheringCallbackInternal();
+ mCallback = new TetheringCallbackInternal(this);
mConnectorSupplier = connectorSupplier;
final String pkgName = mContext.getOpPackageName();
@@ -289,6 +291,23 @@
getConnector(c -> c.registerTetheringEventCallback(mCallback, pkgName));
}
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ final String pkgName = mContext.getOpPackageName();
+ Log.i(TAG, "unregisterTetheringEventCallback:" + pkgName);
+ // 1. It's generally not recommended to perform long operations in finalize, but while
+ // unregisterTetheringEventCallback does an IPC, it's a oneway IPC so should not block.
+ // 2. If the connector is not yet connected, TetheringManager is impossible to finalize
+ // because the connector polling thread strong reference the TetheringManager object. So
+ // it's guaranteed that registerTetheringEventCallback was already called before calling
+ // unregisterTetheringEventCallback in finalize.
+ if (mConnector == null) Log.wtf(TAG, "null connector in finalize!");
+ getConnector(c -> c.unregisterTetheringEventCallback(mCallback, pkgName));
+
+ super.finalize();
+ }
+
private void startPollingForConnector() {
new Thread(() -> {
while (true) {
@@ -415,7 +434,7 @@
}
}
- private void throwIfPermissionFailure(final int errorCode) {
+ private static void throwIfPermissionFailure(final int errorCode) {
switch (errorCode) {
case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION:
throw new SecurityException("No android.permission.TETHER_PRIVILEGED"
@@ -426,21 +445,78 @@
}
}
- private class TetheringCallbackInternal extends ITetheringEventCallback.Stub {
+ /**
+ * A request for a tethered interface.
+ *
+ * There are two reasons why this doesn't implement CLoseable:
+ * 1. To consistency with the existing EthernetManager.TetheredInterfaceRequest, which is
+ * already released.
+ * 2. This is not synchronous, so it's not useful to use try-with-resources.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ @SuppressLint("NotCloseable")
+ public interface TetheredInterfaceRequest {
+ /**
+ * Release the request to tear down tethered interface.
+ */
+ void release();
+ }
+
+ /**
+ * Callback for requestTetheredInterface.
+ *
+ * {@hide}
+ */
+ @SystemApi(client = MODULE_LIBRARIES)
+ public interface TetheredInterfaceCallback {
+ /**
+ * Called when the tethered interface is available.
+ * @param iface The name of the interface.
+ */
+ void onAvailable(@NonNull String iface);
+
+ /**
+ * Called when the tethered interface is now unavailable.
+ */
+ void onUnavailable();
+ }
+
+ private static class TetheringCallbackInternal extends ITetheringEventCallback.Stub {
private volatile int mError = TETHER_ERROR_NO_ERROR;
private final ConditionVariable mWaitForCallback = new ConditionVariable();
+ // This object is never garbage collected because the Tethering code running in
+ // the system server always maintains a reference to it for as long as
+ // mCallback is registered.
+ //
+ // Don't keep a strong reference to TetheringManager because otherwise
+ // TetheringManager cannot be garbage collected, and because TetheringManager
+ // stores the Context that it was created from, this will prevent the calling
+ // Activity from being garbage collected as well.
+ private final WeakReference<TetheringManager> mTetheringMgrRef;
+
+ TetheringCallbackInternal(final TetheringManager tm) {
+ mTetheringMgrRef = new WeakReference<>(tm);
+ }
@Override
public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
- mTetheringConfiguration = parcel.config;
- mTetherStatesParcel = parcel.states;
- mWaitForCallback.open();
+ TetheringManager tetheringMgr = mTetheringMgrRef.get();
+ if (tetheringMgr != null) {
+ tetheringMgr.mTetheringConfiguration = parcel.config;
+ tetheringMgr.mTetherStatesParcel = parcel.states;
+ mWaitForCallback.open();
+ }
}
@Override
public void onCallbackStopped(int errorCode) {
- mError = errorCode;
- mWaitForCallback.open();
+ TetheringManager tetheringMgr = mTetheringMgrRef.get();
+ if (tetheringMgr != null) {
+ mError = errorCode;
+ mWaitForCallback.open();
+ }
}
@Override
@@ -448,12 +524,14 @@
@Override
public void onConfigurationChanged(TetheringConfigurationParcel config) {
- mTetheringConfiguration = config;
+ TetheringManager tetheringMgr = mTetheringMgrRef.get();
+ if (tetheringMgr != null) tetheringMgr.mTetheringConfiguration = config;
}
@Override
public void onTetherStatesChanged(TetherStatesParcel states) {
- mTetherStatesParcel = states;
+ TetheringManager tetheringMgr = mTetheringMgrRef.get();
+ if (tetheringMgr != null) tetheringMgr.mTetherStatesParcel = states;
}
@Override
@@ -1538,4 +1616,25 @@
}
}));
}
+
+ /**
+ * Whether to treat networks that have TRANSPORT_TEST as Tethering upstreams. The effects of
+ * this method apply to any test networks that are already present on the system.
+ *
+ * @throws SecurityException If the caller doesn't have the NETWORK_SETTINGS permission.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void setPreferTestNetworks(final boolean prefer) {
+ Log.i(TAG, "setPreferTestNetworks caller: " + mContext.getOpPackageName());
+
+ final RequestDispatcher dispatcher = new RequestDispatcher();
+ final int ret = dispatcher.waitForResult((connector, listener) -> {
+ try {
+ connector.setPreferTestNetworks(prefer, listener);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ });
+ }
}
diff --git a/Tethering/jni/com_android_networkstack_tethering_BpfMap.cpp b/Tethering/jni/com_android_networkstack_tethering_BpfMap.cpp
deleted file mode 100644
index 7970a23..0000000
--- a/Tethering/jni/com_android_networkstack_tethering_BpfMap.cpp
+++ /dev/null
@@ -1,134 +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.
- */
-
-#include <errno.h>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-
-#include "nativehelper/scoped_primitive_array.h"
-#include "nativehelper/scoped_utf_chars.h"
-
-#define BPF_FD_JUST_USE_INT
-#include "BpfSyscallWrappers.h"
-
-namespace android {
-
-static jint com_android_networkstack_tethering_BpfMap_closeMap(JNIEnv *env, jobject clazz,
- jint fd) {
- int ret = close(fd);
-
- if (ret) jniThrowErrnoException(env, "closeMap", errno);
-
- return ret;
-}
-
-static jint com_android_networkstack_tethering_BpfMap_bpfFdGet(JNIEnv *env, jobject clazz,
- jstring path, jint mode) {
- ScopedUtfChars pathname(env, path);
-
- jint fd = bpf::bpfFdGet(pathname.c_str(), static_cast<unsigned>(mode));
-
- return fd;
-}
-
-static void com_android_networkstack_tethering_BpfMap_writeToMapEntry(JNIEnv *env, jobject clazz,
- jint fd, jbyteArray key, jbyteArray value, jint flags) {
- ScopedByteArrayRO keyRO(env, key);
- ScopedByteArrayRO valueRO(env, value);
-
- int ret = bpf::writeToMapEntry(static_cast<int>(fd), keyRO.get(), valueRO.get(),
- static_cast<int>(flags));
-
- if (ret) jniThrowErrnoException(env, "writeToMapEntry", errno);
-}
-
-static jboolean throwIfNotEnoent(JNIEnv *env, const char* functionName, int ret, int err) {
- if (ret == 0) return true;
-
- if (err != ENOENT) jniThrowErrnoException(env, functionName, err);
- return false;
-}
-
-static jboolean com_android_networkstack_tethering_BpfMap_deleteMapEntry(JNIEnv *env, jobject clazz,
- jint fd, jbyteArray key) {
- ScopedByteArrayRO keyRO(env, key);
-
- // On success, zero is returned. If the element is not found, -1 is returned and errno is set
- // to ENOENT.
- int ret = bpf::deleteMapEntry(static_cast<int>(fd), keyRO.get());
-
- return throwIfNotEnoent(env, "deleteMapEntry", ret, errno);
-}
-
-static jboolean com_android_networkstack_tethering_BpfMap_getNextMapKey(JNIEnv *env, jobject clazz,
- jint fd, jbyteArray key, jbyteArray nextKey) {
- // If key is found, the operation returns zero and sets the next key pointer to the key of the
- // next element. If key is not found, the operation returns zero and sets the next key pointer
- // to the key of the first element. If key is the last element, -1 is returned and errno is
- // set to ENOENT. Other possible errno values are ENOMEM, EFAULT, EPERM, and EINVAL.
- ScopedByteArrayRW nextKeyRW(env, nextKey);
- int ret;
- if (key == nullptr) {
- // Called by getFirstKey. Find the first key in the map.
- ret = bpf::getNextMapKey(static_cast<int>(fd), nullptr, nextKeyRW.get());
- } else {
- ScopedByteArrayRO keyRO(env, key);
- ret = bpf::getNextMapKey(static_cast<int>(fd), keyRO.get(), nextKeyRW.get());
- }
-
- return throwIfNotEnoent(env, "getNextMapKey", ret, errno);
-}
-
-static jboolean com_android_networkstack_tethering_BpfMap_findMapEntry(JNIEnv *env, jobject clazz,
- jint fd, jbyteArray key, jbyteArray value) {
- ScopedByteArrayRO keyRO(env, key);
- ScopedByteArrayRW valueRW(env, value);
-
- // If an element is found, the operation returns zero and stores the element's value into
- // "value". If no element is found, the operation returns -1 and sets errno to ENOENT.
- int ret = bpf::findMapEntry(static_cast<int>(fd), keyRO.get(), valueRW.get());
-
- return throwIfNotEnoent(env, "findMapEntry", ret, errno);
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gMethods[] = {
- /* name, signature, funcPtr */
- { "closeMap", "(I)I",
- (void*) com_android_networkstack_tethering_BpfMap_closeMap },
- { "bpfFdGet", "(Ljava/lang/String;I)I",
- (void*) com_android_networkstack_tethering_BpfMap_bpfFdGet },
- { "writeToMapEntry", "(I[B[BI)V",
- (void*) com_android_networkstack_tethering_BpfMap_writeToMapEntry },
- { "deleteMapEntry", "(I[B)Z",
- (void*) com_android_networkstack_tethering_BpfMap_deleteMapEntry },
- { "getNextMapKey", "(I[B[B)Z",
- (void*) com_android_networkstack_tethering_BpfMap_getNextMapKey },
- { "findMapEntry", "(I[B[B)Z",
- (void*) com_android_networkstack_tethering_BpfMap_findMapEntry },
-
-};
-
-int register_com_android_networkstack_tethering_BpfMap(JNIEnv* env) {
- return jniRegisterNativeMethods(env,
- "com/android/networkstack/tethering/BpfMap",
- gMethods, NELEM(gMethods));
-}
-
-}; // namespace android
diff --git a/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
index 2d679a8..f9e4824 100644
--- a/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
+++ b/Tethering/jni/com_android_networkstack_tethering_BpfUtils.cpp
@@ -26,6 +26,7 @@
#include <net/if.h>
#include <stdio.h>
#include <sys/socket.h>
+#include <sys/utsname.h>
// TODO: use unique_fd.
#define BPF_FD_JUST_USE_INT
@@ -158,6 +159,37 @@
return rv;
}
+// -----------------------------------------------------------------------------
+// TODO - just use BpfUtils.h once that is available in sc-mainline-prod and has kernelVersion()
+//
+// In the mean time copying verbatim from:
+// system/bpf/libbpf_android/include/bpf/BpfUtils.h
+// and
+// system/bpf/libbpf_android/BpfUtils.cpp
+
+#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
+
+static unsigned kernelVersion() {
+ struct utsname buf;
+ int ret = uname(&buf);
+ if (ret) return 0;
+
+ unsigned kver_major;
+ unsigned kver_minor;
+ unsigned kver_sub;
+ char discard;
+ ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub, &discard);
+ // Check the device kernel version
+ if (ret < 3) return 0;
+
+ return KVER(kver_major, kver_minor, kver_sub);
+}
+
+static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
+ return kernelVersion() >= KVER(major, minor, sub);
+}
+// -----------------------------------------------------------------------------
+
static jboolean com_android_networkstack_tethering_BpfUtils_isEthernet(JNIEnv* env, jobject clazz,
jstring iface) {
ScopedUtfChars interface(env, iface);
@@ -170,12 +202,30 @@
return false;
}
+ // Backwards compatibility with pre-GKI kernels that use various custom
+ // ARPHRD_* for their cellular interface
+ switch (rv) {
+ // ARPHRD_PUREIP on at least some Mediatek Android kernels
+ // example: wembley with 4.19 kernel
+ case 520:
+ // in Linux 4.14+ rmnet support was upstreamed and ARHRD_RAWIP became 519,
+ // but it is 530 on at least some Qualcomm Android 4.9 kernels with rmnet
+ // example: Pixel 3 family
+ case 530:
+ // >5.4 kernels are GKI2.0 and thus upstream compatible, however 5.10
+ // shipped with Android S, so (for safety) let's limit ourselves to
+ // >5.10, ie. 5.11+ as a guarantee we're on Android T+ and thus no
+ // longer need this non-upstream compatibility logic
+ static bool is_pre_5_11_kernel = !isAtLeastKernelVersion(5, 11, 0);
+ if (is_pre_5_11_kernel) return false;
+ }
+
switch (rv) {
case ARPHRD_ETHER:
return true;
case ARPHRD_NONE:
- case ARPHRD_RAWIP: // in Linux 4.14+ rmnet support was upstreamed and this is 519
- case 530: // this is ARPHRD_RAWIP on some Android 4.9 kernels with rmnet
+ case ARPHRD_PPP:
+ case ARPHRD_RAWIP:
return false;
default:
jniThrowExceptionFmt(env, "java/io/IOException",
diff --git a/Tethering/jni/android_net_util_TetheringUtils.cpp b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
similarity index 83%
rename from Tethering/jni/android_net_util_TetheringUtils.cpp
rename to Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
index 2e76501..291bf54 100644
--- a/Tethering/jni/android_net_util_TetheringUtils.cpp
+++ b/Tethering/jni/com_android_networkstack_tethering_util_TetheringUtils.cpp
@@ -38,7 +38,8 @@
jniThrowExceptionFmt(env, "java/net/SocketException", "%s: %s", msg, strerror(error));
}
-static void android_net_util_setupIcmpFilter(JNIEnv *env, jobject javaFd, uint32_t type) {
+static void com_android_networkstack_tethering_util_setupIcmpFilter(JNIEnv *env, jobject javaFd,
+ uint32_t type) {
sock_filter filter_code[] = {
// Check header is ICMPv6.
BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeaderOffset),
@@ -64,19 +65,18 @@
}
}
-static void android_net_util_setupNaSocket(JNIEnv *env, jobject clazz, jobject javaFd)
-{
- android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT);
+static void com_android_networkstack_tethering_util_setupNaSocket(JNIEnv *env, jobject clazz,
+ jobject javaFd) {
+ com_android_networkstack_tethering_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_ADVERT);
}
-static void android_net_util_setupNsSocket(JNIEnv *env, jobject clazz, jobject javaFd)
-{
- android_net_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT);
+static void com_android_networkstack_tethering_util_setupNsSocket(JNIEnv *env, jobject clazz,
+ jobject javaFd) {
+ com_android_networkstack_tethering_util_setupIcmpFilter(env, javaFd, ND_NEIGHBOR_SOLICIT);
}
-static void android_net_util_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
- jint ifIndex)
-{
+static void com_android_networkstack_tethering_util_setupRaSocket(JNIEnv *env, jobject clazz,
+ jobject javaFd, jint ifIndex) {
static const int kLinkLocalHopLimit = 255;
int fd = netjniutils::GetNativeFileDescriptor(env, javaFd);
@@ -164,16 +164,16 @@
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "setupNaSocket", "(Ljava/io/FileDescriptor;)V",
- (void*) android_net_util_setupNaSocket },
+ (void*) com_android_networkstack_tethering_util_setupNaSocket },
{ "setupNsSocket", "(Ljava/io/FileDescriptor;)V",
- (void*) android_net_util_setupNsSocket },
+ (void*) com_android_networkstack_tethering_util_setupNsSocket },
{ "setupRaSocket", "(Ljava/io/FileDescriptor;I)V",
- (void*) android_net_util_setupRaSocket },
+ (void*) com_android_networkstack_tethering_util_setupRaSocket },
};
-int register_android_net_util_TetheringUtils(JNIEnv* env) {
+int register_com_android_networkstack_tethering_util_TetheringUtils(JNIEnv* env) {
return jniRegisterNativeMethods(env,
- "android/net/util/TetheringUtils",
+ "com/android/networkstack/tethering/util/TetheringUtils",
gMethods, NELEM(gMethods));
}
diff --git a/Tethering/jni/onload.cpp b/Tethering/jni/onload.cpp
index 02e602d..72895f1 100644
--- a/Tethering/jni/onload.cpp
+++ b/Tethering/jni/onload.cpp
@@ -22,10 +22,10 @@
namespace android {
-int register_android_net_util_TetheringUtils(JNIEnv* env);
-int register_com_android_networkstack_tethering_BpfMap(JNIEnv* env);
+int register_com_android_net_module_util_BpfMap(JNIEnv* env, char const* class_name);
int register_com_android_networkstack_tethering_BpfCoordinator(JNIEnv* env);
int register_com_android_networkstack_tethering_BpfUtils(JNIEnv* env);
+int register_com_android_networkstack_tethering_util_TetheringUtils(JNIEnv* env);
extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
JNIEnv *env;
@@ -34,9 +34,10 @@
return JNI_ERR;
}
- if (register_android_net_util_TetheringUtils(env) < 0) return JNI_ERR;
+ if (register_com_android_networkstack_tethering_util_TetheringUtils(env) < 0) return JNI_ERR;
- if (register_com_android_networkstack_tethering_BpfMap(env) < 0) return JNI_ERR;
+ if (register_com_android_net_module_util_BpfMap(env,
+ "com/android/networkstack/tethering/util/BpfMap") < 0) return JNI_ERR;
if (register_com_android_networkstack_tethering_BpfCoordinator(env) < 0) return JNI_ERR;
diff --git a/Tethering/proguard.flags b/Tethering/proguard.flags
index 75ecdce..f62df7f 100644
--- a/Tethering/proguard.flags
+++ b/Tethering/proguard.flags
@@ -4,7 +4,7 @@
static final int EVENT_*;
}
--keep class com.android.networkstack.tethering.BpfMap {
+-keep class com.android.networkstack.tethering.util.BpfMap {
native <methods>;
}
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 859f23a..b4228da 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -21,12 +21,12 @@
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
-import static android.net.util.PrefixUtils.asIpPrefix;
-import static android.net.util.TetheringMessageBase.BASE_IPSERVER;
import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
import static com.android.networkstack.tethering.UpstreamNetworkState.isVcnInterface;
+import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
+import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_IPSERVER;
import android.net.INetd;
import android.net.INetworkStackStatusCallback;
@@ -46,13 +46,8 @@
import android.net.dhcp.IDhcpServer;
import android.net.ip.IpNeighborMonitor.NeighborEvent;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
-import android.net.shared.NetdUtils;
-import android.net.shared.RouteUtils;
import android.net.util.InterfaceParams;
-import android.net.util.InterfaceSet;
-import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -67,10 +62,14 @@
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.NetdUtils;
import com.android.networkstack.tethering.BpfCoordinator;
import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
+import com.android.networkstack.tethering.util.InterfaceSet;
+import com.android.networkstack.tethering.util.PrefixUtils;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -676,9 +675,7 @@
return false;
}
- // TODO: use ShimUtils instead of explicitly checking the version here.
- if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R || "S".equals(Build.VERSION.CODENAME)
- || "T".equals(Build.VERSION.CODENAME)) {
+ if (SdkLevel.isAtLeastS()) {
// DAD Proxy starts forwarding packets after IPv6 upstream is present.
mDadProxy = mDeps.getDadProxy(getHandler(), mInterfaceParams);
}
@@ -769,7 +766,7 @@
}
private void removeRoutesFromLocalNetwork(@NonNull final List<RouteInfo> toBeRemoved) {
- final int removalFailures = RouteUtils.removeRoutesFromLocalNetwork(
+ final int removalFailures = NetdUtils.removeRoutesFromLocalNetwork(
mNetd, toBeRemoved);
if (removalFailures > 0) {
mLog.e(String.format("Failed to remove %d IPv6 routes from local table.",
@@ -787,7 +784,7 @@
try {
// Add routes from local network. Note that adding routes that
// already exist does not cause an error (EEXIST is silently ignored).
- RouteUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
+ NetdUtils.addRoutesToLocalNetwork(mNetd, mIfaceName, toBeAdded);
} catch (IllegalStateException e) {
mLog.e("Failed to add IPv4/v6 routes to local table: " + e);
return;
diff --git a/Tethering/src/android/net/ip/NeighborPacketForwarder.java b/Tethering/src/android/net/ip/NeighborPacketForwarder.java
index 084743d..27e59a1 100644
--- a/Tethering/src/android/net/ip/NeighborPacketForwarder.java
+++ b/Tethering/src/android/net/ip/NeighborPacketForwarder.java
@@ -26,13 +26,13 @@
import android.net.util.InterfaceParams;
import android.net.util.SocketUtils;
-import android.net.util.TetheringUtils;
import android.os.Handler;
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
import com.android.net.module.util.PacketReader;
+import com.android.networkstack.tethering.util.TetheringUtils;
import java.io.FileDescriptor;
import java.io.IOException;
diff --git a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
index 543a5c7..74f9369 100644
--- a/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
+++ b/Tethering/src/android/net/ip/RouterAdvertisementDaemon.java
@@ -17,7 +17,6 @@
package android.net.ip;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
-import static android.net.util.TetheringUtils.getAllNodesForScopeId;
import static android.system.OsConstants.AF_INET6;
import static android.system.OsConstants.IPPROTO_ICMPV6;
import static android.system.OsConstants.SOCK_RAW;
@@ -32,6 +31,7 @@
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 com.android.net.module.util.NetworkStackConstants.TAG_SYSTEM_NEIGHBOR;
+import static com.android.networkstack.tethering.util.TetheringUtils.getAllNodesForScopeId;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -39,7 +39,6 @@
import android.net.TrafficStats;
import android.net.util.InterfaceParams;
import android.net.util.SocketUtils;
-import android.net.util.TetheringUtils;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructTimeval;
@@ -52,6 +51,7 @@
import com.android.net.module.util.structs.PrefixInformationOption;
import com.android.net.module.util.structs.RaHeader;
import com.android.net.module.util.structs.RdnssOption;
+import com.android.networkstack.tethering.util.TetheringUtils;
import java.io.FileDescriptor;
import java.io.IOException;
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 01be97a..8c8a2fd 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -32,6 +32,7 @@
import static com.android.networkstack.tethering.BpfUtils.UPSTREAM;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
import static com.android.networkstack.tethering.UpstreamNetworkState.isVcnInterface;
+import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
import android.app.usage.NetworkStatsManager;
import android.net.INetd;
@@ -42,13 +43,9 @@
import android.net.ip.ConntrackMonitor;
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
import android.net.ip.IpServer;
-import android.net.netlink.ConntrackMessage;
-import android.net.netlink.NetlinkConstants;
-import android.net.netlink.NetlinkSocket;
import android.net.netstats.provider.NetworkStatsProvider;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
-import android.net.util.TetheringUtils.ForwardedStats;
import android.os.Handler;
import android.os.SystemClock;
import android.system.ErrnoException;
@@ -64,9 +61,15 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.BpfMap;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.Struct;
+import com.android.net.module.util.netlink.ConntrackMessage;
+import com.android.net.module.util.netlink.NetlinkConstants;
+import com.android.net.module.util.netlink.NetlinkSocket;
import com.android.networkstack.tethering.apishim.common.BpfCoordinatorShim;
+import com.android.networkstack.tethering.util.TetheringUtils.ForwardedStats;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -97,7 +100,7 @@
// TetherService, but for tests it needs to be either loaded here or loaded by every test.
// TODO: is there a better way?
static {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
}
private static final String TAG = BpfCoordinator.class.getSimpleName();
@@ -131,6 +134,12 @@
@VisibleForTesting
static final int NF_CONNTRACK_UDP_TIMEOUT_STREAM = 180;
+ // List of TCP port numbers which aren't offloaded because the packets require the netfilter
+ // conntrack helper. See also TetherController::setForwardRules in netd.
+ @VisibleForTesting
+ static final short [] NON_OFFLOADED_UPSTREAM_IPV4_TCP_PORTS = new short [] {
+ 21 /* ftp */, 1723 /* pptp */};
+
@VisibleForTesting
enum StatsType {
STATS_PER_IFACE,
@@ -1556,7 +1565,15 @@
0 /* lastUsed, filled by bpf prog only */);
}
+ private boolean allowOffload(ConntrackEvent e) {
+ if (e.tupleOrig.protoNum != OsConstants.IPPROTO_TCP) return true;
+ return !CollectionUtils.contains(
+ NON_OFFLOADED_UPSTREAM_IPV4_TCP_PORTS, e.tupleOrig.dstPort);
+ }
+
public void accept(ConntrackEvent e) {
+ if (!allowOffload(e)) return;
+
final ClientInfo tetherClient = getClientInfo(e.tupleOrig.srcIp);
if (tetherClient == null) return;
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfMap.java b/Tethering/src/com/android/networkstack/tethering/BpfMap.java
deleted file mode 100644
index 1363dc5..0000000
--- a/Tethering/src/com/android/networkstack/tethering/BpfMap.java
+++ /dev/null
@@ -1,288 +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.tethering;
-
-import static android.system.OsConstants.EEXIST;
-import static android.system.OsConstants.ENOENT;
-
-import android.system.ErrnoException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.net.module.util.Struct;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.NoSuchElementException;
-import java.util.Objects;
-import java.util.function.BiConsumer;
-
-/**
- * BpfMap is a key -> value mapping structure that is designed to maintained the bpf map entries.
- * This is a wrapper class of in-kernel data structure. The in-kernel data can be read/written by
- * passing syscalls with map file descriptor.
- *
- * @param <K> the key of the map.
- * @param <V> the value of the map.
- */
-public class BpfMap<K extends Struct, V extends Struct> implements AutoCloseable {
- static {
- System.loadLibrary("tetherutilsjni");
- }
-
- // Following definitions from kernel include/uapi/linux/bpf.h
- public static final int BPF_F_RDWR = 0;
- public static final int BPF_F_RDONLY = 1 << 3;
- public static final int BPF_F_WRONLY = 1 << 4;
-
- public static final int BPF_MAP_TYPE_HASH = 1;
-
- private static final int BPF_F_NO_PREALLOC = 1;
-
- private static final int BPF_ANY = 0;
- private static final int BPF_NOEXIST = 1;
- private static final int BPF_EXIST = 2;
-
- private final int mMapFd;
- private final Class<K> mKeyClass;
- private final Class<V> mValueClass;
- private final int mKeySize;
- private final int mValueSize;
-
- /**
- * Create a BpfMap map wrapper with "path" of filesystem.
- *
- * @param flag the access mode, one of BPF_F_RDWR, BPF_F_RDONLY, or BPF_F_WRONLY.
- * @throws ErrnoException if the BPF map associated with {@code path} cannot be retrieved.
- * @throws NullPointerException if {@code path} is null.
- */
- public BpfMap(@NonNull final String path, final int flag, final Class<K> key,
- final Class<V> value) throws ErrnoException, NullPointerException {
- mMapFd = bpfFdGet(path, flag);
-
- mKeyClass = key;
- mValueClass = value;
- mKeySize = Struct.getSize(key);
- mValueSize = Struct.getSize(value);
- }
-
- /**
- * Constructor for testing only.
- * The derived class implements an internal mocked map. It need to implement all functions
- * which are related with the native BPF map because the BPF map handler is not initialized.
- * See BpfCoordinatorTest#TestBpfMap.
- */
- @VisibleForTesting
- protected BpfMap(final Class<K> key, final Class<V> value) {
- mMapFd = -1;
- mKeyClass = key;
- mValueClass = value;
- mKeySize = Struct.getSize(key);
- mValueSize = Struct.getSize(value);
- }
-
- /**
- * Update an existing or create a new key -> value entry in an eBbpf map.
- * (use insertOrReplaceEntry() if you need to know whether insert or replace happened)
- */
- public void updateEntry(K key, V value) throws ErrnoException {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_ANY);
- }
-
- /**
- * If the key does not exist in the map, insert key -> value entry into eBpf map.
- * Otherwise IllegalStateException will be thrown.
- */
- public void insertEntry(K key, V value)
- throws ErrnoException, IllegalStateException {
- try {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_NOEXIST);
- } catch (ErrnoException e) {
- if (e.errno == EEXIST) throw new IllegalStateException(key + " already exists");
-
- throw e;
- }
- }
-
- /**
- * If the key already exists in the map, replace its value. Otherwise NoSuchElementException
- * will be thrown.
- */
- public void replaceEntry(K key, V value)
- throws ErrnoException, NoSuchElementException {
- try {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_EXIST);
- } catch (ErrnoException e) {
- if (e.errno == ENOENT) throw new NoSuchElementException(key + " not found");
-
- throw e;
- }
- }
-
- /**
- * Update an existing or create a new key -> value entry in an eBbpf map.
- * Returns true if inserted, false if replaced.
- * (use updateEntry() if you don't care whether insert or replace happened)
- * Note: see inline comment below if running concurrently with delete operations.
- */
- public boolean insertOrReplaceEntry(K key, V value)
- throws ErrnoException {
- try {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_NOEXIST);
- return true; /* insert succeeded */
- } catch (ErrnoException e) {
- if (e.errno != EEXIST) throw e;
- }
- try {
- writeToMapEntry(mMapFd, key.writeToBytes(), value.writeToBytes(), BPF_EXIST);
- return false; /* replace succeeded */
- } catch (ErrnoException e) {
- if (e.errno != ENOENT) throw e;
- }
- /* If we reach here somebody deleted after our insert attempt and before our replace:
- * this implies a race happened. The kernel bpf delete interface only takes a key,
- * and not the value, so we can safely pretend the replace actually succeeded and
- * was immediately followed by the other thread's delete, since the delete cannot
- * observe the potential change to the value.
- */
- return false; /* pretend replace succeeded */
- }
-
- /** Remove existing key from eBpf map. Return false if map was not modified. */
- public boolean deleteEntry(K key) throws ErrnoException {
- return deleteMapEntry(mMapFd, key.writeToBytes());
- }
-
- /** Returns {@code true} if this map contains no elements. */
- public boolean isEmpty() throws ErrnoException {
- return getFirstKey() == null;
- }
-
- private K getNextKeyInternal(@Nullable K key) throws ErrnoException {
- final byte[] rawKey = getNextRawKey(
- key == null ? null : key.writeToBytes());
- if (rawKey == null) return null;
-
- final ByteBuffer buffer = ByteBuffer.wrap(rawKey);
- buffer.order(ByteOrder.nativeOrder());
- return Struct.parse(mKeyClass, buffer);
- }
-
- /**
- * Get the next key of the passed-in key. If the passed-in key is not found, return the first
- * key. If the passed-in key is the last one, return null.
- *
- * TODO: consider allowing null passed-in key.
- */
- public K getNextKey(@NonNull K key) throws ErrnoException {
- Objects.requireNonNull(key);
- return getNextKeyInternal(key);
- }
-
- private byte[] getNextRawKey(@Nullable final byte[] key) throws ErrnoException {
- byte[] nextKey = new byte[mKeySize];
- if (getNextMapKey(mMapFd, key, nextKey)) return nextKey;
-
- return null;
- }
-
- /** Get the first key of eBpf map. */
- public K getFirstKey() throws ErrnoException {
- return getNextKeyInternal(null);
- }
-
- /** Check whether a key exists in the map. */
- public boolean containsKey(@NonNull K key) throws ErrnoException {
- Objects.requireNonNull(key);
-
- final byte[] rawValue = getRawValue(key.writeToBytes());
- return rawValue != null;
- }
-
- /** Retrieve a value from the map. Return null if there is no such key. */
- public V getValue(@NonNull K key) throws ErrnoException {
- Objects.requireNonNull(key);
- final byte[] rawValue = getRawValue(key.writeToBytes());
-
- if (rawValue == null) return null;
-
- final ByteBuffer buffer = ByteBuffer.wrap(rawValue);
- buffer.order(ByteOrder.nativeOrder());
- return Struct.parse(mValueClass, buffer);
- }
-
- private byte[] getRawValue(final byte[] key) throws ErrnoException {
- byte[] value = new byte[mValueSize];
- if (findMapEntry(mMapFd, key, value)) return value;
-
- return null;
- }
-
- /**
- * Iterate through the map and handle each key -> value retrieved base on the given BiConsumer.
- * The given BiConsumer may to delete the passed-in entry, but is not allowed to perform any
- * other structural modifications to the map, such as adding entries or deleting other entries.
- * Otherwise, iteration will result in undefined behaviour.
- */
- public void forEach(BiConsumer<K, V> action) throws ErrnoException {
- @Nullable K nextKey = getFirstKey();
-
- while (nextKey != null) {
- @NonNull final K curKey = nextKey;
- @NonNull final V value = getValue(curKey);
-
- nextKey = getNextKey(curKey);
- action.accept(curKey, value);
- }
- }
-
- @Override
- public void close() throws ErrnoException {
- closeMap(mMapFd);
- }
-
- /**
- * Clears the map. The map may already be empty.
- *
- * @throws ErrnoException if the map is already closed, if an error occurred during iteration,
- * or if a non-ENOENT error occurred when deleting a key.
- */
- public void clear() throws ErrnoException {
- K key = getFirstKey();
- while (key != null) {
- deleteEntry(key); // ignores ENOENT.
- key = getFirstKey();
- }
- }
-
- private static native int closeMap(int fd) throws ErrnoException;
-
- private native int bpfFdGet(String path, int mode) throws ErrnoException, NullPointerException;
-
- private native void writeToMapEntry(int fd, byte[] key, byte[] value, int flags)
- throws ErrnoException;
-
- private native boolean deleteMapEntry(int fd, byte[] key) throws ErrnoException;
-
- // If key is found, the operation returns true and the nextKey would reference to the next
- // element. If key is not found, the operation returns true and the nextKey would reference to
- // the first element. If key is the last element, false is returned.
- private native boolean getNextMapKey(int fd, byte[] key, byte[] nextKey) throws ErrnoException;
-
- private native boolean findMapEntry(int fd, byte[] key, byte[] value) throws ErrnoException;
-}
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
index 0b44249..4f095cf 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfUtils.java
@@ -18,6 +18,8 @@
import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6;
+import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
+
import android.net.util.InterfaceParams;
import androidx.annotation.NonNull;
@@ -31,7 +33,7 @@
*/
public class BpfUtils {
static {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
}
// For better code clarity when used for 'bool ingress' parameter.
diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 60fcfd0..4ca36df 100644
--- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -70,7 +70,8 @@
@VisibleForTesting
protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
- private static final String ACTION_PROVISIONING_ALARM =
+ @VisibleForTesting
+ protected static final String ACTION_PROVISIONING_ALARM =
"com.android.networkstack.tethering.PROVISIONING_RECHECK_ALARM";
private final ComponentName mSilentProvisioningService;
@@ -410,20 +411,23 @@
return intent;
}
+ @VisibleForTesting
+ PendingIntent createRecheckAlarmIntent() {
+ final Intent intent = new Intent(ACTION_PROVISIONING_ALARM);
+ return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+ }
+
// Not needed to check if this don't run on the handler thread because it's private.
- private void scheduleProvisioningRechecks(final TetheringConfiguration config) {
+ private void scheduleProvisioningRecheck(final TetheringConfiguration config) {
if (mProvisioningRecheckAlarm == null) {
final int period = config.provisioningCheckPeriod;
if (period <= 0) return;
- Intent intent = new Intent(ACTION_PROVISIONING_ALARM);
- mProvisioningRecheckAlarm = PendingIntent.getBroadcast(mContext, 0, intent,
- PendingIntent.FLAG_IMMUTABLE);
+ mProvisioningRecheckAlarm = createRecheckAlarmIntent();
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(
Context.ALARM_SERVICE);
- long periodMs = period * MS_PER_HOUR;
- long firstAlarmTime = SystemClock.elapsedRealtime() + periodMs;
- alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, firstAlarmTime, periodMs,
+ long triggerAtMillis = SystemClock.elapsedRealtime() + (period * MS_PER_HOUR);
+ alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtMillis,
mProvisioningRecheckAlarm);
}
}
@@ -437,6 +441,11 @@
}
}
+ private void rescheduleProvisioningRecheck(final TetheringConfiguration config) {
+ cancelTetherProvisioningRechecks();
+ scheduleProvisioningRecheck(config);
+ }
+
private void evaluateCellularPermission(final TetheringConfiguration config) {
final boolean permitted = isCellularUpstreamPermitted(config);
@@ -452,7 +461,7 @@
// Only schedule periodic re-check when tether is provisioned
// and the result is ok.
if (permitted && mCurrentEntitlementResults.size() > 0) {
- scheduleProvisioningRechecks(config);
+ scheduleProvisioningRecheck(config);
} else {
cancelTetherProvisioningRechecks();
}
@@ -493,6 +502,7 @@
if (ACTION_PROVISIONING_ALARM.equals(intent.getAction())) {
mLog.log("Received provisioning alarm");
final TetheringConfiguration config = mFetcher.fetchTetheringConfiguration();
+ rescheduleProvisioningRecheck(config);
reevaluateSimCardProvisioning(config);
}
}
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
index beb1821..d60c21d 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
@@ -42,9 +42,6 @@
import android.net.NetworkStats;
import android.net.NetworkStats.Entry;
import android.net.RouteInfo;
-import android.net.netlink.ConntrackMessage;
-import android.net.netlink.NetlinkConstants;
-import android.net.netlink.NetlinkSocket;
import android.net.netstats.provider.NetworkStatsProvider;
import android.net.util.SharedLog;
import android.os.Handler;
@@ -56,6 +53,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.net.module.util.netlink.ConntrackMessage;
+import com.android.net.module.util.netlink.NetlinkConstants;
+import com.android.net.module.util.netlink.NetlinkSocket;
import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats;
import java.net.Inet4Address;
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index e3ac660..9da66d8 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -16,9 +16,9 @@
package com.android.networkstack.tethering;
-import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
-import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-import static android.net.util.TetheringUtils.uint16;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static com.android.networkstack.tethering.util.TetheringUtils.uint16;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -28,9 +28,6 @@
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
-import android.net.netlink.NetlinkSocket;
-import android.net.netlink.StructNfGenMsg;
-import android.net.netlink.StructNlMsgHdr;
import android.net.util.SharedLog;
import android.net.util.SocketUtils;
import android.os.Handler;
@@ -43,6 +40,9 @@
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.netlink.NetlinkSocket;
+import com.android.net.module.util.netlink.StructNfGenMsg;
+import com.android.net.module.util.netlink.StructNlMsgHdr;
import java.io.FileDescriptor;
import java.io.IOException;
diff --git a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
index 4f616cd..c1e7127 100644
--- a/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java
@@ -18,11 +18,11 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.TetheringManager.TETHERING_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.util.PrefixUtils.asIpPrefix;
import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
import static com.android.net.module.util.Inet4AddressUtils.prefixLengthToV4NetmaskIntHTH;
+import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
import static java.util.Arrays.asList;
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 08170f9..55c24d3 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -52,7 +52,6 @@
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STARTED;
import static android.net.TetheringManager.TETHER_HARDWARE_OFFLOAD_STOPPED;
import static android.net.TetheringManager.toIfaces;
-import static android.net.util.TetheringMessageBase.BASE_MAIN_SM;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
@@ -67,6 +66,7 @@
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
import static com.android.networkstack.tethering.TetheringNotificationUpdater.DOWNSTREAM_NONE;
import static com.android.networkstack.tethering.UpstreamNetworkMonitor.isCellular;
+import static com.android.networkstack.tethering.util.TetheringMessageBase.BASE_MAIN_SM;
import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
@@ -100,11 +100,7 @@
import android.net.TetheringRequestParcel;
import android.net.ip.IpServer;
import android.net.shared.NetdUtils;
-import android.net.util.InterfaceSet;
-import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
-import android.net.util.TetheringUtils;
-import android.net.util.VersionedBroadcastListener;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.WifiP2pGroup;
@@ -127,6 +123,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import androidx.annotation.NonNull;
@@ -138,6 +135,10 @@
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.networkstack.tethering.util.InterfaceSet;
+import com.android.networkstack.tethering.util.PrefixUtils;
+import com.android.networkstack.tethering.util.TetheringUtils;
+import com.android.networkstack.tethering.util.VersionedBroadcastListener;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -267,6 +268,9 @@
private String mConfiguredEthernetIface;
private EthernetCallback mEthernetCallback;
private SettingsObserver mSettingsObserver;
+ private BluetoothPan mBluetoothPan;
+ private PanServiceListener mBluetoothPanListener;
+ private ArrayList<Pair<Boolean, IIntResultListener>> mPendingPanRequests;
public Tethering(TetheringDependencies deps) {
mLog.mark("Tethering.constructed");
@@ -276,6 +280,11 @@
mLooper = mDeps.getTetheringLooper();
mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
+ // This is intended to ensrure that if something calls startTethering(bluetooth) just after
+ // bluetooth is enabled. Before onServiceConnected is called, store the calls into this
+ // list and handle them as soon as onServiceConnected is called.
+ mPendingPanRequests = new ArrayList<>();
+
mTetherStates = new ArrayMap<>();
mConnectedClientsTracker = new ConnectedClientsTracker();
@@ -701,35 +710,82 @@
return;
}
- adapter.getProfileProxy(mContext, new ServiceListener() {
- @Override
- public void onServiceDisconnected(int profile) { }
+ if (mBluetoothPanListener != null && mBluetoothPanListener.isConnected()) {
+ // The PAN service is connected. Enable or disable bluetooth tethering.
+ // When bluetooth tethering is enabled, any time a PAN client pairs with this
+ // host, bluetooth will bring up a bt-pan interface and notify tethering to
+ // enable IP serving.
+ setBluetoothTetheringSettings(mBluetoothPan, enable, listener);
+ return;
+ }
- @Override
- public void onServiceConnected(int profile, BluetoothProfile proxy) {
- // Clear identify is fine because caller already pass tethering permission at
- // ConnectivityService#startTethering()(or stopTethering) before the control comes
- // here. Bluetooth will check tethering permission again that there is
- // Context#getOpPackageName() under BluetoothPan#setBluetoothTethering() to get
- // caller's package name for permission check.
- // Calling BluetoothPan#setBluetoothTethering() here means the package name always
- // be system server. If calling identity is not cleared, that package's uid might
- // not match calling uid and end up in permission denied.
- final long identityToken = Binder.clearCallingIdentity();
- try {
- ((BluetoothPan) proxy).setBluetoothTethering(enable);
- } finally {
- Binder.restoreCallingIdentity(identityToken);
+ // The reference of IIntResultListener should only exist when application want to start
+ // tethering but tethering is not bound to pan service yet. Even if the calling process
+ // dies, the referenice of IIntResultListener would still keep in mPendingPanRequests. Once
+ // tethering bound to pan service (onServiceConnected) or bluetooth just crash
+ // (onServiceDisconnected), all the references from mPendingPanRequests would be cleared.
+ mPendingPanRequests.add(new Pair(enable, listener));
+
+ // Bluetooth tethering is not a popular feature. To avoid bind to bluetooth pan service all
+ // the time but user never use bluetooth tethering. mBluetoothPanListener is created first
+ // time someone calls a bluetooth tethering method (even if it's just to disable tethering
+ // when it's already disabled) and never unset after that.
+ if (mBluetoothPanListener == null) {
+ mBluetoothPanListener = new PanServiceListener();
+ adapter.getProfileProxy(mContext, mBluetoothPanListener, BluetoothProfile.PAN);
+ }
+ }
+
+ private class PanServiceListener implements ServiceListener {
+ private boolean mIsConnected = false;
+
+ @Override
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ // Posting this to handling onServiceConnected in tethering handler thread may have
+ // race condition that bluetooth service may disconnected when tethering thread
+ // actaully handle onServiceconnected. If this race happen, calling
+ // BluetoothPan#setBluetoothTethering would silently fail. It is fine because pan
+ // service is unreachable and both bluetooth and bluetooth tethering settings are off.
+ mHandler.post(() -> {
+ mBluetoothPan = (BluetoothPan) proxy;
+ mIsConnected = true;
+
+ for (Pair<Boolean, IIntResultListener> request : mPendingPanRequests) {
+ setBluetoothTetheringSettings(mBluetoothPan, request.first, request.second);
}
- // TODO: Enabling bluetooth tethering can fail asynchronously here.
- // We should figure out a way to bubble up that failure instead of sending success.
- final int result = (((BluetoothPan) proxy).isTetheringOn() == enable)
- ? TETHER_ERROR_NO_ERROR
- : TETHER_ERROR_INTERNAL_ERROR;
- sendTetherResult(listener, result, TETHERING_BLUETOOTH);
- adapter.closeProfileProxy(BluetoothProfile.PAN, proxy);
- }
- }, BluetoothProfile.PAN);
+ mPendingPanRequests.clear();
+ });
+ }
+
+ @Override
+ public void onServiceDisconnected(int profile) {
+ mHandler.post(() -> {
+ // onServiceDisconnected means Bluetooth is off (or crashed) and is not
+ // reachable before next onServiceConnected.
+ mIsConnected = false;
+
+ for (Pair<Boolean, IIntResultListener> request : mPendingPanRequests) {
+ sendTetherResult(request.second, TETHER_ERROR_SERVICE_UNAVAIL,
+ TETHERING_BLUETOOTH);
+ }
+ mPendingPanRequests.clear();
+ });
+ }
+
+ public boolean isConnected() {
+ return mIsConnected;
+ }
+ }
+
+ private void setBluetoothTetheringSettings(@NonNull final BluetoothPan bluetoothPan,
+ final boolean enable, final IIntResultListener listener) {
+ bluetoothPan.setBluetoothTethering(enable);
+
+ // Enabling bluetooth tethering settings can silently fail. Send internal error if the
+ // result is not expected.
+ final int result = bluetoothPan.isTetheringOn() == enable
+ ? TETHER_ERROR_NO_ERROR : TETHER_ERROR_INTERNAL_ERROR;
+ sendTetherResult(listener, result, TETHERING_BLUETOOTH);
}
private int setEthernetTethering(final boolean enable) {
@@ -1140,9 +1196,7 @@
final WifiP2pGroup group =
(WifiP2pGroup) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP);
- if (VDBG) {
- Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group);
- }
+ mLog.i("WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group);
// if no group is formed, bring it down if needed.
if (p2pInfo == null || !p2pInfo.groupFormed) {
@@ -2601,4 +2655,13 @@
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}
+
+ void setPreferTestNetworks(final boolean prefer, IIntResultListener listener) {
+ mHandler.post(() -> {
+ mUpstreamNetworkMonitor.setPreferTestNetworks(prefer);
+ try {
+ listener.onResult(TETHER_ERROR_NO_ERROR);
+ } catch (RemoteException e) { }
+ });
+ }
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index b6240c4..dd0d567 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -23,6 +23,8 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
+
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
@@ -60,8 +62,6 @@
private static final String[] EMPTY_STRING_ARRAY = new String[0];
- private static final String TETHERING_MODULE_NAME = "com.android.tethering";
-
// Default ranges used for the legacy DHCP server.
// USB is 192.168.42.1 and 255.255.255.0
// Wifi is 192.168.43.1 and 255.255.255.0
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java b/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
index ff38f71..3974fa5 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringInterfaceUtils.java
@@ -16,13 +16,17 @@
package com.android.networkstack.tethering;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.net.RouteInfo;
-import android.net.util.InterfaceSet;
import com.android.net.module.util.NetUtils;
+import com.android.networkstack.tethering.util.InterfaceSet;
import java.net.InetAddress;
import java.net.UnknownHostException;
@@ -78,13 +82,17 @@
// Minimal amount of IPv6 provisioning:
&& ns.linkProperties.hasGlobalIpv6Address()
// Temporary approximation of "dedicated prefix":
- && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+ && allowIpv6Tethering(ns.networkCapabilities);
return canTether
? getInterfaceForDestination(ns.linkProperties, IN6ADDR_ANY)
: null;
}
+ private static boolean allowIpv6Tethering(@NonNull final NetworkCapabilities nc) {
+ return nc.hasTransport(TRANSPORT_CELLULAR) || nc.hasTransport(TRANSPORT_TEST);
+ }
+
private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) {
final RouteInfo ri = (lp != null)
? NetUtils.selectBestRoute(lp.getAllRoutes(), dst)
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index 722ec8f..9fb61fe 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -17,6 +17,7 @@
package com.android.networkstack.tethering;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -204,6 +205,18 @@
}
@Override
+ public void setPreferTestNetworks(boolean prefer, IIntResultListener listener) {
+ if (!checkCallingOrSelfPermission(NETWORK_SETTINGS)) {
+ try {
+ listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ } catch (RemoteException e) { }
+ return;
+ }
+
+ mTethering.setPreferTestNetworks(prefer, listener);
+ }
+
+ @Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
@Nullable String[] args) {
mTethering.dump(fd, writer, args);
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 5584db2..9d19335 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -36,7 +36,6 @@
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
-import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.os.Handler;
import android.util.Log;
@@ -49,6 +48,7 @@
import com.android.internal.util.StateMachine;
import com.android.networkstack.apishim.ConnectivityManagerShimImpl;
import com.android.networkstack.apishim.common.ConnectivityManagerShim;
+import com.android.networkstack.tethering.util.PrefixUtils;
import java.util.HashMap;
import java.util.HashSet;
@@ -135,6 +135,7 @@
private Network mDefaultInternetNetwork;
// The current upstream network used for tethering.
private Network mTetheringUpstreamNetwork;
+ private boolean mPreferTestNetworks;
public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
mContext = ctx;
@@ -325,6 +326,11 @@
final UpstreamNetworkState dfltState = (mDefaultInternetNetwork != null)
? mNetworkMap.get(mDefaultInternetNetwork)
: null;
+ if (mPreferTestNetworks) {
+ final UpstreamNetworkState testState = findFirstTestNetwork(mNetworkMap.values());
+ if (testState != null) return testState;
+ }
+
if (isNetworkUsableAndNotCellular(dfltState)) return dfltState;
if (!isCellularUpstreamPermitted()) return null;
@@ -656,6 +662,20 @@
return null;
}
+ static boolean isTestNetwork(UpstreamNetworkState ns) {
+ return ((ns != null) && (ns.networkCapabilities != null)
+ && ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_TEST));
+ }
+
+ private UpstreamNetworkState findFirstTestNetwork(
+ Iterable<UpstreamNetworkState> netStates) {
+ for (UpstreamNetworkState ns : netStates) {
+ if (isTestNetwork(ns)) return ns;
+ }
+
+ return null;
+ }
+
/**
* Given a legacy type (TYPE_WIFI, ...) returns the corresponding NetworkCapabilities instance.
* This function is used for deprecated legacy type and be disabled by default.
@@ -681,4 +701,9 @@
}
return builder.build();
}
+
+ /** Set test network as preferred upstream. */
+ public void setPreferTestNetworks(boolean prefer) {
+ mPreferTestNetworks = prefer;
+ }
}
diff --git a/Tethering/src/android/net/util/InterfaceSet.java b/Tethering/src/com/android/networkstack/tethering/util/InterfaceSet.java
similarity index 96%
rename from Tethering/src/android/net/util/InterfaceSet.java
rename to Tethering/src/com/android/networkstack/tethering/util/InterfaceSet.java
index 7589787..44573f8 100644
--- a/Tethering/src/android/net/util/InterfaceSet.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/InterfaceSet.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import java.util.Collections;
import java.util.HashSet;
diff --git a/Tethering/src/android/net/util/PrefixUtils.java b/Tethering/src/com/android/networkstack/tethering/util/PrefixUtils.java
similarity index 96%
rename from Tethering/src/android/net/util/PrefixUtils.java
rename to Tethering/src/com/android/networkstack/tethering/util/PrefixUtils.java
index f203e99..50e5c4a 100644
--- a/Tethering/src/android/net/util/PrefixUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/PrefixUtils.java
@@ -14,11 +14,12 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.util.NetworkConstants;
import java.net.Inet4Address;
import java.net.InetAddress;
diff --git a/Tethering/src/android/net/util/TetheringMessageBase.java b/Tethering/src/com/android/networkstack/tethering/util/TetheringMessageBase.java
similarity index 94%
rename from Tethering/src/android/net/util/TetheringMessageBase.java
rename to Tethering/src/com/android/networkstack/tethering/util/TetheringMessageBase.java
index 29c0a81..27bb0f7 100644
--- a/Tethering/src/android/net/util/TetheringMessageBase.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringMessageBase.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
/**
* This class defines Message.what base addresses for various state machine.
diff --git a/Tethering/src/android/net/util/TetheringUtils.java b/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
similarity index 94%
rename from Tethering/src/android/net/util/TetheringUtils.java
rename to Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
index 29900d9..66d67a1 100644
--- a/Tethering/src/android/net/util/TetheringUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import android.net.TetherStatsParcel;
import android.net.TetheringRequestParcel;
@@ -21,6 +21,7 @@
import androidx.annotation.NonNull;
+import com.android.net.module.util.JniUtil;
import com.android.networkstack.tethering.TetherStatsValue;
import java.io.FileDescriptor;
@@ -37,13 +38,18 @@
*/
public class TetheringUtils {
static {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
}
public static final byte[] ALL_NODES = new byte[] {
(byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
};
+ /** The name should be com_android_networkstack_tethering_util_jni. */
+ public static String getTetheringJniLibraryName() {
+ return JniUtil.getJniLibraryName(TetheringUtils.class.getPackage());
+ }
+
/**
* Configures a socket for receiving and sending ICMPv6 neighbor advertisments.
* @param fd the socket's {@link FileDescriptor}.
diff --git a/Tethering/src/android/net/util/VersionedBroadcastListener.java b/Tethering/src/com/android/networkstack/tethering/util/VersionedBroadcastListener.java
similarity index 98%
rename from Tethering/src/android/net/util/VersionedBroadcastListener.java
rename to Tethering/src/com/android/networkstack/tethering/util/VersionedBroadcastListener.java
index e2804ab..c9e75c0 100644
--- a/Tethering/src/android/net/util/VersionedBroadcastListener.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/VersionedBroadcastListener.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp
index b93a969..a2bd1a5 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -19,6 +19,7 @@
java_defaults {
name: "TetheringIntegrationTestsDefaults",
+ defaults: ["framework-connectivity-test-defaults"],
srcs: [
"src/**/*.java",
"src/**/*.kt",
@@ -41,9 +42,10 @@
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
],
- jarjar_rules: ":NetworkStackJarJarRules",
}
+// Library including tethering integration tests targeting the latest stable SDK.
+// Use with NetworkStackJarJarRules.
android_library {
name: "TetheringIntegrationTestsLatestSdkLib",
target_sdk_version: "30",
@@ -56,6 +58,8 @@
]
}
+// Library including tethering integration tests targeting current development SDK.
+// Use with NetworkStackJarJarRules.
android_library {
name: "TetheringIntegrationTestsLib",
target_sdk_version: "current",
@@ -73,9 +77,10 @@
defaults: ["TetheringIntegrationTestsDefaults"],
test_suites: [
"device-tests",
- "mts",
+ "mts-tethering",
],
compile_multilib: "both",
+ jarjar_rules: ":NetworkStackJarJarRules",
}
android_library {
@@ -88,13 +93,33 @@
"TetheringTestsLatestSdkLib",
"TetheringIntegrationTestsLatestSdkLib",
],
- jarjar_rules: ":TetheringTestsJarJarRules",
+ // Jarjar rules should normally be applied on final artifacts and not intermediate libraries as
+ // applying different rules on intermediate libraries can cause conflicts when combining them
+ // (the resulting artifact can end up with multiple incompatible implementations of the same
+ // classes). But this library is used to combine tethering coverage tests with connectivity
+ // coverage tests into a single coverage target. The tests need to use the same jarjar rules as
+ // covered production code for coverage to be calculated properly, so jarjar is applied
+ // separately on each set of tests.
+ jarjar_rules: ":TetheringCoverageJarJarRules",
manifest: "AndroidManifest_coverage.xml",
visibility: [
"//packages/modules/Connectivity/tests:__subpackages__"
],
}
+// Combine NetworkStack and Tethering jarjar rules for coverage target. The jarjar files are
+// simply concatenated in the order specified in srcs.
+genrule {
+ name: "TetheringCoverageJarJarRules",
+ srcs: [
+ ":TetheringTestsJarJarRules",
+ ":NetworkStackJarJarRules",
+ ],
+ out: ["jarjar-rules-tethering-coverage.txt"],
+ cmd: "cat $(in) > $(out)",
+ visibility: ["//visibility:private"],
+}
+
// Special version of the tethering tests that includes all tests necessary for code coverage
// purposes. This is currently the union of TetheringTests, TetheringIntegrationTests and
// NetworkStackTests.
@@ -104,7 +129,7 @@
platform_apis: true,
min_sdk_version: "30",
target_sdk_version: "30",
- test_suites: ["device-tests", "mts"],
+ test_suites: ["device-tests", "mts-tethering"],
test_config: "AndroidTest_Coverage.xml",
defaults: ["libnetworkstackutilsjni_deps"],
static_libs: [
@@ -117,7 +142,7 @@
"libstaticjvmtiagent",
// For NetworkStackUtils included in NetworkStackBase
"libnetworkstackutilsjni",
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
],
compile_multilib: "both",
manifest: "AndroidManifest_coverage.xml",
diff --git a/Tethering/tests/integration/AndroidManifest.xml b/Tethering/tests/integration/AndroidManifest.xml
index fddfaad..c89c556 100644
--- a/Tethering/tests/integration/AndroidManifest.xml
+++ b/Tethering/tests/integration/AndroidManifest.xml
@@ -17,6 +17,11 @@
package="com.android.networkstack.tethering.tests.integration">
<uses-permission android:name="android.permission.INTERNET"/>
+ <!-- The test need CHANGE_NETWORK_STATE permission to use requestNetwork API to setup test
+ network. Since R shell application don't have such permission, grant permission to the test
+ here. TODO: Remove CHANGE_NETWORK_STATE permission here and use adopt shell perssion to
+ obtain CHANGE_NETWORK_STATE for testing once R device is no longer supported. -->
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index f1ddc6d..15f07f2 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -17,9 +17,11 @@
package android.net;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
+import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.TETHER_PRIVILEGED;
+import static android.net.InetAddresses.parseNumericAddress;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_GLOBAL;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_ETHERNET;
@@ -28,6 +30,7 @@
import static com.android.net.module.util.ConnectivityUtils.isIPv6ULA;
import static com.android.net.module.util.NetworkStackConstants.ETHER_TYPE_IPV6;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -44,14 +47,10 @@
import android.net.TetheringManager.StartTetheringCallback;
import android.net.TetheringManager.TetheringEventCallback;
import android.net.TetheringManager.TetheringRequest;
-import android.net.dhcp.DhcpAckPacket;
-import android.net.dhcp.DhcpOfferPacket;
-import android.net.dhcp.DhcpPacket;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.os.SystemProperties;
-import android.system.Os;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -64,6 +63,7 @@
import com.android.net.module.util.structs.Ipv6Header;
import com.android.testutils.HandlerUtils;
import com.android.testutils.TapPacketReader;
+import com.android.testutils.TestNetworkTracker;
import org.junit.After;
import org.junit.Before;
@@ -71,12 +71,12 @@
import org.junit.runner.RunWith;
import java.io.FileDescriptor;
-import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.nio.ByteBuffer;
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Random;
@@ -92,24 +92,19 @@
private static final String TAG = EthernetTetheringTest.class.getSimpleName();
private static final int TIMEOUT_MS = 5000;
- private static final int PACKET_READ_TIMEOUT_MS = 100;
- private static final int DHCP_DISCOVER_ATTEMPTS = 10;
- private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] {
- DhcpPacket.DHCP_SUBNET_MASK,
- DhcpPacket.DHCP_ROUTER,
- DhcpPacket.DHCP_DNS_SERVER,
- DhcpPacket.DHCP_LEASE_TIME,
- };
- private static final String DHCP_HOSTNAME = "testhostname";
+ private static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/8");
+ private static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64");
+ private static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8");
+ private static final InetAddress TEST_IP6_DNS = parseNumericAddress("2001:db8:1::888");
private final Context mContext = InstrumentationRegistry.getContext();
private final EthernetManager mEm = mContext.getSystemService(EthernetManager.class);
private final TetheringManager mTm = mContext.getSystemService(TetheringManager.class);
- private TestNetworkInterface mTestIface;
+ private TestNetworkInterface mDownstreamIface;
private HandlerThread mHandlerThread;
private Handler mHandler;
- private TapPacketReader mTapPacketReader;
+ private TapPacketReader mDownstreamReader;
private TetheredInterfaceRequester mTetheredInterfaceRequester;
private MyTetheringEventCallback mTetheringEventCallback;
@@ -118,12 +113,17 @@
InstrumentationRegistry.getInstrumentation().getUiAutomation();
private boolean mRunTests;
+ private TestNetworkTracker mUpstreamTracker;
+
@Before
public void setUp() throws Exception {
// Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
- // tethered client callbacks.
+ // tethered client callbacks. The restricted networks permission is needed to ensure that
+ // EthernetManager#isAvailable will correctly return true on devices where Ethernet is
+ // marked restricted, like cuttlefish.
mUiAutomation.adoptShellPermissionIdentity(
- MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE);
+ MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS);
mRunTests = mTm.isTetheringSupported() && mEm != null;
assumeTrue(mRunTests);
@@ -134,16 +134,23 @@
}
private void cleanUp() throws Exception {
+ mTm.setPreferTestNetworks(false);
+
+ if (mUpstreamTracker != null) {
+ mUpstreamTracker.teardown();
+ mUpstreamTracker = null;
+ }
+
mTm.stopTethering(TETHERING_ETHERNET);
if (mTetheringEventCallback != null) {
mTetheringEventCallback.awaitInterfaceUntethered();
mTetheringEventCallback.unregister();
mTetheringEventCallback = null;
}
- if (mTapPacketReader != null) {
- TapPacketReader reader = mTapPacketReader;
+ if (mDownstreamReader != null) {
+ TapPacketReader reader = mDownstreamReader;
mHandler.post(() -> reader.stop());
- mTapPacketReader = null;
+ mDownstreamReader = null;
}
mHandlerThread.quitSafely();
mTetheredInterfaceRequester.release();
@@ -165,21 +172,21 @@
// This test requires manipulating packets. Skip if there is a physical Ethernet connected.
assumeFalse(mEm.isAvailable());
- mTestIface = createTestInterface();
+ mDownstreamIface = createTestInterface();
// This must be done now because as soon as setIncludeTestInterfaces(true) is called, the
// interface will be placed in client mode, which will delete the link-local address.
// At that point NetworkInterface.getByName() will cease to work on the interface, because
// starting in R NetworkInterface can no longer see interfaces without IP addresses.
- int mtu = getMTU(mTestIface);
+ int mtu = getMTU(mDownstreamIface);
Log.d(TAG, "Including test interfaces");
mEm.setIncludeTestInterfaces(true);
final String iface = mTetheredInterfaceRequester.getInterface();
assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
+ mDownstreamIface.getInterfaceName(), iface);
- checkVirtualEthernet(mTestIface, mtu);
+ checkVirtualEthernet(mDownstreamIface, mtu);
}
@Test
@@ -191,13 +198,13 @@
mEm.setIncludeTestInterfaces(true);
- mTestIface = createTestInterface();
+ mDownstreamIface = createTestInterface();
final String iface = futureIface.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
+ mDownstreamIface.getInterfaceName(), iface);
- checkVirtualEthernet(mTestIface, getMTU(mTestIface));
+ checkVirtualEthernet(mDownstreamIface, getMTU(mDownstreamIface));
}
@Test
@@ -206,11 +213,11 @@
mEm.setIncludeTestInterfaces(true);
- mTestIface = createTestInterface();
+ mDownstreamIface = createTestInterface();
final String iface = mTetheredInterfaceRequester.getInterface();
assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
+ mDownstreamIface.getInterfaceName(), iface);
assertInvalidStaticIpv4Request(iface, null, null);
assertInvalidStaticIpv4Request(iface, "2001:db8::1/64", "2001:db8:2::/64");
@@ -231,13 +238,14 @@
byte[] client1 = MacAddress.fromString("1:2:3:4:5:6").toByteArray();
byte[] client2 = MacAddress.fromString("a:b:c:d:e:f").toByteArray();
- FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor();
- mTapPacketReader = makePacketReader(fd, getMTU(mTestIface));
- DhcpResults dhcpResults = runDhcp(fd, client1);
+ FileDescriptor fd = mDownstreamIface.getFileDescriptor().getFileDescriptor();
+ mDownstreamReader = makePacketReader(fd, getMTU(mDownstreamIface));
+ TetheringTester tester = new TetheringTester(mDownstreamReader);
+ DhcpResults dhcpResults = tester.runDhcp(client1);
assertEquals(new LinkAddress(clientAddr), dhcpResults.ipAddress);
try {
- runDhcp(fd, client2);
+ tester.runDhcp(client2);
fail("Only one client should get an IP address");
} catch (TimeoutException expected) { }
@@ -297,11 +305,11 @@
mEm.setIncludeTestInterfaces(true);
- mTestIface = createTestInterface();
+ mDownstreamIface = createTestInterface();
final String iface = mTetheredInterfaceRequester.getInterface();
assertEquals("TetheredInterfaceCallback for unexpected interface",
- mTestIface.getInterfaceName(), iface);
+ mDownstreamIface.getInterfaceName(), iface);
final TetheringRequest request = new TetheringRequest.Builder(TETHERING_ETHERNET)
.setConnectivityScope(CONNECTIVITY_SCOPE_LOCAL).build();
@@ -312,10 +320,9 @@
// does not have an IP address, and unprivileged apps cannot see interfaces without IP
// addresses. This shouldn't be flaky because the TAP interface will buffer all packets even
// before the reader is started.
- FileDescriptor fd = mTestIface.getFileDescriptor().getFileDescriptor();
- mTapPacketReader = makePacketReader(fd, getMTU(mTestIface));
+ mDownstreamReader = makePacketReader(mDownstreamIface);
- expectRouterAdvertisement(mTapPacketReader, iface, 2000 /* timeoutMs */);
+ expectRouterAdvertisement(mDownstreamReader, iface, 2000 /* timeoutMs */);
expectLocalOnlyAddresses(iface);
}
@@ -350,12 +357,14 @@
private final CountDownLatch mLocalOnlyStartedLatch = new CountDownLatch(1);
private final CountDownLatch mLocalOnlyStoppedLatch = new CountDownLatch(1);
private final CountDownLatch mClientConnectedLatch = new CountDownLatch(1);
+ private final CountDownLatch mUpstreamConnectedLatch = new CountDownLatch(1);
private final TetheringInterface mIface;
private volatile boolean mInterfaceWasTethered = false;
private volatile boolean mInterfaceWasLocalOnly = false;
private volatile boolean mUnregistered = false;
private volatile Collection<TetheredClient> mClients = null;
+ private volatile Network mUpstream = null;
MyTetheringEventCallback(TetheringManager tm, String iface) {
mTm = tm;
@@ -461,6 +470,22 @@
mClientConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
return mClients;
}
+
+ @Override
+ public void onUpstreamChanged(Network network) {
+ // Ignore stale callbacks registered by previous test cases.
+ if (mUnregistered) return;
+
+ Log.d(TAG, "Got upstream changed: " + network);
+ mUpstream = network;
+ if (mUpstream != null) mUpstreamConnectedLatch.countDown();
+ }
+
+ public Network awaitFirstUpstreamConnected() throws Exception {
+ assertTrue("Did not receive upstream connected callback after " + TIMEOUT_MS + "ms",
+ mUpstreamConnectedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ return mUpstream;
+ }
}
private MyTetheringEventCallback enableEthernetTethering(String iface,
@@ -504,6 +529,11 @@
return nif.getMTU();
}
+ private TapPacketReader makePacketReader(final TestNetworkInterface iface) throws Exception {
+ FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
+ return makePacketReader(fd, getMTU(iface));
+ }
+
private TapPacketReader makePacketReader(FileDescriptor fd, int mtu) {
final TapPacketReader reader = new TapPacketReader(mHandler, fd, mtu);
mHandler.post(() -> reader.start());
@@ -513,40 +543,18 @@
private void checkVirtualEthernet(TestNetworkInterface iface, int mtu) throws Exception {
FileDescriptor fd = iface.getFileDescriptor().getFileDescriptor();
- mTapPacketReader = makePacketReader(fd, mtu);
+ mDownstreamReader = makePacketReader(fd, mtu);
mTetheringEventCallback = enableEthernetTethering(iface.getInterfaceName());
- checkTetheredClientCallbacks(fd);
+ checkTetheredClientCallbacks(mDownstreamReader);
}
- private DhcpResults runDhcp(FileDescriptor fd, byte[] clientMacAddr) throws Exception {
- // We have to retransmit DHCP requests because IpServer declares itself to be ready before
- // its DhcpServer is actually started. TODO: fix this race and remove this loop.
- DhcpPacket offerPacket = null;
- for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) {
- Log.d(TAG, "Sending DHCP discover");
- sendDhcpDiscover(fd, clientMacAddr);
- offerPacket = getNextDhcpPacket();
- if (offerPacket instanceof DhcpOfferPacket) break;
- }
- if (!(offerPacket instanceof DhcpOfferPacket)) {
- throw new TimeoutException("No DHCPOFFER received on interface within timeout");
- }
-
- sendDhcpRequest(fd, offerPacket, clientMacAddr);
- DhcpPacket ackPacket = getNextDhcpPacket();
- if (!(ackPacket instanceof DhcpAckPacket)) {
- throw new TimeoutException("No DHCPACK received on interface within timeout");
- }
-
- return ackPacket.toDhcpResults();
- }
-
- private void checkTetheredClientCallbacks(FileDescriptor fd) throws Exception {
+ private void checkTetheredClientCallbacks(TapPacketReader packetReader) throws Exception {
// Create a fake client.
byte[] clientMacAddr = new byte[6];
new Random().nextBytes(clientMacAddr);
- DhcpResults dhcpResults = runDhcp(fd, clientMacAddr);
+ TetheringTester tester = new TetheringTester(packetReader);
+ DhcpResults dhcpResults = tester.runDhcp(clientMacAddr);
final Collection<TetheredClient> clients = mTetheringEventCallback.awaitClientConnected();
assertEquals(1, clients.size());
@@ -559,7 +567,7 @@
// Check the hostname.
assertEquals(1, client.getAddresses().size());
TetheredClient.AddressInfo info = client.getAddresses().get(0);
- assertEquals(DHCP_HOSTNAME, info.getHostname());
+ assertEquals(TetheringTester.DHCP_HOSTNAME, info.getHostname());
// Check the address is the one that was handed out in the DHCP ACK.
assertLinkAddressMatches(dhcpResults.ipAddress, info.getAddress());
@@ -572,18 +580,6 @@
assertTrue(msg, Math.abs(dhcpResults.leaseDuration - actualLeaseDuration) < 10);
}
- private DhcpPacket getNextDhcpPacket() throws ParseException {
- byte[] packet;
- while ((packet = mTapPacketReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) {
- try {
- return DhcpPacket.decodeFullPacket(packet, packet.length, DhcpPacket.ENCAP_L2);
- } catch (DhcpPacket.ParseException e) {
- // Not a DHCP packet. Continue.
- }
- }
- return null;
- }
-
private static final class TetheredInterfaceRequester implements TetheredInterfaceCallback {
private final Handler mHandler;
private final EthernetManager mEm;
@@ -627,31 +623,6 @@
}
}
- private void sendDhcpDiscover(FileDescriptor fd, byte[] macAddress) throws Exception {
- ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2,
- new Random().nextInt() /* transactionId */, (short) 0 /* secs */,
- macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS,
- false /* rapid commit */, DHCP_HOSTNAME);
- sendPacket(fd, packet);
- }
-
- private void sendDhcpRequest(FileDescriptor fd, DhcpPacket offerPacket, byte[] macAddress)
- throws Exception {
- DhcpResults results = offerPacket.toDhcpResults();
- Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress();
- Inet4Address serverIdentifier = results.serverAddress;
- ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2,
- 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */,
- false /* broadcast */, macAddress, clientIp /* requestedIpAddress */,
- serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME);
- sendPacket(fd, packet);
- }
-
- private void sendPacket(FileDescriptor fd, ByteBuffer packet) throws Exception {
- assertNotNull("Only tests on virtual interfaces can send packets", fd);
- Os.write(fd, packet);
- }
-
public void assertLinkAddressMatches(LinkAddress l1, LinkAddress l2) {
// Check all fields except the deprecation and expiry times.
String msg = String.format("LinkAddresses do not match. expected: %s actual: %s", l1, l2);
@@ -697,10 +668,45 @@
}
private void maybeDeleteTestInterface() throws Exception {
- if (mTestIface != null) {
- mTestIface.getFileDescriptor().close();
- Log.d(TAG, "Deleted test interface " + mTestIface.getInterfaceName());
- mTestIface = null;
+ if (mDownstreamIface != null) {
+ mDownstreamIface.getFileDescriptor().close();
+ Log.d(TAG, "Deleted test interface " + mDownstreamIface.getInterfaceName());
+ mDownstreamIface = null;
}
}
+
+ private TestNetworkTracker createTestUpstream(final List<LinkAddress> addresses)
+ throws Exception {
+ mTm.setPreferTestNetworks(true);
+
+ return initTestNetwork(mContext, addresses, TIMEOUT_MS);
+ }
+
+ @Test
+ public void testTestNetworkUpstream() throws Exception {
+ assumeFalse(mEm.isAvailable());
+
+ // MyTetheringEventCallback currently only support await first available upstream. Tethering
+ // may select internet network as upstream if test network is not available and not be
+ // preferred yet. Create test upstream network before enable tethering.
+ mUpstreamTracker = createTestUpstream(toList(TEST_IP4_ADDR, TEST_IP6_ADDR));
+
+ mDownstreamIface = createTestInterface();
+ mEm.setIncludeTestInterfaces(true);
+
+ final String iface = mTetheredInterfaceRequester.getInterface();
+ assertEquals("TetheredInterfaceCallback for unexpected interface",
+ mDownstreamIface.getInterfaceName(), iface);
+
+ mTetheringEventCallback = enableEthernetTethering(mDownstreamIface.getInterfaceName());
+ assertEquals("onUpstreamChanged for unexpected network", mUpstreamTracker.getNetwork(),
+ mTetheringEventCallback.awaitFirstUpstreamConnected());
+
+ mDownstreamReader = makePacketReader(mDownstreamIface);
+ // TODO: do basic forwarding test here.
+ }
+
+ private <T> List<T> toList(T... array) {
+ return Arrays.asList(array);
+ }
}
diff --git a/Tethering/tests/integration/src/android/net/TetheringTester.java b/Tethering/tests/integration/src/android/net/TetheringTester.java
new file mode 100644
index 0000000..38d74ad
--- /dev/null
+++ b/Tethering/tests/integration/src/android/net/TetheringTester.java
@@ -0,0 +1,155 @@
+/*
+ * 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 android.net;
+
+import static org.junit.Assert.fail;
+
+import android.net.dhcp.DhcpAckPacket;
+import android.net.dhcp.DhcpOfferPacket;
+import android.net.dhcp.DhcpPacket;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.testutils.TapPacketReader;
+
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+import java.util.Random;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Function;
+
+/**
+ * A class simulate tethered client. When caller create TetheringTester, it would connect to
+ * tethering module that do the dhcp and slaac to obtain ipv4 and ipv6 address. Then caller can
+ * send/receive packets by this class.
+ */
+public final class TetheringTester {
+ private static final String TAG = TetheringTester.class.getSimpleName();
+ private static final int PACKET_READ_TIMEOUT_MS = 100;
+ private static final int DHCP_DISCOVER_ATTEMPTS = 10;
+ private static final byte[] DHCP_REQUESTED_PARAMS = new byte[] {
+ DhcpPacket.DHCP_SUBNET_MASK,
+ DhcpPacket.DHCP_ROUTER,
+ DhcpPacket.DHCP_DNS_SERVER,
+ DhcpPacket.DHCP_LEASE_TIME,
+ };
+
+ public static final String DHCP_HOSTNAME = "testhostname";
+
+ private final ArrayMap<MacAddress, TetheredDevice> mTetheredDevices;
+ private final TapPacketReader mDownstreamReader;
+
+ public TetheringTester(TapPacketReader downstream) {
+ if (downstream == null) fail("Downstream reader could not be NULL");
+
+ mDownstreamReader = downstream;
+ mTetheredDevices = new ArrayMap<>();
+ }
+
+ public TetheredDevice createTetheredDevice(MacAddress macAddr) throws Exception {
+ if (mTetheredDevices.get(macAddr) != null) {
+ fail("Tethered device already created");
+ }
+
+ TetheredDevice tethered = new TetheredDevice(macAddr);
+ mTetheredDevices.put(macAddr, tethered);
+
+ return tethered;
+ }
+
+ public class TetheredDevice {
+ private final MacAddress mMacAddr;
+
+ public final Inet4Address mIpv4Addr;
+
+ private TetheredDevice(MacAddress mac) throws Exception {
+ mMacAddr = mac;
+
+ DhcpResults dhcpResults = runDhcp(mMacAddr.toByteArray());
+ mIpv4Addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
+ }
+ }
+
+ /** Simulate dhcp client to obtain ipv4 address. */
+ public DhcpResults runDhcp(byte[] clientMacAddr)
+ throws Exception {
+ // We have to retransmit DHCP requests because IpServer declares itself to be ready before
+ // its DhcpServer is actually started. TODO: fix this race and remove this loop.
+ DhcpPacket offerPacket = null;
+ for (int i = 0; i < DHCP_DISCOVER_ATTEMPTS; i++) {
+ Log.d(TAG, "Sending DHCP discover");
+ sendDhcpDiscover(clientMacAddr);
+ offerPacket = getNextDhcpPacket();
+ if (offerPacket instanceof DhcpOfferPacket) break;
+ }
+ if (!(offerPacket instanceof DhcpOfferPacket)) {
+ throw new TimeoutException("No DHCPOFFER received on interface within timeout");
+ }
+
+ sendDhcpRequest(offerPacket, clientMacAddr);
+ DhcpPacket ackPacket = getNextDhcpPacket();
+ if (!(ackPacket instanceof DhcpAckPacket)) {
+ throw new TimeoutException("No DHCPACK received on interface within timeout");
+ }
+
+ return ackPacket.toDhcpResults();
+ }
+
+ private void sendDhcpDiscover(byte[] macAddress) throws Exception {
+ ByteBuffer packet = DhcpPacket.buildDiscoverPacket(DhcpPacket.ENCAP_L2,
+ new Random().nextInt() /* transactionId */, (short) 0 /* secs */,
+ macAddress, false /* unicast */, DHCP_REQUESTED_PARAMS,
+ false /* rapid commit */, DHCP_HOSTNAME);
+ mDownstreamReader.sendResponse(packet);
+ }
+
+ private void sendDhcpRequest(DhcpPacket offerPacket, byte[] macAddress)
+ throws Exception {
+ DhcpResults results = offerPacket.toDhcpResults();
+ Inet4Address clientIp = (Inet4Address) results.ipAddress.getAddress();
+ Inet4Address serverIdentifier = results.serverAddress;
+ ByteBuffer packet = DhcpPacket.buildRequestPacket(DhcpPacket.ENCAP_L2,
+ 0 /* transactionId */, (short) 0 /* secs */, DhcpPacket.INADDR_ANY /* clientIp */,
+ false /* broadcast */, macAddress, clientIp /* requestedIpAddress */,
+ serverIdentifier, DHCP_REQUESTED_PARAMS, DHCP_HOSTNAME);
+ mDownstreamReader.sendResponse(packet);
+ }
+
+ private DhcpPacket getNextDhcpPacket() {
+ return getNextMatchedPacket((p) -> {
+ try {
+ return DhcpPacket.decodeFullPacket(p, p.length, DhcpPacket.ENCAP_L2);
+ } catch (DhcpPacket.ParseException e) {
+ // Not a DHCP packet. Continue.
+ }
+
+ return null;
+ });
+ }
+
+ private <R> R getNextMatchedPacket(Function<byte[], R> match) {
+ byte[] packet;
+ R result;
+ while ((packet = mDownstreamReader.popPacket(PACKET_READ_TIMEOUT_MS)) != null) {
+ result = match.apply(packet);
+
+ if (result != null) return result;
+ }
+
+ return null;
+ }
+}
diff --git a/Tethering/tests/jarjar-rules.txt b/Tethering/tests/jarjar-rules.txt
index 9cb143e..23d3f56 100644
--- a/Tethering/tests/jarjar-rules.txt
+++ b/Tethering/tests/jarjar-rules.txt
@@ -17,3 +17,8 @@
# TestableLooper from "testables" can be used instead of TestLooper from frameworks-base-testutils.
zap android.os.test.TestLooperTest*
zap com.android.test.filters.SelectTestTests*
+
+# When used in combined test suites like ConnectivityCoverageTests, these test jarjar rules are
+# combined with the jarjar-rules.txt of other included modules (like NetworkStack jarjar rules).
+# They will effectively be added after the following line break. Note that jarjar stops at the first
+# matching rule, so any rule in this file takes precedence over rules in the following ones.
diff --git a/Tethering/tests/privileged/Android.bp b/Tethering/tests/privileged/Android.bp
index 75fdd6e..c890197 100644
--- a/Tethering/tests/privileged/Android.bp
+++ b/Tethering/tests/privileged/Android.bp
@@ -23,9 +23,10 @@
jni_libs: [
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
],
jni_uses_sdk_apis: true,
+ jarjar_rules: ":TetheringTestsJarJarRules",
visibility: ["//visibility:private"],
}
@@ -33,6 +34,7 @@
name: "TetheringPrivilegedTests",
defaults: [
"TetheringPrivilegedTestsJniDefaults",
+ "ConnectivityNextEnableDefaults",
],
srcs: [
"src/**/*.java",
@@ -42,7 +44,7 @@
platform_apis: true,
test_suites: [
"device-tests",
- "mts",
+ "mts-tethering",
],
static_libs: [
"androidx.test.rules",
diff --git a/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java b/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
index 23d9055..eb9cf71 100644
--- a/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
+++ b/Tethering/tests/privileged/src/android/net/ip/DadProxyTest.java
@@ -20,6 +20,7 @@
import static com.android.net.module.util.IpUtils.icmpv6Checksum;
import static com.android.net.module.util.NetworkStackConstants.ETHER_SRC_ADDR_OFFSET;
+import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -30,7 +31,6 @@
import android.net.InetAddresses;
import android.net.MacAddress;
import android.net.util.InterfaceParams;
-import android.net.util.TetheringUtils;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
@@ -40,6 +40,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.networkstack.tethering.util.TetheringUtils;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.TapPacketReader;
@@ -80,7 +81,7 @@
@BeforeClass
public static void setupOnce() {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
final Instrumentation inst = InstrumentationRegistry.getInstrumentation();
final IBinder netdIBinder =
diff --git a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
index 1d94214..34f3e0e 100644
--- a/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
+++ b/Tethering/tests/privileged/src/android/net/ip/RouterAdvertisementDaemonTest.java
@@ -44,7 +44,6 @@
import android.net.MacAddress;
import android.net.RouteInfo;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
-import android.net.shared.RouteUtils;
import android.net.util.InterfaceParams;
import android.os.Handler;
import android.os.HandlerThread;
@@ -56,6 +55,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.net.module.util.Ipv6Utils;
+import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.Struct;
import com.android.net.module.util.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv6Header;
@@ -335,7 +335,7 @@
final String iface = mTetheredParams.name;
final RouteInfo linkLocalRoute =
new RouteInfo(new IpPrefix("fe80::/64"), null, iface, RTN_UNICAST);
- RouteUtils.addRoutesToLocalNetwork(sNetd, iface, List.of(linkLocalRoute));
+ NetdUtils.addRoutesToLocalNetwork(sNetd, iface, List.of(linkLocalRoute));
final ByteBuffer rs = createRsPacket("fe80::1122:3344:5566:7788");
mTetheredPacketReader.sendResponse(rs);
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
index 0deb177..ad2faa0 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
@@ -18,6 +18,8 @@
import static android.system.OsConstants.ETH_P_IPV6;
+import static com.android.networkstack.tethering.util.TetheringUtils.getTetheringJniLibraryName;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -31,6 +33,7 @@
import android.system.OsConstants;
import android.util.ArrayMap;
+import com.android.net.module.util.BpfMap;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -47,7 +50,7 @@
@RunWith(DevSdkIgnoreRunner.class)
@IgnoreUpTo(Build.VERSION_CODES.R)
public final class BpfMapTest {
- // Sync from packages/modules/Connectivity/Tethering/bpf_progs/offload.c.
+ // Sync from packages/modules/Connectivity/bpf_progs/offload.c.
private static final int TEST_MAP_SIZE = 16;
private static final String TETHER_DOWNSTREAM6_FS_PATH =
"/sys/fs/bpf/tethering/map_test_tether_downstream6_map";
@@ -58,7 +61,7 @@
@BeforeClass
public static void setupOnce() {
- System.loadLibrary("tetherutilsjni");
+ System.loadLibrary(getTetheringJniLibraryName());
}
@Before
@@ -388,4 +391,15 @@
assertEquals(OsConstants.E2BIG, expected.errno);
}
}
+
+ @Test
+ public void testOpenNonexistentMap() throws Exception {
+ try {
+ final BpfMap<TetherDownstream6Key, Tether6Value> nonexistentMap = new BpfMap<>(
+ "/sys/fs/bpf/tethering/nonexistent", BpfMap.BPF_F_RDWR,
+ TetherDownstream6Key.class, Tether6Value.class);
+ } catch (ErrnoException expected) {
+ assertEquals(OsConstants.ENOENT, expected.errno);
+ }
+ }
}
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
index 57c28fc..7ee69b2 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java
@@ -16,10 +16,9 @@
package com.android.networkstack.tethering;
-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 com.android.net.module.util.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_GET;
import static com.android.networkstack.tethering.OffloadHardwareInterface.IPCTNL_MSG_CT_NEW;
import static com.android.networkstack.tethering.OffloadHardwareInterface.NFNL_SUBSYS_CTNETLINK;
@@ -29,7 +28,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import android.net.netlink.StructNlMsgHdr;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.HandlerThread;
@@ -40,6 +38,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.netlink.StructNlMsgHdr;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index 0eb682b..5150d39 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -49,7 +49,6 @@
"src/**/*.kt",
],
static_libs: [
- "TetheringApiCurrentLib",
"TetheringCommonTests",
"androidx.test.rules",
"frameworks-base-testutils",
@@ -75,7 +74,7 @@
// For mockito extended
"libdexmakerjvmtiagent",
"libstaticjvmtiagent",
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
],
}
@@ -85,6 +84,9 @@
android_library {
name: "TetheringTestsLatestSdkLib",
defaults: ["TetheringTestsDefaults"],
+ static_libs: [
+ "TetheringApiStableLib",
+ ],
target_sdk_version: "30",
visibility: [
"//packages/modules/Connectivity/tests:__subpackages__",
@@ -97,9 +99,15 @@
platform_apis: true,
test_suites: [
"device-tests",
- "mts",
+ "mts-tethering",
],
- defaults: ["TetheringTestsDefaults"],
+ defaults: [
+ "TetheringTestsDefaults",
+ "ConnectivityNextEnableDefaults",
+ ],
+ static_libs: [
+ "TetheringApiCurrentLib",
+ ],
compile_multilib: "both",
jarjar_rules: ":TetheringTestsJarJarRules",
}
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 6bf6a9f..2f2cde0 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -31,14 +31,14 @@
import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
-import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
-import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH;
-import static android.net.netlink.StructNdMsg.NUD_FAILED;
-import static android.net.netlink.StructNdMsg.NUD_REACHABLE;
-import static android.net.netlink.StructNdMsg.NUD_STALE;
import static android.system.OsConstants.ETH_P_IPV6;
import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH;
+import static com.android.net.module.util.netlink.NetlinkConstants.RTM_DELNEIGH;
+import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWNEIGH;
+import static com.android.net.module.util.netlink.StructNdMsg.NUD_FAILED;
+import static com.android.net.module.util.netlink.StructNdMsg.NUD_REACHABLE;
+import static com.android.net.module.util.netlink.StructNdMsg.NUD_STALE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -85,8 +85,6 @@
import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams;
-import android.net.util.InterfaceSet;
-import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.os.Build;
import android.os.Handler;
@@ -99,10 +97,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.BpfMap;
import com.android.net.module.util.NetworkStackConstants;
import com.android.networkstack.tethering.BpfCoordinator;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.networkstack.tethering.BpfMap;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
import com.android.networkstack.tethering.Tether4Key;
import com.android.networkstack.tethering.Tether4Value;
@@ -116,6 +114,8 @@
import com.android.networkstack.tethering.TetherStatsValue;
import com.android.networkstack.tethering.TetherUpstream6Key;
import com.android.networkstack.tethering.TetheringConfiguration;
+import com.android.networkstack.tethering.util.InterfaceSet;
+import com.android.networkstack.tethering.util.PrefixUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 7e9e34f..c5969d2 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -24,13 +24,6 @@
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStats.UID_TETHERING;
import static android.net.ip.ConntrackMonitor.ConntrackEvent;
-import static android.net.netlink.ConntrackMessage.DYING_MASK;
-import static android.net.netlink.ConntrackMessage.ESTABLISHED_MASK;
-import static android.net.netlink.ConntrackMessage.Tuple;
-import static android.net.netlink.ConntrackMessage.TupleIpv4;
-import static android.net.netlink.ConntrackMessage.TupleProto;
-import static android.net.netlink.NetlinkConstants.IPCTNL_MSG_CT_DELETE;
-import static android.net.netlink.NetlinkConstants.IPCTNL_MSG_CT_NEW;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6;
@@ -40,9 +33,17 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
+import static com.android.net.module.util.netlink.ConntrackMessage.DYING_MASK;
+import static com.android.net.module.util.netlink.ConntrackMessage.ESTABLISHED_MASK;
+import static com.android.net.module.util.netlink.ConntrackMessage.Tuple;
+import static com.android.net.module.util.netlink.ConntrackMessage.TupleIpv4;
+import static com.android.net.module.util.netlink.ConntrackMessage.TupleProto;
+import static com.android.net.module.util.netlink.NetlinkConstants.IPCTNL_MSG_CT_DELETE;
+import static com.android.net.module.util.netlink.NetlinkConstants.IPCTNL_MSG_CT_NEW;
import static com.android.networkstack.tethering.BpfCoordinator.CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS;
import static com.android.networkstack.tethering.BpfCoordinator.NF_CONNTRACK_TCP_TIMEOUT_ESTABLISHED;
import static com.android.networkstack.tethering.BpfCoordinator.NF_CONNTRACK_UDP_TIMEOUT_STREAM;
+import static com.android.networkstack.tethering.BpfCoordinator.NON_OFFLOADED_UPSTREAM_IPV4_TCP_PORTS;
import static com.android.networkstack.tethering.BpfCoordinator.StatsType;
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_IFACE;
import static com.android.networkstack.tethering.BpfCoordinator.StatsType.STATS_PER_UID;
@@ -51,6 +52,7 @@
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -82,9 +84,6 @@
import android.net.ip.ConntrackMonitor;
import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
import android.net.ip.IpServer;
-import android.net.netlink.ConntrackMessage;
-import android.net.netlink.NetlinkConstants;
-import android.net.netlink.NetlinkSocket;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.Build;
@@ -98,8 +97,13 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.net.module.util.BpfMap;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.Struct;
+import com.android.net.module.util.netlink.ConntrackMessage;
+import com.android.net.module.util.netlink.NetlinkConstants;
+import com.android.net.module.util.netlink.NetlinkSocket;
import com.android.networkstack.tethering.BpfCoordinator.BpfConntrackEventConsumer;
import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
@@ -1369,7 +1373,7 @@
}
@NonNull
- private ConntrackEvent makeTestConntrackEvent(short msgType, int proto) {
+ private ConntrackEvent makeTestConntrackEvent(short msgType, int proto, short remotePort) {
if (msgType != IPCTNL_MSG_CT_NEW && msgType != IPCTNL_MSG_CT_DELETE) {
fail("Not support message type " + msgType);
}
@@ -1383,13 +1387,18 @@
return new ConntrackEvent(
(short) (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8 | msgType),
new Tuple(new TupleIpv4(PRIVATE_ADDR, REMOTE_ADDR),
- new TupleProto((byte) proto, PRIVATE_PORT, REMOTE_PORT)),
+ new TupleProto((byte) proto, PRIVATE_PORT, remotePort)),
new Tuple(new TupleIpv4(REMOTE_ADDR, PUBLIC_ADDR),
- new TupleProto((byte) proto, REMOTE_PORT, PUBLIC_PORT)),
+ new TupleProto((byte) proto, remotePort, PUBLIC_PORT)),
status,
timeoutSec);
}
+ @NonNull
+ private ConntrackEvent makeTestConntrackEvent(short msgType, int proto) {
+ return makeTestConntrackEvent(msgType, proto, REMOTE_PORT);
+ }
+
private void setUpstreamInformationTo(final BpfCoordinator coordinator) {
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(UPSTREAM_IFACE);
@@ -1563,14 +1572,14 @@
bpfMap.insertEntry(tcpKey, tcpValue);
bpfMap.insertEntry(udpKey, udpValue);
- // [1] Don't refresh contrack timeout.
+ // [1] Don't refresh conntrack timeout.
setElapsedRealtimeNanos(expiredTime);
mTestLooper.moveTimeForward(CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS);
waitForIdle();
ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class));
ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class));
- // [2] Refresh contrack timeout.
+ // [2] Refresh conntrack timeout.
setElapsedRealtimeNanos(validTime);
mTestLooper.moveTimeForward(CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS);
waitForIdle();
@@ -1587,7 +1596,7 @@
ExtendedMockito.verifyNoMoreInteractions(staticMockMarker(NetlinkSocket.class));
ExtendedMockito.clearInvocations(staticMockMarker(NetlinkSocket.class));
- // [3] Don't refresh contrack timeout if polling stopped.
+ // [3] Don't refresh conntrack timeout if polling stopped.
coordinator.stopPolling();
mTestLooper.moveTimeForward(CONNTRACK_TIMEOUT_UPDATE_INTERVAL_MS);
waitForIdle();
@@ -1629,4 +1638,39 @@
checkRefreshConntrackTimeout(bpfDownstream4Map, tcpKey, tcpValue, udpKey, udpValue);
}
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testNotAllowOffloadByConntrackMessageDestinationPort() throws Exception {
+ final BpfCoordinator coordinator = makeBpfCoordinator();
+ initBpfCoordinatorForRule4(coordinator);
+
+ final short offloadedPort = 42;
+ assertFalse(CollectionUtils.contains(NON_OFFLOADED_UPSTREAM_IPV4_TCP_PORTS,
+ offloadedPort));
+ mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_TCP, offloadedPort));
+ verify(mBpfUpstream4Map).insertEntry(any(), any());
+ verify(mBpfDownstream4Map).insertEntry(any(), any());
+ clearInvocations(mBpfUpstream4Map, mBpfDownstream4Map);
+
+ for (final short port : NON_OFFLOADED_UPSTREAM_IPV4_TCP_PORTS) {
+ mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_TCP, port));
+ verify(mBpfUpstream4Map, never()).insertEntry(any(), any());
+ verify(mBpfDownstream4Map, never()).insertEntry(any(), any());
+
+ mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_TCP, port));
+ verify(mBpfUpstream4Map, never()).deleteEntry(any());
+ verify(mBpfDownstream4Map, never()).deleteEntry(any());
+
+ mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_NEW, IPPROTO_UDP, port));
+ verify(mBpfUpstream4Map).insertEntry(any(), any());
+ verify(mBpfDownstream4Map).insertEntry(any(), any());
+ clearInvocations(mBpfUpstream4Map, mBpfDownstream4Map);
+
+ mConsumer.accept(makeTestConntrackEvent(IPCTNL_MSG_CT_DELETE, IPPROTO_UDP, port));
+ verify(mBpfUpstream4Map).deleteEntry(any());
+ verify(mBpfDownstream4Map).deleteEntry(any());
+ clearInvocations(mBpfUpstream4Map, mBpfDownstream4Map);
+ }
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index 442be1e..46ce82c 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -43,14 +43,18 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ModuleInfo;
@@ -63,6 +67,7 @@
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -91,6 +96,7 @@
private static final String PROVISIONING_NO_UI_APP_NAME = "no_ui_app";
private static final String PROVISIONING_APP_RESPONSE = "app_response";
private static final String TEST_PACKAGE_NAME = "com.android.tethering.test";
+ private static final int RECHECK_TIMER_HOURS = 24;
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private Context mContext;
@@ -98,12 +104,14 @@
@Mock private SharedLog mLog;
@Mock private PackageManager mPm;
@Mock private EntitlementManager.OnUiEntitlementFailedListener mEntitlementFailedListener;
+ @Mock private AlarmManager mAlarmManager;
+ @Mock private PendingIntent mAlarmIntent;
// Like so many Android system APIs, these cannot be mocked because it is marked final.
// We have to use the real versions.
private final PersistableBundle mCarrierConfig = new PersistableBundle();
private final TestLooper mLooper = new TestLooper();
- private Context mMockContext;
+ private MockContext mMockContext;
private Runnable mPermissionChangeCallback;
private WrappedEntitlementManager mEnMgr;
@@ -119,6 +127,13 @@
public Resources getResources() {
return mResources;
}
+
+ @Override
+ public Object getSystemService(String name) {
+ if (Context.ALARM_SERVICE.equals(name)) return mAlarmManager;
+
+ return super.getSystemService(name);
+ }
}
public class WrappedEntitlementManager extends EntitlementManager {
@@ -184,6 +199,11 @@
assertEquals(config.activeDataSubId,
intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID));
}
+
+ @Override
+ PendingIntent createRecheckAlarmIntent() {
+ return mAlarmIntent;
+ }
}
@Before
@@ -245,6 +265,8 @@
.thenReturn(PROVISIONING_NO_UI_APP_NAME);
when(mResources.getString(R.string.config_mobile_hotspot_provision_response)).thenReturn(
PROVISIONING_APP_RESPONSE);
+ when(mResources.getInteger(R.integer.config_mobile_hotspot_provision_check_period))
+ .thenReturn(RECHECK_TIMER_HOURS);
// Act like the CarrierConfigManager is present and ready unless told otherwise.
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(mCarrierConfigManager);
@@ -629,4 +651,31 @@
mEnMgr.stopProvisioningIfNeeded(TETHERING_WIFI);
assertFalse(mEnMgr.isCellularUpstreamPermitted());
}
+
+ private void sendProvisioningRecheckAlarm() {
+ final Intent intent = new Intent(EntitlementManager.ACTION_PROVISIONING_ALARM);
+ mMockContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ mLooper.dispatchAll();
+ }
+
+ @Test
+ public void testScheduleProvisioningReCheck() throws Exception {
+ setupForRequiredProvisioning();
+ assertFalse(mEnMgr.isCellularUpstreamPermitted());
+
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+ mEnMgr.notifyUpstream(true);
+ mLooper.dispatchAll();
+ mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
+ mLooper.dispatchAll();
+ assertTrue(mEnMgr.isCellularUpstreamPermitted());
+ verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(),
+ eq(mAlarmIntent));
+ reset(mAlarmManager);
+
+ sendProvisioningRecheckAlarm();
+ verify(mAlarmManager).cancel(eq(mAlarmIntent));
+ verify(mAlarmManager).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(),
+ eq(mAlarmIntent));
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
index 4865e03..3c07580 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.Intent;
-import android.net.ITetheringConnector;
import android.os.Binder;
import android.os.IBinder;
import android.util.ArrayMap;
@@ -72,8 +71,8 @@
mBase = base;
}
- public ITetheringConnector getTetheringConnector() {
- return ITetheringConnector.Stub.asInterface(mBase);
+ public IBinder getIBinder() {
+ return mBase;
}
public MockTetheringService getService() {
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
index a8b3b92..d1891ed 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -16,13 +16,13 @@
package com.android.networkstack.tethering;
-import static android.net.util.TetheringUtils.uint16;
import static android.system.OsConstants.AF_INET;
import static android.system.OsConstants.AF_UNIX;
import static android.system.OsConstants.SOCK_STREAM;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
+import static com.android.networkstack.tethering.util.TetheringUtils.uint16;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -43,8 +43,6 @@
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_1.OffloadCallbackEvent;
-import android.net.netlink.StructNfGenMsg;
-import android.net.netlink.StructNlMsgHdr;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.NativeHandle;
@@ -57,6 +55,9 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.netlink.StructNfGenMsg;
+import com.android.net.module.util.netlink.StructNlMsgHdr;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
index 41d46e5..6c98f2f 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java
@@ -23,7 +23,8 @@
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
-import static android.net.util.PrefixUtils.asIpPrefix;
+
+import static com.android.networkstack.tethering.util.PrefixUtils.asIpPrefix;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index 1b52f6e..f664d5d 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -26,6 +26,9 @@
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
@@ -40,10 +43,13 @@
import android.net.IIntResultListener;
import android.net.ITetheringConnector;
import android.net.ITetheringEventCallback;
+import android.net.TetheringManager;
import android.net.TetheringRequestParcel;
import android.net.ip.IpServer;
import android.os.Bundle;
+import android.os.ConditionVariable;
import android.os.Handler;
+import android.os.IBinder;
import android.os.ResultReceiver;
import androidx.test.InstrumentationRegistry;
@@ -62,6 +68,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.function.Supplier;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public final class TetheringServiceTest {
@@ -113,7 +123,7 @@
InstrumentationRegistry.getTargetContext(),
MockTetheringService.class);
mMockConnector = (MockTetheringConnector) mServiceTestRule.bindService(mMockServiceIntent);
- mTetheringConnector = mMockConnector.getTetheringConnector();
+ mTetheringConnector = ITetheringConnector.Stub.asInterface(mMockConnector.getIBinder());
final MockTetheringService service = mMockConnector.getService();
mTethering = service.getTethering();
}
@@ -493,4 +503,81 @@
verifyNoMoreInteractionsForTethering();
});
}
+
+ private class ConnectorSupplier<T> implements Supplier<T> {
+ private T mResult = null;
+
+ public void set(T result) {
+ mResult = result;
+ }
+
+ @Override
+ public T get() {
+ return mResult;
+ }
+ }
+
+ private void forceGc() {
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ }
+
+ @Test
+ public void testTetheringManagerLeak() throws Exception {
+ runAsAccessNetworkState((none) -> {
+ final ArrayList<ITetheringEventCallback> callbacks = new ArrayList<>();
+ final ConditionVariable registeredCv = new ConditionVariable(false);
+ doAnswer((invocation) -> {
+ final Object[] args = invocation.getArguments();
+ callbacks.add((ITetheringEventCallback) args[0]);
+ registeredCv.open();
+ return null;
+ }).when(mTethering).registerTetheringEventCallback(any());
+
+ doAnswer((invocation) -> {
+ final Object[] args = invocation.getArguments();
+ callbacks.remove((ITetheringEventCallback) args[0]);
+ return null;
+ }).when(mTethering).unregisterTetheringEventCallback(any());
+
+ final ConnectorSupplier<IBinder> supplier = new ConnectorSupplier<>();
+
+ TetheringManager tm = new TetheringManager(mMockConnector.getService(), supplier);
+ assertNotNull(tm);
+ assertEquals("Internal callback should not be registered", 0, callbacks.size());
+
+ final WeakReference<TetheringManager> weakTm = new WeakReference(tm);
+ assertNotNull(weakTm.get());
+
+ // TetheringManager couldn't be GCed because pollingConnector thread implicitly
+ // reference TetheringManager object.
+ tm = null;
+ forceGc();
+ assertNotNull(weakTm.get());
+
+ // After getting connector, pollingConnector thread stops and internal callback is
+ // registered.
+ supplier.set(mMockConnector.getIBinder());
+ final long timeout = 500L;
+ if (!registeredCv.block(timeout)) {
+ fail("TetheringManager poll connector fail after " + timeout + " ms");
+ }
+ assertEquals("Internal callback is not registered", 1, callbacks.size());
+ assertNotNull(weakTm.get());
+
+ final int attempts = 100;
+ final long waitIntervalMs = 50;
+ for (int i = 0; i < attempts; i++) {
+ forceGc();
+ if (weakTm.get() == null) break;
+
+ Thread.sleep(waitIntervalMs);
+ }
+ assertNull("TetheringManager weak reference still not null after " + attempts
+ + " attempts", weakTm.get());
+
+ assertEquals("Internal callback is not unregistered", 0, callbacks.size());
+ });
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index f45768f..40d133a 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -2558,10 +2558,10 @@
@Test
public void testBluetoothTethering() throws Exception {
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
- when(mBluetoothAdapter.isEnabled()).thenReturn(true);
+ mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
mLooper.dispatchAll();
- verifySetBluetoothTethering(true);
+ verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
result.assertHasResult();
mTethering.interfaceAdded(TEST_BT_IFNAME);
@@ -2574,6 +2574,64 @@
mLooper.dispatchAll();
tetherResult.assertHasResult();
+ verifyNetdCommandForBtSetup();
+
+ // Turning tethering on a second time does not bind to the PAN service again, since it's
+ // already bound.
+ mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
+ final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), secondResult);
+ mLooper.dispatchAll();
+ verifySetBluetoothTethering(true /* enable */, false /* bindToPanService */);
+ secondResult.assertHasResult();
+
+ mockBluetoothSettings(true /* bluetoothOn */, false /* tetheringOn */);
+ mTethering.stopTethering(TETHERING_BLUETOOTH);
+ mLooper.dispatchAll();
+ final ResultListener untetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mTethering.untether(TEST_BT_IFNAME, untetherResult);
+ mLooper.dispatchAll();
+ untetherResult.assertHasResult();
+ verifySetBluetoothTethering(false /* enable */, false /* bindToPanService */);
+
+ verifyNetdCommandForBtTearDown();
+ }
+
+ @Test
+ public void testBluetoothServiceDisconnects() throws Exception {
+ final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
+ mLooper.dispatchAll();
+ ServiceListener panListener = verifySetBluetoothTethering(true /* enable */,
+ true /* bindToPanService */);
+ result.assertHasResult();
+
+ mTethering.interfaceAdded(TEST_BT_IFNAME);
+ mLooper.dispatchAll();
+
+ mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
+ mTethering.interfaceStatusChanged(TEST_BT_IFNAME, true);
+ final ResultListener tetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
+ mTethering.tether(TEST_BT_IFNAME, IpServer.STATE_TETHERED, tetherResult);
+ mLooper.dispatchAll();
+ tetherResult.assertHasResult();
+
+ verifyNetdCommandForBtSetup();
+
+ panListener.onServiceDisconnected(BluetoothProfile.PAN);
+ mTethering.interfaceStatusChanged(TEST_BT_IFNAME, false);
+ mLooper.dispatchAll();
+
+ verifyNetdCommandForBtTearDown();
+ }
+
+ private void mockBluetoothSettings(boolean bluetoothOn, boolean tetheringOn) {
+ when(mBluetoothAdapter.isEnabled()).thenReturn(bluetoothOn);
+ when(mBluetoothPan.isTetheringOn()).thenReturn(tetheringOn);
+ }
+
+ private void verifyNetdCommandForBtSetup() throws Exception {
verify(mNetd).tetherInterfaceAdd(TEST_BT_IFNAME);
verify(mNetd).networkAddInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
verify(mNetd, times(2)).networkAddRoute(eq(INetd.LOCAL_NET_ID), eq(TEST_BT_IFNAME),
@@ -2584,39 +2642,41 @@
anyString(), anyString());
verifyNoMoreInteractions(mNetd);
reset(mNetd);
+ }
- when(mBluetoothAdapter.isEnabled()).thenReturn(true);
- mTethering.stopTethering(TETHERING_BLUETOOTH);
- mLooper.dispatchAll();
- final ResultListener untetherResult = new ResultListener(TETHER_ERROR_NO_ERROR);
- mTethering.untether(TEST_BT_IFNAME, untetherResult);
- mLooper.dispatchAll();
- untetherResult.assertHasResult();
- verifySetBluetoothTethering(false);
-
+ private void verifyNetdCommandForBtTearDown() throws Exception {
verify(mNetd).tetherApplyDnsInterfaces();
verify(mNetd).tetherInterfaceRemove(TEST_BT_IFNAME);
verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, TEST_BT_IFNAME);
verify(mNetd).interfaceSetCfg(any(InterfaceConfigurationParcel.class));
verify(mNetd).tetherStop();
verify(mNetd).ipfwdDisableForwarding(TETHERING_NAME);
- verifyNoMoreInteractions(mNetd);
}
- private void verifySetBluetoothTethering(final boolean enable) {
- final ArgumentCaptor<ServiceListener> listenerCaptor =
- ArgumentCaptor.forClass(ServiceListener.class);
+ // If bindToPanService is true, this function would return ServiceListener which could notify
+ // PanService is connected or disconnected.
+ private ServiceListener verifySetBluetoothTethering(final boolean enable,
+ final boolean bindToPanService) {
+ ServiceListener listener = null;
verify(mBluetoothAdapter).isEnabled();
- verify(mBluetoothAdapter).getProfileProxy(eq(mServiceContext), listenerCaptor.capture(),
- eq(BluetoothProfile.PAN));
- final ServiceListener listener = listenerCaptor.getValue();
- when(mBluetoothPan.isTetheringOn()).thenReturn(enable);
- listener.onServiceConnected(BluetoothProfile.PAN, mBluetoothPan);
+ if (bindToPanService) {
+ final ArgumentCaptor<ServiceListener> listenerCaptor =
+ ArgumentCaptor.forClass(ServiceListener.class);
+ verify(mBluetoothAdapter).getProfileProxy(eq(mServiceContext), listenerCaptor.capture(),
+ eq(BluetoothProfile.PAN));
+ listener = listenerCaptor.getValue();
+ listener.onServiceConnected(BluetoothProfile.PAN, mBluetoothPan);
+ mLooper.dispatchAll();
+ } else {
+ verify(mBluetoothAdapter, never()).getProfileProxy(eq(mServiceContext), any(),
+ anyInt());
+ }
verify(mBluetoothPan).setBluetoothTethering(enable);
verify(mBluetoothPan).isTetheringOn();
- verify(mBluetoothAdapter).closeProfileProxy(eq(BluetoothProfile.PAN), eq(mBluetoothPan));
verifyNoMoreInteractions(mBluetoothAdapter, mBluetoothPan);
reset(mBluetoothAdapter, mBluetoothPan);
+
+ return listener;
}
private void runDualStackUsbTethering(final String expectedIface) throws Exception {
diff --git a/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/InterfaceSetTest.java
similarity index 97%
rename from Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
rename to Tethering/tests/unit/src/com/android/networkstack/tethering/util/InterfaceSetTest.java
index ea084b6..d52dc0f 100644
--- a/Tethering/tests/unit/src/android/net/util/InterfaceSetTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/InterfaceSetTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
diff --git a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java
similarity index 99%
rename from Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
rename to Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java
index e5d0b1c..94ce2b6 100644
--- a/Tethering/tests/unit/src/android/net/util/TetheringUtilsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringUtilsTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import static android.net.TetheringManager.CONNECTIVITY_SCOPE_LOCAL;
import static android.net.TetheringManager.TETHERING_USB;
diff --git a/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/VersionedBroadcastListenerTest.java
similarity index 98%
rename from Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java
rename to Tethering/tests/unit/src/com/android/networkstack/tethering/util/VersionedBroadcastListenerTest.java
index 5a9b6e3..b7dc66e 100644
--- a/Tethering/tests/unit/src/android/net/util/VersionedBroadcastListenerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/VersionedBroadcastListenerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.util;
+package com.android.networkstack.tethering.util;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.reset;
diff --git a/Tethering/bpf_progs/Android.bp b/bpf_progs/Android.bp
similarity index 69%
rename from Tethering/bpf_progs/Android.bp
rename to bpf_progs/Android.bp
index 5b00dfe..17eebe0 100644
--- a/Tethering/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -22,7 +22,7 @@
}
cc_library_headers {
- name: "bpf_tethering_headers",
+ name: "bpf_connectivity_headers",
vendor_available: false,
host_supported: false,
export_include_dirs: ["."],
@@ -32,9 +32,20 @@
],
sdk_version: "30",
min_sdk_version: "30",
- apex_available: ["com.android.tethering"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.tethering",
+ ],
visibility: [
+ // TODO: remove it when NetworkStatsService is moved into the mainline module and no more
+ // calls to JNI in libservices.core.
+ "//frameworks/base/services/core/jni",
"//packages/modules/Connectivity/Tethering",
+ "//packages/modules/Connectivity/tests/unit/jni",
+ // TODO: remove system/netd/* when all BPF code is moved out of Netd.
+ "//system/netd/libnetdbpf",
+ "//system/netd/server",
+ "//system/netd/tests",
],
}
diff --git a/Tethering/bpf_progs/bpf_net_helpers.h b/bpf_progs/bpf_net_helpers.h
similarity index 100%
rename from Tethering/bpf_progs/bpf_net_helpers.h
rename to bpf_progs/bpf_net_helpers.h
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/bpf_shared.h
new file mode 100644
index 0000000..8577d9d
--- /dev/null
+++ b/bpf_progs/bpf_shared.h
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+#include <netdutils/UidConstants.h>
+
+// This header file is shared by eBPF kernel programs (C) and netd (C++) and
+// some of the maps are also accessed directly from Java mainline module code.
+//
+// Hence: explicitly pad all relevant structures and assert that their size
+// is the sum of the sizes of their fields.
+#define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
+
+typedef struct {
+ uint32_t uid;
+ uint32_t tag;
+} UidTagValue;
+STRUCT_SIZE(UidTagValue, 2 * 4); // 8
+
+typedef struct {
+ uint32_t uid;
+ uint32_t tag;
+ uint32_t counterSet;
+ uint32_t ifaceIndex;
+} StatsKey;
+STRUCT_SIZE(StatsKey, 4 * 4); // 16
+
+typedef struct {
+ uint64_t rxPackets;
+ uint64_t rxBytes;
+ uint64_t txPackets;
+ uint64_t txBytes;
+} StatsValue;
+STRUCT_SIZE(StatsValue, 4 * 8); // 32
+
+typedef struct {
+ char name[IFNAMSIZ];
+} IfaceValue;
+STRUCT_SIZE(IfaceValue, 16);
+
+typedef struct {
+ uint64_t rxBytes;
+ uint64_t rxPackets;
+ uint64_t txBytes;
+ uint64_t txPackets;
+ uint64_t tcpRxPackets;
+ uint64_t tcpTxPackets;
+} Stats;
+
+// Since we cannot garbage collect the stats map since device boot, we need to make these maps as
+// large as possible. The maximum size of number of map entries we can have is depend on the rlimit
+// of MEM_LOCK granted to netd. The memory space needed by each map can be calculated by the
+// following fomula:
+// elem_size = 40 + roundup(key_size, 8) + roundup(value_size, 8)
+// cost = roundup_pow_of_two(max_entries) * 16 + elem_size * max_entries +
+// elem_size * number_of_CPU
+// And the cost of each map currently used is(assume the device have 8 CPUs):
+// cookie_tag_map: key: 8 bytes, value: 8 bytes, cost: 822592 bytes = 823Kbytes
+// uid_counter_set_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// app_uid_stats_map: key: 4 bytes, value: 32 bytes, cost: 1062784 bytes = 1063Kbytes
+// uid_stats_map: key: 16 bytes, value: 32 bytes, cost: 1142848 bytes = 1143Kbytes
+// tag_stats_map: key: 16 bytes, value: 32 bytes, cost: 1142848 bytes = 1143Kbytes
+// iface_index_name_map:key: 4 bytes, value: 16 bytes, cost: 80896 bytes = 81Kbytes
+// iface_stats_map: key: 4 bytes, value: 32 bytes, cost: 97024 bytes = 97Kbytes
+// dozable_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// standby_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// powersave_uid_map: key: 4 bytes, value: 1 bytes, cost: 145216 bytes = 145Kbytes
+// total: 4930Kbytes
+// It takes maximum 4.9MB kernel memory space if all maps are full, which requires any devices
+// running this module to have a memlock rlimit to be larger then 5MB. In the old qtaguid module,
+// we don't have a total limit for data entries but only have limitation of tags each uid can have.
+// (default is 1024 in kernel);
+
+// 'static' - otherwise these constants end up in .rodata in the resulting .o post compilation
+static const int COOKIE_UID_MAP_SIZE = 10000;
+static const int UID_COUNTERSET_MAP_SIZE = 2000;
+static const int APP_STATS_MAP_SIZE = 10000;
+static const int STATS_MAP_SIZE = 5000;
+static const int IFACE_INDEX_NAME_MAP_SIZE = 1000;
+static const int IFACE_STATS_MAP_SIZE = 1000;
+static const int CONFIGURATION_MAP_SIZE = 2;
+static const int UID_OWNER_MAP_SIZE = 2000;
+
+#define BPF_PATH "/sys/fs/bpf/"
+
+#define BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_egress_stats"
+#define BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_ingress_stats"
+#define XT_BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_skfilter_ingress_xtbpf"
+#define XT_BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_skfilter_egress_xtbpf"
+#define XT_BPF_ALLOWLIST_PROG_PATH BPF_PATH "prog_netd_skfilter_allowlist_xtbpf"
+#define XT_BPF_DENYLIST_PROG_PATH BPF_PATH "prog_netd_skfilter_denylist_xtbpf"
+#define CGROUP_SOCKET_PROG_PATH BPF_PATH "prog_netd_cgroupsock_inet_create"
+
+#define TC_BPF_INGRESS_ACCOUNT_PROG_NAME "prog_netd_schedact_ingress_account"
+#define TC_BPF_INGRESS_ACCOUNT_PROG_PATH BPF_PATH TC_BPF_INGRESS_ACCOUNT_PROG_NAME
+
+#define COOKIE_TAG_MAP_PATH BPF_PATH "map_netd_cookie_tag_map"
+#define UID_COUNTERSET_MAP_PATH BPF_PATH "map_netd_uid_counterset_map"
+#define APP_UID_STATS_MAP_PATH BPF_PATH "map_netd_app_uid_stats_map"
+#define STATS_MAP_A_PATH BPF_PATH "map_netd_stats_map_A"
+#define STATS_MAP_B_PATH BPF_PATH "map_netd_stats_map_B"
+#define IFACE_INDEX_NAME_MAP_PATH BPF_PATH "map_netd_iface_index_name_map"
+#define IFACE_STATS_MAP_PATH BPF_PATH "map_netd_iface_stats_map"
+#define CONFIGURATION_MAP_PATH BPF_PATH "map_netd_configuration_map"
+#define UID_OWNER_MAP_PATH BPF_PATH "map_netd_uid_owner_map"
+#define UID_PERMISSION_MAP_PATH BPF_PATH "map_netd_uid_permission_map"
+
+enum UidOwnerMatchType {
+ NO_MATCH = 0,
+ HAPPY_BOX_MATCH = (1 << 0),
+ PENALTY_BOX_MATCH = (1 << 1),
+ DOZABLE_MATCH = (1 << 2),
+ STANDBY_MATCH = (1 << 3),
+ POWERSAVE_MATCH = (1 << 4),
+ RESTRICTED_MATCH = (1 << 5),
+ IIF_MATCH = (1 << 6),
+};
+
+enum BpfPermissionMatch {
+ BPF_PERMISSION_INTERNET = 1 << 2,
+ BPF_PERMISSION_UPDATE_DEVICE_STATS = 1 << 3,
+};
+// In production we use two identical stats maps to record per uid stats and
+// do swap and clean based on the configuration specified here. The statsMapType
+// value in configuration map specified which map is currently in use.
+enum StatsMapType {
+ SELECT_MAP_A,
+ SELECT_MAP_B,
+};
+
+// TODO: change the configuration object from an 8-bit bitmask to an object with clearer
+// semantics, like a struct.
+typedef uint8_t BpfConfig;
+static const BpfConfig DEFAULT_CONFIG = 0;
+
+typedef struct {
+ // Allowed interface index. Only applicable if IIF_MATCH is set in the rule bitmask above.
+ uint32_t iif;
+ // A bitmask of enum values in UidOwnerMatchType.
+ uint32_t rule;
+} UidOwnerValue;
+STRUCT_SIZE(UidOwnerValue, 2 * 4); // 8
+
+#define UID_RULES_CONFIGURATION_KEY 1
+#define CURRENT_STATS_MAP_CONFIGURATION_KEY 2
+
+#define CLAT_INGRESS6_PROG_RAWIP_NAME "prog_clatd_schedcls_ingress6_clat_rawip"
+#define CLAT_INGRESS6_PROG_ETHER_NAME "prog_clatd_schedcls_ingress6_clat_ether"
+
+#define CLAT_INGRESS6_PROG_RAWIP_PATH BPF_PATH CLAT_INGRESS6_PROG_RAWIP_NAME
+#define CLAT_INGRESS6_PROG_ETHER_PATH BPF_PATH CLAT_INGRESS6_PROG_ETHER_NAME
+
+#define CLAT_INGRESS6_MAP_PATH BPF_PATH "map_clatd_clat_ingress6_map"
+
+typedef struct {
+ uint32_t iif; // The input interface index
+ struct in6_addr pfx96; // The source /96 nat64 prefix, bottom 32 bits must be 0
+ struct in6_addr local6; // The full 128-bits of the destination IPv6 address
+} ClatIngress6Key;
+STRUCT_SIZE(ClatIngress6Key, 4 + 2 * 16); // 36
+
+typedef struct {
+ uint32_t oif; // The output interface to redirect to (0 means don't redirect)
+ struct in_addr local4; // The destination IPv4 address
+} ClatIngress6Value;
+STRUCT_SIZE(ClatIngress6Value, 4 + 4); // 8
+
+#define CLAT_EGRESS4_PROG_RAWIP_NAME "prog_clatd_schedcls_egress4_clat_rawip"
+#define CLAT_EGRESS4_PROG_ETHER_NAME "prog_clatd_schedcls_egress4_clat_ether"
+
+#define CLAT_EGRESS4_PROG_RAWIP_PATH BPF_PATH CLAT_EGRESS4_PROG_RAWIP_NAME
+#define CLAT_EGRESS4_PROG_ETHER_PATH BPF_PATH CLAT_EGRESS4_PROG_ETHER_NAME
+
+#define CLAT_EGRESS4_MAP_PATH BPF_PATH "map_clatd_clat_egress4_map"
+
+typedef struct {
+ uint32_t iif; // The input interface index
+ struct in_addr local4; // The source IPv4 address
+} ClatEgress4Key;
+STRUCT_SIZE(ClatEgress4Key, 4 + 4); // 8
+
+typedef struct {
+ uint32_t oif; // The output interface to redirect to
+ struct in6_addr local6; // The full 128-bits of the source IPv6 address
+ struct in6_addr pfx96; // The destination /96 nat64 prefix, bottom 32 bits must be 0
+ bool oifIsEthernet; // Whether the output interface requires ethernet header
+ uint8_t pad[3];
+} ClatEgress4Value;
+STRUCT_SIZE(ClatEgress4Value, 4 + 2 * 16 + 1 + 3); // 40
+
+#undef STRUCT_SIZE
diff --git a/Tethering/bpf_progs/bpf_tethering.h b/bpf_progs/bpf_tethering.h
similarity index 98%
rename from Tethering/bpf_progs/bpf_tethering.h
rename to bpf_progs/bpf_tethering.h
index 5fdf8cd..b0ec8f6 100644
--- a/Tethering/bpf_progs/bpf_tethering.h
+++ b/bpf_progs/bpf_tethering.h
@@ -24,7 +24,7 @@
// Common definitions for BPF code in the tethering mainline module.
// These definitions are available to:
// - The BPF programs in Tethering/bpf_progs/
-// - JNI code that depends on the bpf_tethering_headers library.
+// - JNI code that depends on the bpf_connectivity_headers library.
#define BPF_TETHER_ERRORS \
ERR(INVALID_IP_VERSION) \
diff --git a/Tethering/bpf_progs/offload.c b/bpf_progs/offload.c
similarity index 98%
rename from Tethering/bpf_progs/offload.c
rename to bpf_progs/offload.c
index 336d27a..977e918 100644
--- a/Tethering/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -24,6 +24,9 @@
#define __kernel_udphdr udphdr
#include <linux/udp.h>
+// The resulting .o needs to load on the Android S bpfloader v0.2
+#define BPFLOADER_MIN_VER 2u
+
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
#include "bpf_tethering.h"
@@ -80,7 +83,7 @@
} while(0)
#define TC_DROP(counter) COUNT_AND_RETURN(counter, TC_ACT_SHOT)
-#define TC_PUNT(counter) COUNT_AND_RETURN(counter, TC_ACT_OK)
+#define TC_PUNT(counter) COUNT_AND_RETURN(counter, TC_ACT_PIPE)
#define XDP_DROP(counter) COUNT_AND_RETURN(counter, XDP_DROP)
#define XDP_PUNT(counter) COUNT_AND_RETURN(counter, XDP_PASS)
@@ -108,10 +111,10 @@
static inline __always_inline int do_forward6(struct __sk_buff* skb, const bool is_ethernet,
const bool downstream) {
// Must be meta-ethernet IPv6 frame
- if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_OK;
+ if (skb->protocol != htons(ETH_P_IPV6)) return TC_ACT_PIPE;
// Require ethernet dst mac address to be our unicast address.
- if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_OK;
+ if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_PIPE;
const int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
@@ -127,10 +130,10 @@
struct ipv6hdr* ip6 = is_ethernet ? (void*)(eth + 1) : data;
// Must have (ethernet and) ipv6 header
- if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_OK;
+ if (data + l2_header_size + sizeof(*ip6) > data_end) return TC_ACT_PIPE;
// Ethertype - if present - must be IPv6
- if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_OK;
+ if (is_ethernet && (eth->h_proto != htons(ETH_P_IPV6))) return TC_ACT_PIPE;
// IP version must be 6
if (ip6->version != 6) TC_PUNT(INVALID_IP_VERSION);
@@ -182,7 +185,7 @@
: bpf_tether_upstream6_map_lookup_elem(&ku);
// If we don't find any offload information then simply let the core stack handle it...
- if (!v) return TC_ACT_OK;
+ if (!v) return TC_ACT_PIPE;
uint32_t stat_and_limit_k = downstream ? skb->ifindex : v->oif;
@@ -337,13 +340,13 @@
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream6_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream6_rawip_stub, KVER_NONE, KVER(5, 4, 0))
(struct __sk_buff* skb) {
- return TC_ACT_OK;
+ return TC_ACT_PIPE;
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream6_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream6_rawip_stub, KVER_NONE, KVER(5, 4, 0))
(struct __sk_buff* skb) {
- return TC_ACT_OK;
+ return TC_ACT_PIPE;
}
// ----- IPv4 Support -----
@@ -355,10 +358,10 @@
static inline __always_inline int do_forward4(struct __sk_buff* skb, const bool is_ethernet,
const bool downstream, const bool updatetime) {
// Require ethernet dst mac address to be our unicast address.
- if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_OK;
+ if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_PIPE;
// Must be meta-ethernet IPv4 frame
- if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_OK;
+ if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_PIPE;
const int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
@@ -374,10 +377,10 @@
struct iphdr* ip = is_ethernet ? (void*)(eth + 1) : data;
// Must have (ethernet and) ipv4 header
- if (data + l2_header_size + sizeof(*ip) > data_end) return TC_ACT_OK;
+ if (data + l2_header_size + sizeof(*ip) > data_end) return TC_ACT_PIPE;
// Ethertype - if present - must be IPv4
- if (is_ethernet && (eth->h_proto != htons(ETH_P_IP))) return TC_ACT_OK;
+ if (is_ethernet && (eth->h_proto != htons(ETH_P_IP))) return TC_ACT_PIPE;
// IP version must be 4
if (ip->version != 4) TC_PUNT(INVALID_IP_VERSION);
@@ -495,7 +498,7 @@
: bpf_tether_upstream4_map_lookup_elem(&k);
// If we don't find any offload information then simply let the core stack handle it...
- if (!v) return TC_ACT_OK;
+ if (!v) return TC_ACT_PIPE;
uint32_t stat_and_limit_k = downstream ? skb->ifindex : v->oif;
@@ -749,13 +752,13 @@
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_rawip_stub, KVER_NONE, KVER(5, 4, 0))
(struct __sk_buff* skb) {
- return TC_ACT_OK;
+ return TC_ACT_PIPE;
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_rawip$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_rawip_stub, KVER_NONE, KVER(5, 4, 0))
(struct __sk_buff* skb) {
- return TC_ACT_OK;
+ return TC_ACT_PIPE;
}
// ETHER: 4.9-P/Q kernel
@@ -763,13 +766,13 @@
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_downstream4_ether$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_downstream4_ether_stub, KVER_NONE, KVER(4, 14, 0))
(struct __sk_buff* skb) {
- return TC_ACT_OK;
+ return TC_ACT_PIPE;
}
DEFINE_BPF_PROG_KVER_RANGE("schedcls/tether_upstream4_ether$stub", AID_ROOT, AID_NETWORK_STACK,
sched_cls_tether_upstream4_ether_stub, KVER_NONE, KVER(4, 14, 0))
(struct __sk_buff* skb) {
- return TC_ACT_OK;
+ return TC_ACT_PIPE;
}
// ----- XDP Support -----
diff --git a/Tethering/bpf_progs/test.c b/bpf_progs/test.c
similarity index 93%
rename from Tethering/bpf_progs/test.c
rename to bpf_progs/test.c
index 3f0df2e..a76e346 100644
--- a/Tethering/bpf_progs/test.c
+++ b/bpf_progs/test.c
@@ -18,6 +18,9 @@
#include <linux/in.h>
#include <linux/ip.h>
+// The resulting .o needs to load on the Android S bpfloader v0.2
+#define BPFLOADER_MIN_VER 2u
+
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
#include "bpf_tethering.h"
diff --git a/framework/Android.bp b/framework/Android.bp
index 5e7262a..e765ee8 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -80,6 +80,9 @@
"framework-wifi.stubs.module_lib",
"net-utils-device-common",
],
+ static_libs: [
+ "modules-utils-build",
+ ],
libs: [
"unsupportedappusage",
],
@@ -109,6 +112,7 @@
apex_available: [
"com.android.tethering",
],
+ lint: { strict_updatability_linting: true },
}
cc_library_shared {
diff --git a/framework/aidl-export/android/net/DhcpOption.aidl b/framework/aidl-export/android/net/DhcpOption.aidl
new file mode 100644
index 0000000..9ed0e62
--- /dev/null
+++ b/framework/aidl-export/android/net/DhcpOption.aidl
@@ -0,0 +1,20 @@
+/**
+ * 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 android.net;
+
+parcelable DhcpOption;
+
diff --git a/framework/api/current.txt b/framework/api/current.txt
index 33f4d14..9a77a3c 100644
--- a/framework/api/current.txt
+++ b/framework/api/current.txt
@@ -196,6 +196,7 @@
}
public static class DnsResolver.DnsException extends java.lang.Exception {
+ ctor public DnsResolver.DnsException(int, @Nullable Throwable);
field public final int code;
}
diff --git a/framework/api/module-lib-current.txt b/framework/api/module-lib-current.txt
index 7fc0382..50dd2ad 100644
--- a/framework/api/module-lib-current.txt
+++ b/framework/api/module-lib-current.txt
@@ -99,6 +99,15 @@
field public static final int PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = 3; // 0x3
}
+ public final class DhcpOption implements android.os.Parcelable {
+ ctor public DhcpOption(byte, @Nullable byte[]);
+ method public int describeContents();
+ method public byte getType();
+ method @Nullable public byte[] getValue();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.DhcpOption> CREATOR;
+ }
+
public final class NetworkAgentConfig implements android.os.Parcelable {
method @Nullable public String getSubscriberId();
method public boolean isBypassableVpn();
diff --git a/framework/api/module-lib-lint-baseline.txt b/framework/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..c7b0db5
--- /dev/null
+++ b/framework/api/module-lib-lint-baseline.txt
@@ -0,0 +1,7 @@
+// Baseline format: 1.0
+NoByteOrShort: android.net.DhcpOption#DhcpOption(byte, byte[]) parameter #0:
+ Should avoid odd sized primitives; use `int` instead of `byte` in parameter type in android.net.DhcpOption(byte type, byte[] value)
+NoByteOrShort: android.net.DhcpOption#describeContents():
+ Should avoid odd sized primitives; use `int` instead of `byte` in method android.net.DhcpOption.describeContents()
+NoByteOrShort: android.net.DhcpOption#getType():
+ Should avoid odd sized primitives; use `int` instead of `byte` in method android.net.DhcpOption.getType()
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index d1d51da..cfab872 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -279,6 +279,7 @@
method @Nullable public String getSsid();
method @NonNull public java.util.Set<java.lang.Integer> getSubscriptionIds();
method @NonNull public int[] getTransportTypes();
+ method @Nullable public java.util.List<android.net.Network> getUnderlyingNetworks();
method public boolean isPrivateDnsBroken();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
field public static final int NET_CAPABILITY_BIP = 31; // 0x1f
@@ -309,6 +310,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
method @NonNull public android.net.NetworkCapabilities.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
+ method @NonNull public android.net.NetworkCapabilities.Builder setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method @NonNull public static android.net.NetworkCapabilities.Builder withoutDefaultCapabilities();
}
diff --git a/framework/jarjar-rules.txt b/framework/jarjar-rules.txt
index 2e5848c..ae6b9c3 100644
--- a/framework/jarjar-rules.txt
+++ b/framework/jarjar-rules.txt
@@ -1,2 +1,3 @@
rule com.android.net.module.util.** android.net.connectivity.framework.util.@1
+rule com.android.modules.utils.** android.net.connectivity.framework.modules.utils.@1
rule android.net.NetworkFactory* android.net.connectivity.framework.NetworkFactory@1
diff --git a/framework/lint-baseline.xml b/framework/lint-baseline.xml
deleted file mode 100644
index 099202f..0000000
--- a/framework/lint-baseline.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?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 31 (current min is 30): `new android.net.ParseException`"
- errorLine1=" ParseException pe = new ParseException(e.reason, e.getCause());"
- errorLine2=" ~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/framework/src/android/net/DnsResolver.java"
- line="301"
- column="37"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback`"
- errorLine1=" protected class ActiveDataSubscriptionIdListener extends TelephonyCallback"
- errorLine2=" ~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java"
- line="96"
- column="62"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Class requires API level 31 (current min is 30): `android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener`"
- errorLine1=" implements TelephonyCallback.ActiveDataSubscriptionIdListener {"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java"
- line="97"
- column="24"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.telephony.TelephonyManager#registerTelephonyCallback`"
- errorLine1=" ctx.getSystemService(TelephonyManager.class).registerTelephonyCallback("
- errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java"
- line="126"
- column="54"/>
- </issue>
-
-</issues>
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 2eb5fb7..c21bcfa 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -46,7 +46,6 @@
import android.net.TetheringManager.StartTetheringCallback;
import android.net.TetheringManager.TetheringEventCallback;
import android.net.TetheringManager.TetheringRequest;
-import android.net.wifi.WifiNetworkSuggestion;
import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
@@ -144,6 +143,11 @@
* <p/>
* For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
* is set to {@code true} if there are no connected networks at all.
+ * <p />
+ * Note that this broadcast is deprecated and generally tries to implement backwards
+ * compatibility with older versions of Android. As such, it may not reflect new
+ * capabilities of the system, like multiple networks being connected at the same
+ * time, the details of newer technology, or changes in tethering state.
*
* @deprecated apps should use the more versatile {@link #requestNetwork},
* {@link #registerNetworkCallback} or {@link #registerDefaultNetworkCallback}
@@ -1163,10 +1167,11 @@
* default data network. In the event that the current active default data
* network disconnects, the returned {@code Network} object will no longer
* be usable. This will return {@code null} when there is no default
- * network.
+ * network, or when the default network is blocked.
*
* @return a {@link Network} object for the current default network or
- * {@code null} if no default network is currently active
+ * {@code null} if no default network is currently active or if
+ * the default network is blocked for the caller
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
@Nullable
@@ -2334,6 +2339,7 @@
void onNetworkActive();
}
+ @GuardedBy("mNetworkActivityListeners")
private final ArrayMap<OnNetworkActiveListener, INetworkActivityListener>
mNetworkActivityListeners = new ArrayMap<>();
@@ -2350,18 +2356,20 @@
* @param l The listener to be told when the network is active.
*/
public void addDefaultNetworkActiveListener(final OnNetworkActiveListener l) {
- INetworkActivityListener rl = new INetworkActivityListener.Stub() {
+ final INetworkActivityListener rl = new INetworkActivityListener.Stub() {
@Override
public void onNetworkActive() throws RemoteException {
l.onNetworkActive();
}
};
- try {
- mService.registerNetworkActivityListener(rl);
- mNetworkActivityListeners.put(l, rl);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mNetworkActivityListeners) {
+ try {
+ mService.registerNetworkActivityListener(rl);
+ mNetworkActivityListeners.put(l, rl);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
@@ -2372,14 +2380,17 @@
* @param l Previously registered listener.
*/
public void removeDefaultNetworkActiveListener(@NonNull OnNetworkActiveListener l) {
- INetworkActivityListener rl = mNetworkActivityListeners.get(l);
- if (rl == null) {
- throw new IllegalArgumentException("Listener was not registered.");
- }
- try {
- mService.registerNetworkActivityListener(rl);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mNetworkActivityListeners) {
+ final INetworkActivityListener rl = mNetworkActivityListeners.get(l);
+ if (rl == null) {
+ throw new IllegalArgumentException("Listener was not registered.");
+ }
+ try {
+ mService.unregisterNetworkActivityListener(rl);
+ mNetworkActivityListeners.remove(l);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
@@ -3467,7 +3478,8 @@
* {@link NetworkCapabilities#getTransportInfo()}) like {@link android.net.wifi.WifiInfo}
* contain location sensitive information.
* <li> OwnerUid (retrieved via {@link NetworkCapabilities#getOwnerUid()} is location
- * sensitive for wifi suggestor apps (i.e using {@link WifiNetworkSuggestion}).</li>
+ * sensitive for wifi suggestor apps (i.e using
+ * {@link android.net.wifi.WifiNetworkSuggestion WifiNetworkSuggestion}).</li>
* </p>
* <p>
* Note:
diff --git a/framework/src/android/net/DhcpOption.java b/framework/src/android/net/DhcpOption.java
new file mode 100644
index 0000000..a125290
--- /dev/null
+++ b/framework/src/android/net/DhcpOption.java
@@ -0,0 +1,80 @@
+/*
+ * 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 android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing an option in the DHCP protocol.
+ *
+ * @hide
+ */
+@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+public final class DhcpOption implements Parcelable {
+ private final byte mType;
+ private final byte[] mValue;
+
+ /**
+ * Constructs a DhcpOption object.
+ *
+ * @param type the type of this option
+ * @param value the value of this option. If {@code null}, DHCP packets containing this option
+ * will include the option type in the Parameter Request List. Otherwise, DHCP
+ * packets containing this option will include the option in the options section.
+ */
+ public DhcpOption(byte type, @Nullable byte[] value) {
+ mType = type;
+ mValue = value;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeByte(mType);
+ dest.writeByteArray(mValue);
+ }
+
+ /** Implement the Parcelable interface */
+ public static final @NonNull Creator<DhcpOption> CREATOR =
+ new Creator<DhcpOption>() {
+ public DhcpOption createFromParcel(Parcel in) {
+ return new DhcpOption(in.readByte(), in.createByteArray());
+ }
+
+ public DhcpOption[] newArray(int size) {
+ return new DhcpOption[size];
+ }
+ };
+
+ /** Get the type of DHCP option */
+ public byte getType() {
+ return mType;
+ }
+
+ /** Get the value of DHCP option */
+ @Nullable public byte[] getValue() {
+ return mValue == null ? null : mValue.clone();
+ }
+}
diff --git a/framework/src/android/net/DnsResolver.java b/framework/src/android/net/DnsResolver.java
index dac88ad..164160f 100644
--- a/framework/src/android/net/DnsResolver.java
+++ b/framework/src/android/net/DnsResolver.java
@@ -164,7 +164,7 @@
*/
@DnsError public final int code;
- DnsException(@DnsError int code, @Nullable Throwable cause) {
+ public DnsException(@DnsError int code, @Nullable Throwable cause) {
super(cause);
this.code = code;
}
diff --git a/framework/src/android/net/Network.java b/framework/src/android/net/Network.java
index b3770ea..53f171a 100644
--- a/framework/src/android/net/Network.java
+++ b/framework/src/android/net/Network.java
@@ -382,13 +382,14 @@
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
- bindSocket(pfd.getFileDescriptor());
- // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
- // dup share the underlying socket in the kernel. The socket is never truly closed until the
- // last fd pointing to the socket being closed. So close the dup one after binding the
- // socket to control the lifetime of the dup fd.
- pfd.close();
+
+ // ParcelFileDescriptor.fromDatagramSocket() creates a dup of the original fd. The original
+ // and the dup share the underlying socket in the kernel. The socket is never truly closed
+ // until the last fd pointing to the socket being closed. Try and eventually close the dup
+ // one after binding the socket to control the lifetime of the dup fd.
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket)) {
+ bindSocket(pfd.getFileDescriptor());
+ }
}
/**
@@ -400,13 +401,13 @@
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
- bindSocket(pfd.getFileDescriptor());
- // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
- // dup share the underlying socket in the kernel. The socket is never truly closed until the
- // last fd pointing to the socket being closed. So close the dup one after binding the
- // socket to control the lifetime of the dup fd.
- pfd.close();
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and
+ // the dup share the underlying socket in the kernel. The socket is never truly closed
+ // until the last fd pointing to the socket being closed. Try and eventually close the dup
+ // one after binding the socket to control the lifetime of the dup fd.
+ try (ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket)) {
+ bindSocket(pfd.getFileDescriptor());
+ }
}
/**
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index ec71d3d..ca5dc34 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -27,7 +27,6 @@
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.ConnectivityManager.NetworkCallback;
-import android.net.wifi.WifiNetworkSuggestion;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
@@ -42,7 +41,10 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;
@@ -129,6 +131,11 @@
// Set to true when private DNS is broken.
private boolean mPrivateDnsBroken;
+ // Underlying networks, if any. VPNs and VCNs typically have underlying networks.
+ // This is an unmodifiable list and it will be returned as is in the getter.
+ @Nullable
+ private List<Network> mUnderlyingNetworks;
+
/**
* Uid of the app making the request.
*/
@@ -184,6 +191,7 @@
mRequestorUid = Process.INVALID_UID;
mRequestorPackageName = null;
mSubIds = new ArraySet<>();
+ mUnderlyingNetworks = null;
}
/**
@@ -213,6 +221,9 @@
mRequestorUid = nc.mRequestorUid;
mRequestorPackageName = nc.mRequestorPackageName;
mSubIds = new ArraySet<>(nc.mSubIds);
+ // mUnderlyingNetworks is an unmodifiable list if non-null, so a defensive copy is not
+ // necessary.
+ mUnderlyingNetworks = nc.mUnderlyingNetworks;
}
/**
@@ -699,6 +710,41 @@
}
/**
+ * Set the underlying networks of this network.
+ *
+ * @param networks The underlying networks of this network.
+ *
+ * @hide
+ */
+ public void setUnderlyingNetworks(@Nullable List<Network> networks) {
+ mUnderlyingNetworks =
+ (networks == null) ? null : Collections.unmodifiableList(new ArrayList<>(networks));
+ }
+
+ /**
+ * Get the underlying networks of this network. If the caller is not system privileged, this is
+ * always redacted to null and it will be never useful to the caller.
+ *
+ * @return <li>If the list is null, this network hasn't declared underlying networks.</li>
+ * <li>If the list is empty, this network has declared that it has no underlying
+ * networks or it doesn't run on any of the available networks.</li>
+ * <li>The list can contain multiple underlying networks, e.g. a VPN running over
+ * multiple networks at the same time.</li>
+ *
+ * @hide
+ */
+ @SuppressLint("NullableCollection")
+ @Nullable
+ @SystemApi
+ public List<Network> getUnderlyingNetworks() {
+ return mUnderlyingNetworks;
+ }
+
+ private boolean equalsUnderlyingNetworks(@NonNull NetworkCapabilities nc) {
+ return Objects.equals(getUnderlyingNetworks(), nc.getUnderlyingNetworks());
+ }
+
+ /**
* Tests for the presence of a capability on this instance.
*
* @param capability the capabilities to be tested for.
@@ -741,18 +787,6 @@
}
}
- private void combineNetCapabilities(@NonNull NetworkCapabilities nc) {
- final long wantedCaps = this.mNetworkCapabilities | nc.mNetworkCapabilities;
- final long forbiddenCaps =
- this.mForbiddenNetworkCapabilities | nc.mForbiddenNetworkCapabilities;
- if ((wantedCaps & forbiddenCaps) != 0) {
- throw new IllegalArgumentException(
- "Cannot have the same capability in wanted and forbidden lists.");
- }
- this.mNetworkCapabilities = wantedCaps;
- this.mForbiddenNetworkCapabilities = forbiddenCaps;
- }
-
/**
* Convenience function that returns a human-readable description of the first mutable
* capability we find. Used to present an error message to apps that request mutable
@@ -814,11 +848,24 @@
}
/**
+ * @see #restrictCapabilitiesForTestNetwork(int)
+ * @deprecated Use {@link #restrictCapabilitiesForTestNetwork(int)} (without the typo) instead.
+ * @hide
+ */
+ @Deprecated
+ public void restrictCapabilitesForTestNetwork(int creatorUid) {
+ // Do not remove without careful consideration: this method has a typo in its name but is
+ // called by the first S CTS releases, therefore it cannot be removed from the connectivity
+ // module as long as such CTS releases are valid for testing S devices.
+ restrictCapabilitiesForTestNetwork(creatorUid);
+ }
+
+ /**
* Test networks have strong restrictions on what capabilities they can have. Enforce these
* restrictions.
* @hide
*/
- public void restrictCapabilitesForTestNetwork(int creatorUid) {
+ public void restrictCapabilitiesForTestNetwork(int creatorUid) {
final long originalCapabilities = mNetworkCapabilities;
final long originalTransportTypes = mTransportTypes;
final NetworkSpecifier originalSpecifier = mNetworkSpecifier;
@@ -828,7 +875,7 @@
final TransportInfo originalTransportInfo = getTransportInfo();
final Set<Integer> originalSubIds = getSubscriptionIds();
clearAll();
- if (0 != (originalCapabilities & NET_CAPABILITY_NOT_RESTRICTED)) {
+ if (0 != (originalCapabilities & (1 << NET_CAPABILITY_NOT_RESTRICTED))) {
// If the test network is not restricted, then it is only allowed to declare some
// specific transports. This is to minimize impact on running apps in case an app
// run from the shell creates a test a network.
@@ -839,7 +886,7 @@
// SubIds are only allowed for Test Networks that only declare TRANSPORT_TEST.
setSubscriptionIds(originalSubIds);
} else {
- // If the test transport is restricted, then it may declare any transport.
+ // If the test network is restricted, then it may declare any transport.
mTransportTypes = (originalTransportTypes | (1 << TRANSPORT_TEST));
}
mNetworkCapabilities = originalCapabilities & TEST_NETWORKS_ALLOWED_CAPABILITIES;
@@ -1042,8 +1089,12 @@
return isValidTransport(transportType) && ((mTransportTypes & (1 << transportType)) != 0);
}
- private void combineTransportTypes(NetworkCapabilities nc) {
- this.mTransportTypes |= nc.mTransportTypes;
+ /**
+ * Returns true iff this NetworkCapabilities has the specified transport and no other.
+ * @hide
+ */
+ public boolean hasSingleTransport(@Transport int transportType) {
+ return mTransportTypes == (1 << transportType);
}
private boolean satisfiedByTransportTypes(NetworkCapabilities nc) {
@@ -1124,14 +1175,14 @@
*
* <p>
* This field will only be populated for VPN and wifi network suggestor apps (i.e using
- * {@link WifiNetworkSuggestion}), and only for the network they own.
- * In the case of wifi network suggestors apps, this field is also location sensitive, so the
- * app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. If the
- * app targets SDK version greater than or equal to {@link Build.VERSION_CODES#S}, then they
- * also need to use {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their
- * callback. If the apps targets SDK version equal to {{@link Build.VERSION_CODES#R}, this field
- * will always be included. The app will be blamed for location access if this field is
- * included.
+ * {@link android.net.wifi.WifiNetworkSuggestion WifiNetworkSuggestion}), and only for the
+ * network they own. In the case of wifi network suggestors apps, this field is also location
+ * sensitive, so the app needs to hold {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission. If the app targets SDK version greater than or equal to
+ * {@link Build.VERSION_CODES#S}, then they also need to use
+ * {@link NetworkCallback#FLAG_INCLUDE_LOCATION_INFO} to get the info in their callback. If the
+ * apps targets SDK version equal to {{@link Build.VERSION_CODES#R}, this field will always be
+ * included. The app will be blamed for location access if this field is included.
* </p>
*/
public int getOwnerUid() {
@@ -1226,26 +1277,6 @@
}
/**
- * Combine the administrator UIDs of the capabilities.
- *
- * <p>This is only legal if either of the administrators lists are empty, or if they are equal.
- * Combining administrator UIDs is only possible for combining non-overlapping sets of UIDs.
- *
- * <p>If both administrator lists are non-empty but not equal, they conflict with each other. In
- * this case, it would not make sense to add them together.
- */
- private void combineAdministratorUids(@NonNull final NetworkCapabilities nc) {
- if (nc.mAdministratorUids.length == 0) return;
- if (mAdministratorUids.length == 0) {
- mAdministratorUids = Arrays.copyOf(nc.mAdministratorUids, nc.mAdministratorUids.length);
- return;
- }
- if (!equalsAdministratorUids(nc)) {
- throw new IllegalStateException("Can't combine two different administrator UID lists");
- }
- }
-
- /**
* Value indicating that link bandwidth is unspecified.
* @hide
*/
@@ -1307,12 +1338,6 @@
return mLinkDownBandwidthKbps;
}
- private void combineLinkBandwidths(NetworkCapabilities nc) {
- this.mLinkUpBandwidthKbps =
- Math.max(this.mLinkUpBandwidthKbps, nc.mLinkUpBandwidthKbps);
- this.mLinkDownBandwidthKbps =
- Math.max(this.mLinkDownBandwidthKbps, nc.mLinkDownBandwidthKbps);
- }
private boolean satisfiedByLinkBandwidths(NetworkCapabilities nc) {
return !(this.mLinkUpBandwidthKbps > nc.mLinkUpBandwidthKbps
|| this.mLinkDownBandwidthKbps > nc.mLinkDownBandwidthKbps);
@@ -1399,13 +1424,6 @@
return mTransportInfo;
}
- private void combineSpecifiers(NetworkCapabilities nc) {
- if (mNetworkSpecifier != null && !mNetworkSpecifier.equals(nc.mNetworkSpecifier)) {
- throw new IllegalStateException("Can't combine two networkSpecifiers");
- }
- setNetworkSpecifier(nc.mNetworkSpecifier);
- }
-
private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
return mNetworkSpecifier == null || mNetworkSpecifier.canBeSatisfiedBy(nc.mNetworkSpecifier)
|| nc.mNetworkSpecifier instanceof MatchAllNetworkSpecifier;
@@ -1415,13 +1433,6 @@
return Objects.equals(mNetworkSpecifier, nc.mNetworkSpecifier);
}
- private void combineTransportInfos(NetworkCapabilities nc) {
- if (mTransportInfo != null && !mTransportInfo.equals(nc.mTransportInfo)) {
- throw new IllegalStateException("Can't combine two TransportInfos");
- }
- setTransportInfo(nc.mTransportInfo);
- }
-
private boolean equalsTransportInfo(NetworkCapabilities nc) {
return Objects.equals(mTransportInfo, nc.mTransportInfo);
}
@@ -1476,10 +1487,6 @@
return mSignalStrength;
}
- private void combineSignalStrength(NetworkCapabilities nc) {
- this.mSignalStrength = Math.max(this.mSignalStrength, nc.mSignalStrength);
- }
-
private boolean satisfiedBySignalStrength(NetworkCapabilities nc) {
return this.mSignalStrength <= nc.mSignalStrength;
}
@@ -1662,7 +1669,7 @@
* @hide
*/
@VisibleForTesting
- public boolean appliesToUidRange(@Nullable UidRange requiredRange) {
+ public boolean appliesToUidRange(@NonNull UidRange requiredRange) {
if (null == mUids) return true;
for (UidRange uidRange : mUids) {
if (uidRange.containsRange(requiredRange)) {
@@ -1673,20 +1680,6 @@
}
/**
- * Combine the UIDs this network currently applies to with the UIDs the passed
- * NetworkCapabilities apply to.
- * nc is assumed nonnull.
- */
- private void combineUids(@NonNull NetworkCapabilities nc) {
- if (null == nc.mUids || null == mUids) {
- mUids = null;
- return;
- }
- mUids.addAll(nc.mUids);
- }
-
-
- /**
* The SSID of the network, or null if not applicable or unknown.
* <p>
* This is filled in by wifi code.
@@ -1729,42 +1722,6 @@
}
/**
- * Combine SSIDs of the capabilities.
- * <p>
- * This is only legal if either the SSID of this object is null, or both SSIDs are
- * equal.
- * @hide
- */
- private void combineSSIDs(@NonNull NetworkCapabilities nc) {
- if (mSSID != null && !mSSID.equals(nc.mSSID)) {
- throw new IllegalStateException("Can't combine two SSIDs");
- }
- setSSID(nc.mSSID);
- }
-
- /**
- * Combine a set of Capabilities to this one. Useful for coming up with the complete set.
- * <p>
- * Note that this method may break an invariant of having a particular capability in either
- * wanted or forbidden lists but never in both. Requests that have the same capability in
- * both lists will never be satisfied.
- * @hide
- */
- public void combineCapabilities(@NonNull NetworkCapabilities nc) {
- combineNetCapabilities(nc);
- combineTransportTypes(nc);
- combineLinkBandwidths(nc);
- combineSpecifiers(nc);
- combineTransportInfos(nc);
- combineSignalStrength(nc);
- combineUids(nc);
- combineSSIDs(nc);
- combineRequestor(nc);
- combineAdministratorUids(nc);
- combineSubscriptionIds(nc);
- }
-
- /**
* Check if our requirements are satisfied by the given {@code NetworkCapabilities}.
*
* @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
@@ -1880,7 +1837,8 @@
&& equalsPrivateDnsBroken(that)
&& equalsRequestor(that)
&& equalsAdministratorUids(that)
- && equalsSubscriptionIds(that);
+ && equalsSubscriptionIds(that)
+ && equalsUnderlyingNetworks(that);
}
@Override
@@ -1903,7 +1861,8 @@
+ Objects.hashCode(mRequestorUid) * 53
+ Objects.hashCode(mRequestorPackageName) * 59
+ Arrays.hashCode(mAdministratorUids) * 61
- + Objects.hashCode(mSubIds) * 67;
+ + Objects.hashCode(mSubIds) * 67
+ + Objects.hashCode(mUnderlyingNetworks) * 71;
}
@Override
@@ -1938,6 +1897,7 @@
dest.writeInt(mRequestorUid);
dest.writeString(mRequestorPackageName);
dest.writeIntArray(CollectionUtils.toIntArray(mSubIds));
+ dest.writeTypedList(mUnderlyingNetworks);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1966,6 +1926,7 @@
for (int i = 0; i < subIdInts.length; i++) {
netCap.mSubIds.add(subIdInts[i]);
}
+ netCap.setUnderlyingNetworks(in.createTypedArrayList(Network.CREATOR));
return netCap;
}
@Override
@@ -2057,6 +2018,19 @@
sb.append(" SubscriptionIds: ").append(mSubIds);
}
+ sb.append(" UnderlyingNetworks: ");
+ if (mUnderlyingNetworks != null) {
+ sb.append("[");
+ final StringJoiner joiner = new StringJoiner(",");
+ for (int i = 0; i < mUnderlyingNetworks.size(); i++) {
+ joiner.add(mUnderlyingNetworks.get(i).toString());
+ }
+ sb.append(joiner.toString());
+ sb.append("]");
+ } else {
+ sb.append("Null");
+ }
+
sb.append("]");
return sb.toString();
}
@@ -2322,25 +2296,6 @@
return TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
}
- /**
- * Combine requestor info of the capabilities.
- * <p>
- * This is only legal if either the requestor info of this object is reset, or both info are
- * equal.
- * nc is assumed nonnull.
- */
- private void combineRequestor(@NonNull NetworkCapabilities nc) {
- if (mRequestorUid != Process.INVALID_UID && mRequestorUid != nc.mOwnerUid) {
- throw new IllegalStateException("Can't combine two uids");
- }
- if (mRequestorPackageName != null
- && !mRequestorPackageName.equals(nc.mRequestorPackageName)) {
- throw new IllegalStateException("Can't combine two package names");
- }
- setRequestorUid(nc.mRequestorUid);
- setRequestorPackageName(nc.mRequestorPackageName);
- }
-
private boolean equalsRequestor(NetworkCapabilities nc) {
return mRequestorUid == nc.mRequestorUid
&& TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
@@ -2400,20 +2355,6 @@
}
/**
- * Combine subscription ID set of the capabilities.
- *
- * <p>This is only legal if the subscription Ids are equal.
- *
- * <p>If both subscription IDs are not equal, they belong to different subscription
- * (or no subscription). In this case, it would not make sense to add them together.
- */
- private void combineSubscriptionIds(@NonNull NetworkCapabilities nc) {
- if (!Objects.equals(mSubIds, nc.mSubIds)) {
- throw new IllegalStateException("Can't combine two subscription ID sets");
- }
- }
-
- /**
* Returns a bitmask of all the applicable redactions (based on the permissions held by the
* receiving app) to be performed on this object.
*
@@ -2775,6 +2716,17 @@
}
/**
+ * Set the underlying networks of this network.
+ *
+ * @param networks The underlying networks of this network.
+ */
+ @NonNull
+ public Builder setUnderlyingNetworks(@Nullable List<Network> networks) {
+ mCaps.setUnderlyingNetworks(networks);
+ return this;
+ }
+
+ /**
* Builds the instance of the capabilities.
*
* @return the built instance of NetworkCapabilities.
diff --git a/framework/src/android/net/NetworkInfo.java b/framework/src/android/net/NetworkInfo.java
index bb23494..b7ec519 100644
--- a/framework/src/android/net/NetworkInfo.java
+++ b/framework/src/android/net/NetworkInfo.java
@@ -24,6 +24,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
import java.util.EnumMap;
@@ -179,21 +180,23 @@
/** {@hide} */
@UnsupportedAppUsage
- public NetworkInfo(NetworkInfo source) {
- if (source != null) {
- synchronized (source) {
- mNetworkType = source.mNetworkType;
- mSubtype = source.mSubtype;
- mTypeName = source.mTypeName;
- mSubtypeName = source.mSubtypeName;
- mState = source.mState;
- mDetailedState = source.mDetailedState;
- mReason = source.mReason;
- mExtraInfo = source.mExtraInfo;
- mIsFailover = source.mIsFailover;
- mIsAvailable = source.mIsAvailable;
- mIsRoaming = source.mIsRoaming;
- }
+ public NetworkInfo(@NonNull NetworkInfo source) {
+ // S- didn't use to crash when passing null. This plants a timebomb where mState and
+ // some other fields are null, but there may be existing code that relies on this behavior
+ // and doesn't trip the timebomb, so on SdkLevel < T, keep the old behavior. b/145972387
+ if (null == source && !SdkLevel.isAtLeastT()) return;
+ synchronized (source) {
+ mNetworkType = source.mNetworkType;
+ mSubtype = source.mSubtype;
+ mTypeName = source.mTypeName;
+ mSubtypeName = source.mSubtypeName;
+ mState = source.mState;
+ mDetailedState = source.mDetailedState;
+ mReason = source.mReason;
+ mExtraInfo = source.mExtraInfo;
+ mIsFailover = source.mIsFailover;
+ mIsAvailable = source.mIsAvailable;
+ mIsRoaming = source.mIsRoaming;
}
}
@@ -479,7 +482,7 @@
* @param detailedState the {@link DetailedState}.
* @param reason a {@code String} indicating the reason for the state change,
* if one was supplied. May be {@code null}.
- * @param extraInfo an optional {@code String} providing addditional network state
+ * @param extraInfo an optional {@code String} providing additional network state
* information passed up from the lower networking layers.
* @deprecated Use {@link NetworkCapabilities} instead.
*/
@@ -491,6 +494,12 @@
this.mState = stateMap.get(detailedState);
this.mReason = reason;
this.mExtraInfo = extraInfo;
+ // Catch both the case where detailedState is null and the case where it's some
+ // unknown value. This is clearly incorrect usage, but S- didn't use to crash (at
+ // least immediately) so keep the old behavior on older frameworks for safety.
+ if (null == mState && SdkLevel.isAtLeastT()) {
+ throw new NullPointerException("Unknown DetailedState : " + detailedState);
+ }
}
}
diff --git a/framework/src/android/net/util/MultinetworkPolicyTracker.java b/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 3e7cb80..c1790c9 100644
--- a/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -20,6 +20,7 @@
import static android.net.ConnectivitySettingsManager.NETWORK_METERED_MULTIPATH_PREFERENCE;
import android.annotation.NonNull;
+import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -29,6 +30,7 @@
import android.database.ContentObserver;
import android.net.ConnectivityResources;
import android.net.Uri;
+import android.os.Build;
import android.os.Handler;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
@@ -92,8 +94,8 @@
}
}
}
-
- @VisibleForTesting
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+ @VisibleForTesting @TargetApi(Build.VERSION_CODES.S)
protected class ActiveDataSubscriptionIdListener extends TelephonyCallback
implements TelephonyCallback.ActiveDataSubscriptionIdListener {
@Override
@@ -107,6 +109,8 @@
this(ctx, handler, null);
}
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+ @TargetApi(Build.VERSION_CODES.S)
public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) {
mContext = ctx;
mResources = new ConnectivityResources(ctx);
diff --git a/service/Android.bp b/service/Android.bp
index abaeeb3..b595ef2 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -65,13 +65,13 @@
"ServiceConnectivityResources",
],
static_libs: [
- "dnsresolver_aidl_interface-V8-java",
+ "dnsresolver_aidl_interface-V9-java",
"modules-utils-build",
- "modules-utils-os",
+ "modules-utils-shell-command-handler",
"net-utils-device-common",
+ "net-utils-device-common-netlink",
"net-utils-framework-common",
"netd-client",
- "netlink-client",
"networkstack-client",
"PlatformProperties",
"service-connectivity-protos",
@@ -79,6 +79,7 @@
apex_available: [
"com.android.tethering",
],
+ lint: { strict_updatability_linting: true },
}
java_library {
@@ -95,6 +96,7 @@
apex_available: [
"com.android.tethering",
],
+ lint: { strict_updatability_linting: true },
}
java_library {
@@ -109,6 +111,7 @@
apex_available: [
"com.android.tethering",
],
+ lint: { strict_updatability_linting: true },
}
filegroup {
diff --git a/service/ServiceConnectivityResources/res/values-eu/strings.xml b/service/ServiceConnectivityResources/res/values-eu/strings.xml
index 6b0d8ef..2c4e431 100644
--- a/service/ServiceConnectivityResources/res/values-eu/strings.xml
+++ b/service/ServiceConnectivityResources/res/values-eu/strings.xml
@@ -30,7 +30,7 @@
<string name="network_partial_connectivity" msgid="5549503845834993258">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> sareak konektagarritasun murriztua du"</string>
<string name="network_partial_connectivity_detailed" msgid="4732435946300249845">"Sakatu hala ere konektatzeko"</string>
<string name="network_switch_metered" msgid="5016937523571166319">"<xliff:g id="NETWORK_TYPE">%1$s</xliff:g> erabiltzen ari zara orain"</string>
- <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> Internetera konektatzeko gauza ez denean, <xliff:g id="NEW_NETWORK">%1$s</xliff:g> erabiltzen du gailuak. Agian kostuak ordaindu beharko dituzu."</string>
+ <string name="network_switch_metered_detail" msgid="1257300152739542096">"<xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> Internetera konektatzeko gauza ez denean, <xliff:g id="NEW_NETWORK">%1$s</xliff:g> erabiltzen du gailuak. Baliteke zerbait ordaindu behar izatea."</string>
<string name="network_switch_metered_toast" msgid="70691146054130335">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> erabiltzen ari zinen, baina <xliff:g id="NEW_NETWORK">%2$s</xliff:g> erabiltzen ari zara orain"</string>
<string-array name="network_switch_type_name">
<item msgid="3004933964374161223">"datu-konexioa"</item>
diff --git a/service/ServiceConnectivityResources/res/values/config.xml b/service/ServiceConnectivityResources/res/values/config.xml
index bf32ad5..f8f86a2 100644
--- a/service/ServiceConnectivityResources/res/values/config.xml
+++ b/service/ServiceConnectivityResources/res/values/config.xml
@@ -114,4 +114,26 @@
<!-- Whether to cancel network notifications automatically when tapped -->
<bool name="config_autoCancelNetworkNotifications">true</bool>
+ <!-- When no internet or partial connectivity is detected on a network, and a high priority
+ (heads up) notification would be shown due to the network being explicitly selected,
+ directly show the dialog that would normally be shown when tapping the notification
+ instead of showing the notification. -->
+ <bool name="config_notifyNoInternetAsDialogWhenHighPriority">false</bool>
+
+ <!-- When showing notifications indicating partial connectivity, display the same notifications
+ as no connectivity instead. This may be easier to understand for users but offers less
+ details on what is happening. -->
+ <bool name="config_partialConnectivityNotifiedAsNoInternet">false</bool>
+
+ <!-- Whether the cell radio of the device is capable of timesharing.
+
+ Whether the cell radio is capable of timesharing between two different networks
+ even for a few seconds. When this is false, the networking stack will ask telephony
+ networks to disconnect immediately, instead of lingering, when outscored by some
+ other telephony network (typically on another subscription). This deprives apps
+ of a chance to gracefully migrate to the new network and degrades the experience
+ for apps, so it should only be set to false when timesharing on the cell radio has
+ extreme adverse effects on performance of the new network.
+ -->
+ <bool translatable="false" name="config_cellular_radio_timesharing_capable">true</bool>
</resources>
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index 6ac6a0e..e5010d7 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -32,8 +32,11 @@
<item type="array" name="config_networkNotifySwitches"/>
<item type="bool" name="config_ongoingSignInNotification"/>
<item type="bool" name="config_autoCancelNetworkNotifications"/>
+ <item type="bool" name="config_notifyNoInternetAsDialogWhenHighPriority"/>
+ <item type="bool" name="config_partialConnectivityNotifiedAsNoInternet"/>
<item type="drawable" name="stat_notify_wifi_in_range"/>
<item type="drawable" name="stat_notify_rssi_in_range"/>
+ <item type="bool" name="config_cellular_radio_timesharing_capable" />
</policy>
</overlayable>
</resources>
diff --git a/service/jarjar-rules.txt b/service/jarjar-rules.txt
index 4ba6837..e5d1a88 100644
--- a/service/jarjar-rules.txt
+++ b/service/jarjar-rules.txt
@@ -8,10 +8,7 @@
# the one in com.android.internal.util
rule android.util.IndentingPrintWriter* com.android.connectivity.@0
rule com.android.internal.util.IndentingPrintWriter* com.android.connectivity.@0
-rule com.android.internal.util.IState* com.android.connectivity.@0
rule com.android.internal.util.MessageUtils* com.android.connectivity.@0
-rule com.android.internal.util.State* com.android.connectivity.@0
-rule com.android.internal.util.StateMachine* com.android.connectivity.@0
rule com.android.internal.util.WakeupMessage* com.android.connectivity.@0
rule com.android.internal.messages.** com.android.connectivity.@0
diff --git a/service/jni/com_android_server_TestNetworkService.cpp b/service/jni/com_android_server_TestNetworkService.cpp
index e7a40e5..1a0de32 100644
--- a/service/jni/com_android_server_TestNetworkService.cpp
+++ b/service/jni/com_android_server_TestNetworkService.cpp
@@ -66,6 +66,8 @@
// Activate interface using an unconnected datagram socket.
base::unique_fd inet6CtrlSock(socket(AF_INET6, SOCK_DGRAM, 0));
ifr.ifr_flags = IFF_UP;
+ // Mark TAP interfaces as supporting multicast
+ if (!isTun) ifr.ifr_flags |= IFF_MULTICAST;
if (ioctl(inet6CtrlSock.get(), SIOCSIFFLAGS, &ifr)) {
throwException(env, errno, "activating", ifr.ifr_name);
diff --git a/service/lint-baseline.xml b/service/lint-baseline.xml
deleted file mode 100644
index 119b64f..0000000
--- a/service/lint-baseline.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?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 31 (current min is 30): `android.telephony.TelephonyManager#isDataCapable`"
- errorLine1=" if (tm.isDataCapable()) {"
- errorLine2=" ~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java"
- line="787"
- column="20"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.content.Context#sendStickyBroadcast`"
- errorLine1=" mUserAllContext.sendStickyBroadcast(intent, options);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java"
- line="2681"
- column="33"/>
- </issue>
-
- <issue
- id="NewApi"
- message="Call requires API level 31 (current min is 30): `android.content.pm.PackageManager#getTargetSdkVersion`"
- errorLine1=" final int callingVersion = pm.getTargetSdkVersion(callingPackageName);"
- errorLine2=" ~~~~~~~~~~~~~~~~~~~">
- <location
- file="packages/modules/Connectivity/service/src/com/android/server/ConnectivityService.java"
- line="5851"
- column="43"/>
- </issue>
-
-</issues>
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index b126791..d507b4b 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -81,19 +81,24 @@
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkRequest.Type.LISTEN_FOR_BEST;
+import static android.net.NetworkScore.POLICY_TRANSPORT_PRIMARY;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST;
import static android.net.OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY;
import static android.net.shared.NetworkMonitorUtils.isPrivateDnsValidationRequired;
import static android.os.Process.INVALID_UID;
import static android.os.Process.VPN_UID;
+import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
+import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
+
import static java.util.Map.Entry;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
@@ -180,7 +185,6 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.netd.aidl.NativeUidRangeConfig;
-import android.net.netlink.InetDiagMessage;
import android.net.networkstack.ModuleNetworkStackClient;
import android.net.networkstack.NetworkStackClientBase;
import android.net.resolv.aidl.DnsHealthEventParcel;
@@ -229,14 +233,18 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.DeviceConfigUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
import com.android.net.module.util.LocationPermissionChecker;
import com.android.net.module.util.NetworkCapabilitiesUtils;
import com.android.net.module.util.PermissionUtils;
+import com.android.net.module.util.netlink.InetDiagMessage;
import com.android.server.connectivity.AutodestructReference;
+import com.android.server.connectivity.ConnectivityFlags;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.FullScore;
@@ -258,6 +266,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.io.Writer;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -272,6 +281,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
@@ -325,7 +335,8 @@
private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
// The maximum number of network request allowed per uid before an exception is thrown.
- private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
+ @VisibleForTesting
+ static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
// The maximum number of network request allowed for system UIDs before an exception is thrown.
@VisibleForTesting
@@ -335,6 +346,9 @@
protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
@VisibleForTesting
protected int mNascentDelayMs;
+ // True if the cell radio of the device is capable of time-sharing.
+ @VisibleForTesting
+ protected boolean mCellularRadioTimesharingCapable = true;
// How long to delay to removal of a pending intent based request.
// See ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
@@ -345,7 +359,8 @@
@VisibleForTesting
protected final PermissionMonitor mPermissionMonitor;
- private final PerUidCounter mNetworkRequestCounter;
+ @VisibleForTesting
+ final PerUidCounter mNetworkRequestCounter;
@VisibleForTesting
final PerUidCounter mSystemNetworkRequestCounter;
@@ -362,6 +377,7 @@
// The Context is created for UserHandle.ALL.
private final Context mUserAllContext;
private final Dependencies mDeps;
+ private final ConnectivityFlags mFlags;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -838,6 +854,9 @@
mTypeLists = new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE + 1];
}
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is
+ // addressed.
+ @TargetApi(Build.VERSION_CODES.S)
public void loadSupportedTypes(@NonNull Context ctx, @NonNull TelephonyManager tm) {
final PackageManager pm = ctx.getPackageManager();
if (pm.hasSystemFeature(FEATURE_WIFI)) {
@@ -1155,9 +1174,20 @@
private void incrementCountOrThrow(final int uid, final int numToIncrement) {
final int newRequestCount =
mUidToNetworkRequestCount.get(uid, 0) + numToIncrement;
- if (newRequestCount >= mMaxCountPerUid) {
+ if (newRequestCount >= mMaxCountPerUid
+ // HACK : the system server is allowed to go over the request count limit
+ // when it is creating requests on behalf of another app (but not itself,
+ // so it can still detect its own request leaks). This only happens in the
+ // per-app API flows in which case the old requests for that particular
+ // UID will be removed soon.
+ // TODO : instead of this hack, addPerAppDefaultNetworkRequests and other
+ // users of transact() should unregister the requests to decrease the count
+ // before they increase it again by creating a new NRI. Then remove the
+ // transact() method.
+ && (Process.myUid() == uid || Process.myUid() != Binder.getCallingUid())) {
throw new ServiceSpecificException(
- ConnectivityManager.Errors.TOO_MANY_REQUESTS);
+ ConnectivityManager.Errors.TOO_MANY_REQUESTS,
+ "Uid " + uid + " exceeded its allotted requests limit");
}
mUidToNetworkRequestCount.put(uid, newRequestCount);
}
@@ -1184,35 +1214,6 @@
mUidToNetworkRequestCount.put(uid, newRequestCount);
}
}
-
- /**
- * Used to adjust the request counter for the per-app API flows. Directly adjusting the
- * counter is not ideal however in the per-app flows, the nris can't be removed until they
- * are used to create the new nris upon set. Therefore the request count limit can be
- * artificially hit. This method is used as a workaround for this particular case so that
- * the request counts are accounted for correctly.
- * @param uid the uid to adjust counts for
- * @param numOfNewRequests the new request count to account for
- * @param r the runnable to execute
- */
- public void transact(final int uid, final int numOfNewRequests, @NonNull final Runnable r) {
- // This should only be used on the handler thread as per all current and foreseen
- // use-cases. ensureRunningOnConnectivityServiceThread() can't be used because there is
- // no ref to the outer ConnectivityService.
- synchronized (mUidToNetworkRequestCount) {
- final int reqCountOverage = getCallingUidRequestCountOverage(uid, numOfNewRequests);
- decrementCount(uid, reqCountOverage);
- r.run();
- incrementCountOrThrow(uid, reqCountOverage);
- }
- }
-
- private int getCallingUidRequestCountOverage(final int uid, final int numOfNewRequests) {
- final int newUidRequestCount = mUidToNetworkRequestCount.get(uid, 0)
- + numOfNewRequests;
- return newUidRequestCount >= MAX_NETWORK_REQUESTS_PER_SYSTEM_UID
- ? newUidRequestCount - (MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - 1) : 0;
- }
}
/**
@@ -1318,6 +1319,14 @@
public LocationPermissionChecker makeLocationPermissionChecker(Context context) {
return new LocationPermissionChecker(context);
}
+
+ /**
+ * @see DeviceConfigUtils#isFeatureEnabled
+ */
+ public boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled) {
+ return DeviceConfigUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name,
+ TETHERING_MODULE_NAME, defaultEnabled);
+ }
}
public ConnectivityService(Context context) {
@@ -1332,6 +1341,7 @@
if (DBG) log("ConnectivityService starting up");
mDeps = Objects.requireNonNull(deps, "missing Dependencies");
+ mFlags = new ConnectivityFlags();
mSystemProperties = mDeps.getSystemProperties();
mNetIdManager = mDeps.makeNetIdManager();
mContext = Objects.requireNonNull(context, "missing Context");
@@ -1344,7 +1354,7 @@
final NetworkRequest defaultInternetRequest = createDefaultRequest();
mDefaultRequest = new NetworkRequestInfo(
Process.myUid(), defaultInternetRequest, null,
- new Binder(), NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
+ null /* binder */, NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
null /* attributionTags */);
mNetworkRequests.put(defaultInternetRequest, mDefaultRequest);
mDefaultNetworkRequests.add(mDefaultRequest);
@@ -1363,6 +1373,12 @@
NetworkCapabilities.NET_CAPABILITY_VEHICLE_INTERNAL,
NetworkRequest.Type.BACKGROUND_REQUEST);
+ mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+ // TODO: Consider making the timer customizable.
+ mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
+ mCellularRadioTimesharingCapable =
+ mResources.get().getBoolean(R.bool.config_cellular_radio_timesharing_capable);
+
mHandlerThread = mDeps.makeHandlerThread();
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
@@ -1373,10 +1389,6 @@
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
ConnectivitySettingsManager.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
- mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
- // TODO: Consider making the timer customizable.
- mNascentDelayMs = DEFAULT_NASCENT_DELAY_MS;
-
mStatsManager = mContext.getSystemService(NetworkStatsManager.class);
mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
mDnsResolver = Objects.requireNonNull(dnsresolver, "missing IDnsResolver");
@@ -1562,7 +1574,7 @@
if (enable) {
handleRegisterNetworkRequest(new NetworkRequestInfo(
- Process.myUid(), networkRequest, null, new Binder(),
+ Process.myUid(), networkRequest, null /* messenger */, null /* binder */,
NetworkCallback.FLAG_INCLUDE_LOCATION_INFO,
null /* attributionTags */));
} else {
@@ -2042,6 +2054,7 @@
if (!checkSettingsPermission(callerPid, callerUid)) {
newNc.setUids(null);
newNc.setSSID(null);
+ newNc.setUnderlyingNetworks(null);
}
if (newNc.getNetworkSpecifier() != null) {
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
@@ -2306,9 +2319,7 @@
final ArrayList<NetworkStateSnapshot> result = new ArrayList<>();
for (Network network : getAllNetworks()) {
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
- // TODO: Consider include SUSPENDED networks, which should be considered as
- // temporary shortage of connectivity of a connected network.
- if (nai != null && nai.networkInfo.isConnected()) {
+ if (nai != null && nai.everConnected) {
// TODO (b/73321673) : NetworkStateSnapshot contains a copy of the
// NetworkCapabilities, which may contain UIDs of apps to which the
// network applies. Should the UIDs be cleared so as not to leak or
@@ -2349,6 +2360,26 @@
return false;
}
+ private int getAppUid(final String app, final UserHandle user) {
+ final PackageManager pm =
+ mContext.createContextAsUser(user, 0 /* flags */).getPackageManager();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return pm.getPackageUid(app, 0 /* flags */);
+ } catch (PackageManager.NameNotFoundException e) {
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void verifyCallingUidAndPackage(String packageName, int callingUid) {
+ final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
+ if (getAppUid(packageName, user) != callingUid) {
+ throw new SecurityException(packageName + " does not belong to uid " + callingUid);
+ }
+ }
+
/**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
@@ -2364,6 +2395,7 @@
if (disallowedBecauseSystemCaller()) {
return false;
}
+ verifyCallingUidAndPackage(callingPackageName, mDeps.getCallingUid());
enforceChangePermission(callingPackageName, callingAttributionTag);
if (mProtectedNetworks.contains(networkType)) {
enforceConnectivityRestrictedNetworksPermission();
@@ -2753,6 +2785,8 @@
sendStickyBroadcast(makeGeneralIntent(info, bcastType));
}
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+ @TargetApi(Build.VERSION_CODES.S)
private void sendStickyBroadcast(Intent intent) {
synchronized (this) {
if (!mSystemReady
@@ -2798,6 +2832,9 @@
*/
@VisibleForTesting
public void systemReadyInternal() {
+ // Load flags after PackageManager is ready to query module version
+ mFlags.loadFlags(mDeps, mContext);
+
// Since mApps in PermissionMonitor needs to be populated first to ensure that
// listening network request which is sent by MultipathPolicyTracker won't be added
// NET_CAPABILITY_FOREGROUND capability. Thus, MultipathPolicyTracker.start() must
@@ -3005,9 +3042,9 @@
}
pw.println();
- pw.print("Current per-app default networks: ");
+ pw.println("Current network preferences: ");
pw.increaseIndent();
- dumpPerAppNetworkPreferences(pw);
+ dumpNetworkPreferences(pw);
pw.decreaseIndent();
pw.println();
@@ -3135,37 +3172,55 @@
}
}
- private void dumpPerAppNetworkPreferences(IndentingPrintWriter pw) {
- pw.println("Per-App Network Preference:");
- pw.increaseIndent();
- if (0 == mOemNetworkPreferences.getNetworkPreferences().size()) {
- pw.println("none");
- } else {
- pw.println(mOemNetworkPreferences.toString());
+ private void dumpNetworkPreferences(IndentingPrintWriter pw) {
+ if (!mProfileNetworkPreferences.isEmpty()) {
+ pw.println("Profile preferences:");
+ pw.increaseIndent();
+ pw.println(mProfileNetworkPreferences.preferences);
+ pw.decreaseIndent();
}
- pw.decreaseIndent();
+ if (!mOemNetworkPreferences.isEmpty()) {
+ pw.println("OEM preferences:");
+ pw.increaseIndent();
+ pw.println(mOemNetworkPreferences);
+ pw.decreaseIndent();
+ }
+ if (!mMobileDataPreferredUids.isEmpty()) {
+ pw.println("Mobile data preferred UIDs:");
+ pw.increaseIndent();
+ pw.println(mMobileDataPreferredUids);
+ pw.decreaseIndent();
+ }
+ pw.println("Default requests:");
+ pw.increaseIndent();
+ dumpPerAppDefaultRequests(pw);
+ pw.decreaseIndent();
+ }
+
+ private void dumpPerAppDefaultRequests(IndentingPrintWriter pw) {
for (final NetworkRequestInfo defaultRequest : mDefaultNetworkRequests) {
if (mDefaultRequest == defaultRequest) {
continue;
}
- final boolean isActive = null != defaultRequest.getSatisfier();
- pw.println("Is per-app network active:");
- pw.increaseIndent();
- pw.println(isActive);
- if (isActive) {
- pw.println("Active network: " + defaultRequest.getSatisfier().network.netId);
- }
- pw.println("Tracked UIDs:");
- pw.increaseIndent();
- if (0 == defaultRequest.mRequests.size()) {
- pw.println("none, this should never occur.");
+ final NetworkAgentInfo satisfier = defaultRequest.getSatisfier();
+ final String networkOutput;
+ if (null == satisfier) {
+ networkOutput = "null";
+ } else if (mNoServiceNetwork.equals(satisfier)) {
+ networkOutput = "no service network";
} else {
- pw.println(defaultRequest.mRequests.get(0).networkCapabilities.getUidRanges());
+ networkOutput = String.valueOf(satisfier.network.netId);
}
- pw.decreaseIndent();
- pw.decreaseIndent();
+ final String asUidString = (defaultRequest.mAsUid == defaultRequest.mUid)
+ ? "" : " asUid: " + defaultRequest.mAsUid;
+ final String requestInfo = "Request: [uid/pid:" + defaultRequest.mUid + "/"
+ + defaultRequest.mPid + asUidString + "]";
+ final String satisfierOutput = "Satisfier: [" + networkOutput + "]"
+ + " Preference order: " + defaultRequest.mPreferenceOrder
+ + " Tracked UIDs: " + defaultRequest.getUids();
+ pw.println(requestInfo + " - " + satisfierOutput);
}
}
@@ -3186,6 +3241,22 @@
}
}
+ private void dumpAllRequestInfoLogsToLogcat() {
+ try (PrintWriter logPw = new PrintWriter(new Writer() {
+ @Override
+ public void write(final char[] cbuf, final int off, final int len) {
+ // This method is called with 0-length and 1-length arrays for empty strings
+ // or strings containing only the DEL character.
+ if (len <= 1) return;
+ Log.e(TAG, new String(cbuf, off, len));
+ }
+ @Override public void flush() {}
+ @Override public void close() {}
+ })) {
+ mNetworkRequestInfoLogs.dump(logPw);
+ }
+ }
+
/**
* Return an array of all current NetworkAgentInfos sorted by network id.
*/
@@ -3250,7 +3321,7 @@
// the Messenger, but if this ever changes, not making a defensive copy
// here will give attack vectors to clients using this code path.
networkCapabilities = new NetworkCapabilities(networkCapabilities);
- networkCapabilities.restrictCapabilitesForTestNetwork(nai.creatorUid);
+ networkCapabilities.restrictCapabilitiesForTestNetwork(nai.creatorUid);
}
processCapabilitiesFromAgent(nai, networkCapabilities);
updateCapabilities(nai.getCurrentScore(), nai, networkCapabilities);
@@ -3839,9 +3910,7 @@
private void handleNetworkAgentDisconnected(Message msg) {
NetworkAgentInfo nai = (NetworkAgentInfo) msg.obj;
- if (mNetworkAgentInfos.contains(nai)) {
- disconnectAndDestroyNetwork(nai);
- }
+ disconnectAndDestroyNetwork(nai);
}
// Destroys a network, remove references to it from the internal state managed by
@@ -3849,6 +3918,9 @@
// Must be called on the Handler thread.
private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
ensureRunningOnConnectivityServiceThread();
+
+ if (!mNetworkAgentInfos.contains(nai)) return;
+
if (DBG) {
log(nai.toShortString() + " disconnected, was satisfying " + nai.numNetworkRequests());
}
@@ -3931,10 +4003,12 @@
}
// Delayed teardown.
- try {
- mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM);
- } catch (RemoteException e) {
- Log.d(TAG, "Error marking network restricted during teardown: " + e);
+ if (nai.created) {
+ try {
+ mNetd.networkSetPermissionForNetwork(nai.network.netId, INetd.PERMISSION_SYSTEM);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Error marking network restricted during teardown: ", e);
+ }
}
mHandler.postDelayed(() -> destroyNetwork(nai), nai.teardownDelayMs);
}
@@ -4011,6 +4085,20 @@
return null;
}
+ private void checkNrisConsistency(final NetworkRequestInfo nri) {
+ if (SdkLevel.isAtLeastT()) {
+ for (final NetworkRequestInfo n : mNetworkRequests.values()) {
+ if (n.mBinder != null && n.mBinder == nri.mBinder) {
+ // Temporary help to debug b/194394697 ; TODO : remove this function when the
+ // bug is fixed.
+ dumpAllRequestInfoLogsToLogcat();
+ throw new IllegalStateException("This NRI is already registered. New : " + nri
+ + ", existing : " + n);
+ }
+ }
+ }
+ }
+
private void handleRegisterNetworkRequestWithIntent(@NonNull final Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
// handleRegisterNetworkRequestWithIntent() doesn't apply to multilayer requests.
@@ -4036,6 +4124,7 @@
ensureRunningOnConnectivityServiceThread();
for (final NetworkRequestInfo nri : nris) {
mNetworkRequestInfoLogs.log("REGISTER " + nri);
+ checkNrisConsistency(nri);
for (final NetworkRequest req : nri.mRequests) {
mNetworkRequests.put(req, nri);
// TODO: Consider update signal strength for other types.
@@ -4057,7 +4146,11 @@
}
}
- rematchAllNetworksAndRequests();
+ if (mFlags.noRematchAllRequestsOnRegister()) {
+ rematchNetworksAndRequests(nris);
+ } else {
+ rematchAllNetworksAndRequests();
+ }
// Requests that have not been matched to a network will not have been sent to the
// providers, because the old satisfier and the new satisfier are the same (null in this
@@ -4272,12 +4365,11 @@
}
nri.decrementRequestCount();
mNetworkRequestInfoLogs.log("RELEASE " + nri);
+ checkNrisConsistency(nri);
if (null != nri.getActiveRequest()) {
if (!nri.getActiveRequest().isListen()) {
removeSatisfiedNetworkRequestFromNetwork(nri);
- } else {
- nri.setSatisfier(null, null);
}
}
@@ -4342,7 +4434,6 @@
} else {
wasKept = true;
}
- nri.setSatisfier(null, null);
if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
// Went from foreground to background.
updateCapabilitiesForNetwork(nai);
@@ -4617,9 +4708,16 @@
}
private void updateAvoidBadWifi() {
+ ensureRunningOnConnectivityServiceThread();
+ // Agent info scores and offer scores depend on whether cells yields to bad wifi.
for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
nai.updateScoreForNetworkAgentUpdate();
}
+ // UpdateOfferScore will update mNetworkOffers inline, so make a copy first.
+ final ArrayList<NetworkOfferInfo> offersToUpdate = new ArrayList<>(mNetworkOffers);
+ for (final NetworkOfferInfo noi : offersToUpdate) {
+ updateOfferScore(noi.offer);
+ }
rematchAllNetworksAndRequests();
}
@@ -5853,7 +5951,7 @@
mUid = nri.mUid;
mAsUid = nri.mAsUid;
mPendingIntent = nri.mPendingIntent;
- mPerUidCounter = getRequestCounter(this);
+ mPerUidCounter = nri.mPerUidCounter;
mPerUidCounter.incrementCountOrThrow(mUid);
mCallbackFlags = nri.mCallbackFlags;
mCallingAttributionTag = nri.mCallingAttributionTag;
@@ -5905,7 +6003,12 @@
void unlinkDeathRecipient() {
if (null != mBinder) {
- mBinder.unlinkToDeath(this, 0);
+ try {
+ mBinder.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ // Temporary workaround for b/194394697 pending analysis of additional logs
+ Log.wtf(TAG, "unlinkToDeath for already unlinked NRI " + this);
+ }
}
}
@@ -5925,7 +6028,7 @@
@Override
public void binderDied() {
log("ConnectivityService NetworkRequestInfo binderDied(" +
- "uid/pid:" + mUid + "/" + mPid + ", " + mBinder + ")");
+ "uid/pid:" + mUid + "/" + mPid + ", " + mRequests + ", " + mBinder + ")");
// As an immutable collection, mRequests cannot change by the time the
// lambda is evaluated on the handler thread so calling .get() from a binder thread
// is acceptable. Use handleReleaseNetworkRequest and not directly
@@ -6033,6 +6136,8 @@
}
}
+ // TODO: Set the mini sdk to 31 and remove @TargetApi annotation when b/205923322 is addressed.
+ @TargetApi(Build.VERSION_CODES.S)
private boolean isTargetSdkAtleast(int version, int callingUid,
@NonNull String callingPackageName) {
final UserHandle user = UserHandle.getUserHandleForUid(callingUid);
@@ -6047,7 +6152,7 @@
@Override
public NetworkRequest requestNetwork(int asUid, NetworkCapabilities networkCapabilities,
- int reqTypeInt, Messenger messenger, int timeoutMs, IBinder binder,
+ int reqTypeInt, Messenger messenger, int timeoutMs, final IBinder binder,
int legacyType, int callbackFlags, @NonNull String callingPackageName,
@Nullable String callingAttributionTag) {
if (legacyType != TYPE_NONE && !checkNetworkStackPermission()) {
@@ -6408,11 +6513,23 @@
Objects.requireNonNull(score);
Objects.requireNonNull(caps);
Objects.requireNonNull(callback);
+ final boolean yieldToBadWiFi = caps.hasTransport(TRANSPORT_CELLULAR) && !avoidBadWifi();
final NetworkOffer offer = new NetworkOffer(
- FullScore.makeProspectiveScore(score, caps), caps, callback, providerId);
+ FullScore.makeProspectiveScore(score, caps, yieldToBadWiFi),
+ caps, callback, providerId);
mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_OFFER, offer));
}
+ private void updateOfferScore(final NetworkOffer offer) {
+ final boolean yieldToBadWiFi =
+ offer.caps.hasTransport(TRANSPORT_CELLULAR) && !avoidBadWifi();
+ final NetworkOffer newOffer = new NetworkOffer(
+ offer.score.withYieldToBadWiFi(yieldToBadWiFi),
+ offer.caps, offer.callback, offer.providerId);
+ if (offer.equals(newOffer)) return;
+ handleRegisterNetworkOffer(newOffer);
+ }
+
@Override
public void unofferNetwork(@NonNull final INetworkOfferCallback callback) {
mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_OFFER, callback));
@@ -6759,7 +6876,7 @@
// the call to mixInCapabilities below anyway, but sanitizing here means the NAI never
// sees capabilities that may be malicious, which might prevent mistakes in the future.
networkCapabilities = new NetworkCapabilities(networkCapabilities);
- networkCapabilities.restrictCapabilitesForTestNetwork(uid);
+ networkCapabilities.restrictCapabilitiesForTestNetwork(uid);
}
LinkProperties lp = new LinkProperties(linkProperties);
@@ -6833,6 +6950,7 @@
* @param newOffer The new offer. If the callback member is the same as an existing
* offer, it is an update of that offer.
*/
+ // TODO : rename this to handleRegisterOrUpdateNetworkOffer
private void handleRegisterNetworkOffer(@NonNull final NetworkOffer newOffer) {
ensureRunningOnConnectivityServiceThread();
if (!isNetworkProviderWithIdRegistered(newOffer.providerId)) {
@@ -6846,6 +6964,14 @@
if (null != existingOffer) {
handleUnregisterNetworkOffer(existingOffer);
newOffer.migrateFrom(existingOffer.offer);
+ if (DBG) {
+ // handleUnregisterNetworkOffer has already logged the old offer
+ log("update offer from providerId " + newOffer.providerId + " new : " + newOffer);
+ }
+ } else {
+ if (DBG) {
+ log("register offer from providerId " + newOffer.providerId + " : " + newOffer);
+ }
}
final NetworkOfferInfo noi = new NetworkOfferInfo(newOffer);
try {
@@ -6860,7 +6986,14 @@
private void handleUnregisterNetworkOffer(@NonNull final NetworkOfferInfo noi) {
ensureRunningOnConnectivityServiceThread();
- mNetworkOffers.remove(noi);
+ if (DBG) {
+ log("unregister offer from providerId " + noi.offer.providerId + " : " + noi.offer);
+ }
+
+ // If the provider removes the offer and dies immediately afterwards this
+ // function may be called twice in a row, but the array will no longer contain
+ // the offer.
+ if (!mNetworkOffers.remove(noi)) return;
noi.offer.callback.asBinder().unlinkToDeath(noi, 0 /* flags */);
}
@@ -7264,7 +7397,9 @@
boolean suspended = true; // suspended if all underlying are suspended
boolean hadUnderlyingNetworks = false;
+ ArrayList<Network> newUnderlyingNetworks = null;
if (null != underlyingNetworks) {
+ newUnderlyingNetworks = new ArrayList<>();
for (Network underlyingNetwork : underlyingNetworks) {
final NetworkAgentInfo underlying =
getNetworkAgentInfoForNetwork(underlyingNetwork);
@@ -7294,6 +7429,7 @@
// If this network is not suspended, the VPN is not suspended (the VPN
// is able to transfer some data).
suspended &= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ newUnderlyingNetworks.add(underlyingNetwork);
}
}
if (!hadUnderlyingNetworks) {
@@ -7311,6 +7447,7 @@
newNc.setCapability(NET_CAPABILITY_NOT_ROAMING, !roaming);
newNc.setCapability(NET_CAPABILITY_NOT_CONGESTED, !congested);
newNc.setCapability(NET_CAPABILITY_NOT_SUSPENDED, !suspended);
+ newNc.setUnderlyingNetworks(newUnderlyingNetworks);
}
/**
@@ -7781,6 +7918,30 @@
bundle.putParcelable(t.getClass().getSimpleName(), t);
}
+ /**
+ * Returns whether reassigning a request from an NAI to another can be done gracefully.
+ *
+ * When a request should be assigned to a new network, it is normally lingered to give
+ * time for apps to gracefully migrate their connections. When both networks are on the same
+ * radio, but that radio can't do time-sharing efficiently, this may end up being
+ * counter-productive because any traffic on the old network may drastically reduce the
+ * performance of the new network.
+ * The stack supports a configuration to let modem vendors state that their radio can't
+ * do time-sharing efficiently. If this configuration is set, the stack assumes moving
+ * from one cell network to another can't be done gracefully.
+ *
+ * @param oldNai the old network serving the request
+ * @param newNai the new network serving the request
+ * @return whether the switch can be graceful
+ */
+ private boolean canSupportGracefulNetworkSwitch(@NonNull final NetworkAgentInfo oldSatisfier,
+ @NonNull final NetworkAgentInfo newSatisfier) {
+ if (mCellularRadioTimesharingCapable) return true;
+ return !oldSatisfier.networkCapabilities.hasSingleTransport(TRANSPORT_CELLULAR)
+ || !newSatisfier.networkCapabilities.hasSingleTransport(TRANSPORT_CELLULAR)
+ || !newSatisfier.getScore().hasPolicy(POLICY_TRANSPORT_PRIMARY);
+ }
+
private void teardownUnneededNetwork(NetworkAgentInfo nai) {
if (nai.numRequestNetworkRequests() != 0) {
for (int i = 0; i < nai.numNetworkRequests(); i++) {
@@ -8041,7 +8202,13 @@
log(" accepting network in place of " + previousSatisfier.toShortString());
}
previousSatisfier.removeRequest(previousRequest.requestId);
- previousSatisfier.lingerRequest(previousRequest.requestId, now);
+ if (canSupportGracefulNetworkSwitch(previousSatisfier, newSatisfier)) {
+ // If this network switch can't be supported gracefully, the request is not
+ // lingered. This allows letting go of the network sooner to reclaim some
+ // performance on the new network, since the radio can't do both at the same
+ // time while preserving good performance.
+ previousSatisfier.lingerRequest(previousRequest.requestId, now);
+ }
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
@@ -9511,6 +9678,10 @@
@NonNull IConnectivityDiagnosticsCallback callback,
@NonNull NetworkRequest request,
@NonNull String callingPackageName) {
+ Objects.requireNonNull(callback, "callback must not be null");
+ Objects.requireNonNull(request, "request must not be null");
+ Objects.requireNonNull(callingPackageName, "callingPackageName must not be null");
+
if (request.legacyType != TYPE_NONE) {
throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
+ " Please use NetworkCapabilities instead.");
@@ -9559,6 +9730,9 @@
@Override
public void simulateDataStall(int detectionMethod, long timestampMillis,
@NonNull Network network, @NonNull PersistableBundle extras) {
+ Objects.requireNonNull(network, "network must not be null");
+ Objects.requireNonNull(extras, "extras must not be null");
+
enforceAnyPermissionOf(android.Manifest.permission.MANAGE_TEST_NETWORKS,
android.Manifest.permission.NETWORK_STACK);
final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
@@ -10004,13 +10178,9 @@
validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
- mSystemNetworkRequestCounter.transact(
- mDeps.getCallingUid(), mProfileNetworkPreferences.preferences.size(),
- () -> {
- final ArraySet<NetworkRequestInfo> nris =
- createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences);
- replaceDefaultNetworkRequestsForPreference(nris, PREFERENCE_ORDER_PROFILE);
- });
+ removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_PROFILE);
+ addPerAppDefaultNetworkRequests(
+ createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences));
// Finally, rematch.
rematchAllNetworksAndRequests();
@@ -10055,14 +10225,9 @@
private void handleMobileDataPreferredUidsChanged() {
mMobileDataPreferredUids = ConnectivitySettingsManager.getMobileDataPreferredUids(mContext);
- mSystemNetworkRequestCounter.transact(
- mDeps.getCallingUid(), 1 /* numOfNewRequests */,
- () -> {
- final ArraySet<NetworkRequestInfo> nris =
- createNrisFromMobileDataPreferredUids(mMobileDataPreferredUids);
- replaceDefaultNetworkRequestsForPreference(nris,
- PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED);
- });
+ removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED);
+ addPerAppDefaultNetworkRequests(
+ createNrisFromMobileDataPreferredUids(mMobileDataPreferredUids));
// Finally, rematch.
rematchAllNetworksAndRequests();
}
@@ -10151,16 +10316,9 @@
}
mOemNetworkPreferencesLogs.log("UPDATE INITIATED: " + preference);
- final int uniquePreferenceCount = new ArraySet<>(
- preference.getNetworkPreferences().values()).size();
- mSystemNetworkRequestCounter.transact(
- mDeps.getCallingUid(), uniquePreferenceCount,
- () -> {
- final ArraySet<NetworkRequestInfo> nris =
- new OemNetworkRequestFactory()
- .createNrisFromOemNetworkPreferences(preference);
- replaceDefaultNetworkRequestsForPreference(nris, PREFERENCE_ORDER_OEM);
- });
+ removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_OEM);
+ addPerAppDefaultNetworkRequests(new OemNetworkRequestFactory()
+ .createNrisFromOemNetworkPreferences(preference));
mOemNetworkPreferences = preference;
if (null != listener) {
@@ -10172,14 +10330,12 @@
}
}
- private void replaceDefaultNetworkRequestsForPreference(
- @NonNull final Set<NetworkRequestInfo> nris, final int preferenceOrder) {
+ private void removeDefaultNetworkRequestsForPreference(final int preferenceOrder) {
// Skip the requests which are set by other network preference. Because the uid range rules
// should stay in netd.
final Set<NetworkRequestInfo> requests = new ArraySet<>(mDefaultNetworkRequests);
requests.removeIf(request -> request.mPreferenceOrder != preferenceOrder);
handleRemoveNetworkRequests(requests);
- addPerAppDefaultNetworkRequests(nris);
}
private void addPerAppDefaultNetworkRequests(@NonNull final Set<NetworkRequestInfo> nris) {
@@ -10188,14 +10344,10 @@
final ArraySet<NetworkRequestInfo> perAppCallbackRequestsToUpdate =
getPerAppCallbackRequestsToUpdate();
final ArraySet<NetworkRequestInfo> nrisToRegister = new ArraySet<>(nris);
- mSystemNetworkRequestCounter.transact(
- mDeps.getCallingUid(), perAppCallbackRequestsToUpdate.size(),
- () -> {
- nrisToRegister.addAll(
- createPerAppCallbackRequestsToRegister(perAppCallbackRequestsToUpdate));
- handleRemoveNetworkRequests(perAppCallbackRequestsToUpdate);
- handleRegisterNetworkRequests(nrisToRegister);
- });
+ handleRemoveNetworkRequests(perAppCallbackRequestsToUpdate);
+ nrisToRegister.addAll(
+ createPerAppCallbackRequestsToRegister(perAppCallbackRequestsToUpdate));
+ handleRegisterNetworkRequests(nrisToRegister);
}
/**
@@ -10247,7 +10399,7 @@
final NetworkRequestInfo trackingNri =
getDefaultRequestTrackingUid(callbackRequest.mAsUid);
- // If this nri is not being tracked, the change it back to an untracked nri.
+ // If this nri is not being tracked, then change it back to an untracked nri.
if (trackingNri == mDefaultRequest) {
callbackRequestsToRegister.add(new NetworkRequestInfo(
callbackRequest,
diff --git a/service/src/com/android/server/ConnectivityServiceInitializer.java b/service/src/com/android/server/ConnectivityServiceInitializer.java
index 2465479..b1a56ae 100644
--- a/service/src/com/android/server/ConnectivityServiceInitializer.java
+++ b/service/src/com/android/server/ConnectivityServiceInitializer.java
@@ -31,7 +31,6 @@
super(context);
// Load JNI libraries used by ConnectivityService and its dependencies
System.loadLibrary("service-connectivity");
- // TODO: Define formal APIs to get the needed services.
mConnectivity = new ConnectivityService(context);
}
diff --git a/service/src/com/android/server/connectivity/ConnectivityFlags.java b/service/src/com/android/server/connectivity/ConnectivityFlags.java
new file mode 100644
index 0000000..122ea1c
--- /dev/null
+++ b/service/src/com/android/server/connectivity/ConnectivityFlags.java
@@ -0,0 +1,66 @@
+/*
+ * 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.server.connectivity;
+
+import android.content.Context;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.ConnectivityService;
+
+/**
+ * Collection of constants for the connectivity module.
+ */
+public final class ConnectivityFlags {
+ /**
+ * Minimum module version at which to avoid rematching all requests when a network request is
+ * registered, and rematch only the registered requests instead.
+ */
+ @VisibleForTesting
+ public static final String NO_REMATCH_ALL_REQUESTS_ON_REGISTER =
+ "no_rematch_all_requests_on_register";
+
+ private boolean mNoRematchAllRequestsOnRegister;
+
+ /**
+ * Whether ConnectivityService should avoid avoid rematching all requests when a network
+ * request is registered, and rematch only the registered requests instead.
+ *
+ * This flag is disabled by default.
+ *
+ * IMPORTANT NOTE: This flag is false by default and will only be loaded in ConnectivityService
+ * systemReady. It is also not volatile for performance reasons, so for most threads it may
+ * only change to true after some time. This is fine for this particular flag because it only
+ * controls whether all requests or a subset of requests should be rematched, which is only
+ * a performance optimization, so its value does not need to be consistent over time; but most
+ * flags will not have these properties and should not use the same model.
+ *
+ * TODO: when adding other flags, consider the appropriate timing to load them, and necessary
+ * threading guarantees according to the semantics of the flags.
+ */
+ public boolean noRematchAllRequestsOnRegister() {
+ return mNoRematchAllRequestsOnRegister;
+ }
+
+ /**
+ * Load flag values. Should only be called once, and can only be called once PackageManager is
+ * ready.
+ */
+ public void loadFlags(ConnectivityService.Dependencies deps, Context ctx) {
+ mNoRematchAllRequestsOnRegister = deps.isFeatureEnabled(
+ ctx, NO_REMATCH_ALL_REQUESTS_ON_REGISTER, false /* defaultEnabled */);
+ }
+}
diff --git a/service/src/com/android/server/connectivity/DnsManager.java b/service/src/com/android/server/connectivity/DnsManager.java
index 05b12ba..1493cae 100644
--- a/service/src/com/android/server/connectivity/DnsManager.java
+++ b/service/src/com/android/server/connectivity/DnsManager.java
@@ -38,7 +38,6 @@
import android.net.InetAddresses;
import android.net.LinkProperties;
import android.net.Network;
-import android.net.ResolverOptionsParcel;
import android.net.ResolverParamsParcel;
import android.net.Uri;
import android.net.shared.PrivateDnsConfig;
@@ -384,7 +383,6 @@
.collect(Collectors.toList()))
: useTls ? paramsParcel.servers // Opportunistic
: new String[0]; // Off
- paramsParcel.resolverOptions = new ResolverOptionsParcel();
paramsParcel.transportTypes = transportTypes;
// Prepare to track the validation status of the DNS servers in the
// resolver config when private DNS is in opportunistic or strict mode.
diff --git a/service/src/com/android/server/connectivity/FullScore.java b/service/src/com/android/server/connectivity/FullScore.java
index fbfa7a1..aebb80d 100644
--- a/service/src/com/android/server/connectivity/FullScore.java
+++ b/service/src/com/android/server/connectivity/FullScore.java
@@ -108,10 +108,9 @@
// and all bits managed by FullScore unset. As bits are handled from 0 up in NetworkScore and
// from 63 down in FullScore, cut at the 32nd bit for simplicity, but change this if some day
// there are more than 32 bits handled on either side.
- // YIELD_TO_BAD_WIFI is temporarily handled by ConnectivityService, but the factory is still
- // allowed to set it, so that it's possible to transition from handling it in CS to handling
- // it in the factory.
- private static final long EXTERNAL_POLICIES_MASK = 0x00000000FFFFFFFFL;
+ // YIELD_TO_BAD_WIFI is temporarily handled by ConnectivityService.
+ private static final long EXTERNAL_POLICIES_MASK =
+ 0x00000000FFFFFFFFL & ~(1L << POLICY_YIELD_TO_BAD_WIFI);
@VisibleForTesting
static @NonNull String policyNameOf(final int policy) {
@@ -184,7 +183,7 @@
* @return a FullScore appropriate for comparing to actual network's scores.
*/
public static FullScore makeProspectiveScore(@NonNull final NetworkScore score,
- @NonNull final NetworkCapabilities caps) {
+ @NonNull final NetworkCapabilities caps, final boolean yieldToBadWiFi) {
// If the network offers Internet access, it may validate.
final boolean mayValidate = caps.hasCapability(NET_CAPABILITY_INTERNET);
// VPN transports are known in advance.
@@ -198,8 +197,6 @@
final boolean everUserSelected = false;
// Don't assume the user will accept unvalidated connectivity.
final boolean acceptUnvalidated = false;
- // Don't assume clinging to bad wifi
- final boolean yieldToBadWiFi = false;
// A prospective score is invincible if the legacy int in the filter is over the maximum
// score.
final boolean invincible = score.getLegacyInt() > NetworkRanker.LEGACY_INT_MAX;
@@ -260,6 +257,16 @@
}
/**
+ * Returns this score but with the specified yield to bad wifi policy.
+ */
+ public FullScore withYieldToBadWiFi(final boolean newYield) {
+ return new FullScore(mLegacyInt,
+ newYield ? mPolicies | (1L << POLICY_YIELD_TO_BAD_WIFI)
+ : mPolicies & ~(1L << POLICY_YIELD_TO_BAD_WIFI),
+ mKeepConnectedReason);
+ }
+
+ /**
* Returns this score but validated.
*/
public FullScore asValidated() {
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index bbf523a..b7f3ed9 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -377,6 +377,9 @@
this.creatorUid = creatorUid;
mLingerDurationMs = lingerDurationMs;
mQosCallbackTracker = qosCallbackTracker;
+ declaredUnderlyingNetworks = (nc.getUnderlyingNetworks() != null)
+ ? nc.getUnderlyingNetworks().toArray(new Network[0])
+ : null;
}
private class AgentDeathMonitor implements IBinder.DeathRecipient {
@@ -1187,6 +1190,7 @@
? " underlying{" + Arrays.toString(declaredUnderlyingNetworks) + "}" : "")
+ " lp{" + linkProperties + "}"
+ " nc{" + networkCapabilities + "}"
+ + " factorySerialNumber=" + factorySerialNumber
+ "}";
}
diff --git a/service/src/com/android/server/connectivity/NetworkNotificationManager.java b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
index ae98d92..155f6c4 100644
--- a/service/src/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/service/src/com/android/server/connectivity/NetworkNotificationManager.java
@@ -198,11 +198,22 @@
}
final Resources r = mResources.get();
+ if (highPriority && maybeNotifyViaDialog(r, notifyType, intent)) {
+ Log.d(TAG, "Notified via dialog for event " + nameOf(eventId));
+ return;
+ }
+
final CharSequence title;
final CharSequence details;
Icon icon = Icon.createWithResource(
mResources.getResourcesContext(), getIcon(transportType));
- if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
+ final boolean showAsNoInternet = notifyType == NotificationType.PARTIAL_CONNECTIVITY
+ && r.getBoolean(R.bool.config_partialConnectivityNotifiedAsNoInternet);
+ if (showAsNoInternet) {
+ Log.d(TAG, "Showing partial connectivity as NO_INTERNET");
+ }
+ if ((notifyType == NotificationType.NO_INTERNET || showAsNoInternet)
+ && transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet, name);
details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
@@ -306,6 +317,24 @@
}
}
+ private boolean maybeNotifyViaDialog(Resources res, NotificationType notifyType,
+ PendingIntent intent) {
+ if (notifyType != NotificationType.NO_INTERNET
+ && notifyType != NotificationType.PARTIAL_CONNECTIVITY) {
+ return false;
+ }
+ if (!res.getBoolean(R.bool.config_notifyNoInternetAsDialogWhenHighPriority)) {
+ return false;
+ }
+
+ try {
+ intent.send();
+ } catch (PendingIntent.CanceledException e) {
+ Log.e(TAG, "Error sending dialog PendingIntent", e);
+ }
+ return true;
+ }
+
/**
* Clear the notification with the given id, only if it matches the given type.
*/
diff --git a/service/src/com/android/server/connectivity/NetworkRanker.java b/service/src/com/android/server/connectivity/NetworkRanker.java
index d7eb9c8..43da1d0 100644
--- a/service/src/com/android/server/connectivity/NetworkRanker.java
+++ b/service/src/com/android/server/connectivity/NetworkRanker.java
@@ -63,8 +63,6 @@
NetworkCapabilities getCapsNoCopy();
}
- private static final boolean USE_POLICY_RANKING = true;
-
public NetworkRanker() { }
/**
@@ -77,11 +75,7 @@
final ArrayList<NetworkAgentInfo> candidates = filter(nais, nai -> nai.satisfies(request));
if (candidates.size() == 1) return candidates.get(0); // Only one potential satisfier
if (candidates.size() <= 0) return null; // No network can satisfy this request
- if (USE_POLICY_RANKING) {
- return getBestNetworkByPolicy(candidates, currentSatisfier);
- } else {
- return getBestNetworkByLegacyInt(candidates);
- }
+ return getBestNetworkByPolicy(candidates, currentSatisfier);
}
// Transport preference order, if it comes down to that.
@@ -278,23 +272,6 @@
return candidates.get(0);
}
- // TODO : switch to the policy implementation and remove
- // Almost equivalent to Collections.max(nais), but allows returning null if no network
- // satisfies the request.
- private NetworkAgentInfo getBestNetworkByLegacyInt(
- @NonNull final Collection<NetworkAgentInfo> nais) {
- NetworkAgentInfo bestNetwork = null;
- int bestScore = Integer.MIN_VALUE;
- for (final NetworkAgentInfo nai : nais) {
- final int naiScore = nai.getCurrentScore();
- if (naiScore > bestScore) {
- bestNetwork = nai;
- bestScore = naiScore;
- }
- }
- return bestNetwork;
- }
-
/**
* Returns whether a {@link Scoreable} has a chance to beat a champion network for a request.
*
@@ -322,30 +299,11 @@
// If there is no satisfying network, then this network can beat, because some network
// is always better than no network.
if (null == champion) return true;
- if (USE_POLICY_RANKING) {
- // If there is no champion, the offer can always beat.
- // Otherwise rank them.
- final ArrayList<Scoreable> candidates = new ArrayList<>();
- candidates.add(champion);
- candidates.add(contestant);
- return contestant == getBestNetworkByPolicy(candidates, champion);
- } else {
- return mightBeatByLegacyInt(champion.getScore(), contestant);
- }
- }
-
- /**
- * Returns whether a contestant might beat a champion according to the legacy int.
- */
- private boolean mightBeatByLegacyInt(@Nullable final FullScore championScore,
- @NonNull final Scoreable contestant) {
- final int offerIntScore;
- if (contestant.getCapsNoCopy().hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
- // If the offer might have Internet access, then it might validate.
- offerIntScore = contestant.getScore().getLegacyIntAsValidated();
- } else {
- offerIntScore = contestant.getScore().getLegacyInt();
- }
- return championScore.getLegacyInt() < offerIntScore;
+ // If there is no champion, the offer can always beat.
+ // Otherwise rank them.
+ final ArrayList<Scoreable> candidates = new ArrayList<>();
+ candidates.add(champion);
+ candidates.add(contestant);
+ return contestant == getBestNetworkByPolicy(candidates, champion);
}
}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 7c0fb91..439db89 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -23,7 +23,6 @@
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
import static android.net.INetd.PERMISSION_INTERNET;
import static android.net.INetd.PERMISSION_NETWORK;
@@ -51,6 +50,7 @@
import android.net.INetd;
import android.net.UidRange;
import android.net.Uri;
+import android.net.util.SharedLog;
import android.os.Build;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -59,9 +59,9 @@
import android.os.UserManager;
import android.provider.Settings;
import android.system.OsConstants;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
-import android.util.SparseArray;
import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
@@ -74,11 +74,10 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
/**
- * A utility class to inform Netd of UID permisisons.
+ * A utility class to inform Netd of UID permissions.
* Does a mass update at boot and then monitors for app install/remove.
*
* @hide
@@ -86,8 +85,6 @@
public class PermissionMonitor {
private static final String TAG = "PermissionMonitor";
private static final boolean DBG = true;
- protected static final Boolean SYSTEM = Boolean.TRUE;
- protected static final Boolean NETWORK = Boolean.FALSE;
private static final int VERSION_Q = Build.VERSION_CODES.Q;
private final PackageManager mPackageManager;
@@ -100,9 +97,9 @@
@GuardedBy("this")
private final Set<UserHandle> mUsers = new HashSet<>();
- // Keys are app uids. Values are true for SYSTEM permission and false for NETWORK permission.
+ // Keys are uids. Values are netd network permissions.
@GuardedBy("this")
- private final Map<Integer, Boolean> mApps = new HashMap<>();
+ private final SparseIntArray mUidToNetworkPerm = new SparseIntArray();
// Keys are active non-bypassable and fully-routed VPN's interface name, Values are uid ranges
// for apps under the VPN
@@ -123,6 +120,14 @@
@GuardedBy("this")
private final Set<Integer> mUidsAllowedOnRestrictedNetworks = new ArraySet<>();
+ @GuardedBy("this")
+ private final Map<UserHandle, PackageManager> mUsersPackageManager = new ArrayMap<>();
+
+ private static final int SYSTEM_APPID = SYSTEM_UID;
+
+ private static final int MAX_PERMISSION_UPDATE_LOGS = 40;
+ private final SharedLog mPermissionUpdateLogs = new SharedLog(MAX_PERMISSION_UPDATE_LOGS, TAG);
+
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -194,6 +199,100 @@
mContext = context;
}
+ private int getPackageNetdNetworkPermission(@NonNull final PackageInfo app) {
+ if (hasRestrictedNetworkPermission(app)) {
+ return PERMISSION_SYSTEM;
+ }
+ if (hasNetworkPermission(app)) {
+ return PERMISSION_NETWORK;
+ }
+ return PERMISSION_NONE;
+ }
+
+ static boolean isHigherNetworkPermission(final int targetPermission,
+ final int currentPermission) {
+ // This is relied on strict order of network permissions (SYSTEM > NETWORK > NONE), and it
+ // is enforced in tests.
+ return targetPermission > currentPermission;
+ }
+
+ private List<PackageInfo> getInstalledPackagesAsUser(final UserHandle user) {
+ return mPackageManager.getInstalledPackagesAsUser(GET_PERMISSIONS, user.getIdentifier());
+ }
+
+ private synchronized void updateAllApps(final List<PackageInfo> apps) {
+ for (PackageInfo app : apps) {
+ final int appId = app.applicationInfo != null
+ ? UserHandle.getAppId(app.applicationInfo.uid) : INVALID_UID;
+ if (appId < 0) {
+ continue;
+ }
+ mAllApps.add(appId);
+ }
+ }
+
+ // Return the network permission for the passed list of apps. Note that this depends on the
+ // current settings of the device (See isUidAllowedOnRestrictedNetworks).
+ private SparseIntArray makeUidsNetworkPerm(final List<PackageInfo> apps) {
+ final SparseIntArray uidsPerm = new SparseIntArray();
+ for (PackageInfo app : apps) {
+ final int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
+ if (uid < 0) {
+ continue;
+ }
+ final int permission = getPackageNetdNetworkPermission(app);
+ if (isHigherNetworkPermission(permission, uidsPerm.get(uid, PERMISSION_NONE))) {
+ uidsPerm.put(uid, permission);
+ }
+ }
+ return uidsPerm;
+ }
+
+ private static SparseIntArray makeAppIdsTrafficPerm(final List<PackageInfo> apps) {
+ final SparseIntArray appIdsPerm = new SparseIntArray();
+ for (PackageInfo app : apps) {
+ final int appId = app.applicationInfo != null
+ ? UserHandle.getAppId(app.applicationInfo.uid) : INVALID_UID;
+ if (appId < 0) {
+ continue;
+ }
+ final int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
+ app.requestedPermissionsFlags);
+ appIdsPerm.put(appId, appIdsPerm.get(appId) | otherNetdPerms);
+ }
+ return appIdsPerm;
+ }
+
+ private synchronized void updateUidsNetworkPermission(final SparseIntArray uids) {
+ for (int i = 0; i < uids.size(); i++) {
+ mUidToNetworkPerm.put(uids.keyAt(i), uids.valueAt(i));
+ }
+ sendUidsNetworkPermission(uids, true /* add */);
+ }
+
+ private void updateAppIdsTrafficPermission(final SparseIntArray appIds,
+ final SparseIntArray extraAppIds) {
+ for (int i = 0; i < extraAppIds.size(); i++) {
+ final int appId = extraAppIds.keyAt(i);
+ final int permission = extraAppIds.valueAt(i);
+ appIds.put(appId, appIds.get(appId) | permission);
+ }
+ sendAppIdsTrafficPermission(appIds);
+ }
+
+ private SparseIntArray getSystemTrafficPerm() {
+ final SparseIntArray appIdsPerm = new SparseIntArray();
+ for (final int uid : mSystemConfigManager.getSystemPermissionUids(INTERNET)) {
+ final int appId = UserHandle.getAppId(uid);
+ appIdsPerm.put(appId, appIdsPerm.get(appId) | PERMISSION_INTERNET);
+ }
+ for (final int uid : mSystemConfigManager.getSystemPermissionUids(UPDATE_DEVICE_STATS)) {
+ final int appId = UserHandle.getAppId(uid);
+ appIdsPerm.put(appId, appIdsPerm.get(appId) | PERMISSION_UPDATE_DEVICE_STATS);
+ }
+ return appIdsPerm;
+ }
+
// Intended to be called only once at startup, after the system is ready. Installs a broadcast
// receiver to monitor ongoing UID changes, so this shouldn't/needn't be called again.
public synchronized void startMonitoring() {
@@ -208,6 +307,10 @@
mIntentReceiver, intentFilter, null /* broadcastPermission */,
null /* scheduler */);
+ // Listen to EXTERNAL_APPLICATIONS_AVAILABLE is that an app becoming available means it may
+ // need to gain a permission. But an app that becomes unavailable can neither gain nor lose
+ // permissions on that account, it just can no longer run. Thus, doesn't need to listen to
+ // EXTERNAL_APPLICATIONS_UNAVAILABLE.
final IntentFilter externalIntentFilter =
new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
userAllContext.registerReceiver(
@@ -230,71 +333,18 @@
// mUidsAllowedOnRestrictedNetworks.
updateUidsAllowedOnRestrictedNetworks(mDeps.getUidsAllowedOnRestrictedNetworks(mContext));
- List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS
- | MATCH_ANY_USER);
- if (apps == null) {
- loge("No apps");
- return;
+ final List<UserHandle> usrs = mUserManager.getUserHandles(true /* excludeDying */);
+ // Update netd permissions for all users.
+ for (UserHandle user : usrs) {
+ onUserAdded(user);
}
-
- SparseIntArray netdPermsUids = new SparseIntArray();
-
- for (PackageInfo app : apps) {
- int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID;
- if (uid < 0) {
- continue;
- }
- mAllApps.add(UserHandle.getAppId(uid));
-
- boolean isNetwork = hasNetworkPermission(app);
- boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
-
- if (isNetwork || hasRestrictedPermission) {
- Boolean permission = mApps.get(UserHandle.getAppId(uid));
- // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
- // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
- if (permission == null || permission == NETWORK) {
- mApps.put(UserHandle.getAppId(uid), hasRestrictedPermission);
- }
- }
-
- //TODO: unify the management of the permissions into one codepath.
- int otherNetdPerms = getNetdPermissionMask(app.requestedPermissions,
- app.requestedPermissionsFlags);
- netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms);
- }
-
- mUsers.addAll(mUserManager.getUserHandles(true /* excludeDying */));
-
- final SparseArray<String> netdPermToSystemPerm = new SparseArray<>();
- netdPermToSystemPerm.put(PERMISSION_INTERNET, INTERNET);
- netdPermToSystemPerm.put(PERMISSION_UPDATE_DEVICE_STATS, UPDATE_DEVICE_STATS);
- for (int i = 0; i < netdPermToSystemPerm.size(); i++) {
- final int netdPermission = netdPermToSystemPerm.keyAt(i);
- final String systemPermission = netdPermToSystemPerm.valueAt(i);
- final int[] hasPermissionUids =
- mSystemConfigManager.getSystemPermissionUids(systemPermission);
- for (int j = 0; j < hasPermissionUids.length; j++) {
- final int uid = hasPermissionUids[j];
- netdPermsUids.put(uid, netdPermsUids.get(uid) | netdPermission);
- }
- }
- log("Users: " + mUsers.size() + ", Apps: " + mApps.size());
- update(mUsers, mApps, true);
- sendPackagePermissionsToNetd(netdPermsUids);
+ log("Users: " + mUsers.size() + ", UidToNetworkPerm: " + mUidToNetworkPerm.size());
}
@VisibleForTesting
synchronized void updateUidsAllowedOnRestrictedNetworks(final Set<Integer> uids) {
mUidsAllowedOnRestrictedNetworks.clear();
- // This is necessary for the app id to match in isUidAllowedOnRestrictedNetworks, and will
- // grant the permission to all uids associated with the app ID. This is safe even if the app
- // is only installed on some users because the uid cannot match some other app – this uid is
- // in effect not installed and can't be run.
- // TODO (b/192431153): Change appIds back to uids.
- for (int uid : uids) {
- mUidsAllowedOnRestrictedNetworks.add(UserHandle.getAppId(uid));
- }
+ mUidsAllowedOnRestrictedNetworks.addAll(uids);
}
@VisibleForTesting
@@ -308,7 +358,8 @@
return (appInfo.targetSdkVersion < VERSION_Q && isVendorApp(appInfo))
// Backward compatibility for b/114245686, on devices that launched before Q daemons
// and apps running as the system UID are exempted from this check.
- || (appInfo.uid == SYSTEM_UID && mDeps.getDeviceFirstSdkInt() < VERSION_Q);
+ || (UserHandle.getAppId(appInfo.uid) == SYSTEM_APPID
+ && mDeps.getDeviceFirstSdkInt() < VERSION_Q);
}
@VisibleForTesting
@@ -316,7 +367,7 @@
if (appInfo == null) return false;
// Check whether package's uid is in allowed on restricted networks uid list. If so, this
// uid can have netd system permission.
- return mUidsAllowedOnRestrictedNetworks.contains(UserHandle.getAppId(appInfo.uid));
+ return mUidsAllowedOnRestrictedNetworks.contains(appInfo.uid);
}
@VisibleForTesting
@@ -349,29 +400,30 @@
public synchronized boolean hasUseBackgroundNetworksPermission(final int uid) {
// Apps with any of the CHANGE_NETWORK_STATE, NETWORK_STACK, CONNECTIVITY_INTERNAL or
// CONNECTIVITY_USE_RESTRICTED_NETWORKS permission has the permission to use background
- // networks. mApps contains the result of checks for both hasNetworkPermission and
- // hasRestrictedNetworkPermission. If uid is in the mApps list that means uid has one of
- // permissions at least.
- return mApps.containsKey(UserHandle.getAppId(uid));
+ // networks. mUidToNetworkPerm contains the result of checks for hasNetworkPermission and
+ // hasRestrictedNetworkPermission, as well as the list of UIDs allowed on restricted
+ // networks. If uid is in the mUidToNetworkPerm list that means uid has one of permissions
+ // at least.
+ return mUidToNetworkPerm.get(uid, PERMISSION_NONE) != PERMISSION_NONE;
}
/**
* Returns whether the given uid has permission to use restricted networks.
*/
public synchronized boolean hasRestrictedNetworksPermission(int uid) {
- return Boolean.TRUE.equals(mApps.get(UserHandle.getAppId(uid)));
+ return PERMISSION_SYSTEM == mUidToNetworkPerm.get(uid, PERMISSION_NONE);
}
- private void update(Set<UserHandle> users, Map<Integer, Boolean> apps, boolean add) {
+ private void sendUidsNetworkPermission(SparseIntArray uids, boolean add) {
List<Integer> network = new ArrayList<>();
List<Integer> system = new ArrayList<>();
- for (Entry<Integer, Boolean> app : apps.entrySet()) {
- List<Integer> list = app.getValue() ? system : network;
- for (UserHandle user : users) {
- if (user == null) continue;
-
- list.add(user.getUid(app.getKey()));
+ for (int i = 0; i < uids.size(); i++) {
+ final int permission = uids.valueAt(i);
+ if (PERMISSION_NONE == permission) {
+ continue; // Normally NONE is not stored in this map, but just in case
}
+ List<Integer> list = (PERMISSION_SYSTEM == permission) ? system : network;
+ list.add(uids.keyAt(i));
}
try {
if (add) {
@@ -396,9 +448,18 @@
public synchronized void onUserAdded(@NonNull UserHandle user) {
mUsers.add(user);
- Set<UserHandle> users = new HashSet<>();
- users.add(user);
- update(users, mApps, true);
+ final List<PackageInfo> apps = getInstalledPackagesAsUser(user);
+
+ // Save all apps
+ updateAllApps(apps);
+
+ // Uids network permissions
+ final SparseIntArray uids = makeUidsNetworkPerm(apps);
+ updateUidsNetworkPermission(uids);
+
+ // App ids traffic permission
+ final SparseIntArray appIds = makeAppIdsTrafficPerm(apps);
+ updateAppIdsTrafficPermission(appIds, getSystemTrafficPerm());
}
/**
@@ -411,47 +472,53 @@
public synchronized void onUserRemoved(@NonNull UserHandle user) {
mUsers.remove(user);
- Set<UserHandle> users = new HashSet<>();
- users.add(user);
- update(users, mApps, false);
+ final SparseIntArray removedUids = new SparseIntArray();
+ final SparseIntArray allUids = mUidToNetworkPerm.clone();
+ for (int i = 0; i < allUids.size(); i++) {
+ final int uid = allUids.keyAt(i);
+ if (user.equals(UserHandle.getUserHandleForUid(uid))) {
+ mUidToNetworkPerm.delete(uid);
+ removedUids.put(uid, allUids.valueAt(i));
+ }
+ }
+ sendUidsNetworkPermission(removedUids, false /* add */);
}
/**
* Compare the current network permission and the given package's permission to find out highest
* permission for the uid.
*
+ * @param uid The target uid
* @param currentPermission Current uid network permission
* @param name The package has same uid that need compare its permission to update uid network
* permission.
*/
@VisibleForTesting
- protected Boolean highestPermissionForUid(Boolean currentPermission, String name) {
- if (currentPermission == SYSTEM) {
+ protected int highestPermissionForUid(int uid, int currentPermission, String name) {
+ // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
+ // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
+ if (currentPermission == PERMISSION_SYSTEM) {
return currentPermission;
}
- try {
- final PackageInfo app = mPackageManager.getPackageInfo(name,
- GET_PERMISSIONS | MATCH_ANY_USER);
- final boolean isNetwork = hasNetworkPermission(app);
- final boolean hasRestrictedPermission = hasRestrictedNetworkPermission(app);
- if (isNetwork || hasRestrictedPermission) {
- currentPermission = hasRestrictedPermission;
- }
- } catch (NameNotFoundException e) {
- // App not found.
- loge("NameNotFoundException " + name);
+ final PackageInfo app = getPackageInfoAsUser(name, UserHandle.getUserHandleForUid(uid));
+ if (app == null) return currentPermission;
+
+ final int permission = getPackageNetdNetworkPermission(app);
+ if (isHigherNetworkPermission(permission, currentPermission)) {
+ return permission;
}
return currentPermission;
}
- private int getPermissionForUid(final int uid) {
+ private int getTrafficPermissionForUid(final int uid) {
int permission = PERMISSION_NONE;
// Check all the packages for this UID. The UID has the permission if any of the
// packages in it has the permission.
final String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
for (String name : packages) {
- final PackageInfo app = getPackageInfo(name);
+ final PackageInfo app = getPackageInfoAsUser(name,
+ UserHandle.getUserHandleForUid(uid));
if (app != null && app.requestedPermissions != null) {
permission |= getNetdPermissionMask(app.requestedPermissions,
app.requestedPermissionsFlags);
@@ -464,6 +531,43 @@
return permission;
}
+ private synchronized void updateVpnUid(int uid, boolean add) {
+ for (Map.Entry<String, Set<UidRange>> vpn : mVpnUidRanges.entrySet()) {
+ if (UidRange.containsUid(vpn.getValue(), uid)) {
+ final Set<Integer> changedUids = new HashSet<>();
+ changedUids.add(uid);
+ removeBypassingUids(changedUids, -1 /* vpnAppUid */);
+ updateVpnUidsInterfaceRules(vpn.getKey(), changedUids, add);
+ }
+ }
+ }
+
+ /**
+ * This handles both network and traffic permission, because there is no overlap in actual
+ * values, where network permission is NETWORK or SYSTEM, and traffic permission is INTERNET
+ * or UPDATE_DEVICE_STATS
+ */
+ private String permissionToString(int permission) {
+ switch (permission) {
+ case PERMISSION_NONE:
+ return "NONE";
+ case PERMISSION_NETWORK:
+ return "NETWORK";
+ case PERMISSION_SYSTEM:
+ return "SYSTEM";
+ case PERMISSION_INTERNET:
+ return "INTERNET";
+ case PERMISSION_UPDATE_DEVICE_STATS:
+ return "UPDATE_DEVICE_STATS";
+ case (PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS):
+ return "ALL";
+ case PERMISSION_UNINSTALLED:
+ return "UNINSTALLED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
/**
* Called when a package is added.
*
@@ -473,45 +577,41 @@
* @hide
*/
public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
- // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
- // using appId instead of uid actually
- sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
-
- // If multiple packages share a UID (cf: android:sharedUserId) and ask for different
- // permissions, don't downgrade (i.e., if it's already SYSTEM, leave it as is).
final int appId = UserHandle.getAppId(uid);
- final Boolean permission = highestPermissionForUid(mApps.get(appId), packageName);
- if (permission != mApps.get(appId)) {
- mApps.put(appId, permission);
+ final int trafficPerm = getTrafficPermissionForUid(uid);
+ sendPackagePermissionsForAppId(appId, trafficPerm);
- Map<Integer, Boolean> apps = new HashMap<>();
- apps.put(appId, permission);
- update(mUsers, apps, true);
+ final int currentPermission = mUidToNetworkPerm.get(uid, PERMISSION_NONE);
+ final int permission = highestPermissionForUid(uid, currentPermission, packageName);
+ if (permission != currentPermission) {
+ mUidToNetworkPerm.put(uid, permission);
+
+ SparseIntArray apps = new SparseIntArray();
+ apps.put(uid, permission);
+ sendUidsNetworkPermission(apps, true /* add */);
}
// If the newly-installed package falls within some VPN's uid range, update Netd with it.
- // This needs to happen after the mApps update above, since removeBypassingUids() depends
- // on mApps to check if the package can bypass VPN.
- for (Map.Entry<String, Set<UidRange>> vpn : mVpnUidRanges.entrySet()) {
- if (UidRange.containsUid(vpn.getValue(), uid)) {
- final Set<Integer> changedUids = new HashSet<>();
- changedUids.add(uid);
- removeBypassingUids(changedUids, /* vpnAppUid */ -1);
- updateVpnUids(vpn.getKey(), changedUids, true);
- }
- }
+ // This needs to happen after the mUidToNetworkPerm update above, since
+ // removeBypassingUids() in updateVpnUid() depends on mUidToNetworkPerm to check if the
+ // package can bypass VPN.
+ updateVpnUid(uid, true /* add */);
mAllApps.add(appId);
+ mPermissionUpdateLogs.log("Package add: name=" + packageName + ", uid=" + uid
+ + ", nPerm=(" + permissionToString(permission) + "/"
+ + permissionToString(currentPermission) + ")"
+ + ", tPerm=" + permissionToString(trafficPerm));
}
- private Boolean highestUidNetworkPermission(int uid) {
- Boolean permission = null;
+ private int highestUidNetworkPermission(int uid) {
+ int permission = PERMISSION_NONE;
final String[] packages = mPackageManager.getPackagesForUid(uid);
if (!CollectionUtils.isEmpty(packages)) {
for (String name : packages) {
// If multiple packages have the same UID, give the UID all permissions that
// any package in that UID has.
- permission = highestPermissionForUid(permission, name);
- if (permission == SYSTEM) {
+ permission = highestPermissionForUid(uid, permission, name);
+ if (permission == PERMISSION_SYSTEM) {
break;
}
}
@@ -528,53 +628,43 @@
* @hide
*/
public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
- // TODO: Netd is using appId for checking traffic permission. Correct the methods that are
- // using appId instead of uid actually
- sendPackagePermissionsForUid(UserHandle.getAppId(uid), getPermissionForUid(uid));
+ final int appId = UserHandle.getAppId(uid);
+ final int trafficPerm = getTrafficPermissionForUid(uid);
+ sendPackagePermissionsForAppId(appId, trafficPerm);
// If the newly-removed package falls within some VPN's uid range, update Netd with it.
- // This needs to happen before the mApps update below, since removeBypassingUids() depends
- // on mApps to check if the package can bypass VPN.
- for (Map.Entry<String, Set<UidRange>> vpn : mVpnUidRanges.entrySet()) {
- if (UidRange.containsUid(vpn.getValue(), uid)) {
- final Set<Integer> changedUids = new HashSet<>();
- changedUids.add(uid);
- removeBypassingUids(changedUids, /* vpnAppUid */ -1);
- updateVpnUids(vpn.getKey(), changedUids, false);
- }
- }
+ // This needs to happen before the mUidToNetworkPerm update below, since
+ // removeBypassingUids() in updateVpnUid() depends on mUidToNetworkPerm to check if the
+ // package can bypass VPN.
+ updateVpnUid(uid, false /* add */);
// If the package has been removed from all users on the device, clear it form mAllApps.
if (mPackageManager.getNameForUid(uid) == null) {
- mAllApps.remove(UserHandle.getAppId(uid));
+ mAllApps.remove(appId);
}
- Map<Integer, Boolean> apps = new HashMap<>();
- final Boolean permission = highestUidNetworkPermission(uid);
- if (permission == SYSTEM) {
- // An app with this UID still has the SYSTEM permission.
- // Therefore, this UID must already have the SYSTEM permission.
- // Nothing to do.
- return;
- }
-
- final int appId = UserHandle.getAppId(uid);
- if (permission == mApps.get(appId)) {
- // The permissions of this UID have not changed. Nothing to do.
- return;
- } else if (permission != null) {
- mApps.put(appId, permission);
- apps.put(appId, permission);
- update(mUsers, apps, true);
- } else {
- mApps.remove(appId);
- apps.put(appId, NETWORK); // doesn't matter which permission we pick here
- update(mUsers, apps, false);
+ final int currentPermission = mUidToNetworkPerm.get(uid, PERMISSION_NONE);
+ final int permission = highestUidNetworkPermission(uid);
+ mPermissionUpdateLogs.log("Package remove: name=" + packageName + ", uid=" + uid
+ + ", nPerm=(" + permissionToString(permission) + "/"
+ + permissionToString(currentPermission) + ")"
+ + ", tPerm=" + permissionToString(trafficPerm));
+ if (permission != currentPermission) {
+ final SparseIntArray apps = new SparseIntArray();
+ if (permission == PERMISSION_NONE) {
+ mUidToNetworkPerm.delete(uid);
+ apps.put(uid, PERMISSION_NETWORK); // doesn't matter which permission we pick here
+ sendUidsNetworkPermission(apps, false);
+ } else {
+ mUidToNetworkPerm.put(uid, permission);
+ apps.put(uid, permission);
+ sendUidsNetworkPermission(apps, true);
+ }
}
}
private static int getNetdPermissionMask(String[] requestedPermissions,
int[] requestedPermissionsFlags) {
- int permissions = 0;
+ int permissions = PERMISSION_NONE;
if (requestedPermissions == null || requestedPermissionsFlags == null) return permissions;
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i].equals(INTERNET)
@@ -589,12 +679,23 @@
return permissions;
}
- private PackageInfo getPackageInfo(String packageName) {
+ private synchronized PackageManager getPackageManagerAsUser(UserHandle user) {
+ PackageManager pm = mUsersPackageManager.get(user);
+ if (pm == null) {
+ pm = mContext.createContextAsUser(user, 0 /* flag */).getPackageManager();
+ mUsersPackageManager.put(user, pm);
+ }
+ return pm;
+ }
+
+ private PackageInfo getPackageInfoAsUser(String packageName, UserHandle user) {
try {
- PackageInfo app = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS
- | MATCH_ANY_USER);
- return app;
+ final PackageInfo info = getPackageManagerAsUser(user)
+ .getPackageInfo(packageName, GET_PERMISSIONS);
+ return info;
} catch (NameNotFoundException e) {
+ // App not found.
+ loge("NameNotFoundException " + packageName);
return null;
}
}
@@ -614,7 +715,7 @@
// but that's safe.
final Set<Integer> changedUids = intersectUids(rangesToAdd, mAllApps);
removeBypassingUids(changedUids, vpnAppUid);
- updateVpnUids(iface, changedUids, true);
+ updateVpnUidsInterfaceRules(iface, changedUids, true /* add */);
if (mVpnUidRanges.containsKey(iface)) {
mVpnUidRanges.get(iface).addAll(rangesToAdd);
} else {
@@ -635,7 +736,7 @@
// ranges and update Netd about them.
final Set<Integer> changedUids = intersectUids(rangesToRemove, mAllApps);
removeBypassingUids(changedUids, vpnAppUid);
- updateVpnUids(iface, changedUids, false);
+ updateVpnUidsInterfaceRules(iface, changedUids, false /* add */);
Set<UidRange> existingRanges = mVpnUidRanges.getOrDefault(iface, null);
if (existingRanges == null) {
loge("Attempt to remove unknown vpn uid Range iface = " + iface);
@@ -674,7 +775,7 @@
/**
* Remove all apps which can elect to bypass the VPN from the list of uids
*
- * An app can elect to bypass the VPN if it hold SYSTEM permission, or if its the active VPN
+ * An app can elect to bypass the VPN if it holds SYSTEM permission, or if it's the active VPN
* app itself.
*
* @param uids The list of uids to operate on
@@ -682,7 +783,7 @@
*/
private void removeBypassingUids(Set<Integer> uids, int vpnAppUid) {
uids.remove(vpnAppUid);
- uids.removeIf(uid -> mApps.getOrDefault(UserHandle.getAppId(uid), NETWORK) == SYSTEM);
+ uids.removeIf(uid -> mUidToNetworkPerm.get(uid, PERMISSION_NONE) == PERMISSION_SYSTEM);
}
/**
@@ -696,7 +797,7 @@
* @param add {@code true} if the uids are to be added to the interface, {@code false} if they
* are to be removed from the interface.
*/
- private void updateVpnUids(String iface, Set<Integer> uids, boolean add) {
+ private void updateVpnUidsInterfaceRules(String iface, Set<Integer> uids, boolean add) {
if (uids.size() == 0) {
return;
}
@@ -718,41 +819,39 @@
}
/**
- * Called by PackageListObserver when a package is installed/uninstalled. Send the updated
- * permission information to netd.
+ * Send the updated permission information to netd. Called upon package install/uninstall.
*
- * @param uid the app uid of the package installed
+ * @param appId the appId of the package installed
* @param permissions the permissions the app requested and netd cares about.
*
* @hide
*/
@VisibleForTesting
- void sendPackagePermissionsForUid(int uid, int permissions) {
+ void sendPackagePermissionsForAppId(int appId, int permissions) {
SparseIntArray netdPermissionsAppIds = new SparseIntArray();
- netdPermissionsAppIds.put(uid, permissions);
- sendPackagePermissionsToNetd(netdPermissionsAppIds);
+ netdPermissionsAppIds.put(appId, permissions);
+ sendAppIdsTrafficPermission(netdPermissionsAppIds);
}
/**
- * Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET
- * and/or UPDATE_DEVICE_STATS permission of the uids in array.
+ * Grant or revoke the INTERNET and/or UPDATE_DEVICE_STATS permission of the appIds in array.
*
- * @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the
- * permission is 0, revoke all permissions of that uid.
+ * @param netdPermissionsAppIds integer pairs of appIds and the permission granted to it. If the
+ * permission is 0, revoke all permissions of that appId.
*
* @hide
*/
@VisibleForTesting
- void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) {
+ void sendAppIdsTrafficPermission(SparseIntArray netdPermissionsAppIds) {
if (mNetd == null) {
Log.e(TAG, "Failed to get the netd service");
return;
}
- ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
- ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
- ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
- ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
- ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
+ final ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
+ final ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
+ final ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
+ final ArrayList<Integer> noPermissionAppIds = new ArrayList<>();
+ final ArrayList<Integer> uninstalledAppIds = new ArrayList<>();
for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
int permissions = netdPermissionsAppIds.valueAt(i);
switch(permissions) {
@@ -816,26 +915,28 @@
updateUidsAllowedOnRestrictedNetworks(mDeps.getUidsAllowedOnRestrictedNetworks(mContext));
uidsToUpdate.addAll(mUidsAllowedOnRestrictedNetworks);
- final Map<Integer, Boolean> updatedUids = new HashMap<>();
- final Map<Integer, Boolean> removedUids = new HashMap<>();
+ final SparseIntArray updatedUids = new SparseIntArray();
+ final SparseIntArray removedUids = new SparseIntArray();
// Step2. For each uid to update, find out its new permission.
for (Integer uid : uidsToUpdate) {
- final Boolean permission = highestUidNetworkPermission(uid);
+ final int permission = highestUidNetworkPermission(uid);
- final int appId = UserHandle.getAppId(uid);
- if (null == permission) {
- removedUids.put(appId, NETWORK); // Doesn't matter which permission is set here.
- mApps.remove(appId);
+ if (PERMISSION_NONE == permission) {
+ // Doesn't matter which permission is set here.
+ removedUids.put(uid, PERMISSION_NETWORK);
+ mUidToNetworkPerm.delete(uid);
} else {
- updatedUids.put(appId, permission);
- mApps.put(appId, permission);
+ updatedUids.put(uid, permission);
+ mUidToNetworkPerm.put(uid, permission);
}
}
// Step3. Update or revoke permission for uids with netd.
- update(mUsers, updatedUids, true /* add */);
- update(mUsers, removedUids, false /* add */);
+ sendUidsNetworkPermission(updatedUids, true /* add */);
+ sendUidsNetworkPermission(removedUids, false /* add */);
+ mPermissionUpdateLogs.log("Setting change: update=" + updatedUids
+ + ", remove=" + removedUids);
}
private synchronized void onExternalApplicationsAvailable(String[] pkgList) {
@@ -845,11 +946,13 @@
}
for (String app : pkgList) {
- final PackageInfo info = getPackageInfo(app);
- if (info == null || info.applicationInfo == null) continue;
+ for (UserHandle user : mUsers) {
+ final PackageInfo info = getPackageInfoAsUser(app, user);
+ if (info == null || info.applicationInfo == null) continue;
- final int appId = info.applicationInfo.uid;
- onPackageAdded(app, appId); // Use onPackageAdded to add package one by one.
+ final int uid = info.applicationInfo.uid;
+ onPackageAdded(app, uid); // Use onPackageAdded to add package one by one.
+ }
}
}
@@ -863,6 +966,12 @@
pw.println();
}
pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Update logs:");
+ pw.increaseIndent();
+ mPermissionUpdateLogs.reverseDump(pw);
+ pw.decreaseIndent();
}
private static void log(String s) {
diff --git a/service/src/com/android/server/connectivity/TcpKeepaliveController.java b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
index c480594..acfbb3c 100644
--- a/service/src/com/android/server/connectivity/TcpKeepaliveController.java
+++ b/service/src/com/android/server/connectivity/TcpKeepaliveController.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
import static android.net.SocketKeepalive.DATA_RECEIVED;
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
import static android.net.SocketKeepalive.ERROR_SOCKET_NOT_IDLE;
import static android.net.SocketKeepalive.ERROR_UNSUPPORTED;
@@ -29,6 +30,8 @@
import static android.system.OsConstants.IP_TTL;
import static android.system.OsConstants.TIOCOUTQ;
+import static com.android.net.module.util.NetworkStackConstants.IPV4_HEADER_MIN_LEN;
+
import android.annotation.NonNull;
import android.net.InvalidPacketException;
import android.net.NetworkUtils;
@@ -36,7 +39,6 @@
import android.net.TcpKeepalivePacketData;
import android.net.TcpKeepalivePacketDataParcelable;
import android.net.TcpRepairWindow;
-import android.net.util.KeepalivePacketDataUtil;
import android.os.Handler;
import android.os.MessageQueue;
import android.os.Messenger;
@@ -46,12 +48,18 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.IpUtils;
import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo;
import java.io.FileDescriptor;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
/**
* Manage tcp socket which offloads tcp keepalive.
@@ -82,6 +90,8 @@
private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+ private static final int TCP_HEADER_LENGTH = 20;
+
// Reference include/uapi/linux/tcp.h
private static final int TCP_REPAIR = 19;
private static final int TCP_REPAIR_QUEUE = 20;
@@ -112,12 +122,81 @@
throws InvalidPacketException, InvalidSocketException {
try {
final TcpKeepalivePacketDataParcelable tcpDetails = switchToRepairMode(fd);
- return KeepalivePacketDataUtil.fromStableParcelable(tcpDetails);
+ // TODO: consider building a TcpKeepalivePacketData directly from switchToRepairMode
+ return fromStableParcelable(tcpDetails);
} catch (InvalidPacketException | InvalidSocketException e) {
switchOutOfRepairMode(fd);
throw e;
}
}
+
+ /**
+ * Factory method to create tcp keepalive packet structure.
+ */
+ @VisibleForTesting
+ 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.
+ // TODO: consider using Ipv4Utils.buildTcpv4Packet() instead
+ private static byte[] buildV4Packet(TcpKeepalivePacketDataParcelable tcpDetails) {
+ final int length = IPV4_HEADER_MIN_LEN + 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) 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_MIN_LEN, TCP_HEADER_LENGTH));
+
+ return buf.array();
+ }
+
/**
* Switch the tcp socket to repair mode and query detail tcp information.
*
diff --git a/tests/common/Android.bp b/tests/common/Android.bp
index e1fab09..f47f6b0 100644
--- a/tests/common/Android.bp
+++ b/tests/common/Android.bp
@@ -66,7 +66,7 @@
min_sdk_version: "30",
// TODO: change to 31 as soon as it is available
target_sdk_version: "30",
- test_suites: ["general-tests", "mts"],
+ test_suites: ["general-tests", "mts-tethering"],
defaults: [
"framework-connectivity-test-defaults",
"FrameworksNetTests-jni-defaults",
@@ -89,7 +89,7 @@
"libstaticjvmtiagent",
// For NetworkStackUtils included in NetworkStackBase
"libnetworkstackutilsjni",
- "libtetherutilsjni",
+ "libcom_android_networkstack_tethering_util_jni",
// For framework tests
"libservice-connectivity",
],
@@ -115,6 +115,7 @@
// meaning @hide APIs in framework-connectivity are resolved before @SystemApi
// stubs in framework
"framework-connectivity.impl",
+ "framework-tethering.impl",
"framework",
// if sdk_version="" this gets automatically included, but here we need to add manually.
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 493a201..32f00a3 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -50,6 +50,7 @@
import static com.android.modules.utils.build.SdkLevel.isAtLeastR;
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.testutils.MiscAsserts.assertEmpty;
import static com.android.testutils.MiscAsserts.assertThrows;
import static com.android.testutils.ParcelUtils.assertParcelSane;
@@ -75,6 +76,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.testutils.CompatUtil;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -83,11 +85,14 @@
import org.junit.runner.RunWith;
import org.mockito.Mockito;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Set;
@RunWith(AndroidJUnit4.class)
@SmallTest
+@ConnectivityModuleTest
public class NetworkCapabilitiesTest {
private static final String TEST_SSID = "TEST_SSID";
private static final String DIFFERENT_TEST_SSID = "DIFFERENT_TEST_SSID";
@@ -280,19 +285,11 @@
assertFalse(netCap2.satisfiedByUids(netCap));
assertFalse(netCap.appliesToUid(650));
assertTrue(netCap2.appliesToUid(650));
- netCap.combineCapabilities(netCap2);
+ netCap.setUids(uids);
assertTrue(netCap2.satisfiedByUids(netCap));
assertTrue(netCap.appliesToUid(650));
assertFalse(netCap.appliesToUid(500));
- assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
- netCap.combineCapabilities(new NetworkCapabilities());
- assertTrue(netCap.appliesToUid(500));
- assertTrue(netCap.appliesToUidRange(new UidRange(1, 100000)));
- assertFalse(netCap2.appliesToUid(500));
- assertFalse(netCap2.appliesToUidRange(new UidRange(1, 100000)));
- assertTrue(new NetworkCapabilities().satisfiedByUids(netCap));
-
// Null uids satisfies everything.
netCap.setUids(null);
assertTrue(netCap2.satisfiedByUids(netCap));
@@ -341,8 +338,16 @@
}
private void testParcelSane(NetworkCapabilities cap) {
+ // This test can be run as unit test against the latest system image, as CTS to verify
+ // an Android release that is as recent as the test, or as MTS to verify the
+ // Connectivity module. In the first two cases NetworkCapabilities will be as recent
+ // as the test. In the last case, starting from S NetworkCapabilities is updated as part
+ // of Connectivity, so it is also as recent as the test. For MTS on Q and R,
+ // NetworkCapabilities is not updatable, so it may have a different number of fields.
if (isAtLeastS()) {
- assertParcelSane(cap, 16);
+ // When this test is run on S+, NetworkCapabilities is as recent as the test,
+ // so this should be the most recent known number of fields.
+ assertParcelSane(cap, 17);
} else if (isAtLeastR()) {
assertParcelSane(cap, 15);
} else {
@@ -577,103 +582,6 @@
}
@Test
- public void testCombineCapabilities() {
- NetworkCapabilities nc1 = new NetworkCapabilities();
- NetworkCapabilities nc2 = new NetworkCapabilities();
-
- if (isAtLeastS()) {
- nc1.addForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
- }
- nc1.addCapability(NET_CAPABILITY_NOT_ROAMING);
- assertNotEquals(nc1, nc2);
- nc2.combineCapabilities(nc1);
- assertEquals(nc1, nc2);
- assertTrue(nc2.hasCapability(NET_CAPABILITY_NOT_ROAMING));
- if (isAtLeastS()) {
- assertTrue(nc2.hasForbiddenCapability(NET_CAPABILITY_CAPTIVE_PORTAL));
- }
-
- if (isAtLeastS()) {
- // This will effectively move NOT_ROAMING capability from required to forbidden for nc1.
- nc1.addForbiddenCapability(NET_CAPABILITY_NOT_ROAMING);
- // It is not allowed to have the same capability in both wanted and forbidden list.
- assertThrows(IllegalArgumentException.class, () -> nc2.combineCapabilities(nc1));
- // Remove forbidden capability to continue other tests.
- nc1.removeForbiddenCapability(NET_CAPABILITY_NOT_ROAMING);
- }
-
- nc1.setSSID(TEST_SSID);
- nc2.combineCapabilities(nc1);
- if (isAtLeastR()) {
- assertTrue(TEST_SSID.equals(nc2.getSsid()));
- }
-
- // Because they now have the same SSID, the following call should not throw
- nc2.combineCapabilities(nc1);
-
- nc1.setSSID(DIFFERENT_TEST_SSID);
- try {
- nc2.combineCapabilities(nc1);
- fail("Expected IllegalStateException: can't combine different SSIDs");
- } catch (IllegalStateException expected) {}
- nc1.setSSID(TEST_SSID);
-
- if (isAtLeastS()) {
- nc1.setUids(uidRanges(10, 13));
- assertNotEquals(nc1, nc2);
- nc2.combineCapabilities(nc1); // Everything + 10~13 is still everything.
- assertNotEquals(nc1, nc2);
- nc1.combineCapabilities(nc2); // 10~13 + everything is everything.
- assertEquals(nc1, nc2);
- nc1.setUids(uidRanges(10, 13));
- nc2.setUids(uidRanges(20, 23));
- assertNotEquals(nc1, nc2);
- nc1.combineCapabilities(nc2);
- assertTrue(nc1.appliesToUid(12));
- assertFalse(nc2.appliesToUid(12));
- assertTrue(nc1.appliesToUid(22));
- assertTrue(nc2.appliesToUid(22));
-
- // Verify the subscription id list can be combined only when they are equal.
- nc1.setSubscriptionIds(Set.of(TEST_SUBID1, TEST_SUBID2));
- nc2.setSubscriptionIds(Set.of(TEST_SUBID2));
- assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
-
- nc2.setSubscriptionIds(Set.of());
- assertThrows(IllegalStateException.class, () -> nc2.combineCapabilities(nc1));
-
- nc2.setSubscriptionIds(Set.of(TEST_SUBID2, TEST_SUBID1));
- nc2.combineCapabilities(nc1);
- assertEquals(Set.of(TEST_SUBID2, TEST_SUBID1), nc2.getSubscriptionIds());
- }
- }
-
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
- public void testCombineCapabilities_AdministratorUids() {
- final NetworkCapabilities nc1 = new NetworkCapabilities();
- final NetworkCapabilities nc2 = new NetworkCapabilities();
-
- final int[] adminUids = {3, 6, 12};
- nc1.setAdministratorUids(adminUids);
- nc2.combineCapabilities(nc1);
- assertTrue(nc2.equalsAdministratorUids(nc1));
- assertArrayEquals(nc2.getAdministratorUids(), adminUids);
-
- final int[] adminUidsOtherOrder = {3, 12, 6};
- nc1.setAdministratorUids(adminUidsOtherOrder);
- assertTrue(nc2.equalsAdministratorUids(nc1));
-
- final int[] adminUids2 = {11, 1, 12, 3, 6};
- nc1.setAdministratorUids(adminUids2);
- assertFalse(nc2.equalsAdministratorUids(nc1));
- assertFalse(Arrays.equals(nc2.getAdministratorUids(), adminUids2));
- try {
- nc2.combineCapabilities(nc1);
- fail("Shouldn't be able to combine different lists of admin UIDs");
- } catch (IllegalStateException expected) { }
- }
-
- @Test
public void testSetCapabilities() {
final int[] REQUIRED_CAPABILITIES = new int[] {
NET_CAPABILITY_INTERNET, NET_CAPABILITY_NOT_VPN };
@@ -711,6 +619,47 @@
}
@Test
+ public void testUnderlyingNetworks() {
+ assumeTrue(isAtLeastT());
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ final Network network1 = new Network(100);
+ final Network network2 = new Network(101);
+ final ArrayList<Network> inputNetworks = new ArrayList<>();
+ inputNetworks.add(network1);
+ inputNetworks.add(network2);
+ nc.setUnderlyingNetworks(inputNetworks);
+ final ArrayList<Network> outputNetworks = new ArrayList<>(nc.getUnderlyingNetworks());
+ assertEquals(network1, outputNetworks.get(0));
+ assertEquals(network2, outputNetworks.get(1));
+ nc.setUnderlyingNetworks(null);
+ assertNull(nc.getUnderlyingNetworks());
+ }
+
+ @Test
+ public void testEqualsForUnderlyingNetworks() {
+ assumeTrue(isAtLeastT());
+ final NetworkCapabilities nc1 = new NetworkCapabilities();
+ final NetworkCapabilities nc2 = new NetworkCapabilities();
+ assertEquals(nc1, nc2);
+ final Network network = new Network(100);
+ final ArrayList<Network> inputNetworks = new ArrayList<>();
+ final ArrayList<Network> emptyList = new ArrayList<>();
+ inputNetworks.add(network);
+ nc1.setUnderlyingNetworks(inputNetworks);
+ assertNotEquals(nc1, nc2);
+ nc2.setUnderlyingNetworks(inputNetworks);
+ assertEquals(nc1, nc2);
+ nc1.setUnderlyingNetworks(emptyList);
+ assertNotEquals(nc1, nc2);
+ nc2.setUnderlyingNetworks(emptyList);
+ assertEquals(nc1, nc2);
+ nc1.setUnderlyingNetworks(null);
+ assertNotEquals(nc1, nc2);
+ nc2.setUnderlyingNetworks(null);
+ assertEquals(nc1, nc2);
+ }
+
+ @Test
public void testSetNetworkSpecifierOnMultiTransportNc() {
// Sequence 1: Transport + Transport + NetworkSpecifier
NetworkCapabilities nc1 = new NetworkCapabilities();
@@ -748,29 +697,6 @@
}
@Test
- public void testCombineTransportInfo() {
- NetworkCapabilities nc1 = new NetworkCapabilities();
- nc1.setTransportInfo(new TestTransportInfo());
-
- NetworkCapabilities nc2 = new NetworkCapabilities();
- // new TransportInfo so that object is not #equals to nc1's TransportInfo (that's where
- // combine fails)
- nc2.setTransportInfo(new TestTransportInfo());
-
- try {
- nc1.combineCapabilities(nc2);
- fail("Should not be able to combine NetworkCabilities which contain TransportInfos");
- } catch (IllegalStateException expected) {
- // empty
- }
-
- // verify that can combine with identical TransportInfo objects
- NetworkCapabilities nc3 = new NetworkCapabilities();
- nc3.setTransportInfo(nc1.getTransportInfo());
- nc1.combineCapabilities(nc3);
- }
-
- @Test
public void testSet() {
NetworkCapabilities nc1 = new NetworkCapabilities();
NetworkCapabilities nc2 = new NetworkCapabilities();
@@ -1108,7 +1034,7 @@
final TransportInfo transportInfo = new TransportInfo() {};
final String ssid = "TEST_SSID";
final String packageName = "com.google.test.networkcapabilities";
- final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ final NetworkCapabilities.Builder capBuilder = new NetworkCapabilities.Builder()
.addTransportType(TRANSPORT_WIFI)
.addTransportType(TRANSPORT_CELLULAR)
.removeTransportType(TRANSPORT_CELLULAR)
@@ -1124,8 +1050,14 @@
.setSignalStrength(signalStrength)
.setSsid(ssid)
.setRequestorUid(requestUid)
- .setRequestorPackageName(packageName)
- .build();
+ .setRequestorPackageName(packageName);
+ final Network network1 = new Network(100);
+ final Network network2 = new Network(101);
+ final List<Network> inputNetworks = List.of(network1, network2);
+ if (isAtLeastT()) {
+ capBuilder.setUnderlyingNetworks(inputNetworks);
+ }
+ final NetworkCapabilities nc = capBuilder.build();
assertEquals(1, nc.getTransportTypes().length);
assertEquals(TRANSPORT_WIFI, nc.getTransportTypes()[0]);
assertTrue(nc.hasCapability(NET_CAPABILITY_EIMS));
@@ -1143,6 +1075,11 @@
assertEquals(ssid, nc.getSsid());
assertEquals(requestUid, nc.getRequestorUid());
assertEquals(packageName, nc.getRequestorPackageName());
+ if (isAtLeastT()) {
+ final List<Network> outputNetworks = nc.getUnderlyingNetworks();
+ assertEquals(network1, outputNetworks.get(0));
+ assertEquals(network2, outputNetworks.get(1));
+ }
// Cannot assign null into NetworkCapabilities.Builder
try {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder(null);
@@ -1167,4 +1104,106 @@
// Ensure test case fails if new net cap is added into default cap but no update here.
assertEquals(0, nc.getCapabilities().length);
}
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testRestrictCapabilitiesForTestNetworkByNotOwnerWithNonRestrictedNc() {
+ testRestrictCapabilitiesForTestNetworkWithNonRestrictedNc(false /* isOwner */);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testRestrictCapabilitiesForTestNetworkByOwnerWithNonRestrictedNc() {
+ testRestrictCapabilitiesForTestNetworkWithNonRestrictedNc(true /* isOwner */);
+ }
+
+ private void testRestrictCapabilitiesForTestNetworkWithNonRestrictedNc(boolean isOwner) {
+ final int ownerUid = 1234;
+ final int signalStrength = -80;
+ final int[] administratorUids = {1001, ownerUid};
+ final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(TEST_SUBID1);
+ final TransportInfo transportInfo = new TransportInfo() {};
+ final NetworkCapabilities nonRestrictedNc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_MMS)
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .setAdministratorUids(administratorUids)
+ .setOwnerUid(ownerUid)
+ .setNetworkSpecifier(specifier)
+ .setSignalStrength(signalStrength)
+ .setTransportInfo(transportInfo)
+ .setSubscriptionIds(Set.of(TEST_SUBID1)).build();
+ final int creatorUid = isOwner ? ownerUid : INVALID_UID;
+ nonRestrictedNc.restrictCapabilitiesForTestNetwork(creatorUid);
+
+ final NetworkCapabilities.Builder expectedNcBuilder = new NetworkCapabilities.Builder();
+ // Non-UNRESTRICTED_TEST_NETWORKS_ALLOWED_TRANSPORTS will be removed and TRANSPORT_TEST will
+ // be appended for non-restricted net cap.
+ expectedNcBuilder.addTransportType(TRANSPORT_TEST);
+ // Only TEST_NETWORKS_ALLOWED_CAPABILITIES will be kept. SubIds are only allowed for Test
+ // Networks that only declare TRANSPORT_TEST.
+ expectedNcBuilder.addCapability(NET_CAPABILITY_NOT_METERED)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .setSubscriptionIds(Set.of(TEST_SUBID1));
+
+ expectedNcBuilder.setNetworkSpecifier(specifier)
+ .setSignalStrength(signalStrength).setTransportInfo(transportInfo);
+ if (creatorUid == ownerUid) {
+ // Only retain the owner and administrator UIDs if they match the app registering the
+ // remote caller that registered the network.
+ expectedNcBuilder.setAdministratorUids(new int[]{ownerUid}).setOwnerUid(ownerUid);
+ }
+
+ assertEquals(expectedNcBuilder.build(), nonRestrictedNc);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testRestrictCapabilitiesForTestNetworkByNotOwnerWithRestrictedNc() {
+ testRestrictCapabilitiesForTestNetworkWithRestrictedNc(false /* isOwner */);
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testRestrictCapabilitiesForTestNetworkByOwnerWithRestrictedNc() {
+ testRestrictCapabilitiesForTestNetworkWithRestrictedNc(true /* isOwner */);
+ }
+
+ private void testRestrictCapabilitiesForTestNetworkWithRestrictedNc(boolean isOwner) {
+ final int ownerUid = 1234;
+ final int signalStrength = -80;
+ final int[] administratorUids = {1001, ownerUid};
+ final TransportInfo transportInfo = new TransportInfo() {};
+ // No NetworkSpecifier is set because after performing restrictCapabilitiesForTestNetwork
+ // the networkCapabilities will contain more than one transport type. However,
+ // networkCapabilities must have a single transport specified to use NetworkSpecifier. Thus,
+ // do not verify this part since it's verified in other tests.
+ final NetworkCapabilities restrictedNc = new NetworkCapabilities.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_MMS)
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .setAdministratorUids(administratorUids)
+ .setOwnerUid(ownerUid)
+ .setSignalStrength(signalStrength)
+ .setTransportInfo(transportInfo)
+ .setSubscriptionIds(Set.of(TEST_SUBID1)).build();
+ final int creatorUid = isOwner ? ownerUid : INVALID_UID;
+ restrictedNc.restrictCapabilitiesForTestNetwork(creatorUid);
+
+ final NetworkCapabilities.Builder expectedNcBuilder = new NetworkCapabilities.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+ // If the test network is restricted, then the network may declare any transport, and
+ // appended with TRANSPORT_TEST.
+ expectedNcBuilder.addTransportType(TRANSPORT_CELLULAR);
+ expectedNcBuilder.addTransportType(TRANSPORT_TEST);
+ // Only TEST_NETWORKS_ALLOWED_CAPABILITIES will be kept.
+ expectedNcBuilder.addCapability(NET_CAPABILITY_NOT_METERED);
+ expectedNcBuilder.removeCapability(NET_CAPABILITY_TRUSTED);
+
+ expectedNcBuilder.setSignalStrength(signalStrength).setTransportInfo(transportInfo);
+ if (creatorUid == ownerUid) {
+ // Only retain the owner and administrator UIDs if they match the app registering the
+ // remote caller that registered the network.
+ expectedNcBuilder.setAdministratorUids(new int[]{ownerUid}).setOwnerUid(ownerUid);
+ }
+
+ assertEquals(expectedNcBuilder.build(), restrictedNc);
+ }
}
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index 674af14..63572c3 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -45,5 +45,6 @@
test_suites: [
"cts",
"general-tests",
+ "sts",
],
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 215f129..3abc4fb 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -30,9 +30,18 @@
import static android.system.OsConstants.SOCK_DGRAM;
import static android.test.MoreAsserts.assertNotEqual;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.annotation.Nullable;
+import android.app.Activity;
import android.app.DownloadManager;
import android.app.DownloadManager.Query;
import android.app.DownloadManager.Request;
@@ -71,15 +80,21 @@
import android.system.Os;
import android.system.OsConstants;
import android.system.StructPollfd;
-import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
import android.text.TextUtils;
import android.util.Log;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import com.android.compatibility.common.util.BlockingBroadcastReceiver;
import com.android.modules.utils.build.SdkLevel;
import com.android.testutils.TestableNetworkCallback;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.Closeable;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -127,7 +142,8 @@
* https://source.android.com/devices/tech/config/kernel_network_tests.html
*
*/
-public class VpnTest extends InstrumentationTestCase {
+@RunWith(AndroidJUnit4.class)
+public class VpnTest {
// These are neither public nor @TestApi.
// TODO: add them to @TestApi.
@@ -161,17 +177,24 @@
return !pm.hasSystemFeature("android.hardware.type.watch");
}
- @Override
- public void setUp() throws Exception {
- super.setUp();
+ public final <T extends Activity> T launchActivity(String packageName, Class<T> activityClass) {
+ final Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(packageName, activityClass.getName());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ final T activity = (T) getInstrumentation().startActivitySync(intent);
+ getInstrumentation().waitForIdleSync();
+ return activity;
+ }
+ @Before
+ public void setUp() throws Exception {
mNetwork = null;
mCallback = null;
storePrivateDnsSetting();
mDevice = UiDevice.getInstance(getInstrumentation());
mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
- MyActivity.class, null);
+ MyActivity.class);
mPackageName = mActivity.getPackageName();
mCM = (ConnectivityManager) mActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
mWifiManager = (WifiManager) mActivity.getSystemService(Context.WIFI_SERVICE);
@@ -180,7 +203,7 @@
mDevice.waitForIdle();
}
- @Override
+ @After
public void tearDown() throws Exception {
restorePrivateDnsSetting();
mRemoteSocketFactoryClient.unbind();
@@ -190,7 +213,6 @@
Log.i(TAG, "Stopping VPN");
stopVpn();
mActivity.finish();
- super.tearDown();
}
private void prepareVpn() throws Exception {
@@ -702,13 +724,18 @@
setAndVerifyPrivateDns(initialMode);
}
+ @Test
public void testDefault() throws Exception {
if (!supportedHardware()) return;
- // If adb TCP port opened, this test may running by adb over network.
- // All of socket would be destroyed in this test. So this test don't
- // support adb over network, see b/119382723.
- if (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
- || SystemProperties.getInt("service.adb.tcp.port", -1) > -1) {
+ if (!SdkLevel.isAtLeastS() && (
+ SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
+ || SystemProperties.getInt("service.adb.tcp.port", -1) > -1)) {
+ // If adb TCP port opened, this test may running by adb over network.
+ // All of socket would be destroyed in this test. So this test don't
+ // support adb over network, see b/119382723.
+ // This is fixed in S, but still affects previous Android versions,
+ // and this test must be backwards compatible.
+ // TODO: Delete this code entirely when R is no longer supported.
Log.i(TAG, "adb is running over the network, so skip this test");
return;
}
@@ -762,6 +789,15 @@
maybeExpectVpnTransportInfo(vpnNetwork);
assertEquals(TYPE_VPN, mCM.getNetworkInfo(vpnNetwork).getType());
+ if (SdkLevel.isAtLeastT()) {
+ runWithShellPermissionIdentity(() -> {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork);
+ assertNotNull(nc);
+ assertNotNull(nc.getUnderlyingNetworks());
+ assertEquals(defaultNetwork, new ArrayList<>(nc.getUnderlyingNetworks()).get(0));
+ }, NETWORK_SETTINGS);
+ }
+
if (SdkLevel.isAtLeastS()) {
// Check that system default network callback has not seen any network changes, even
// though the app's default network changed. Also check that otherUidCallback saw no
@@ -781,6 +817,7 @@
receiver.unregisterQuietly();
}
+ @Test
public void testAppAllowed() throws Exception {
if (!supportedHardware()) return;
@@ -801,6 +838,7 @@
checkStrictModePrivateDns();
}
+ @Test
public void testAppDisallowed() throws Exception {
if (!supportedHardware()) return;
@@ -808,11 +846,16 @@
FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
- // If adb TCP port opened, this test may running by adb over TCP.
- // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test,
- // see b/119382723.
- // Note: The test don't support running adb over network for root device
- disallowedApps = disallowedApps + ",com.android.shell";
+ if (!SdkLevel.isAtLeastS()) {
+ // If adb TCP port opened, this test may running by adb over TCP.
+ // Add com.android.shell application into disallowedApps to exclude adb socket for VPN
+ // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root).
+ //
+ // This is fixed in S, but still affects previous Android versions,
+ // and this test must be backwards compatible.
+ // TODO: Delete this code entirely when R is no longer supported.
+ disallowedApps = disallowedApps + ",com.android.shell";
+ }
Log.i(TAG, "Append shell app to disallowedApps: " + disallowedApps);
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"192.0.2.0/24", "2001:db8::/32"},
@@ -829,6 +872,7 @@
assertFalse(nc.hasTransport(TRANSPORT_VPN));
}
+ @Test
public void testGetConnectionOwnerUidSecurity() throws Exception {
if (!supportedHardware()) return;
@@ -850,6 +894,7 @@
}
}
+ @Test
public void testSetProxy() throws Exception {
if (!supportedHardware()) return;
ProxyInfo initialProxy = mCM.getDefaultProxy();
@@ -889,15 +934,22 @@
assertDefaultProxy(initialProxy);
}
+ @Test
public void testSetProxyDisallowedApps() throws Exception {
if (!supportedHardware()) return;
ProxyInfo initialProxy = mCM.getDefaultProxy();
- // If adb TCP port opened, this test may running by adb over TCP.
- // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test,
- // see b/119382723.
- // Note: The test don't support running adb over network for root device
- String disallowedApps = mPackageName + ",com.android.shell";
+ String disallowedApps = mPackageName;
+ if (!SdkLevel.isAtLeastS()) {
+ // If adb TCP port opened, this test may running by adb over TCP.
+ // Add com.android.shell application into disallowedApps to exclude adb socket for VPN
+ // test, see b/119382723 (the test doesn't support adb over TCP when adb runs as root).
+ //
+ // This is fixed in S, but still affects previous Android versions,
+ // and this test must be backwards compatible.
+ // TODO: Delete this code entirely when R is no longer supported.
+ disallowedApps += ",com.android.shell";
+ }
ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"0.0.0.0/0", "::/0"}, "", disallowedApps,
@@ -908,6 +960,7 @@
assertDefaultProxy(initialProxy);
}
+ @Test
public void testNoProxy() throws Exception {
if (!supportedHardware()) return;
ProxyInfo initialProxy = mCM.getDefaultProxy();
@@ -942,6 +995,7 @@
assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork());
}
+ @Test
public void testBindToNetworkWithProxy() throws Exception {
if (!supportedHardware()) return;
String allowedApps = mPackageName;
@@ -966,6 +1020,7 @@
assertDefaultProxy(initialProxy);
}
+ @Test
public void testVpnMeterednessWithNoUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -986,8 +1041,18 @@
assertTrue(mCM.isActiveNetworkMetered());
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
+
+ if (SdkLevel.isAtLeastT()) {
+ runWithShellPermissionIdentity(() -> {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(mNetwork);
+ assertNotNull(nc);
+ assertNotNull(nc.getUnderlyingNetworks());
+ assertEquals(underlyingNetworks, new ArrayList<>(nc.getUnderlyingNetworks()));
+ }, NETWORK_SETTINGS);
+ }
}
+ @Test
public void testVpnMeterednessWithNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -1016,6 +1081,7 @@
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
}
+ @Test
public void testVpnMeterednessWithNonNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -1043,8 +1109,21 @@
assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered());
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
+
+ if (SdkLevel.isAtLeastT()) {
+ final Network vpnNetwork = mCM.getActiveNetwork();
+ assertNotEqual(underlyingNetwork, vpnNetwork);
+ runWithShellPermissionIdentity(() -> {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork);
+ assertNotNull(nc);
+ assertNotNull(nc.getUnderlyingNetworks());
+ final List<Network> underlying = nc.getUnderlyingNetworks();
+ assertEquals(underlyingNetwork, underlying.get(0));
+ }, NETWORK_SETTINGS);
+ }
}
+ @Test
public void testAlwaysMeteredVpnWithNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -1071,6 +1150,7 @@
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
}
+ @Test
public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception {
if (!supportedHardware()) {
return;
@@ -1096,8 +1176,21 @@
assertTrue(mCM.isActiveNetworkMetered());
maybeExpectVpnTransportInfo(mCM.getActiveNetwork());
+
+ if (SdkLevel.isAtLeastT()) {
+ final Network vpnNetwork = mCM.getActiveNetwork();
+ assertNotEqual(underlyingNetwork, vpnNetwork);
+ runWithShellPermissionIdentity(() -> {
+ final NetworkCapabilities nc = mCM.getNetworkCapabilities(vpnNetwork);
+ assertNotNull(nc);
+ assertNotNull(nc.getUnderlyingNetworks());
+ final List<Network> underlying = nc.getUnderlyingNetworks();
+ assertEquals(underlyingNetwork, underlying.get(0));
+ }, NETWORK_SETTINGS);
+ }
}
+ @Test
public void testB141603906() throws Exception {
if (!supportedHardware()) {
return;
@@ -1176,7 +1269,7 @@
private boolean received;
public ProxyChangeBroadcastReceiver() {
- super(VpnTest.this.getInstrumentation().getContext(), Proxy.PROXY_CHANGE_ACTION);
+ super(getInstrumentation().getContext(), Proxy.PROXY_CHANGE_ACTION);
received = false;
}
@@ -1196,6 +1289,7 @@
* allowed list.
* See b/165774987.
*/
+ @Test
public void testDownloadWithDownloadManagerDisallowed() throws Exception {
if (!supportedHardware()) return;
@@ -1205,7 +1299,7 @@
"" /* allowedApps */, "com.android.providers.downloads", null /* proxyInfo */,
null /* underlyingNetworks */, false /* isAlwaysMetered */);
- final Context context = VpnTest.this.getInstrumentation().getContext();
+ final Context context = getInstrumentation().getContext();
final DownloadManager dm = context.getSystemService(DownloadManager.class);
final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
try {
diff --git a/tests/cts/hostside/app2/Android.bp b/tests/cts/hostside/app2/Android.bp
index dd33eed..4c9bccf 100644
--- a/tests/cts/hostside/app2/Android.bp
+++ b/tests/cts/hostside/app2/Android.bp
@@ -28,6 +28,7 @@
test_suites: [
"cts",
"general-tests",
+ "sts",
],
certificate: ":cts-net-app",
}
diff --git a/tests/cts/net/Android.bp b/tests/cts/net/Android.bp
index 85942b0..81c30b1 100644
--- a/tests/cts/net/Android.bp
+++ b/tests/cts/net/Android.bp
@@ -60,6 +60,7 @@
// uncomment when b/13249961 is fixed
// sdk_version: "current",
platform_apis: true,
+ required: ["ConnectivityChecker"],
}
// Networking CTS tests for development and release. These tests always target the platform SDK
@@ -68,7 +69,7 @@
// devices.
android_test {
name: "CtsNetTestCases",
- defaults: ["CtsNetTestCasesDefaults"],
+ defaults: ["CtsNetTestCasesDefaults", "NetworkStackNextEnableDefaults"],
// TODO: CTS should not depend on the entirety of the networkstack code.
static_libs: [
"NetworkStackApiCurrentLib",
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 474eefe..d761c27 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -21,12 +21,13 @@
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
- <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="{MODULE}.apk" />
</target_preparer>
+ <target_preparer class="com.android.testutils.ConnectivityCheckTargetPreparer">
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.cts" />
<option name="runtime-hint" value="9m4s" />
diff --git a/tests/cts/net/OWNERS b/tests/cts/net/OWNERS
index 432bd9b..df5569e 100644
--- a/tests/cts/net/OWNERS
+++ b/tests/cts/net/OWNERS
@@ -1,3 +1,5 @@
# Bug component: 31808
# Inherits parent owners
per-file src/android/net/cts/NetworkWatchlistTest.java=alanstokes@google.com
+
+# Bug component: 685852 = per-file *IpSec*
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/AirplaneModeTest.java b/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
deleted file mode 100644
index 524e549..0000000
--- a/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.cts;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
-import android.test.AndroidTestCase;
-import android.util.Log;
-
-import java.lang.Thread;
-
-@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
-public class AirplaneModeTest extends AndroidTestCase {
- private static final String TAG = "AirplaneModeTest";
- private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
- private static final String FEATURE_WIFI = "android.hardware.wifi";
- private static final int TIMEOUT_MS = 10 * 1000;
- private boolean mHasFeature;
- private Context mContext;
- private ContentResolver resolver;
-
- public void setup() {
- mContext= getContext();
- resolver = mContext.getContentResolver();
- mHasFeature = (mContext.getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH)
- || mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI));
- }
-
- public void testAirplaneMode() {
- setup();
- if (!mHasFeature) {
- Log.i(TAG, "The device doesn't support network bluetooth or wifi feature");
- return;
- }
-
- for (int testCount = 0; testCount < 2; testCount++) {
- if (!doOneTest()) {
- fail("Airplane mode failed to change in " + TIMEOUT_MS + "msec");
- return;
- }
- }
- }
-
- private boolean doOneTest() {
- boolean airplaneModeOn = isAirplaneModeOn();
- setAirplaneModeOn(!airplaneModeOn);
-
- try {
- Thread.sleep(TIMEOUT_MS);
- } catch (InterruptedException e) {
- Log.e(TAG, "Sleep time interrupted.", e);
- }
-
- if (airplaneModeOn == isAirplaneModeOn()) {
- return false;
- }
- return true;
- }
-
- private void setAirplaneModeOn(boolean enabling) {
- // Change the system setting for airplane mode
- Settings.Global.putInt(resolver, Settings.Global.AIRPLANE_MODE_ON, enabling ? 1 : 0);
- }
-
- private boolean isAirplaneModeOn() {
- // Read the system setting for airplane mode
- return Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
- }
-}
diff --git a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
index 4d60279..6b2a1ee 100644
--- a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
@@ -17,6 +17,7 @@
package android.net.cts;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
import static androidx.test.InstrumentationRegistry.getContext;
@@ -28,9 +29,11 @@
import static org.junit.Assert.fail;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.cts.util.CtsNetUtils;
+import android.net.wifi.WifiManager;
import android.os.BatteryStatsManager;
import android.os.Build;
import android.os.connectivity.CellularBatteryStats;
@@ -38,11 +41,11 @@
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
+import androidx.test.filters.RequiresDevice;
import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.SkipPresubmit;
import org.junit.Before;
import org.junit.Rule;
@@ -72,6 +75,8 @@
private Context mContext;
private BatteryStatsManager mBsm;
private ConnectivityManager mCm;
+ private WifiManager mWm;
+ private PackageManager mPm;
private CtsNetUtils mCtsNetUtils;
@Before
@@ -79,12 +84,17 @@
mContext = getContext();
mBsm = mContext.getSystemService(BatteryStatsManager.class);
mCm = mContext.getSystemService(ConnectivityManager.class);
+ mWm = mContext.getSystemService(WifiManager.class);
+ mPm = mContext.getPackageManager();
mCtsNetUtils = new CtsNetUtils(mContext);
}
+ // reportNetworkInterfaceForTransports classifies one network interface as wifi or mobile, so
+ // check that the interface is classified properly by checking the data usage is reported
+ // properly.
@Test
@AppModeFull(reason = "Cannot get CHANGE_NETWORK_STATE to request wifi/cell in instant mode")
- @SkipPresubmit(reason = "Virtual hardware does not support wifi battery stats")
+ @RequiresDevice // Virtual hardware does not support wifi battery stats
public void testReportNetworkInterfaceForTransports() throws Exception {
try {
// Simulate the device being unplugged from charging.
@@ -108,42 +118,9 @@
// Make sure wifi is disabled.
mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
- final Network cellNetwork = mCtsNetUtils.connectToCell();
- final URL url = new URL(TEST_URL);
+ verifyGetCellBatteryStats();
+ verifyGetWifiBatteryStats();
- // Get cellular battery stats
- CellularBatteryStats cellularStatsBefore = runAsShell(UPDATE_DEVICE_STATS,
- mBsm::getCellularBatteryStats);
-
- // Generate traffic on cellular network.
- Log.d(TAG, "Generate traffic on cellular network.");
- generateNetworkTraffic(cellNetwork, url);
-
- // The mobile battery stats are updated when a network stops being the default network.
- // ConnectivityService will call BatteryStatsManager.reportMobileRadioPowerState when
- // removing data activity tracking.
- final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
-
- // Check cellular battery stats are updated.
- runAsShell(UPDATE_DEVICE_STATS,
- () -> assertStatsEventually(mBsm::getCellularBatteryStats,
- cellularStatsAfter -> cellularBatteryStatsIncreased(
- cellularStatsBefore, cellularStatsAfter)));
-
- WifiBatteryStats wifiStatsBefore = runAsShell(UPDATE_DEVICE_STATS,
- mBsm::getWifiBatteryStats);
-
- // Generate traffic on wifi network.
- Log.d(TAG, "Generate traffic on wifi network.");
- generateNetworkTraffic(wifiNetwork, url);
- // Wifi battery stats are updated when wifi on.
- mCtsNetUtils.toggleWifi();
-
- // Check wifi battery stats are updated.
- runAsShell(UPDATE_DEVICE_STATS,
- () -> assertStatsEventually(mBsm::getWifiBatteryStats,
- wifiStatsAfter -> wifiBatteryStatsIncreased(wifiStatsBefore,
- wifiStatsAfter)));
} finally {
// Reset battery settings.
executeShellCommand("dumpsys batterystats disable no-auto-reset");
@@ -151,6 +128,62 @@
}
}
+ private void verifyGetCellBatteryStats() throws Exception {
+ final boolean isTelephonySupported = mPm.hasSystemFeature(FEATURE_TELEPHONY);
+
+ if (!isTelephonySupported) {
+ Log.d(TAG, "Skip cell battery stats test because device does not support telephony.");
+ return;
+ }
+
+ final Network cellNetwork = mCtsNetUtils.connectToCell();
+ final URL url = new URL(TEST_URL);
+
+ // Get cellular battery stats
+ CellularBatteryStats cellularStatsBefore = runAsShell(UPDATE_DEVICE_STATS,
+ mBsm::getCellularBatteryStats);
+
+ // Generate traffic on cellular network.
+ Log.d(TAG, "Generate traffic on cellular network.");
+ generateNetworkTraffic(cellNetwork, url);
+
+ // The mobile battery stats are updated when a network stops being the default network.
+ // ConnectivityService will call BatteryStatsManager.reportMobileRadioPowerState when
+ // removing data activity tracking.
+ mCtsNetUtils.ensureWifiConnected();
+
+ // Check cellular battery stats are updated.
+ runAsShell(UPDATE_DEVICE_STATS,
+ () -> assertStatsEventually(mBsm::getCellularBatteryStats,
+ cellularStatsAfter -> cellularBatteryStatsIncreased(
+ cellularStatsBefore, cellularStatsAfter)));
+ }
+
+ private void verifyGetWifiBatteryStats() throws Exception {
+ final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+ final URL url = new URL(TEST_URL);
+
+ if (!mWm.isEnhancedPowerReportingSupported()) {
+ Log.d(TAG, "Skip wifi stats test because wifi does not support link layer stats.");
+ return;
+ }
+
+ WifiBatteryStats wifiStatsBefore = runAsShell(UPDATE_DEVICE_STATS,
+ mBsm::getWifiBatteryStats);
+
+ // Generate traffic on wifi network.
+ Log.d(TAG, "Generate traffic on wifi network.");
+ generateNetworkTraffic(wifiNetwork, url);
+ // Wifi battery stats are updated when wifi on.
+ mCtsNetUtils.toggleWifi();
+
+ // Check wifi battery stats are updated.
+ runAsShell(UPDATE_DEVICE_STATS,
+ () -> assertStatsEventually(mBsm::getWifiBatteryStats,
+ wifiStatsAfter -> wifiBatteryStatsIncreased(wifiStatsBefore,
+ wifiStatsAfter)));
+ }
+
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
diff --git a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
index 9f079c4..a378aa7 100644
--- a/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
+++ b/tests/cts/net/src/android/net/cts/CaptivePortalTest.kt
@@ -40,12 +40,12 @@
import android.net.cts.util.CtsNetUtils
import android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTPS_URL
import android.net.util.NetworkStackUtils.TEST_CAPTIVE_PORTAL_HTTP_URL
-import android.net.wifi.WifiManager
import android.os.Build
import android.platform.test.annotations.AppModeFull
import android.provider.DeviceConfig
import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
import android.text.TextUtils
+import android.util.Log
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import androidx.test.runner.AndroidJUnit4
import com.android.testutils.RecorderCallback
@@ -76,9 +76,11 @@
private const val LOCALHOST_HOSTNAME = "localhost"
// Re-connecting to the AP, obtaining an IP address, revalidating can take a long time
-private const val WIFI_CONNECT_TIMEOUT_MS = 120_000L
+private const val WIFI_CONNECT_TIMEOUT_MS = 40_000L
private const val TEST_TIMEOUT_MS = 10_000L
+private const val TAG = "CaptivePortalTest"
+
private fun <T> CompletableFuture<T>.assertGet(timeoutMs: Long, message: String): T {
try {
return get(timeoutMs, TimeUnit.MILLISECONDS)
@@ -91,7 +93,6 @@
@RunWith(AndroidJUnit4::class)
class CaptivePortalTest {
private val context: android.content.Context by lazy { getInstrumentation().context }
- private val wm by lazy { context.getSystemService(WifiManager::class.java) }
private val cm by lazy { context.getSystemService(ConnectivityManager::class.java) }
private val pm by lazy { context.packageManager }
private val utils by lazy { CtsNetUtils(context) }
@@ -155,6 +156,7 @@
server.addResponse(Request(TEST_HTTP_URL_PATH), Status.REDIRECT, headers)
setHttpsUrlDeviceConfig(makeUrl(TEST_HTTPS_URL_PATH))
setHttpUrlDeviceConfig(makeUrl(TEST_HTTP_URL_PATH))
+ Log.d(TAG, "Set portal URLs to $TEST_HTTPS_URL_PATH and $TEST_HTTP_URL_PATH")
// URL expiration needs to be in the next 10 minutes
assertTrue(WIFI_CONNECT_TIMEOUT_MS < TimeUnit.MINUTES.toMillis(10))
setUrlExpirationDeviceConfig(System.currentTimeMillis() + WIFI_CONNECT_TIMEOUT_MS)
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
index a40c92d..721ad82 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityDiagnosticsManagerTest.java
@@ -73,6 +73,7 @@
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import android.util.Pair;
import androidx.test.InstrumentationRegistry;
@@ -92,7 +93,9 @@
import java.security.MessageDigest;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -293,7 +296,7 @@
final String interfaceName =
mConnectivityManager.getLinkProperties(network).getInterfaceName();
- connDiagsCallback.expectOnConnectivityReportAvailable(
+ connDiagsCallback.maybeVerifyConnectivityReportAvailable(
network, interfaceName, TRANSPORT_CELLULAR, NETWORK_VALIDATION_RESULT_VALID);
connDiagsCallback.assertNoCallback();
}
@@ -426,10 +429,15 @@
cb.expectOnNetworkConnectivityReported(mTestNetwork, hasConnectivity);
// All calls to #onNetworkConnectivityReported are expected to be accompanied by a call to
- // #onConnectivityReportAvailable for S+ (for R, ConnectivityReports were only sent when the
- // Network was re-validated - when reported connectivity != known connectivity).
- if (SdkLevel.isAtLeastS() || !hasConnectivity) {
+ // #onConnectivityReportAvailable for T+ (for R, ConnectivityReports were only sent when the
+ // Network was re-validated - when reported connectivity != known connectivity). On S,
+ // recent module versions will have the callback, but not the earliest ones.
+ if (!hasConnectivity) {
cb.expectOnConnectivityReportAvailable(mTestNetwork, interfaceName);
+ } else if (SdkLevel.isAtLeastS()) {
+ cb.maybeVerifyConnectivityReportAvailable(mTestNetwork, interfaceName, TRANSPORT_TEST,
+ getPossibleDiagnosticsValidationResults(),
+ SdkLevel.isAtLeastT() /* requireCallbackFired */);
}
cb.assertNoCallback();
@@ -485,18 +493,25 @@
// Test Networks both do not require validation and are not tested for validation. This
// results in the validation result being reported as SKIPPED for S+ (for R, the
// platform marked these Networks as VALID).
- final int expectedNetworkValidationResult =
- SdkLevel.isAtLeastS()
- ? NETWORK_VALIDATION_RESULT_SKIPPED
- : NETWORK_VALIDATION_RESULT_VALID;
- expectOnConnectivityReportAvailable(
- network, interfaceName, TRANSPORT_TEST, expectedNetworkValidationResult);
+
+ maybeVerifyConnectivityReportAvailable(network, interfaceName, TRANSPORT_TEST,
+ getPossibleDiagnosticsValidationResults(), true);
}
- public void expectOnConnectivityReportAvailable(@NonNull Network network,
+ public void maybeVerifyConnectivityReportAvailable(@NonNull Network network,
@NonNull String interfaceName, int transportType, int expectedValidationResult) {
+ maybeVerifyConnectivityReportAvailable(network, interfaceName, transportType,
+ new ArraySet<>(Collections.singletonList(expectedValidationResult)), true);
+ }
+
+ public void maybeVerifyConnectivityReportAvailable(@NonNull Network network,
+ @NonNull String interfaceName, int transportType,
+ Set<Integer> possibleValidationResults, boolean requireCallbackFired) {
final ConnectivityReport result =
(ConnectivityReport) mHistory.poll(CALLBACK_TIMEOUT_MILLIS, x -> true);
+ if (!requireCallbackFired && result == null) {
+ return;
+ }
assertEquals(network, result.getNetwork());
final NetworkCapabilities nc = result.getNetworkCapabilities();
@@ -508,8 +523,8 @@
final PersistableBundle extras = result.getAdditionalInfo();
assertTrue(extras.containsKey(KEY_NETWORK_VALIDATION_RESULT));
final int actualValidationResult = extras.getInt(KEY_NETWORK_VALIDATION_RESULT);
- assertEquals("Network validation result is incorrect",
- expectedValidationResult, actualValidationResult);
+ assertTrue("Network validation result is incorrect: " + actualValidationResult,
+ possibleValidationResults.contains(actualValidationResult));
assertTrue(extras.containsKey(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK));
final int probesSucceeded = extras.getInt(KEY_NETWORK_VALIDATION_RESULT);
@@ -556,6 +571,19 @@
}
}
+ private static Set<Integer> getPossibleDiagnosticsValidationResults() {
+ final Set<Integer> possibleValidationResults = new ArraySet<>();
+ possibleValidationResults.add(NETWORK_VALIDATION_RESULT_SKIPPED);
+
+ // In S, some early module versions will return NETWORK_VALIDATION_RESULT_VALID.
+ // Starting from T, all module versions should only return SKIPPED. For platform < T,
+ // accept both values.
+ if (!SdkLevel.isAtLeastT()) {
+ possibleValidationResults.add(NETWORK_VALIDATION_RESULT_VALID);
+ }
+ return possibleValidationResults;
+ }
+
private class CarrierConfigReceiver extends BroadcastReceiver {
// CountDownLatch used to wait for this BroadcastReceiver to be notified of a CarrierConfig
// change. This latch will be counted down if a broadcast indicates this package has carrier
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index ee13619..80c2db4 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -75,6 +75,7 @@
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_LOCKDOWN_VPN;
import static com.android.networkstack.apishim.ConstantsShim.BLOCKED_REASON_NONE;
+import static com.android.testutils.Cleanup.testAndCleanup;
import static com.android.testutils.MiscAsserts.assertThrows;
import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork;
import static com.android.testutils.TestPermissionUtil.runAsShell;
@@ -119,10 +120,8 @@
import android.net.NetworkInfo.State;
import android.net.NetworkProvider;
import android.net.NetworkRequest;
-import android.net.NetworkScore;
import android.net.NetworkSpecifier;
import android.net.NetworkStateSnapshot;
-import android.net.NetworkUtils;
import android.net.OemNetworkPreferences;
import android.net.ProxyInfo;
import android.net.SocketKeepalive;
@@ -156,6 +155,7 @@
import android.util.Range;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.RequiresDevice;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
@@ -169,7 +169,6 @@
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRuleKt;
import com.android.testutils.RecorderCallback.CallbackEntry;
-import com.android.testutils.SkipPresubmit;
import com.android.testutils.TestHttpServer;
import com.android.testutils.TestNetworkTracker;
import com.android.testutils.TestableNetworkCallback;
@@ -238,6 +237,7 @@
private static final int MIN_KEEPALIVE_INTERVAL = 10;
private static final int NETWORK_CALLBACK_TIMEOUT_MS = 30_000;
+ private static final int LISTEN_ACTIVITY_TIMEOUT_MS = 5_000;
private static final int NO_CALLBACK_TIMEOUT_MS = 100;
private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
@@ -260,6 +260,7 @@
"config_allowedUnprivilegedKeepalivePerUid";
private static final String KEEPALIVE_RESERVED_PER_SLOT_RES_NAME =
"config_reservedPrivilegedKeepaliveSlots";
+ private static final String TEST_RESTRICTED_NW_IFACE_NAME = "test-restricted-nw";
private static final LinkAddress TEST_LINKADDR = new LinkAddress(
InetAddresses.parseNumericAddress("2001:db8::8"), 64);
@@ -279,10 +280,12 @@
private ConnectivityManagerShim mCmShim;
private WifiManager mWifiManager;
private PackageManager mPackageManager;
+ private TelephonyManager mTm;
private final ArraySet<Integer> mNetworkTypes = new ArraySet<>();
private UiAutomation mUiAutomation;
private CtsNetUtils mCtsNetUtils;
-
+ // The registered callbacks.
+ private List<NetworkCallback> mRegisteredCallbacks = new ArrayList<>();
// Used for cleanup purposes.
private final List<Range<Integer>> mVpnRequiredUidRanges = new ArrayList<>();
@@ -297,6 +300,7 @@
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mPackageManager = mContext.getPackageManager();
mCtsNetUtils = new CtsNetUtils(mContext);
+ mTm = mContext.getSystemService(TelephonyManager.class);
if (DevSdkIgnoreRuleKt.isDevSdkInRange(null /* minExclusive */,
Build.VERSION_CODES.R /* maxInclusive */)) {
@@ -377,11 +381,12 @@
// All tests in this class require a working Internet connection as they start. Make
// sure there is still one as they end that's ready to use for the next test to use.
final TestNetworkCallback callback = new TestNetworkCallback();
- mCm.registerDefaultNetworkCallback(callback);
+ registerDefaultNetworkCallback(callback);
try {
assertNotNull("Couldn't restore Internet connectivity", callback.waitForAvailable());
} finally {
- mCm.unregisterNetworkCallback(callback);
+ // Unregister all registered callbacks.
+ unregisterRegisteredCallbacks();
}
}
@@ -554,7 +559,7 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- @SkipPresubmit(reason = "Virtual devices use a single internet connection for all networks")
+ @RequiresDevice // Virtual devices use a single internet connection for all networks
public void testOpenConnection() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_TELEPHONY));
@@ -709,6 +714,12 @@
.build();
}
+ private boolean hasPrivateDnsValidated(CallbackEntry entry, Network networkForPrivateDns) {
+ if (!networkForPrivateDns.equals(entry.getNetwork())) return false;
+ final NetworkCapabilities nc = ((CallbackEntry.CapabilitiesChanged) entry).getCaps();
+ return !nc.isPrivateDnsBroken() && nc.hasCapability(NET_CAPABILITY_VALIDATED);
+ }
+
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testIsPrivateDnsBroken() throws InterruptedException {
@@ -716,14 +727,13 @@
final String goodPrivateDnsServer = "dns.google";
mCtsNetUtils.storePrivateDnsSetting();
final TestableNetworkCallback cb = new TestableNetworkCallback();
- mCm.registerNetworkCallback(makeWifiNetworkRequest(), cb);
+ registerNetworkCallback(makeWifiNetworkRequest(), cb);
try {
// Verifying the good private DNS sever
mCtsNetUtils.setPrivateDnsStrictMode(goodPrivateDnsServer);
final Network networkForPrivateDns = mCtsNetUtils.ensureWifiConnected();
cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
- .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
+ entry -> hasPrivateDnsValidated(entry, networkForPrivateDns));
// Verifying the broken private DNS sever
mCtsNetUtils.setPrivateDnsStrictMode(invalidPrivateDnsServer);
@@ -748,15 +758,15 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- public void testRegisterNetworkCallback() {
+ public void testRegisterNetworkCallback() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
// We will register for a WIFI network being available or lost.
final TestNetworkCallback callback = new TestNetworkCallback();
- mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+ registerNetworkCallback(makeWifiNetworkRequest(), callback);
final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback();
- mCm.registerDefaultNetworkCallback(defaultTrackingCallback);
+ registerDefaultNetworkCallback(defaultTrackingCallback);
final TestNetworkCallback systemDefaultCallback = new TestNetworkCallback();
final TestNetworkCallback perUidCallback = new TestNetworkCallback();
@@ -764,51 +774,37 @@
final Handler h = new Handler(Looper.getMainLooper());
if (TestUtils.shouldTestSApis()) {
runWithShellPermissionIdentity(() -> {
- mCmShim.registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
- mCmShim.registerDefaultNetworkCallbackForUid(Process.myUid(), perUidCallback, h);
+ registerSystemDefaultNetworkCallback(systemDefaultCallback, h);
+ registerDefaultNetworkCallbackForUid(Process.myUid(), perUidCallback, h);
}, NETWORK_SETTINGS);
- mCm.registerBestMatchingNetworkCallback(makeDefaultRequest(), bestMatchingCallback, h);
+ registerBestMatchingNetworkCallback(makeDefaultRequest(), bestMatchingCallback, h);
}
Network wifiNetwork = null;
+ mCtsNetUtils.ensureWifiConnected();
- try {
- mCtsNetUtils.ensureWifiConnected();
+ // Now we should expect to get a network callback about availability of the wifi
+ // network even if it was already connected as a state-based action when the callback
+ // is registered.
+ wifiNetwork = callback.waitForAvailable();
+ assertNotNull("Did not receive onAvailable for TRANSPORT_WIFI request",
+ wifiNetwork);
- // Now we should expect to get a network callback about availability of the wifi
- // network even if it was already connected as a state-based action when the callback
- // is registered.
- wifiNetwork = callback.waitForAvailable();
- assertNotNull("Did not receive onAvailable for TRANSPORT_WIFI request",
- wifiNetwork);
+ final Network defaultNetwork = defaultTrackingCallback.waitForAvailable();
+ assertNotNull("Did not receive onAvailable on default network callback",
+ defaultNetwork);
- final Network defaultNetwork = defaultTrackingCallback.waitForAvailable();
- assertNotNull("Did not receive onAvailable on default network callback",
- defaultNetwork);
-
- if (TestUtils.shouldTestSApis()) {
- assertNotNull("Did not receive onAvailable on system default network callback",
- systemDefaultCallback.waitForAvailable());
- final Network perUidNetwork = perUidCallback.waitForAvailable();
- assertNotNull("Did not receive onAvailable on per-UID default network callback",
- perUidNetwork);
- assertEquals(defaultNetwork, perUidNetwork);
- final Network bestMatchingNetwork = bestMatchingCallback.waitForAvailable();
- assertNotNull("Did not receive onAvailable on best matching network callback",
- bestMatchingNetwork);
- assertEquals(defaultNetwork, bestMatchingNetwork);
- }
-
- } catch (InterruptedException e) {
- fail("Broadcast receiver or NetworkCallback wait was interrupted.");
- } finally {
- mCm.unregisterNetworkCallback(callback);
- mCm.unregisterNetworkCallback(defaultTrackingCallback);
- if (TestUtils.shouldTestSApis()) {
- mCm.unregisterNetworkCallback(systemDefaultCallback);
- mCm.unregisterNetworkCallback(perUidCallback);
- mCm.unregisterNetworkCallback(bestMatchingCallback);
- }
+ if (TestUtils.shouldTestSApis()) {
+ assertNotNull("Did not receive onAvailable on system default network callback",
+ systemDefaultCallback.waitForAvailable());
+ final Network perUidNetwork = perUidCallback.waitForAvailable();
+ assertNotNull("Did not receive onAvailable on per-UID default network callback",
+ perUidNetwork);
+ assertEquals(defaultNetwork, perUidNetwork);
+ final Network bestMatchingNetwork = bestMatchingCallback.waitForAvailable();
+ assertNotNull("Did not receive onAvailable on best matching network callback",
+ bestMatchingNetwork);
+ assertEquals(defaultNetwork, bestMatchingNetwork);
}
}
@@ -979,22 +975,15 @@
*/
@AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
@Test
- public void testRequestNetworkCallback() {
+ public void testRequestNetworkCallback() throws Exception {
final TestNetworkCallback callback = new TestNetworkCallback();
- mCm.requestNetwork(new NetworkRequest.Builder()
+ requestNetwork(new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), callback);
- try {
- // Wait to get callback for availability of internet
- Network internetNetwork = callback.waitForAvailable();
- assertNotNull("Did not receive NetworkCallback#onAvailable for INTERNET",
- internetNetwork);
- } catch (InterruptedException e) {
- fail("NetworkCallback wait was interrupted.");
- } finally {
- mCm.unregisterNetworkCallback(callback);
- }
+ // Wait to get callback for availability of internet
+ Network internetNetwork = callback.waitForAvailable();
+ assertNotNull("Did not receive NetworkCallback#onAvailable for INTERNET", internetNetwork);
}
/**
@@ -1010,9 +999,8 @@
}
final TestNetworkCallback callback = new TestNetworkCallback();
- mCm.requestNetwork(new NetworkRequest.Builder()
- .addTransportType(TRANSPORT_WIFI)
- .build(), callback, 100);
+ requestNetwork(new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build(),
+ callback, 100);
try {
// Wait to get callback for unavailability of requested network
@@ -1021,7 +1009,6 @@
} catch (InterruptedException e) {
fail("NetworkCallback wait was interrupted.");
} finally {
- mCm.unregisterNetworkCallback(callback);
if (previousWifiEnabledState) {
mCtsNetUtils.connectToWifi();
}
@@ -1103,7 +1090,8 @@
}
private Network waitForActiveNetworkMetered(final int targetTransportType,
- final boolean requestedMeteredness, final boolean useSystemDefault)
+ final boolean requestedMeteredness, final boolean waitForValidation,
+ final boolean useSystemDefault)
throws Exception {
final CompletableFuture<Network> networkFuture = new CompletableFuture<>();
final NetworkCallback networkCallback = new NetworkCallback() {
@@ -1112,7 +1100,8 @@
if (!nc.hasTransport(targetTransportType)) return;
final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
- if (metered == requestedMeteredness) {
+ final boolean validated = nc.hasCapability(NET_CAPABILITY_VALIDATED);
+ if (metered == requestedMeteredness && (!waitForValidation || validated)) {
networkFuture.complete(network);
}
}
@@ -1124,11 +1113,11 @@
// this method will return right away, and if not, it'll wait for the setting to change.
if (useSystemDefault) {
runWithShellPermissionIdentity(() ->
- mCmShim.registerSystemDefaultNetworkCallback(networkCallback,
+ registerSystemDefaultNetworkCallback(networkCallback,
new Handler(Looper.getMainLooper())),
NETWORK_SETTINGS);
} else {
- mCm.registerDefaultNetworkCallback(networkCallback);
+ registerDefaultNetworkCallback(networkCallback);
}
// Changing meteredness on wifi involves reconnecting, which can take several seconds
@@ -1138,16 +1127,16 @@
throw new AssertionError("Timed out waiting for active network metered status to "
+ "change to " + requestedMeteredness + " ; network = "
+ mCm.getActiveNetwork(), e);
- } finally {
- mCm.unregisterNetworkCallback(networkCallback);
}
}
- private Network setWifiMeteredStatusAndWait(String ssid, boolean isMetered) throws Exception {
+ private Network setWifiMeteredStatusAndWait(String ssid, boolean isMetered,
+ boolean waitForValidation) throws Exception {
setWifiMeteredStatus(ssid, Boolean.toString(isMetered) /* metered */);
mCtsNetUtils.ensureWifiConnected();
return waitForActiveNetworkMetered(TRANSPORT_WIFI,
isMetered /* requestedMeteredness */,
+ waitForValidation,
true /* useSystemDefault */);
}
@@ -1199,6 +1188,7 @@
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
public void testGetMultipathPreference() throws Exception {
+ assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
final ContentResolver resolver = mContext.getContentResolver();
mCtsNetUtils.ensureWifiConnected();
final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID());
@@ -1210,9 +1200,10 @@
int newMeteredPreference = findNextPrefValue(resolver);
Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
Integer.toString(newMeteredPreference));
- // Wifi meterness changes from unmetered to metered will disconnect and reconnect since
- // R.
- final Network network = setWifiMeteredStatusAndWait(ssid, true);
+ // Wifi meteredness changes from unmetered to metered will disconnect and reconnect
+ // since R.
+ final Network network = setWifiMeteredStatusAndWait(ssid, true /* isMetered */,
+ false /* waitForValidation */);
assertEquals(ssid, unquoteSSID(mWifiManager.getConnectionInfo().getSSID()));
assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
NET_CAPABILITY_NOT_METERED), false);
@@ -1229,7 +1220,7 @@
oldMeteredPreference, newMeteredPreference);
// No disconnect from unmetered to metered.
- setWifiMeteredStatusAndWait(ssid, false);
+ setWifiMeteredStatusAndWait(ssid, false /* isMetered */, false /* waitForValidation */);
assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
NET_CAPABILITY_NOT_METERED), true);
assertMultipathPreferenceIsEventually(network, newMeteredPreference,
@@ -1434,7 +1425,7 @@
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
+ @RequiresDevice // Keepalive is not supported on virtual hardware
public void testCreateTcpKeepalive() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
@@ -1641,7 +1632,7 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
+ @RequiresDevice // Keepalive is not supported on virtual hardware
public void testSocketKeepaliveLimitWifi() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
@@ -1691,7 +1682,7 @@
*/
@AppModeFull(reason = "Cannot request network in instant app mode")
@Test
- @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
+ @RequiresDevice // Keepalive is not supported on virtual hardware
public void testSocketKeepaliveLimitTelephony() throws Exception {
if (!mPackageManager.hasSystemFeature(FEATURE_TELEPHONY)) {
Log.i(TAG, "testSocketKeepaliveLimitTelephony cannot execute unless device"
@@ -1737,7 +1728,7 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
- @SkipPresubmit(reason = "Keepalive is not supported on virtual hardware")
+ @RequiresDevice // Keepalive is not supported on virtual hardware
public void testSocketKeepaliveUnprivileged() throws Exception {
assumeTrue(mPackageManager.hasSystemFeature(FEATURE_WIFI));
@@ -1771,6 +1762,40 @@
greater >= lesser);
}
+ private void verifyBindSocketToRestrictedNetworkDisallowed() throws Exception {
+ final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
+ final NetworkRequest testRequest = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
+ .build();
+ runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS,
+ // CONNECTIVITY_INTERNAL is for requesting restricted network because shell does not
+ // have CONNECTIVITY_USE_RESTRICTED_NETWORKS on R.
+ CONNECTIVITY_INTERNAL);
+
+ // Create a restricted network and ensure this package cannot bind to that network either.
+ final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
+ final Network network = agent.getNetwork();
+
+ try (Socket socket = new Socket()) {
+ // Verify that the network is restricted.
+ testNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> network.equals(entry.getNetwork())
+ && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
+ // CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
+ // does not allow to bind socket to restricted network.
+ assertThrows(IOException.class, () -> network.bindSocket(socket));
+ } finally {
+ agent.unregister();
+ }
+ }
+
/**
* Verifies that apps are not allowed to access restricted networks even if they declare the
* CONNECTIVITY_USE_RESTRICTED_NETWORKS permission in their manifests.
@@ -1778,6 +1803,7 @@
*/
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
+ @IgnoreUpTo(Build.VERSION_CODES.Q)
public void testRestrictedNetworkPermission() throws Exception {
// Ensure that CONNECTIVITY_USE_RESTRICTED_NETWORKS isn't granted to this package.
final PackageInfo app = mPackageManager.getPackageInfo(mContext.getPackageName(),
@@ -1787,23 +1813,33 @@
assertTrue(index >= 0);
assertTrue(app.requestedPermissionsFlags[index] != PERMISSION_GRANTED);
- // Ensure that NetworkUtils.queryUserAccess always returns false since this package should
- // not have netd system permission to call this function.
- final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
- assertFalse(NetworkUtils.queryUserAccess(Binder.getCallingUid(), wifiNetwork.netId));
+ if (mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
+ // Expect binding to the wifi network to succeed.
+ final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
+ try (Socket socket = new Socket()) {
+ wifiNetwork.bindSocket(socket);
+ }
+ }
// Ensure that this package cannot bind to any restricted network that's currently
// connected.
Network[] networks = mCm.getAllNetworks();
for (Network network : networks) {
- NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
- if (nc != null && !nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
- try {
- network.bindSocket(new Socket());
- fail("Bind to restricted network " + network + " unexpectedly succeeded");
- } catch (IOException expected) {}
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+ if (nc == null) {
+ continue;
+ }
+
+ try (Socket socket = new Socket()) {
+ if (nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+ network.bindSocket(socket); // binding should succeed
+ } else {
+ assertThrows(IOException.class, () -> network.bindSocket(socket));
+ }
}
}
+
+ verifyBindSocketToRestrictedNetworkDisallowed();
}
/**
@@ -1875,8 +1911,6 @@
if (supportWifi) waitForAvailable(wifiCb);
if (supportTelephony) waitForAvailable(telephonyCb);
} finally {
- if (supportWifi) mCm.unregisterNetworkCallback(wifiCb);
- if (supportTelephony) mCm.unregisterNetworkCallback(telephonyCb);
// Restore the previous state of airplane mode and permissions:
runShellCommand("cmd connectivity airplane-mode "
+ (isAirplaneModeEnabled ? "enable" : "disable"));
@@ -1886,7 +1920,7 @@
private void registerCallbackAndWaitForAvailable(@NonNull final NetworkRequest request,
@NonNull final TestableNetworkCallback cb) {
- mCm.registerNetworkCallback(request, cb);
+ registerNetworkCallback(request, cb);
waitForAvailable(cb);
}
@@ -2004,18 +2038,15 @@
foundNc.complete(nc);
}
};
- try {
- mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
- // Registering a callback here guarantees onCapabilitiesChanged is called immediately
- // because WiFi network should be connected.
- final NetworkCapabilities nc =
- foundNc.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
- // Verify if ssid is contained in the NetworkCapabilities received from callback.
- assertNotNull("NetworkCapabilities of the network is null", nc);
- assertEquals(hasSsid, Pattern.compile(ssid).matcher(nc.toString()).find());
- } finally {
- mCm.unregisterNetworkCallback(callback);
- }
+
+ registerNetworkCallback(makeWifiNetworkRequest(), callback);
+ // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+ // because WiFi network should be connected.
+ final NetworkCapabilities nc =
+ foundNc.get(NETWORK_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ // Verify if ssid is contained in the NetworkCapabilities received from callback.
+ assertNotNull("NetworkCapabilities of the network is null", nc);
+ assertEquals(hasSsid, Pattern.compile(ssid).matcher(nc.toString()).find());
}
/**
@@ -2053,14 +2084,14 @@
final TestableNetworkCallback callback = new TestableNetworkCallback();
final Handler handler = new Handler(Looper.getMainLooper());
assertThrows(SecurityException.class,
- () -> mCmShim.requestBackgroundNetwork(testRequest, callback, handler));
+ () -> requestBackgroundNetwork(testRequest, callback, handler));
Network testNetwork = null;
try {
// Request background test network via Shell identity which has NETWORK_SETTINGS
// permission granted.
runWithShellPermissionIdentity(
- () -> mCmShim.requestBackgroundNetwork(testRequest, callback, handler),
+ () -> requestBackgroundNetwork(testRequest, callback, handler),
new String[] { android.Manifest.permission.NETWORK_SETTINGS });
// Register the test network agent which has no foreground request associated to it.
@@ -2097,7 +2128,6 @@
}
testNetworkInterface.getFileDescriptor().close();
}, new String[] { android.Manifest.permission.MANAGE_TEST_NETWORKS });
- mCm.unregisterNetworkCallback(callback);
}
}
@@ -2137,8 +2167,9 @@
final int myUid = Process.myUid();
final int otherUid = UserHandle.getUid(5, Process.FIRST_APPLICATION_UID);
final Handler handler = new Handler(Looper.getMainLooper());
- mCm.registerDefaultNetworkCallback(myUidCallback, handler);
- mCmShim.registerDefaultNetworkCallbackForUid(otherUid, otherUidCallback, handler);
+
+ registerDefaultNetworkCallback(myUidCallback, handler);
+ registerDefaultNetworkCallbackForUid(otherUid, otherUidCallback, handler);
final Network defaultNetwork = mCm.getActiveNetwork();
final List<DetailedBlockedStatusCallback> allCallbacks =
@@ -2185,16 +2216,15 @@
assertNotNull(info);
assertEquals(DetailedState.CONNECTED, info.getDetailedState());
+ final TestableNetworkCallback callback = new TestableNetworkCallback();
try {
mCmShim.setLegacyLockdownVpnEnabled(true);
// setLegacyLockdownVpnEnabled is asynchronous and only takes effect when the
// ConnectivityService handler thread processes it. Ensure it has taken effect by doing
// something that blocks until the handler thread is idle.
- final TestableNetworkCallback callback = new TestableNetworkCallback();
- mCm.registerDefaultNetworkCallback(callback);
+ registerDefaultNetworkCallback(callback);
waitForAvailable(callback);
- mCm.unregisterNetworkCallback(callback);
// Test one of the effects of setLegacyLockdownVpnEnabled: the fact that any NetworkInfo
// in state CONNECTED is degraded to CONNECTING if the legacy VPN is not connected.
@@ -2350,9 +2380,11 @@
final String ssid = unquoteSSID(wifiNetworkCapabilities.getSsid());
final boolean oldMeteredValue = wifiNetworkCapabilities.isMetered();
- try {
- // This network will be used for unmetered.
- setWifiMeteredStatusAndWait(ssid, false /* isMetered */);
+ testAndCleanup(() -> {
+ // This network will be used for unmetered. Wait for it to be validated because
+ // OEM_NETWORK_PREFERENCE_TEST only prefers NOT_METERED&VALIDATED to a network with
+ // TRANSPORT_TEST, like OEM_NETWORK_PREFERENCE_OEM_PAID.
+ setWifiMeteredStatusAndWait(ssid, false /* isMetered */, true /* waitForValidation */);
setOemNetworkPreferenceForMyPackage(OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST);
registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
@@ -2361,9 +2393,10 @@
waitForAvailable(defaultCallback, wifiNetwork);
waitForAvailable(systemDefaultCallback, wifiNetwork);
- // Validate when setting unmetered to metered, unmetered is lost and replaced by the
- // network with the TEST transport.
- setWifiMeteredStatusAndWait(ssid, true /* isMetered */);
+ // Validate that when setting unmetered to metered, unmetered is lost and replaced by
+ // the network with the TEST transport. Also wait for validation here, in case there
+ // is a bug that's only visible when the network is validated.
+ setWifiMeteredStatusAndWait(ssid, true /* isMetered */, true /* waitForValidation */);
defaultCallback.expectCallback(CallbackEntry.LOST, wifiNetwork,
NETWORK_CALLBACK_TIMEOUT_MS);
waitForAvailable(defaultCallback, tnt.getNetwork());
@@ -2372,19 +2405,18 @@
// callback in any case therefore confirm its receipt before continuing to assure the
// system is in the expected state.
waitForAvailable(systemDefaultCallback, TRANSPORT_WIFI);
- } finally {
+ }, /* cleanup */ () -> {
// Validate that removing the test network will fallback to the default network.
runWithShellPermissionIdentity(tnt::teardown);
defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
NETWORK_CALLBACK_TIMEOUT_MS);
waitForAvailable(defaultCallback);
-
- setWifiMeteredStatusAndWait(ssid, oldMeteredValue);
-
- // Cleanup any prior test state from setOemNetworkPreference
- clearOemNetworkPreference();
- unregisterTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
- }
+ }, /* cleanup */ () -> {
+ setWifiMeteredStatusAndWait(ssid, oldMeteredValue, false /* waitForValidation */);
+ }, /* cleanup */ () -> {
+ // Cleanup any prior test state from setOemNetworkPreference
+ clearOemNetworkPreference();
+ });
}
/**
@@ -2405,45 +2437,38 @@
final Network wifiNetwork = mCtsNetUtils.ensureWifiConnected();
- try {
+ testAndCleanup(() -> {
setOemNetworkPreferenceForMyPackage(
OemNetworkPreferences.OEM_NETWORK_PREFERENCE_TEST_ONLY);
registerTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
waitForAvailable(defaultCallback, tnt.getNetwork());
waitForAvailable(systemDefaultCallback, wifiNetwork);
- } finally {
- runWithShellPermissionIdentity(tnt::teardown);
- defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
- NETWORK_CALLBACK_TIMEOUT_MS);
+ }, /* cleanup */ () -> {
+ runWithShellPermissionIdentity(tnt::teardown);
+ defaultCallback.expectCallback(CallbackEntry.LOST, tnt.getNetwork(),
+ NETWORK_CALLBACK_TIMEOUT_MS);
- // This network preference should only ever use the test network therefore available
- // should not trigger when the test network goes down (e.g. switch to cellular).
- defaultCallback.assertNoCallback();
- // The system default should still be connected to Wi-fi
- assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
+ // This network preference should only ever use the test network therefore available
+ // should not trigger when the test network goes down (e.g. switch to cellular).
+ defaultCallback.assertNoCallback();
+ // The system default should still be connected to Wi-fi
+ assertEquals(wifiNetwork, systemDefaultCallback.getLastAvailableNetwork());
+ }, /* cleanup */ () -> {
+ // Cleanup any prior test state from setOemNetworkPreference
+ clearOemNetworkPreference();
- // Cleanup any prior test state from setOemNetworkPreference
- clearOemNetworkPreference();
-
- // The default (non-test) network should be available as the network pref was cleared.
- waitForAvailable(defaultCallback);
- unregisterTestOemNetworkPreferenceCallbacks(defaultCallback, systemDefaultCallback);
- }
- }
-
- private void unregisterTestOemNetworkPreferenceCallbacks(
- @NonNull final TestableNetworkCallback defaultCallback,
- @NonNull final TestableNetworkCallback systemDefaultCallback) {
- mCm.unregisterNetworkCallback(defaultCallback);
- mCm.unregisterNetworkCallback(systemDefaultCallback);
+ // The default (non-test) network should be available as the network pref was
+ // cleared.
+ waitForAvailable(defaultCallback);
+ });
}
private void registerTestOemNetworkPreferenceCallbacks(
@NonNull final TestableNetworkCallback defaultCallback,
@NonNull final TestableNetworkCallback systemDefaultCallback) {
- mCm.registerDefaultNetworkCallback(defaultCallback);
+ registerDefaultNetworkCallback(defaultCallback);
runWithShellPermissionIdentity(() ->
- mCmShim.registerSystemDefaultNetworkCallback(systemDefaultCallback,
+ registerSystemDefaultNetworkCallback(systemDefaultCallback,
new Handler(Looper.getMainLooper())), NETWORK_SETTINGS);
}
@@ -2544,7 +2569,7 @@
// Wait for partial connectivity to be detected on the network
final Network network = preparePartialConnectivity();
- mCm.requestNetwork(makeWifiNetworkRequest(), cb);
+ requestNetwork(makeWifiNetworkRequest(), cb);
runAsShell(NETWORK_SETTINGS, () -> {
// The always bit is verified in NetworkAgentTest
mCm.setAcceptPartialConnectivity(network, false /* accept */, false /* always */);
@@ -2552,7 +2577,6 @@
// Reject partial connectivity network should cause the network being torn down
assertEquals(network, cb.waitForLost());
} finally {
- mCm.unregisterNetworkCallback(cb);
resetValidationConfig();
// Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
// apply here. Thus, turn off wifi first and restart to restore.
@@ -2587,13 +2611,12 @@
// guarantee that it won't become the default in the future.
assertNotEquals(wifiNetwork, mCm.getActiveNetwork());
- mCm.registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
+ registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
runAsShell(NETWORK_SETTINGS, () -> {
mCm.setAcceptUnvalidated(wifiNetwork, false /* accept */, false /* always */);
});
waitForLost(wifiCb);
} finally {
- mCm.unregisterNetworkCallback(wifiCb);
resetValidationConfig();
/// Wifi will not automatically reconnect to the network. ensureWifiDisconnected cannot
// apply here. Thus, turn off wifi first and restart to restore.
@@ -2623,8 +2646,8 @@
final Network cellNetwork = mCtsNetUtils.connectToCell();
final Network wifiNetwork = prepareValidatedNetwork();
- mCm.registerDefaultNetworkCallback(defaultCb);
- mCm.registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
+ registerDefaultNetworkCallback(defaultCb);
+ registerNetworkCallback(makeWifiNetworkRequest(), wifiCb);
try {
// Verify wifi is the default network.
@@ -2658,8 +2681,6 @@
wifiCb.assertNoCallbackThat(NO_CALLBACK_TIMEOUT_MS,
c -> !(c instanceof CallbackEntry.LinkPropertiesChanged));
} finally {
- mCm.unregisterNetworkCallback(wifiCb);
- mCm.unregisterNetworkCallback(defaultCb);
resetAvoidBadWifi(previousAvoidBadWifi);
resetValidationConfig();
// Reconnect wifi to reset the wifi status
@@ -2697,12 +2718,8 @@
}
};
- try {
- mCm.registerNetworkCallback(new NetworkRequest.Builder().build(), cb);
- return future.get(timeout, TimeUnit.MILLISECONDS);
- } finally {
- mCm.unregisterNetworkCallback(cb);
- }
+ registerNetworkCallback(new NetworkRequest.Builder().build(), cb);
+ return future.get(timeout, TimeUnit.MILLISECONDS);
}
private void resetValidationConfig() {
@@ -2788,6 +2805,49 @@
System.currentTimeMillis() + WIFI_CONNECT_TIMEOUT_MS);
}
+ @AppModeFull(reason = "Need WiFi support to test the default active network")
+ @Test
+ public void testDefaultNetworkActiveListener() throws Exception {
+ final boolean supportWifi = mPackageManager.hasSystemFeature(FEATURE_WIFI);
+ final boolean supportTelephony = mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
+ assumeTrue("testDefaultNetworkActiveListener cannot execute"
+ + " unless device supports WiFi or telephony", (supportWifi || supportTelephony));
+
+ if (supportWifi) {
+ mCtsNetUtils.ensureWifiDisconnected(null /* wifiNetworkToCheck */);
+ } else {
+ mCtsNetUtils.disconnectFromCell();
+ }
+
+ final CompletableFuture<Boolean> future = new CompletableFuture<>();
+ final ConnectivityManager.OnNetworkActiveListener listener = () -> future.complete(true);
+ mCm.addDefaultNetworkActiveListener(listener);
+ testAndCleanup(() -> {
+ // New default network connected will trigger a network activity notification.
+ if (supportWifi) {
+ mCtsNetUtils.ensureWifiConnected();
+ } else {
+ mCtsNetUtils.connectToCell();
+ }
+ assertTrue(future.get(LISTEN_ACTIVITY_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ }, () -> {
+ mCm.removeDefaultNetworkActiveListener(listener);
+ });
+ }
+
+ /**
+ * The networks used in this test are real networks and as such they can see seemingly random
+ * updates of their capabilities or link properties as conditions change, e.g. the network
+ * loses validation or IPv4 shows up. Many tests should simply treat these callbacks as
+ * spurious.
+ */
+ private void assertNoCallbackExceptCapOrLpChange(
+ @NonNull final TestableNetworkCallback cb) {
+ cb.assertNoCallbackThat(NO_CALLBACK_TIMEOUT_MS,
+ c -> !(c instanceof CallbackEntry.CapabilitiesChanged
+ || c instanceof CallbackEntry.LinkPropertiesChanged));
+ }
+
@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@Test
public void testMobileDataPreferredUids() throws Exception {
@@ -2812,16 +2872,15 @@
final TestableNetworkCallback defaultTrackingCb = new TestableNetworkCallback();
final TestableNetworkCallback systemDefaultCb = new TestableNetworkCallback();
final Handler h = new Handler(Looper.getMainLooper());
- runWithShellPermissionIdentity(() -> mCm.registerSystemDefaultNetworkCallback(
+ runWithShellPermissionIdentity(() -> registerSystemDefaultNetworkCallback(
systemDefaultCb, h), NETWORK_SETTINGS);
- mCm.registerDefaultNetworkCallback(defaultTrackingCb);
+ registerDefaultNetworkCallback(defaultTrackingCb);
try {
// CtsNetTestCases uid is not listed in MOBILE_DATA_PREFERRED_UIDS setting, so the
// per-app default network should be same as system default network.
waitForAvailable(systemDefaultCb, wifiNetwork);
- defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> wifiNetwork.equals(entry.getNetwork()));
+ waitForAvailable(defaultTrackingCb, wifiNetwork);
// Active network for CtsNetTestCases uid should be wifi now.
assertEquals(wifiNetwork, mCm.getActiveNetwork());
@@ -2831,10 +2890,10 @@
newMobileDataPreferredUids.add(uid);
ConnectivitySettingsManager.setMobileDataPreferredUids(
mContext, newMobileDataPreferredUids);
- defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> cellNetwork.equals(entry.getNetwork()));
- // System default network doesn't change.
- systemDefaultCb.assertNoCallback();
+ waitForAvailable(defaultTrackingCb, cellNetwork);
+ // No change for system default network. Expect no callback except CapabilitiesChanged
+ // or LinkPropertiesChanged which may be triggered randomly from wifi network.
+ assertNoCallbackExceptCapOrLpChange(systemDefaultCb);
// Active network for CtsNetTestCases uid should change to cell, too.
assertEquals(cellNetwork, mCm.getActiveNetwork());
@@ -2843,16 +2902,13 @@
newMobileDataPreferredUids.remove(uid);
ConnectivitySettingsManager.setMobileDataPreferredUids(
mContext, newMobileDataPreferredUids);
- defaultTrackingCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> wifiNetwork.equals(entry.getNetwork()));
- // System default network still doesn't change.
- systemDefaultCb.assertNoCallback();
+ waitForAvailable(defaultTrackingCb, wifiNetwork);
+ // No change for system default network. Expect no callback except CapabilitiesChanged
+ // or LinkPropertiesChanged which may be triggered randomly from wifi network.
+ assertNoCallbackExceptCapOrLpChange(systemDefaultCb);
// Active network for CtsNetTestCases uid should change back to wifi.
assertEquals(wifiNetwork, mCm.getActiveNetwork());
} finally {
- mCm.unregisterNetworkCallback(systemDefaultCb);
- mCm.unregisterNetworkCallback(defaultTrackingCb);
-
// Restore setting.
ConnectivitySettingsManager.setMobileDataPreferredUids(
mContext, mobileDataPreferredUids);
@@ -2881,6 +2937,24 @@
}
}
+ private static NetworkAgent createRestrictedNetworkAgent(final Context context) {
+ // Create test network agent with restricted network.
+ final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
+ .build();
+ final NetworkAgent agent = new NetworkAgent(context, Looper.getMainLooper(), TAG, nc,
+ new LinkProperties(), 10 /* score */, new NetworkAgentConfig.Builder().build(),
+ new NetworkProvider(context, Looper.getMainLooper(), TAG)) {};
+ runWithShellPermissionIdentity(() -> agent.register(),
+ android.Manifest.permission.MANAGE_TEST_NETWORKS);
+ agent.markConnected();
+
+ return agent;
+ }
+
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
@Test
public void testUidsAllowedOnRestrictedNetworks() throws Exception {
@@ -2901,42 +2975,27 @@
ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
- final Handler h = new Handler(Looper.getMainLooper());
final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
- mCm.registerBestMatchingNetworkCallback(new NetworkRequest.Builder().clearCapabilities()
- .addTransportType(NetworkCapabilities.TRANSPORT_TEST).build(), testNetworkCb, h);
-
- // Create test network agent with restricted network.
- final NetworkCapabilities nc = new NetworkCapabilities.Builder()
+ final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
.build();
- final NetworkScore score = new NetworkScore.Builder()
- .setExiting(false)
- .setTransportPrimary(false)
- .setKeepConnectedReason(NetworkScore.KEEP_CONNECTED_FOR_HANDOVER)
- .build();
- final NetworkAgent agent = new NetworkAgent(mContext, Looper.getMainLooper(),
- TAG, nc, new LinkProperties(), score, new NetworkAgentConfig.Builder().build(),
- new NetworkProvider(mContext, Looper.getMainLooper(), TAG)) {};
- runWithShellPermissionIdentity(() -> agent.register(),
- android.Manifest.permission.MANAGE_TEST_NETWORKS);
- agent.markConnected();
+ runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
final Network network = agent.getNetwork();
try (Socket socket = new Socket()) {
- testNetworkCb.eventuallyExpect(CallbackEntry.AVAILABLE, NETWORK_CALLBACK_TIMEOUT_MS,
- entry -> network.equals(entry.getNetwork()));
// Verify that the network is restricted.
- final NetworkCapabilities testNetworkNc = mCm.getNetworkCapabilities(network);
- assertNotNull(testNetworkNc);
- assertFalse(testNetworkNc.hasCapability(
- NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED));
+ testNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> network.equals(entry.getNetwork())
+ && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
// CtsNetTestCases package doesn't hold CONNECTIVITY_USE_RESTRICTED_NETWORKS, so it
// does not allow to bind socket to restricted network.
assertThrows(IOException.class, () -> network.bindSocket(socket));
@@ -2953,7 +3012,6 @@
// TODD: Have a significant signal to know the uids has been send to netd.
assertBindSocketToNetworkSuccess(network);
} finally {
- mCm.unregisterNetworkCallback(testNetworkCb);
agent.unregister();
// Restore setting.
@@ -2962,4 +3020,58 @@
mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
}
}
+
+ private void unregisterRegisteredCallbacks() {
+ for (NetworkCallback callback: mRegisteredCallbacks) {
+ mCm.unregisterNetworkCallback(callback);
+ }
+ }
+
+ private void registerDefaultNetworkCallback(NetworkCallback callback) {
+ mCm.registerDefaultNetworkCallback(callback);
+ mRegisteredCallbacks.add(callback);
+ }
+
+ private void registerDefaultNetworkCallback(NetworkCallback callback, Handler handler) {
+ mCm.registerDefaultNetworkCallback(callback, handler);
+ mRegisteredCallbacks.add(callback);
+ }
+
+ private void registerNetworkCallback(NetworkRequest request, NetworkCallback callback) {
+ mCm.registerNetworkCallback(request, callback);
+ mRegisteredCallbacks.add(callback);
+ }
+
+ private void registerSystemDefaultNetworkCallback(NetworkCallback callback, Handler handler) {
+ mCmShim.registerSystemDefaultNetworkCallback(callback, handler);
+ mRegisteredCallbacks.add(callback);
+ }
+
+ private void registerDefaultNetworkCallbackForUid(int uid, NetworkCallback callback,
+ Handler handler) throws Exception {
+ mCmShim.registerDefaultNetworkCallbackForUid(uid, callback, handler);
+ mRegisteredCallbacks.add(callback);
+ }
+
+ private void requestNetwork(NetworkRequest request, NetworkCallback callback) {
+ mCm.requestNetwork(request, callback);
+ mRegisteredCallbacks.add(callback);
+ }
+
+ private void requestNetwork(NetworkRequest request, NetworkCallback callback, int timeoutSec) {
+ mCm.requestNetwork(request, callback, timeoutSec);
+ mRegisteredCallbacks.add(callback);
+ }
+
+ private void registerBestMatchingNetworkCallback(NetworkRequest request,
+ NetworkCallback callback, Handler handler) {
+ mCm.registerBestMatchingNetworkCallback(request, callback, handler);
+ mRegisteredCallbacks.add(callback);
+ }
+
+ private void requestBackgroundNetwork(NetworkRequest request, NetworkCallback callback,
+ Handler handler) throws Exception {
+ mCmShim.requestBackgroundNetwork(request, callback, handler);
+ mRegisteredCallbacks.add(callback);
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt b/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt
new file mode 100644
index 0000000..1a62560
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/DhcpOptionTest.kt
@@ -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 android.net.cts
+
+import android.os.Build
+import android.net.DhcpOption
+import androidx.test.filters.SmallTest
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Assert.assertArrayEquals
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNull
+import org.junit.runner.RunWith
+import org.junit.Test
+
+@SmallTest
+@IgnoreUpTo(Build.VERSION_CODES.S)
+@RunWith(DevSdkIgnoreRunner::class)
+class DhcpOptionTest {
+ private val DHCP_OPTION_TYPE: Byte = 2
+ private val DHCP_OPTION_VALUE = byteArrayOf(0, 1, 2, 4, 8, 16)
+
+ @Test
+ fun testConstructor() {
+ val dhcpOption = DhcpOption(DHCP_OPTION_TYPE, DHCP_OPTION_VALUE)
+ assertEquals(DHCP_OPTION_TYPE, dhcpOption.type)
+ assertArrayEquals(DHCP_OPTION_VALUE, dhcpOption.value)
+ }
+
+ @Test
+ fun testConstructorWithNullValue() {
+ val dhcpOption = DhcpOption(DHCP_OPTION_TYPE, null)
+ assertEquals(DHCP_OPTION_TYPE, dhcpOption.type)
+ assertNull(dhcpOption.value)
+ }
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index 4d95fbe..4992795 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -22,34 +22,49 @@
import static android.net.DnsResolver.TYPE_A;
import static android.net.DnsResolver.TYPE_AAAA;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
import static android.system.OsConstants.ETIMEDOUT;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.NetworkCallback;
import android.net.DnsResolver;
-import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.ParseException;
import android.net.cts.util.CtsNetUtils;
+import android.os.Build;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.platform.test.annotations.AppModeFull;
-import android.provider.Settings;
import android.system.ErrnoException;
-import android.test.AndroidTestCase;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
import com.android.net.module.util.DnsPacket;
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.SkipPresubmit;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -60,7 +75,11 @@
import java.util.concurrent.TimeUnit;
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
-public class DnsResolverTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class DnsResolverTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+
private static final String TAG = "DnsResolverTest";
private static final char[] HEX_CHARS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
@@ -90,6 +109,7 @@
static final int QUERY_TIMES = 10;
static final int NXDOMAIN = 3;
+ private Context mContext;
private ContentResolver mCR;
private ConnectivityManager mCM;
private PackageManager mPackageManager;
@@ -98,26 +118,27 @@
private Executor mExecutorInline;
private DnsResolver mDns;
- private String mOldMode;
- private String mOldDnsSpecifier;
+ private TestNetworkCallback mWifiRequestCallback = null;
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mCM = mContext.getSystemService(ConnectivityManager.class);
mDns = DnsResolver.getInstance();
mExecutor = new Handler(Looper.getMainLooper())::post;
mExecutorInline = (Runnable r) -> r.run();
- mCR = getContext().getContentResolver();
- mCtsNetUtils = new CtsNetUtils(getContext());
+ mCR = mContext.getContentResolver();
+ mCtsNetUtils = new CtsNetUtils(mContext);
mCtsNetUtils.storePrivateDnsSetting();
mPackageManager = mContext.getPackageManager();
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
mCtsNetUtils.restorePrivateDnsSetting();
- super.tearDown();
+ if (mWifiRequestCallback != null) {
+ mCM.unregisterNetworkCallback(mWifiRequestCallback);
+ }
}
private static String byteArrayToHexString(byte[] bytes) {
@@ -132,6 +153,14 @@
private Network[] getTestableNetworks() {
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ // File a NetworkRequest for Wi-Fi, so it connects even if a higher-scoring
+ // network, such as Ethernet, is already connected.
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ mWifiRequestCallback = new TestNetworkCallback();
+ mCM.requestNetwork(request, mWifiRequestCallback);
mCtsNetUtils.ensureWifiConnected();
}
final ArrayList<Network> testableNetworks = new ArrayList<Network>();
@@ -285,42 +314,52 @@
}
}
+ @Test
public void testRawQuery() throws Exception {
doTestRawQuery(mExecutor);
}
+ @Test
public void testRawQueryInline() throws Exception {
doTestRawQuery(mExecutorInline);
}
+ @Test
public void testRawQueryBlob() throws Exception {
doTestRawQueryBlob(mExecutor);
}
+ @Test
public void testRawQueryBlobInline() throws Exception {
doTestRawQueryBlob(mExecutorInline);
}
+ @Test
public void testRawQueryRoot() throws Exception {
doTestRawQueryRoot(mExecutor);
}
+ @Test
public void testRawQueryRootInline() throws Exception {
doTestRawQueryRoot(mExecutorInline);
}
+ @Test
public void testRawQueryNXDomain() throws Exception {
doTestRawQueryNXDomain(mExecutor);
}
+ @Test
public void testRawQueryNXDomainInline() throws Exception {
doTestRawQueryNXDomain(mExecutorInline);
}
+ @Test
public void testRawQueryNXDomainWithPrivateDns() throws Exception {
doTestRawQueryNXDomainWithPrivateDns(mExecutor);
}
+ @Test
public void testRawQueryNXDomainInlineWithPrivateDns() throws Exception {
doTestRawQueryNXDomainWithPrivateDns(mExecutorInline);
}
@@ -423,6 +462,7 @@
}
}
+ @Test
public void testRawQueryCancel() throws InterruptedException {
final String msg = "Test cancel RawQuery " + TEST_DOMAIN;
// Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
@@ -452,6 +492,7 @@
}
}
+ @Test
public void testRawQueryBlobCancel() throws InterruptedException {
final String msg = "Test cancel RawQuery blob " + byteArrayToHexString(TEST_BLOB);
// Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
@@ -480,6 +521,7 @@
}
}
+ @Test
public void testCancelBeforeQuery() throws InterruptedException {
final String msg = "Test cancelled RawQuery " + TEST_DOMAIN;
for (Network network : getTestableNetworks()) {
@@ -565,34 +607,42 @@
}
}
+ @Test
public void testQueryForInetAddress() throws Exception {
doTestQueryForInetAddress(mExecutor);
}
+ @Test
public void testQueryForInetAddressInline() throws Exception {
doTestQueryForInetAddress(mExecutorInline);
}
+ @Test
public void testQueryForInetAddressIpv4() throws Exception {
doTestQueryForInetAddressIpv4(mExecutor);
}
+ @Test
public void testQueryForInetAddressIpv4Inline() throws Exception {
doTestQueryForInetAddressIpv4(mExecutorInline);
}
+ @Test
public void testQueryForInetAddressIpv6() throws Exception {
doTestQueryForInetAddressIpv6(mExecutor);
}
+ @Test
public void testQueryForInetAddressIpv6Inline() throws Exception {
doTestQueryForInetAddressIpv6(mExecutorInline);
}
+ @Test
public void testContinuousQueries() throws Exception {
doTestContinuousQueries(mExecutor);
}
+ @Test
@SkipPresubmit(reason = "Flaky: b/159762682; add to presubmit after fixing")
public void testContinuousQueriesInline() throws Exception {
doTestContinuousQueries(mExecutorInline);
@@ -612,6 +662,7 @@
}
}
+ @Test
public void testQueryCancelForInetAddress() throws InterruptedException {
final String msg = "Test cancel query for InetAddress " + TEST_DOMAIN;
// Start a DNS query and the cancel it immediately. Use VerifyCancelInetAddressCallback to
@@ -673,6 +724,7 @@
}
}
+ @Test
public void testPrivateDnsBypass() throws InterruptedException {
final Network[] testNetworks = getTestableNetworks();
@@ -760,4 +812,19 @@
}
}
}
+
+ /** Verifies that DnsResolver.DnsException can be subclassed and its constructor re-used. */
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S)
+ public void testDnsExceptionConstructor() throws InterruptedException {
+ class TestDnsException extends DnsResolver.DnsException {
+ TestDnsException(int code, @Nullable Throwable cause) {
+ super(code, cause);
+ }
+ }
+ try {
+ throw new TestDnsException(DnsResolver.ERROR_SYSTEM, null);
+ } catch (DnsResolver.DnsException e) {
+ assertEquals(DnsResolver.ERROR_SYSTEM, e.code);
+ }
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/DnsTest.java b/tests/cts/net/src/android/net/cts/DnsTest.java
index fde27e9..fb63a19 100644
--- a/tests/cts/net/src/android/net/cts/DnsTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsTest.java
@@ -16,7 +16,6 @@
package android.net.cts;
-import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
@@ -27,7 +26,7 @@
import android.test.AndroidTestCase;
import android.util.Log;
-import com.android.testutils.SkipPresubmit;
+import androidx.test.filters.RequiresDevice;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -70,7 +69,7 @@
* Perf - measure size of first and second tier caches and their effect
* Assert requires network permission
*/
- @SkipPresubmit(reason = "IPv6 support may be missing on presubmit virtual hardware")
+ @RequiresDevice // IPv6 support may be missing on presubmit virtual hardware
public void testDnsWorks() throws Exception {
ensureIpv6Connectivity();
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
index 5c95aa3..7bce3d2 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -413,20 +413,26 @@
// Check that iface stats are within an acceptable range; data might be sent
// on the local interface by other apps.
- assertApproxEquals(
- ifaceTxBytes, newIfaceTxBytes, expectedTxByteDelta, ERROR_MARGIN_BYTES);
- assertApproxEquals(
- ifaceRxBytes, newIfaceRxBytes, expectedRxByteDelta, ERROR_MARGIN_BYTES);
- assertApproxEquals(
- ifaceTxPackets, newIfaceTxPackets, expectedTxPacketDelta, ERROR_MARGIN_PKTS);
- assertApproxEquals(
- ifaceRxPackets, newIfaceRxPackets, expectedRxPacketDelta, ERROR_MARGIN_PKTS);
+ assertApproxEquals("TX bytes", ifaceTxBytes, newIfaceTxBytes, expectedTxByteDelta,
+ ERROR_MARGIN_BYTES);
+ assertApproxEquals("RX bytes", ifaceRxBytes, newIfaceRxBytes, expectedRxByteDelta,
+ ERROR_MARGIN_BYTES);
+ assertApproxEquals("TX packets", ifaceTxPackets, newIfaceTxPackets,
+ expectedTxPacketDelta, ERROR_MARGIN_PKTS);
+ assertApproxEquals("RX packets", ifaceRxPackets, newIfaceRxPackets,
+ expectedRxPacketDelta, ERROR_MARGIN_PKTS);
}
private static void assertApproxEquals(
- long oldStats, long newStats, int expectedDelta, double errorMargin) {
- assertTrue(expectedDelta <= newStats - oldStats);
- assertTrue((expectedDelta * errorMargin) > newStats - oldStats);
+ String what, long oldStats, long newStats, int expectedDelta, double errorMargin) {
+ assertTrue(
+ "Expected at least " + expectedDelta + " " + what
+ + ", got " + (newStats - oldStats),
+ newStats - oldStats >= expectedDelta);
+ assertTrue(
+ "Expected at most " + errorMargin + " * " + expectedDelta + " " + what
+ + ", got " + (newStats - oldStats),
+ newStats - oldStats < (expectedDelta * errorMargin));
}
private static void initStatsChecker() throws Exception {
@@ -717,11 +723,10 @@
algoToRequiredMinSdk.put(AUTH_HMAC_SHA512, Build.VERSION_CODES.P);
algoToRequiredMinSdk.put(AUTH_CRYPT_AES_GCM, Build.VERSION_CODES.P);
- // TODO: b/170424293 Use Build.VERSION_CODES.S when is finalized
- algoToRequiredMinSdk.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1);
- algoToRequiredMinSdk.put(AUTH_AES_CMAC, Build.VERSION_CODES.R + 1);
- algoToRequiredMinSdk.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1);
- algoToRequiredMinSdk.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1);
+ algoToRequiredMinSdk.put(CRYPT_AES_CTR, Build.VERSION_CODES.S);
+ algoToRequiredMinSdk.put(AUTH_AES_CMAC, Build.VERSION_CODES.S);
+ algoToRequiredMinSdk.put(AUTH_AES_XCBC, Build.VERSION_CODES.S);
+ algoToRequiredMinSdk.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.S);
final Set<String> supportedAlgos = IpSecAlgorithm.getSupportedAlgorithms();
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 7c380e3..ef5dc77 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -61,20 +61,6 @@
import android.net.Uri
import android.net.VpnManager
import android.net.VpnTransportInfo
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnRegisterQosCallback
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnRemoveKeepalivePacketFilter
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnSignalStrengthThresholdsUpdated
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnUnregisterQosCallback
-import android.net.cts.NetworkAgentTest.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnError
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionAvailable
import android.net.cts.NetworkAgentTest.TestableQosCallback.CallbackEntry.OnQosSessionLost
@@ -98,6 +84,20 @@
import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.Losing
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
+import com.android.testutils.TestableNetworkAgent
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnRegisterQosCallback
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnRemoveKeepalivePacketFilter
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnSaveAcceptUnvalidated
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnStartSocketKeepalive
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnStopSocketKeepalive
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnUnregisterQosCallback
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnValidationStatus
import com.android.testutils.TestableNetworkCallback
import org.junit.After
import org.junit.Assert.assertArrayEquals
@@ -136,10 +136,6 @@
// and then there is the Binder call), so have a short timeout for this as it will be
// exhausted every time.
private const val NO_CALLBACK_TIMEOUT = 200L
-// Any legal score (0~99) for the test network would do, as it is going to be kept up by the
-// requests filed by the test and should never match normal internet requests. 70 is the default
-// score of Ethernet networks, it's as good a value as any other.
-private const val TEST_NETWORK_SCORE = 70
private const val WORSE_NETWORK_SCORE = 65
private const val BETTER_NETWORK_SCORE = 75
private const val FAKE_NET_ID = 1098
@@ -165,10 +161,6 @@
private val mCM = realContext.getSystemService(ConnectivityManager::class.java)!!
private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread")
private val mFakeConnectivityService = FakeConnectivityService()
-
- private class Provider(context: Context, looper: Looper) :
- NetworkProvider(context, looper, "NetworkAgentTest NetworkProvider")
-
private val agentsToCleanUp = mutableListOf<NetworkAgent>()
private val callbacksToCleanUp = mutableListOf<TestableNetworkCallback>()
private var qosTestSocket: Socket? = null
@@ -219,146 +211,6 @@
fun disconnect() = agent.onDisconnected()
}
- private open class TestableNetworkAgent(
- context: Context,
- looper: Looper,
- val nc: NetworkCapabilities,
- val lp: LinkProperties,
- conf: NetworkAgentConfig
- ) : NetworkAgent(context, looper, TestableNetworkAgent::class.java.simpleName /* tag */,
- nc, lp, TEST_NETWORK_SCORE, conf, Provider(context, looper)) {
- private val history = ArrayTrackRecord<CallbackEntry>().newReadHead()
-
- sealed class CallbackEntry {
- object OnBandwidthUpdateRequested : CallbackEntry()
- object OnNetworkUnwanted : CallbackEntry()
- data class OnAddKeepalivePacketFilter(
- val slot: Int,
- val packet: KeepalivePacketData
- ) : CallbackEntry()
- data class OnRemoveKeepalivePacketFilter(val slot: Int) : CallbackEntry()
- data class OnStartSocketKeepalive(
- val slot: Int,
- val interval: Int,
- val packet: KeepalivePacketData
- ) : CallbackEntry()
- data class OnStopSocketKeepalive(val slot: Int) : CallbackEntry()
- data class OnSaveAcceptUnvalidated(val accept: Boolean) : CallbackEntry()
- object OnAutomaticReconnectDisabled : CallbackEntry()
- data class OnValidationStatus(val status: Int, val uri: Uri?) : CallbackEntry()
- data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry()
- object OnNetworkCreated : CallbackEntry()
- object OnNetworkDestroyed : CallbackEntry()
- data class OnRegisterQosCallback(
- val callbackId: Int,
- val filter: QosFilter
- ) : CallbackEntry()
- data class OnUnregisterQosCallback(val callbackId: Int) : CallbackEntry()
- }
-
- override fun onBandwidthUpdateRequested() {
- history.add(OnBandwidthUpdateRequested)
- }
-
- override fun onNetworkUnwanted() {
- history.add(OnNetworkUnwanted)
- }
-
- override fun onAddKeepalivePacketFilter(slot: Int, packet: KeepalivePacketData) {
- history.add(OnAddKeepalivePacketFilter(slot, packet))
- }
-
- override fun onRemoveKeepalivePacketFilter(slot: Int) {
- history.add(OnRemoveKeepalivePacketFilter(slot))
- }
-
- override fun onStartSocketKeepalive(
- slot: Int,
- interval: Duration,
- packet: KeepalivePacketData
- ) {
- history.add(OnStartSocketKeepalive(slot, interval.seconds.toInt(), packet))
- }
-
- override fun onStopSocketKeepalive(slot: Int) {
- history.add(OnStopSocketKeepalive(slot))
- }
-
- override fun onSaveAcceptUnvalidated(accept: Boolean) {
- history.add(OnSaveAcceptUnvalidated(accept))
- }
-
- override fun onAutomaticReconnectDisabled() {
- history.add(OnAutomaticReconnectDisabled)
- }
-
- override fun onSignalStrengthThresholdsUpdated(thresholds: IntArray) {
- history.add(OnSignalStrengthThresholdsUpdated(thresholds))
- }
-
- fun expectSignalStrengths(thresholds: IntArray? = intArrayOf()) {
- expectCallback<OnSignalStrengthThresholdsUpdated>().let {
- assertArrayEquals(thresholds, it.thresholds)
- }
- }
-
- override fun onQosCallbackRegistered(qosCallbackId: Int, filter: QosFilter) {
- history.add(OnRegisterQosCallback(qosCallbackId, filter))
- }
-
- override fun onQosCallbackUnregistered(qosCallbackId: Int) {
- history.add(OnUnregisterQosCallback(qosCallbackId))
- }
-
- override fun onValidationStatus(status: Int, uri: Uri?) {
- history.add(OnValidationStatus(status, uri))
- }
-
- override fun onNetworkCreated() {
- history.add(OnNetworkCreated)
- }
-
- override fun onNetworkDestroyed() {
- history.add(OnNetworkDestroyed)
- }
-
- // Expects the initial validation event that always occurs immediately after registering
- // a NetworkAgent whose network does not require validation (which test networks do
- // not, since they lack the INTERNET capability). It always contains the default argument
- // for the URI.
- fun expectValidationBypassedStatus() = expectCallback<OnValidationStatus>().let {
- assertEquals(it.status, VALID_NETWORK)
- // The returned Uri is parsed from the empty string, which means it's an
- // instance of the (private) Uri.StringUri. There are no real good ways
- // to check this, the least bad is to just convert it to a string and
- // make sure it's empty.
- assertEquals("", it.uri.toString())
- }
-
- inline fun <reified T : CallbackEntry> expectCallback(): T {
- val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
- assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
- return foundCallback
- }
-
- inline fun <reified T : CallbackEntry> expectCallback(valid: (T) -> Boolean) {
- val foundCallback = history.poll(DEFAULT_TIMEOUT_MS)
- assertTrue(foundCallback is T, "Expected ${T::class} but found $foundCallback")
- assertTrue(valid(foundCallback), "Unexpected callback : $foundCallback")
- }
-
- inline fun <reified T : CallbackEntry> eventuallyExpect() =
- history.poll(DEFAULT_TIMEOUT_MS) { it is T }.also {
- assertNotNull(it, "Callback ${T::class} not received")
- } as T
-
- fun assertNoCallback() {
- assertTrue(waitForIdle(DEFAULT_TIMEOUT_MS),
- "Handler didn't became idle after ${DEFAULT_TIMEOUT_MS}ms")
- assertNull(history.peek())
- }
- }
-
private fun requestNetwork(request: NetworkRequest, callback: TestableNetworkCallback) {
mCM.requestNetwork(request, callback)
callbacksToCleanUp.add(callback)
@@ -1072,6 +924,7 @@
Executors.newSingleThreadExecutor().let { executor ->
try {
val info = QosSocketInfo(agent.network!!, socket)
+ assertEquals(agent.network, info.getNetwork())
mCM.registerQosCallback(info, executor, qosCallback)
val callbackId = agent.expectCallback<OnRegisterQosCallback>().callbackId
diff --git a/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt b/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt
index fa15e8f..d6120f8 100644
--- a/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkInfoTest.kt
@@ -26,16 +26,19 @@
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.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
-import org.junit.Assert.fail
import org.junit.Rule
import org.junit.runner.RunWith
import org.junit.Test
+import kotlin.reflect.jvm.isAccessible
+import kotlin.test.assertFails
+import kotlin.test.assertFailsWith
const val TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE
const val TYPE_WIFI = ConnectivityManager.TYPE_WIFI
@@ -97,12 +100,16 @@
assertNull(networkInfo.reason)
assertNull(networkInfo.extraInfo)
- try {
+ assertFailsWith<IllegalArgumentException> {
NetworkInfo(ConnectivityManager.MAX_NETWORK_TYPE + 1,
TelephonyManager.NETWORK_TYPE_LTE, MOBILE_TYPE_NAME, LTE_SUBTYPE_NAME)
- fail("Unexpected behavior. Network type is invalid.")
- } catch (e: IllegalArgumentException) {
- // Expected behavior.
+ }
+
+ if (SdkLevel.isAtLeastT()) {
+ assertFailsWith<NullPointerException> { NetworkInfo(null) }
+ } else {
+ // Doesn't immediately crash on S-
+ NetworkInfo(null)
}
}
@@ -118,5 +125,29 @@
assertEquals(State.CONNECTED, networkInfo.state)
assertEquals(reason, networkInfo.reason)
assertEquals(extraReason, networkInfo.extraInfo)
+
+ // Create an incorrect enum value by calling the default constructor of the enum
+ val constructor = DetailedState::class.java.declaredConstructors.first {
+ it.parameters.size == 2
+ }
+ constructor.isAccessible = true
+ val incorrectDetailedState = constructor.newInstance("any", 200) as DetailedState
+ if (SdkLevel.isAtLeastT()) {
+ assertFailsWith<NullPointerException> {
+ NetworkInfo(null)
+ }
+ assertFailsWith<NullPointerException> {
+ networkInfo.setDetailedState(null, "reason", "extraInfo")
+ }
+ // This actually throws ArrayOutOfBoundsException because of the implementation of
+ // EnumMap, but that's an implementation detail so accept any crash.
+ assertFails {
+ networkInfo.setDetailedState(incorrectDetailedState, "reason", "extraInfo")
+ }
+ } else {
+ // Doesn't immediately crash on S-
+ NetworkInfo(null)
+ networkInfo.setDetailedState(null, "reason", "extraInfo")
+ }
}
-}
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
index 5290f0d..8e98dba 100644
--- a/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTest.kt
@@ -25,7 +25,6 @@
import android.net.InetAddresses
import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
-import android.net.NetworkCapabilities.TRANSPORT_ETHERNET
import android.net.NetworkCapabilities.TRANSPORT_TEST
import android.net.NetworkRequest
import android.net.TestNetworkInterface
@@ -96,7 +95,6 @@
private val ethRequest = NetworkRequest.Builder()
// ETHERNET|TEST transport networks do not have NET_CAPABILITY_TRUSTED
.removeCapability(NET_CAPABILITY_TRUSTED)
- .addTransportType(TRANSPORT_ETHERNET)
.addTransportType(TRANSPORT_TEST).build()
private val ethRequestCb = TestableNetworkCallback()
diff --git a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
index dde14ac..391d03a 100644
--- a/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkValidationTestUtil.kt
@@ -19,12 +19,20 @@
import android.Manifest
import android.net.util.NetworkStackUtils
import android.provider.DeviceConfig
+import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
+import android.util.Log
import com.android.testutils.runAsShell
+import com.android.testutils.tryTest
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
/**
* Collection of utility methods for configuring network validation.
*/
internal object NetworkValidationTestUtil {
+ val TAG = NetworkValidationTestUtil::class.simpleName
+ const val TIMEOUT_MS = 20_000L
/**
* Clear the test network validation URLs.
@@ -59,10 +67,52 @@
@JvmStatic fun setUrlExpirationDeviceConfig(timestamp: Long?) =
setConfig(NetworkStackUtils.TEST_URL_EXPIRATION_TIME, timestamp?.toString())
- private fun setConfig(configKey: String, value: String?) {
- runAsShell(Manifest.permission.WRITE_DEVICE_CONFIG) {
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_CONNECTIVITY, configKey, value, false /* makeDefault */)
+ private fun setConfig(configKey: String, value: String?): String? {
+ Log.i(TAG, "Setting config \"$configKey\" to \"$value\"")
+ val readWritePermissions = arrayOf(
+ Manifest.permission.READ_DEVICE_CONFIG,
+ Manifest.permission.WRITE_DEVICE_CONFIG)
+
+ val existingValue = runAsShell(*readWritePermissions) {
+ DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY, configKey)
+ }
+ if (existingValue == value) {
+ // Already the correct value. There may be a race if a change is already in flight,
+ // but if multiple threads update the config there is no way to fix that anyway.
+ Log.i(TAG, "\$configKey\" already had value \"$value\"")
+ return value
+ }
+
+ val future = CompletableFuture<String>()
+ val listener = DeviceConfig.OnPropertiesChangedListener {
+ // The listener receives updates for any change to any key, so don't react to
+ // changes that do not affect the relevant key
+ if (!it.keyset.contains(configKey)) return@OnPropertiesChangedListener
+ if (it.getString(configKey, null) == value) {
+ future.complete(value)
+ }
+ }
+
+ return tryTest {
+ runAsShell(*readWritePermissions) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_CONNECTIVITY,
+ inlineExecutor,
+ listener)
+ DeviceConfig.setProperty(
+ NAMESPACE_CONNECTIVITY,
+ configKey,
+ value,
+ false /* makeDefault */)
+ // Don't drop the permission until the config is applied, just in case
+ future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }.also {
+ Log.i(TAG, "Config \"$configKey\" successfully set to \"$value\"")
+ }
+ } cleanup {
+ DeviceConfig.removeOnPropertiesChangedListener(listener)
}
}
-}
\ No newline at end of file
+
+ private val inlineExecutor get() = Executor { r -> r.run() }
+}
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
index fd0cd18..ce873f7 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsNetUtils.java
@@ -16,15 +16,11 @@
package android.net.cts.util;
-import static android.Manifest.permission.ACCESS_WIFI_STATE;
-import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
-import static android.net.wifi.WifiManager.SCAN_RESULTS_AVAILABLE_ACTION;
import static com.android.compatibility.common.util.PropertyUtil.getFirstApiLevel;
-import static com.android.testutils.TestPermissionUtil.runAsShell;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -48,15 +44,12 @@
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
import android.net.TestNetworkManager;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build;
import android.os.ConditionVariable;
import android.os.IBinder;
-import android.os.SystemClock;
import android.system.Os;
import android.system.OsConstants;
import android.text.TextUtils;
@@ -64,23 +57,17 @@
import com.android.compatibility.common.util.SystemUtil;
import com.android.net.module.util.ConnectivitySettingsUtils;
-
-import junit.framework.AssertionFailedError;
+import com.android.testutils.ConnectUtil;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.stream.Collectors;
public final class CtsNetUtils {
private static final String TAG = CtsNetUtils.class.getSimpleName();
@@ -89,13 +76,7 @@
private static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 10_000;
private static final int CONNECTIVITY_CHANGE_TIMEOUT_SECS = 30;
- private static final int MAX_WIFI_CONNECT_RETRIES = 10;
- private static final int WIFI_CONNECT_INTERVAL_MS = 500;
- // Constants used by WifiManager.ActionListener#onFailure. Although onFailure is SystemApi,
- // the error code constants are not (they probably should be ?)
- private static final int WIFI_ERROR_IN_PROGRESS = 1;
- private static final int WIFI_ERROR_BUSY = 2;
private static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
private static final String PRIVATE_DNS_MODE_STRICT = "hostname";
public static final int HTTP_PORT = 80;
@@ -237,10 +218,6 @@
* @return The network that was newly connected.
*/
private Network connectToWifi(boolean expectLegacyBroadcast) {
- final TestNetworkCallback callback = new TestNetworkCallback();
- mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
- Network wifiNetwork = null;
-
ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
mCm, ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
IntentFilter filter = new IntentFilter();
@@ -248,159 +225,17 @@
mContext.registerReceiver(receiver, filter);
try {
- // Clear the wifi config blocklist (not the BSSID blocklist)
- clearWifiBlocklist();
- SystemUtil.runShellCommand("svc wifi enable");
- final WifiConfiguration config = getOrCreateWifiConfiguration();
- connectToWifiConfig(config);
-
- // Ensure we get an onAvailable callback and possibly a CONNECTIVITY_ACTION.
- wifiNetwork = callback.waitForAvailable();
- assertNotNull("onAvailable callback not received after connecting to " + config.SSID,
- wifiNetwork);
+ final Network network = new ConnectUtil(mContext).ensureWifiConnected();
if (expectLegacyBroadcast) {
- assertTrue("CONNECTIVITY_ACTION not received after connecting to " + config.SSID,
+ assertTrue("CONNECTIVITY_ACTION not received after connecting to " + network,
receiver.waitForState());
}
+ return network;
} catch (InterruptedException ex) {
- fail("connectToWifi was interrupted");
+ throw new AssertionError("connectToWifi was interrupted", ex);
} finally {
- mCm.unregisterNetworkCallback(callback);
mContext.unregisterReceiver(receiver);
}
-
- return wifiNetwork;
- }
-
- private void connectToWifiConfig(WifiConfiguration config) {
- for (int i = 0; i < MAX_WIFI_CONNECT_RETRIES; i++) {
- final Integer error = runAsShell(NETWORK_SETTINGS, () -> {
- final ConnectWifiListener listener = new ConnectWifiListener();
- mWifiManager.connect(config, listener);
- return listener.connectFuture.get(
- CONNECTIVITY_CHANGE_TIMEOUT_SECS, TimeUnit.SECONDS);
- });
-
- if (error == null) return;
-
- // Only retry for IN_PROGRESS and BUSY
- if (error != WIFI_ERROR_IN_PROGRESS && error != WIFI_ERROR_BUSY) {
- fail("Failed to connect to " + config.SSID + ": " + error);
- }
-
- Log.w(TAG, "connect failed with " + error + "; waiting before retry");
- SystemClock.sleep(WIFI_CONNECT_INTERVAL_MS);
- }
-
- fail("Failed to connect to " + config.SSID
- + " after " + MAX_WIFI_CONNECT_RETRIES + "retries");
- }
-
- private static class ConnectWifiListener implements WifiManager.ActionListener {
- /**
- * Future completed when the connect process ends. Provides the error code or null if none.
- */
- final CompletableFuture<Integer> connectFuture = new CompletableFuture<>();
- @Override
- public void onSuccess() {
- connectFuture.complete(null);
- }
-
- @Override
- public void onFailure(int reason) {
- connectFuture.complete(reason);
- }
- }
-
- private WifiConfiguration getOrCreateWifiConfiguration() {
- final List<WifiConfiguration> configs = runAsShell(NETWORK_SETTINGS,
- mWifiManager::getConfiguredNetworks);
- // If no network is configured, add a config for virtual access points if applicable
- if (configs.size() == 0) {
- final List<ScanResult> scanResults = getWifiScanResults();
- final WifiConfiguration virtualConfig = maybeConfigureVirtualNetwork(scanResults);
- assertNotNull("The device has no configured wifi network", virtualConfig);
-
- return virtualConfig;
- }
- // No need to add a configuration: there is already one.
- if (configs.size() > 1) {
- // For convenience in case of local testing on devices with multiple saved configs,
- // prefer the first configuration that is in range.
- // In actual tests, there should only be one configuration, and it should be usable as
- // assumed by WifiManagerTest.testConnect.
- Log.w(TAG, "Multiple wifi configurations found: "
- + configs.stream().map(c -> c.SSID).collect(Collectors.joining(", ")));
- final List<ScanResult> scanResultsList = getWifiScanResults();
- Log.i(TAG, "Scan results: " + scanResultsList.stream().map(c ->
- c.SSID + " (" + c.level + ")").collect(Collectors.joining(", ")));
- final Set<String> scanResults = scanResultsList.stream().map(
- s -> "\"" + s.SSID + "\"").collect(Collectors.toSet());
-
- return configs.stream().filter(c -> scanResults.contains(c.SSID))
- .findFirst().orElse(configs.get(0));
- }
- return configs.get(0);
- }
-
- private List<ScanResult> getWifiScanResults() {
- final CompletableFuture<List<ScanResult>> scanResultsFuture = new CompletableFuture<>();
- runAsShell(NETWORK_SETTINGS, () -> {
- final BroadcastReceiver receiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- scanResultsFuture.complete(mWifiManager.getScanResults());
- }
- };
- mContext.registerReceiver(receiver, new IntentFilter(SCAN_RESULTS_AVAILABLE_ACTION));
- mWifiManager.startScan();
- });
-
- try {
- return scanResultsFuture.get(CONNECTIVITY_CHANGE_TIMEOUT_SECS, TimeUnit.SECONDS);
- } catch (ExecutionException | InterruptedException | TimeoutException e) {
- throw new AssertionFailedError("Wifi scan results not received within timeout");
- }
- }
-
- /**
- * If a virtual wifi network is detected, add a configuration for that network.
- * TODO(b/158150376): have the test infrastructure add virtual wifi networks when appropriate.
- */
- private WifiConfiguration maybeConfigureVirtualNetwork(List<ScanResult> scanResults) {
- // Virtual wifi networks used on the emulator and cloud testing infrastructure
- final List<String> virtualSsids = Arrays.asList("VirtWifi", "AndroidWifi");
- Log.d(TAG, "Wifi scan results: " + scanResults);
- final ScanResult virtualScanResult = scanResults.stream().filter(
- s -> virtualSsids.contains(s.SSID)).findFirst().orElse(null);
-
- // Only add the virtual configuration if the virtual AP is detected in scans
- if (virtualScanResult == null) return null;
-
- final WifiConfiguration virtualConfig = new WifiConfiguration();
- // ASCII SSIDs need to be surrounded by double quotes
- virtualConfig.SSID = "\"" + virtualScanResult.SSID + "\"";
- virtualConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
-
- runAsShell(NETWORK_SETTINGS, () -> {
- final int networkId = mWifiManager.addNetwork(virtualConfig);
- assertTrue(networkId >= 0);
- assertTrue(mWifiManager.enableNetwork(networkId, false /* attemptConnect */));
- });
- return virtualConfig;
- }
-
- /**
- * Re-enable wifi networks that were blocklisted, typically because no internet connection was
- * detected the last time they were connected. This is necessary to make sure wifi can reconnect
- * to them.
- */
- private void clearWifiBlocklist() {
- runAsShell(NETWORK_SETTINGS, ACCESS_WIFI_STATE, () -> {
- for (WifiConfiguration cfg : mWifiManager.getConfiguredNetworks()) {
- assertTrue(mWifiManager.enableNetwork(cfg.networkId, false /* attemptConnect */));
- }
- });
}
/**
@@ -471,14 +306,18 @@
}
try {
+ if (wasWifiConnected) {
+ // Make sure the callback is registered before turning off WiFi.
+ callback.waitForAvailable();
+ }
SystemUtil.runShellCommand("svc wifi disable");
if (wasWifiConnected) {
// Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
assertNotNull("Did not receive onLost callback after disabling wifi",
callback.waitForLost());
- }
- if (wasWifiConnected && expectLegacyBroadcast) {
- assertTrue("Wifi failed to reach DISCONNECTED state.", receiver.waitForState());
+ if (expectLegacyBroadcast) {
+ assertTrue("Wifi failed to reach DISCONNECTED state.", receiver.waitForState());
+ }
}
} catch (InterruptedException ex) {
fail("disconnectFromWifi was interrupted");
@@ -582,6 +421,9 @@
}
if (mOldPrivateDnsMode != ConnectivitySettingsUtils.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME) {
+ // Also restore hostname even if the value is not used since private dns is not in
+ // the strict mode to prevent setting being changed after test.
+ ConnectivitySettingsUtils.setPrivateDnsHostname(mContext, mOldPrivateDnsSpecifier);
ConnectivitySettingsUtils.setPrivateDnsMode(mContext, mOldPrivateDnsMode);
return;
}
@@ -634,6 +476,7 @@
NetworkCallback callback = new NetworkCallback() {
@Override
public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
+ Log.i(TAG, "Link properties of network " + n + " changed to " + lp);
if (requiresValidatedServer && lp.getValidatedPrivateDnsServers().isEmpty()) {
return;
}
@@ -761,12 +604,14 @@
@Override
public void onAvailable(Network network) {
+ Log.i(TAG, "CtsNetUtils TestNetworkCallback onAvailable " + network);
currentNetwork = network;
mAvailableCv.open();
}
@Override
public void onLost(Network network) {
+ Log.i(TAG, "CtsNetUtils TestNetworkCallback onLost " + network);
lastLostNetwork = network;
if (network.equals(currentNetwork)) {
mAvailableCv.close();
diff --git a/tests/cts/tethering/Android.bp b/tests/cts/tethering/Android.bp
index 52ce83a..e9c4e5a 100644
--- a/tests/cts/tethering/Android.bp
+++ b/tests/cts/tethering/Android.bp
@@ -71,6 +71,7 @@
// Include both the 32 and 64 bit versions
compile_multilib: "both",
+ jarjar_rules: ":NetworkStackJarJarRules",
}
// Tethering CTS tests for development and release. These tests always target the platform SDK
@@ -95,4 +96,5 @@
// Include both the 32 and 64 bit versions
compile_multilib: "both",
+ jarjar_rules: ":NetworkStackJarJarRules",
}
diff --git a/tests/deflake/Android.bp b/tests/deflake/Android.bp
index 806f805..b3d0363 100644
--- a/tests/deflake/Android.bp
+++ b/tests/deflake/Android.bp
@@ -19,8 +19,17 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// FrameworksNetDeflakeTest depends on FrameworksNetTests so it should be disabled
+// if FrameworksNetTests is disabled.
+enable_frameworks_net_deflake_test = false
+// Placeholder
+// This is a placeholder comment to minimize merge conflicts, as enable_frameworks_net_deflake_test
+// may have different values depending on the branch
+// Placeholder
+
java_test_host {
name: "FrameworksNetDeflakeTest",
+ enabled: enable_frameworks_net_deflake_test,
srcs: ["src/**/*.kt"],
libs: [
"junit",
@@ -32,4 +41,14 @@
],
data: [":FrameworksNetTests"],
test_suites: ["device-tests"],
+ // It will get build error if just set enabled to true. It fails with "windows_common"
+ // depends on some disabled modules that are used by this test and it looks like set
+ // enable_frameworks_net_deflake_test to true also enables "windows" variant. Thus,
+ // disable this on target windows.
+ // TODO: Remove this when b/201754360 is fixed.
+ target: {
+ windows: {
+ enabled: false,
+ },
+ },
}
diff --git a/tests/integration/Android.bp b/tests/integration/Android.bp
index 26f7f4a..7b5b44f 100644
--- a/tests/integration/Android.bp
+++ b/tests/integration/Android.bp
@@ -30,6 +30,7 @@
],
libs: [
"android.test.mock",
+ "ServiceConnectivityResources",
],
static_libs: [
"NetworkStackApiStableLib",
diff --git a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index e039ef0..80338aa 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -23,7 +23,9 @@
import android.content.Context.BIND_IMPORTANT
import android.content.Intent
import android.content.ServiceConnection
+import android.content.res.Resources
import android.net.ConnectivityManager
+import android.net.ConnectivityResources
import android.net.IDnsResolver
import android.net.INetd
import android.net.LinkProperties
@@ -35,6 +37,7 @@
import android.net.TestNetworkStackClient
import android.net.Uri
import android.net.metrics.IpConnectivityLog
+import android.net.util.MultinetworkPolicyTracker
import android.os.ConditionVariable
import android.os.IBinder
import android.os.SystemConfigManager
@@ -43,6 +46,7 @@
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
+import com.android.connectivity.resources.R
import com.android.server.ConnectivityService
import com.android.server.NetworkAgentWrapper
import com.android.server.TestNetIdManager
@@ -59,6 +63,7 @@
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
@@ -93,6 +98,10 @@
private lateinit var dnsResolver: IDnsResolver
@Mock
private lateinit var systemConfigManager: SystemConfigManager
+ @Mock
+ private lateinit var resources: Resources
+ @Mock
+ private lateinit var resourcesContext: Context
@Spy
private var context = TestableContext(realContext)
@@ -110,9 +119,11 @@
private val realContext get() = InstrumentationRegistry.getInstrumentation().context
private val httpProbeUrl get() =
- realContext.getResources().getString(R.string.config_captive_portal_http_url)
+ realContext.getResources().getString(com.android.server.net.integrationtests.R.string
+ .config_captive_portal_http_url)
private val httpsProbeUrl get() =
- realContext.getResources().getString(R.string.config_captive_portal_https_url)
+ realContext.getResources().getString(com.android.server.net.integrationtests.R.string
+ .config_captive_portal_https_url)
private class InstrumentationServiceConnection : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
@@ -156,6 +167,27 @@
.getSystemService(Context.SYSTEM_CONFIG_SERVICE)
doReturn(IntArray(0)).`when`(systemConfigManager).getSystemPermissionUids(anyString())
+ doReturn(60000).`when`(resources).getInteger(R.integer.config_networkTransitionTimeout)
+ doReturn("").`when`(resources).getString(R.string.config_networkCaptivePortalServerUrl)
+ doReturn(arrayOf<String>("test_wlan_wol")).`when`(resources)
+ .getStringArray(R.array.config_wakeonlan_supported_interfaces)
+ doReturn(arrayOf("0,1", "1,3")).`when`(resources)
+ .getStringArray(R.array.config_networkSupportedKeepaliveCount)
+ doReturn(emptyArray<String>()).`when`(resources)
+ .getStringArray(R.array.config_networkNotifySwitches)
+ doReturn(intArrayOf(10, 11, 12, 14, 15)).`when`(resources)
+ .getIntArray(R.array.config_protectedNetworks)
+ // We don't test the actual notification value strings, so just return an empty array.
+ // It doesn't matter what the values are as long as it's not null.
+ doReturn(emptyArray<String>()).`when`(resources).getStringArray(
+ R.array.network_switch_type_name)
+ doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi)
+ doReturn(R.array.config_networkSupportedKeepaliveCount).`when`(resources)
+ .getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any())
+
+ doReturn(resources).`when`(resourcesContext).getResources()
+ ConnectivityResources.setResourcesContextForTest(resourcesContext)
+
networkStackClient = TestNetworkStackClient(realContext)
networkStackClient.start()
@@ -176,12 +208,19 @@
doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any())
doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties
doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
+ doAnswer { inv ->
+ object : MultinetworkPolicyTracker(inv.getArgument(0), inv.getArgument(1),
+ inv.getArgument(2)) {
+ override fun getResourcesForActiveSubId() = resources
+ }
+ }.`when`(deps).makeMultinetworkPolicyTracker(any(), any(), any())
return deps
}
@After
fun tearDown() {
nsInstrumentation.clearAllState()
+ ConnectivityResources.setResourcesContextForTest(null)
}
@Test
diff --git a/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
index eff6658..c7cf040 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -36,7 +36,6 @@
import java.io.ByteArrayInputStream
import java.net.HttpURLConnection
import java.net.URL
-import java.net.URLConnection
import java.nio.charset.StandardCharsets
private const val TEST_NETID = 42
@@ -63,6 +62,28 @@
override fun getPrivateDnsBypassNetwork(network: Network?) = privateDnsBypassNetwork
}
+ /**
+ * Mock [HttpURLConnection] to simulate reply from a server.
+ */
+ private class MockConnection(
+ url: URL,
+ private val response: HttpResponse
+ ) : HttpURLConnection(url) {
+ private val responseBytes = response.content.toByteArray(StandardCharsets.UTF_8)
+ override fun getResponseCode() = response.responseCode
+ override fun getContentLengthLong() = responseBytes.size.toLong()
+ override fun getHeaderField(field: String): String? {
+ return when (field) {
+ "location" -> response.redirectUrl
+ else -> null
+ }
+ }
+ override fun getInputStream() = ByteArrayInputStream(responseBytes)
+ override fun connect() = Unit
+ override fun disconnect() = Unit
+ override fun usingProxy() = false
+ }
+
private inner class TestNetworkStackConnector(context: Context) : NetworkStackConnector(
context, TestPermissionChecker(), NetworkStackService.Dependencies()) {
@@ -70,17 +91,8 @@
private val privateDnsBypassNetwork = TestNetwork(TEST_NETID)
private inner class TestNetwork(netId: Int) : Network(netId) {
- override fun openConnection(url: URL): URLConnection {
- val response = InstrumentationConnector.processRequest(url)
- val responseBytes = response.content.toByteArray(StandardCharsets.UTF_8)
-
- val connection = mock(HttpURLConnection::class.java)
- doReturn(response.responseCode).`when`(connection).responseCode
- doReturn(responseBytes.size.toLong()).`when`(connection).contentLengthLong
- doReturn(response.redirectUrl).`when`(connection).getHeaderField("location")
- doReturn(ByteArrayInputStream(responseBytes)).`when`(connection).inputStream
- return connection
- }
+ override fun openConnection(url: URL) = MockConnection(
+ url, InstrumentationConnector.processRequest(url))
}
override fun makeNetworkMonitor(
diff --git a/tests/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
index 95ea401..4dc86ff 100644
--- a/tests/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -27,6 +27,7 @@
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
+import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
@@ -83,6 +84,12 @@
public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
NetworkCapabilities ncTemplate, Context context) throws Exception {
+ this(transport, linkProperties, ncTemplate, null /* provider */, context);
+ }
+
+ public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
+ NetworkCapabilities ncTemplate, NetworkProvider provider,
+ Context context) throws Exception {
final int type = transportToLegacyType(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
@@ -124,12 +131,12 @@
.setLegacyTypeName(typeName)
.setLegacyExtraInfo(extraInfo)
.build();
- mNetworkAgent = makeNetworkAgent(linkProperties, mNetworkAgentConfig);
+ mNetworkAgent = makeNetworkAgent(linkProperties, mNetworkAgentConfig, provider);
}
protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties,
- final NetworkAgentConfig nac) throws Exception {
- return new InstrumentedNetworkAgent(this, linkProperties, nac);
+ final NetworkAgentConfig nac, NetworkProvider provider) throws Exception {
+ return new InstrumentedNetworkAgent(this, linkProperties, nac, provider);
}
public static class InstrumentedNetworkAgent extends NetworkAgent {
@@ -138,10 +145,15 @@
public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp,
NetworkAgentConfig nac) {
+ this(wrapper, lp, nac, null /* provider */);
+ }
+
+ public InstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp,
+ NetworkAgentConfig nac, NetworkProvider provider) {
super(wrapper.mContext, wrapper.mHandlerThread.getLooper(), wrapper.mLogTag,
wrapper.mNetworkCapabilities, lp, wrapper.mScore, nac,
- new NetworkProvider(wrapper.mContext, wrapper.mHandlerThread.getLooper(),
- PROVIDER_NAME));
+ null != provider ? provider : new NetworkProvider(wrapper.mContext,
+ wrapper.mHandlerThread.getLooper(), PROVIDER_NAME));
mWrapper = wrapper;
register();
}
@@ -299,6 +311,10 @@
assertTrue(mDisconnected.block(timeoutMs));
}
+ public void assertNotDisconnected(long timeoutMs) {
+ assertFalse(mDisconnected.block(timeoutMs));
+ }
+
public void sendLinkProperties(LinkProperties lp) {
mNetworkAgent.sendLinkProperties(lp);
}
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 8dbf564..9e80a25 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -10,11 +10,19 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// Whether to enable the FrameworksNetTests. Set to false in the branches that might have older
+// frameworks/base since FrameworksNetTests includes the test for classes that are not in
+// connectivity module.
+enable_frameworks_net_tests = false
+// Placeholder
+// This is a placeholder comment to minimize merge conflicts, as enable_frameworks_net_tests
+// may have different values depending on the branch
+// Placeholder
+
java_defaults {
name: "FrameworksNetTests-jni-defaults",
jni_libs: [
"ld-android",
- "libbacktrace",
"libbase",
"libbinder",
"libbpf_bcc",
@@ -49,6 +57,52 @@
],
}
+filegroup {
+ name: "non-connectivity-module-test",
+ srcs: [
+ "java/android/app/usage/*.java",
+ "java/android/net/Ikev2VpnProfileTest.java",
+ "java/android/net/IpMemoryStoreTest.java",
+ "java/android/net/IpSecAlgorithmTest.java",
+ "java/android/net/IpSecConfigTest.java",
+ "java/android/net/IpSecManagerTest.java",
+ "java/android/net/IpSecTransformTest.java",
+ "java/android/net/KeepalivePacketDataUtilTest.java",
+ "java/android/net/NetworkIdentityTest.kt",
+ "java/android/net/NetworkStats*.java",
+ "java/android/net/NetworkTemplateTest.kt",
+ "java/android/net/TelephonyNetworkSpecifierTest.java",
+ "java/android/net/VpnManagerTest.java",
+ "java/android/net/ipmemorystore/*.java",
+ "java/android/net/nsd/*.java",
+ "java/com/android/internal/net/NetworkUtilsInternalTest.java",
+ "java/com/android/internal/net/VpnProfileTest.java",
+ "java/com/android/server/IpSecServiceParameterizedTest.java",
+ "java/com/android/server/IpSecServiceRefcountedResourceTest.java",
+ "java/com/android/server/IpSecServiceTest.java",
+ "java/com/android/server/NetworkManagementServiceTest.java",
+ "java/com/android/server/NsdServiceTest.java",
+ "java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java",
+ "java/com/android/server/connectivity/IpConnectivityMetricsTest.java",
+ "java/com/android/server/connectivity/MultipathPolicyTrackerTest.java",
+ "java/com/android/server/connectivity/NetdEventListenerServiceTest.java",
+ "java/com/android/server/connectivity/VpnTest.java",
+ "java/com/android/server/net/ipmemorystore/*.java",
+ "java/com/android/server/net/NetworkStats*.java",
+ ]
+}
+
+// Subset of services-core used to by ConnectivityService tests to test VPN realistically.
+// This is stripped by jarjar (see rules below) from other unrelated classes, so tests do not
+// include most classes from services-core, which are unrelated and cause wrong code coverage
+// calculations.
+java_library {
+ name: "services.core-vpn",
+ static_libs: ["services.core"],
+ jarjar_rules: "vpn-jarjar-rules.txt",
+ visibility: ["//visibility:private"],
+}
+
android_library {
name: "FrameworksNetTestsLib",
min_sdk_version: "30",
@@ -59,9 +113,11 @@
"java/**/*.java",
"java/**/*.kt",
],
+ exclude_srcs: [":non-connectivity-module-test"],
jarjar_rules: "jarjar-rules.txt",
static_libs: [
"androidx.test.rules",
+ "androidx.test.uiautomator",
"bouncycastle-repackaged-unbundled",
"core-tests-support",
"FrameworksNetCommonTests",
@@ -70,11 +126,11 @@
"framework-protos",
"mockito-target-minus-junit4",
"net-tests-utils",
+ "net-utils-services-common",
"platform-compat-test-rules",
"platform-test-annotations",
"service-connectivity-pre-jarjar",
- "services.core",
- "services.net",
+ "services.core-vpn",
],
libs: [
"android.net.ipsec.ike.stubs.module_lib",
@@ -88,15 +144,24 @@
android_test {
name: "FrameworksNetTests",
+ enabled: enable_frameworks_net_tests,
min_sdk_version: "30",
defaults: [
"framework-connectivity-test-defaults",
"FrameworksNetTests-jni-defaults",
],
+ // this is in addition to FrameworksNetTestsLib.
+ srcs: [":non-connectivity-module-test"],
test_suites: ["device-tests"],
static_libs: [
+ "services.core",
+ "services.net",
"FrameworksNetTestsLib",
],
+ libs: [
+ "android.test.mock",
+ "android.test.base",
+ ],
jni_libs: [
"libservice-connectivity",
]
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index 4c60ccf..887f171 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -53,6 +53,8 @@
<application>
<uses-library android:name="android.test.runner" />
<uses-library android:name="android.net.ipsec.ike" />
+ <activity
+ android:name="com.android.server.connectivity.NetworkNotificationManagerTest$TestDialogActivity"/>
</application>
<instrumentation
diff --git a/tests/unit/java/android/net/ConnectivityManagerTest.java b/tests/unit/java/android/net/ConnectivityManagerTest.java
index e7873af..f324630 100644
--- a/tests/unit/java/android/net/ConnectivityManagerTest.java
+++ b/tests/unit/java/android/net/ConnectivityManagerTest.java
@@ -37,6 +37,8 @@
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
import static android.net.NetworkRequest.Type.TRACK_SYSTEM_DEFAULT;
+import static com.android.testutils.MiscAsserts.assertThrows;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -45,6 +47,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
@@ -83,6 +86,8 @@
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
public class ConnectivityManagerTest {
+ private static final int TIMEOUT_MS = 30_000;
+ private static final int SHORT_TIMEOUT_MS = 150;
@Mock Context mCtx;
@Mock IConnectivityManager mService;
@@ -231,7 +236,7 @@
// callback triggers
captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_AVAILABLE));
- verify(callback, timeout(500).times(1)).onAvailable(any(Network.class),
+ verify(callback, timeout(TIMEOUT_MS).times(1)).onAvailable(any(Network.class),
any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
@@ -240,7 +245,7 @@
// callback does not trigger anymore.
captor.getValue().send(makeMessage(request, ConnectivityManager.CALLBACK_LOSING));
- verify(callback, timeout(500).times(0)).onLosing(any(), anyInt());
+ verify(callback, after(SHORT_TIMEOUT_MS).never()).onLosing(any(), anyInt());
}
@Test
@@ -260,7 +265,7 @@
// callback triggers
captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_AVAILABLE));
- verify(callback, timeout(100).times(1)).onAvailable(any(Network.class),
+ verify(callback, timeout(TIMEOUT_MS).times(1)).onAvailable(any(Network.class),
any(NetworkCapabilities.class), any(LinkProperties.class), anyBoolean());
// unregister callback
@@ -269,7 +274,7 @@
// callback does not trigger anymore.
captor.getValue().send(makeMessage(req1, ConnectivityManager.CALLBACK_LOSING));
- verify(callback, timeout(100).times(0)).onLosing(any(), anyInt());
+ verify(callback, after(SHORT_TIMEOUT_MS).never()).onLosing(any(), anyInt());
// callback can be registered again
when(mService.requestNetwork(anyInt(), any(), anyInt(), captor.capture(), anyInt(), any(),
@@ -278,7 +283,7 @@
// callback triggers
captor.getValue().send(makeMessage(req2, ConnectivityManager.CALLBACK_LOST));
- verify(callback, timeout(100).times(1)).onLost(any());
+ verify(callback, timeout(TIMEOUT_MS).times(1)).onLost(any());
// unregister callback
manager.unregisterNetworkCallback(callback);
@@ -314,6 +319,21 @@
}
@Test
+ public void testDefaultNetworkActiveListener() throws Exception {
+ final ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
+ final ConnectivityManager.OnNetworkActiveListener listener =
+ mock(ConnectivityManager.OnNetworkActiveListener.class);
+ assertThrows(IllegalArgumentException.class,
+ () -> manager.removeDefaultNetworkActiveListener(listener));
+ manager.addDefaultNetworkActiveListener(listener);
+ verify(mService, times(1)).registerNetworkActivityListener(any());
+ manager.removeDefaultNetworkActiveListener(listener);
+ verify(mService, times(1)).unregisterNetworkActivityListener(any());
+ assertThrows(IllegalArgumentException.class,
+ () -> manager.removeDefaultNetworkActiveListener(listener));
+ }
+
+ @Test
public void testArgumentValidation() throws Exception {
ConnectivityManager manager = new ConnectivityManager(mCtx, mService);
diff --git a/tests/unit/java/android/net/IpSecAlgorithmTest.java b/tests/unit/java/android/net/IpSecAlgorithmTest.java
index c2a759b..c473e82 100644
--- a/tests/unit/java/android/net/IpSecAlgorithmTest.java
+++ b/tests/unit/java/android/net/IpSecAlgorithmTest.java
@@ -217,8 +217,11 @@
final Set<String> optionalAlgoSet = getOptionalAlgos();
final String[] optionalAlgos = optionalAlgoSet.toArray(new String[0]);
- doReturn(optionalAlgos).when(mMockResources)
- .getStringArray(com.android.internal.R.array.config_optionalIpSecAlgorithms);
+ // Query the identifier instead of using the R.array constant, as the test may be built
+ // separately from the platform and they may not match.
+ final int resId = Resources.getSystem().getIdentifier("config_optionalIpSecAlgorithms",
+ "array", "android");
+ doReturn(optionalAlgos).when(mMockResources).getStringArray(resId);
final Set<String> enabledAlgos = new HashSet<>(IpSecAlgorithm.loadAlgos(mMockResources));
final Set<String> expectedAlgos = ALGO_TO_REQUIRED_FIRST_SDK.keySet();
diff --git a/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java b/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
index 8498b6f..6afa4e9 100644
--- a/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
+++ b/tests/unit/java/android/net/KeepalivePacketDataUtilTest.java
@@ -27,6 +27,7 @@
import android.os.Build;
import android.util.Log;
+import com.android.server.connectivity.TcpKeepaliveController;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -81,7 +82,7 @@
testInfo.tos = tos;
testInfo.ttl = ttl;
try {
- resultData = KeepalivePacketDataUtil.fromStableParcelable(testInfo);
+ resultData = TcpKeepaliveController.fromStableParcelable(testInfo);
} catch (InvalidPacketException e) {
fail("InvalidPacketException: " + e);
}
@@ -155,7 +156,7 @@
testInfo.ttl = ttl;
TcpKeepalivePacketData testData = null;
TcpKeepalivePacketDataParcelable resultData = null;
- testData = KeepalivePacketDataUtil.fromStableParcelable(testInfo);
+ testData = TcpKeepaliveController.fromStableParcelable(testInfo);
resultData = KeepalivePacketDataUtil.toStableParcelable(testData);
assertArrayEquals(resultData.srcAddress, IPV4_KEEPALIVE_SRC_ADDR);
assertArrayEquals(resultData.dstAddress, IPV4_KEEPALIVE_DST_ADDR);
@@ -198,11 +199,11 @@
testParcel.ttl = ttl;
final KeepalivePacketData testData =
- KeepalivePacketDataUtil.fromStableParcelable(testParcel);
+ TcpKeepaliveController.fromStableParcelable(testParcel);
final TcpKeepalivePacketDataParcelable parsedParcelable =
KeepalivePacketDataUtil.parseTcpKeepalivePacketData(testData);
final TcpKeepalivePacketData roundTripData =
- KeepalivePacketDataUtil.fromStableParcelable(parsedParcelable);
+ TcpKeepaliveController.fromStableParcelable(parsedParcelable);
// Generated packet is the same, but rcvWnd / wndScale will differ if scale is non-zero
assertTrue(testData.getPacket().length > 0);
@@ -210,11 +211,11 @@
testParcel.rcvWndScale = 0;
final KeepalivePacketData noScaleTestData =
- KeepalivePacketDataUtil.fromStableParcelable(testParcel);
+ TcpKeepaliveController.fromStableParcelable(testParcel);
final TcpKeepalivePacketDataParcelable noScaleParsedParcelable =
KeepalivePacketDataUtil.parseTcpKeepalivePacketData(noScaleTestData);
final TcpKeepalivePacketData noScaleRoundTripData =
- KeepalivePacketDataUtil.fromStableParcelable(noScaleParsedParcelable);
+ TcpKeepaliveController.fromStableParcelable(noScaleParsedParcelable);
assertEquals(noScaleTestData, noScaleRoundTripData);
assertTrue(noScaleTestData.getPacket().length > 0);
assertArrayEquals(noScaleTestData.getPacket(), noScaleRoundTripData.getPacket());
diff --git a/tests/unit/java/android/net/NetworkIdentityTest.kt b/tests/unit/java/android/net/NetworkIdentityTest.kt
index f963593..b1ffc92 100644
--- a/tests/unit/java/android/net/NetworkIdentityTest.kt
+++ b/tests/unit/java/android/net/NetworkIdentityTest.kt
@@ -16,20 +16,38 @@
package android.net
+import android.content.Context
+import android.net.ConnectivityManager.TYPE_MOBILE
import android.net.NetworkIdentity.OEM_NONE
import android.net.NetworkIdentity.OEM_PAID
import android.net.NetworkIdentity.OEM_PRIVATE
import android.net.NetworkIdentity.getOemBitfield
+import android.telephony.TelephonyManager
import android.os.Build
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+private const val TEST_IMSI = "testimsi"
@RunWith(DevSdkIgnoreRunner::class)
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
class NetworkIdentityTest {
+ private val mockContext = mock(Context::class.java)
+
+ private fun buildMobileNetworkStateSnapshot(
+ caps: NetworkCapabilities,
+ subscriberId: String
+ ): NetworkStateSnapshot {
+ return NetworkStateSnapshot(mock(Network::class.java), caps,
+ LinkProperties(), subscriberId, TYPE_MOBILE)
+ }
+
@Test
fun testGetOemBitfield() {
val oemNone = NetworkCapabilities().apply {
@@ -54,4 +72,32 @@
assertEquals(getOemBitfield(oemPrivate), OEM_PRIVATE)
assertEquals(getOemBitfield(oemAll), OEM_PAID or OEM_PRIVATE)
}
+
+ @Test
+ fun testGetMetered() {
+ // Verify network is metered.
+ val netIdent1 = NetworkIdentity.buildNetworkIdentity(mockContext,
+ buildMobileNetworkStateSnapshot(NetworkCapabilities(), TEST_IMSI),
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ assertTrue(netIdent1.getMetered())
+
+ // Verify network is not metered because it has NET_CAPABILITY_NOT_METERED capability.
+ val capsNotMetered = NetworkCapabilities.Builder().apply {
+ addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
+ }.build()
+ val netIdent2 = NetworkIdentity.buildNetworkIdentity(mockContext,
+ buildMobileNetworkStateSnapshot(capsNotMetered, TEST_IMSI),
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ assertFalse(netIdent2.getMetered())
+
+ // Verify network is not metered because it has NET_CAPABILITY_TEMPORARILY_NOT_METERED
+ // capability .
+ val capsTempNotMetered = NetworkCapabilities().apply {
+ setCapability(NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED, true)
+ }
+ val netIdent3 = NetworkIdentity.buildNetworkIdentity(mockContext,
+ buildMobileNetworkStateSnapshot(capsTempNotMetered, TEST_IMSI),
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ assertFalse(netIdent3.getMetered())
+ }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java b/tests/unit/java/android/net/NetworkStatsAccessTest.java
similarity index 97%
rename from tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
rename to tests/unit/java/android/net/NetworkStatsAccessTest.java
index 03d9404..0f9ed41 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsAccessTest.java
+++ b/tests/unit/java/android/net/NetworkStatsAccessTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 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.
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.server.net;
+package android.net;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;
@@ -43,7 +43,7 @@
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
public class NetworkStatsAccessTest {
private static final String TEST_PKG = "com.example.test";
private static final int TEST_UID = 12345;
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
similarity index 95%
rename from tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
rename to tests/unit/java/android/net/NetworkStatsCollectionTest.java
index e771558..0c4ffac 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.net;
+package android.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.NetworkIdentity.OEM_NONE;
@@ -37,11 +37,6 @@
import static org.junit.Assert.fail;
import android.content.res.Resources;
-import android.net.ConnectivityManager;
-import android.net.NetworkIdentity;
-import android.net.NetworkStats;
-import android.net.NetworkStatsHistory;
-import android.net.NetworkTemplate;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
@@ -83,7 +78,7 @@
*/
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
public class NetworkStatsCollectionTest {
private static final String TEST_FILE = "test.bin";
@@ -98,14 +93,11 @@
@Before
public void setUp() throws Exception {
sOriginalClock = RecurrenceRule.sClock;
- // ignore any device overlay while testing
- NetworkTemplate.forceAllNetworkTypes();
}
@After
public void tearDown() throws Exception {
RecurrenceRule.sClock = sOriginalClock;
- NetworkTemplate.resetForceAllNetworkTypes();
}
private void setClock(Instant instant) {
@@ -123,7 +115,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
+ 636014522L, 709291L, 88037144L, 518820L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -137,7 +129,7 @@
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
+ 636014522L, 709291L, 88037144L, 518820L, NetworkStatsAccess.Level.DEVICE);
}
@Test
@@ -151,7 +143,7 @@
// verify that history read correctly
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
+ 637073904L, 711398L, 88342093L, 521006L, NetworkStatsAccess.Level.DEVICE);
// now export into a unified format
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
@@ -165,7 +157,7 @@
// and read back into structure, verifying that totals are same
collection.read(new ByteArrayInputStream(bos.toByteArray()));
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
- 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
+ 637073904L, 711398L, 88342093L, 521006L, NetworkStatsAccess.Level.DEVICE);
}
@Test
@@ -253,8 +245,8 @@
collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE));
// Verify security check in getHistory.
- assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, myUid, SET_DEFAULT,
- TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid));
+ assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null,
+ myUid, SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid));
try {
collection.getHistory(buildTemplateMobileAll(TEST_IMSI), null, otherUidInSameUser,
SET_DEFAULT, TAG_NONE, 0, 0L, 0L, NetworkStatsAccess.Level.DEFAULT, myUid);
@@ -278,7 +270,8 @@
new File(InstrumentationRegistry.getContext().getFilesDir(), TEST_FILE);
stageFile(R.raw.netstats_v1, testFile);
- final NetworkStatsCollection emptyCollection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
+ final NetworkStatsCollection emptyCollection =
+ new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
collection.readLegacyNetwork(testFile);
@@ -316,7 +309,8 @@
assertEquals(0L, history.getTotalBytes());
// Normal collection should be untouched
- history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+ history = getHistory(collection, plan, TIME_A, TIME_C);
+ i = 0;
assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
@@ -345,7 +339,8 @@
// Slice from middle should be untouched
history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
- TIME_B + HOUR_IN_MILLIS); i = 0;
+ TIME_B + HOUR_IN_MILLIS);
+ i = 0;
assertEntry(3821, 23, 4525, 26, history.getValues(i++, null));
assertEntry(3820, 21, 4524, 26, history.getValues(i++, null));
assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
@@ -368,7 +363,8 @@
assertEquals(200000L, history.getTotalBytes());
// Normal collection should be augmented
- history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+ history = getHistory(collection, plan, TIME_A, TIME_C);
+ i = 0;
assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
@@ -400,7 +396,8 @@
// Slice from middle should be augmented
history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
- TIME_B + HOUR_IN_MILLIS); i = 0;
+ TIME_B + HOUR_IN_MILLIS);
+ i = 0;
assertEntry(2669, 0, 3161, 0, history.getValues(i++, null));
assertEntry(2668, 0, 3160, 0, history.getValues(i++, null));
assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
@@ -423,7 +420,8 @@
assertEquals(400000L, history.getTotalBytes());
// Normal collection should be augmented
- history = getHistory(collection, plan, TIME_A, TIME_C); i = 0;
+ history = getHistory(collection, plan, TIME_A, TIME_C);
+ i = 0;
assertEntry(100647, 197, 23649, 185, history.getValues(i++, null));
assertEntry(100647, 196, 23648, 185, history.getValues(i++, null));
assertEntry(18323, 76, 15032, 76, history.getValues(i++, null));
@@ -454,7 +452,8 @@
// Slice from middle should be augmented
history = getHistory(collection, plan, TIME_B - HOUR_IN_MILLIS,
- TIME_B + HOUR_IN_MILLIS); i = 0;
+ TIME_B + HOUR_IN_MILLIS);
+ i = 0;
assertEntry(5338, 0, 6322, 0, history.getValues(i++, null));
assertEntry(5337, 0, 6320, 0, history.getValues(i++, null));
assertEntry(91686, 159, 18576, 146, history.getValues(i++, null));
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index 49c7271..572c1ef 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -26,6 +26,8 @@
import android.net.NetworkIdentity.buildNetworkIdentity
import android.net.NetworkStats.DEFAULT_NETWORK_ALL
import android.net.NetworkStats.METERED_ALL
+import android.net.NetworkStats.METERED_NO
+import android.net.NetworkStats.METERED_YES
import android.net.NetworkStats.ROAMING_ALL
import android.net.NetworkTemplate.MATCH_MOBILE
import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD
@@ -39,9 +41,12 @@
import android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT
import android.net.NetworkTemplate.WIFI_NETWORKID_ALL
import android.net.NetworkTemplate.buildTemplateCarrierMetered
+import android.net.NetworkTemplate.buildTemplateMobileAll
+import android.net.NetworkTemplate.buildTemplateMobileWildcard
import android.net.NetworkTemplate.buildTemplateMobileWithRatType
import android.net.NetworkTemplate.buildTemplateWifi
import android.net.NetworkTemplate.buildTemplateWifiWildcard
+import android.net.NetworkTemplate.normalize
import android.os.Build
import android.telephony.TelephonyManager
import com.android.testutils.DevSdkIgnoreRule
@@ -59,6 +64,7 @@
private const val TEST_IMSI1 = "imsi1"
private const val TEST_IMSI2 = "imsi2"
+private const val TEST_IMSI3 = "imsi3"
private const val TEST_SSID1 = "ssid1"
private const val TEST_SSID2 = "ssid2"
@@ -171,6 +177,57 @@
}
@Test
+ fun testMobileMatches() {
+ val templateMobileImsi1 = buildTemplateMobileAll(TEST_IMSI1)
+ val templateMobileImsi2WithRatType = buildTemplateMobileWithRatType(TEST_IMSI2,
+ TelephonyManager.NETWORK_TYPE_UMTS, METERED_YES)
+
+ val mobileImsi1 = buildNetworkState(TYPE_MOBILE, TEST_IMSI1, null /* ssid */,
+ OEM_NONE, true /* metered */)
+ val identMobile1 = buildNetworkIdentity(mockContext, mobileImsi1,
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ val mobileImsi2 = buildMobileNetworkState(TEST_IMSI2)
+ val identMobile2Umts = buildNetworkIdentity(mockContext, mobileImsi2,
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+
+ val identWifiImsi1Ssid1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0)
+
+ // Verify that the template matches type and the subscriberId.
+ templateMobileImsi1.assertMatches(identMobile1)
+ templateMobileImsi2WithRatType.assertMatches(identMobile2Umts)
+
+ // Verify that the template does not match the different subscriberId.
+ templateMobileImsi1.assertDoesNotMatch(identMobile2Umts)
+ templateMobileImsi2WithRatType.assertDoesNotMatch(identMobile1)
+
+ // Verify that the different type does not match.
+ templateMobileImsi1.assertDoesNotMatch(identWifiImsi1Ssid1)
+ }
+
+ @Test
+ fun testMobileWildcardMatches() {
+ val templateMobileWildcard = buildTemplateMobileWildcard()
+ val templateMobileNullImsiWithRatType = buildTemplateMobileWithRatType(null,
+ TelephonyManager.NETWORK_TYPE_UMTS, METERED_ALL)
+
+ val mobileImsi1 = buildMobileNetworkState(TEST_IMSI1)
+ val identMobile1 = buildNetworkIdentity(mockContext, mobileImsi1,
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+
+ // Verify that the template matches any subscriberId.
+ templateMobileWildcard.assertMatches(identMobile1)
+ templateMobileNullImsiWithRatType.assertMatches(identMobile1)
+
+ val identWifiImsi1Ssid1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0)
+
+ // Verify that the different type does not match.
+ templateMobileWildcard.assertDoesNotMatch(identWifiImsi1Ssid1)
+ templateMobileNullImsiWithRatType.assertDoesNotMatch(identWifiImsi1Ssid1)
+ }
+
+ @Test
fun testCarrierMeteredMatches() {
val templateCarrierImsi1Metered = buildTemplateCarrierMetered(TEST_IMSI1)
@@ -205,58 +262,131 @@
templateCarrierImsi1Metered.assertDoesNotMatch(identCarrierWifiImsi1NonMetered)
}
+ // TODO: Refactor this test to reduce the line of codes.
@Test
fun testRatTypeGroupMatches() {
- val stateMobile = buildMobileNetworkState(TEST_IMSI1)
+ val stateMobileImsi1Metered = buildMobileNetworkState(TEST_IMSI1)
+ val stateMobileImsi1NonMetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI1,
+ null /* ssid */, OEM_NONE, false /* metered */)
+ val stateMobileImsi2NonMetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI2,
+ null /* ssid */, OEM_NONE, false /* metered */)
+
// Build UMTS template that matches mobile identities with RAT in the same
// group with any IMSI. See {@link NetworkTemplate#getCollapsedRatType}.
- val templateUmts = buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS)
+ val templateUmtsMetered = buildTemplateMobileWithRatType(null,
+ TelephonyManager.NETWORK_TYPE_UMTS, METERED_YES)
// Build normal template that matches mobile identities with any RAT and IMSI.
- val templateAll = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL)
+ val templateAllMetered = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL,
+ METERED_YES)
// Build template with UNKNOWN RAT that matches mobile identities with RAT that
// cannot be determined.
- val templateUnknown =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN)
+ val templateUnknownMetered =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ METERED_YES)
- val identUmts = buildNetworkIdentity(
- mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_UMTS)
- val identHsdpa = buildNetworkIdentity(
- mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_HSDPA)
- val identLte = buildNetworkIdentity(
- mockContext, stateMobile, false, TelephonyManager.NETWORK_TYPE_LTE)
- val identCombined = buildNetworkIdentity(
- mockContext, stateMobile, false, SUBTYPE_COMBINED)
- val identImsi2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2),
- false, TelephonyManager.NETWORK_TYPE_UMTS)
+ val templateUmtsNonMetered = buildTemplateMobileWithRatType(null,
+ TelephonyManager.NETWORK_TYPE_UMTS, METERED_NO)
+ val templateAllNonMetered = buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL,
+ METERED_NO)
+ val templateUnknownNonMetered =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ METERED_NO)
+
+ val identUmtsMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1Metered, false, TelephonyManager.NETWORK_TYPE_UMTS)
+ val identHsdpaMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1Metered, false, TelephonyManager.NETWORK_TYPE_HSDPA)
+ val identLteMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1Metered, false, TelephonyManager.NETWORK_TYPE_LTE)
+ val identCombinedMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1Metered, false, SUBTYPE_COMBINED)
+ val identImsi2UmtsMetered = buildNetworkIdentity(mockContext,
+ buildMobileNetworkState(TEST_IMSI2), false, TelephonyManager.NETWORK_TYPE_UMTS)
val identWifi = buildNetworkIdentity(
mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0)
- // Assert that identity with the same RAT matches.
- templateUmts.assertMatches(identUmts)
- templateAll.assertMatches(identUmts)
- templateUnknown.assertDoesNotMatch(identUmts)
+ val identUmtsNonMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1NonMetered, false, TelephonyManager.NETWORK_TYPE_UMTS)
+ val identHsdpaNonMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1NonMetered, false,
+ TelephonyManager.NETWORK_TYPE_HSDPA)
+ val identLteNonMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1NonMetered, false, TelephonyManager.NETWORK_TYPE_LTE)
+ val identCombinedNonMetered = buildNetworkIdentity(
+ mockContext, stateMobileImsi1NonMetered, false, SUBTYPE_COMBINED)
+ val identImsi2UmtsNonMetered = buildNetworkIdentity(mockContext,
+ stateMobileImsi2NonMetered, false, TelephonyManager.NETWORK_TYPE_UMTS)
+
+ // Assert that identity with the same RAT and meteredness matches.
+ // Verify metered template.
+ templateUmtsMetered.assertMatches(identUmtsMetered)
+ templateAllMetered.assertMatches(identUmtsMetered)
+ templateUnknownMetered.assertDoesNotMatch(identUmtsMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertMatches(identUmtsNonMetered)
+ templateAllNonMetered.assertMatches(identUmtsNonMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identUmtsNonMetered)
+
+ // Assert that identity with the same RAT but meteredness is different.
+ // Thus, it does not match.
+ templateUmtsNonMetered.assertDoesNotMatch(identUmtsMetered)
+ templateAllNonMetered.assertDoesNotMatch(identUmtsMetered)
+
// Assert that identity with the RAT within the same group matches.
- templateUmts.assertMatches(identHsdpa)
- templateAll.assertMatches(identHsdpa)
- templateUnknown.assertDoesNotMatch(identHsdpa)
+ // Verify metered template.
+ templateUmtsMetered.assertMatches(identHsdpaMetered)
+ templateAllMetered.assertMatches(identHsdpaMetered)
+ templateUnknownMetered.assertDoesNotMatch(identHsdpaMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertMatches(identHsdpaNonMetered)
+ templateAllNonMetered.assertMatches(identHsdpaNonMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identHsdpaNonMetered)
+
// Assert that identity with the RAT out of the same group only matches template with
// NETWORK_TYPE_ALL.
- templateUmts.assertDoesNotMatch(identLte)
- templateAll.assertMatches(identLte)
- templateUnknown.assertDoesNotMatch(identLte)
+ // Verify metered template.
+ templateUmtsMetered.assertDoesNotMatch(identLteMetered)
+ templateAllMetered.assertMatches(identLteMetered)
+ templateUnknownMetered.assertDoesNotMatch(identLteMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertDoesNotMatch(identLteNonMetered)
+ templateAllNonMetered.assertMatches(identLteNonMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identLteNonMetered)
+ // Verify non-metered template does not match identity with metered.
+ templateAllNonMetered.assertDoesNotMatch(identLteMetered)
+
// Assert that identity with combined RAT only matches with template with NETWORK_TYPE_ALL
// and NETWORK_TYPE_UNKNOWN.
- templateUmts.assertDoesNotMatch(identCombined)
- templateAll.assertMatches(identCombined)
- templateUnknown.assertMatches(identCombined)
+ // Verify metered template.
+ templateUmtsMetered.assertDoesNotMatch(identCombinedMetered)
+ templateAllMetered.assertMatches(identCombinedMetered)
+ templateUnknownMetered.assertMatches(identCombinedMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertDoesNotMatch(identCombinedNonMetered)
+ templateAllNonMetered.assertMatches(identCombinedNonMetered)
+ templateUnknownNonMetered.assertMatches(identCombinedNonMetered)
+ // Verify that identity with metered does not match non-metered template.
+ templateAllNonMetered.assertDoesNotMatch(identCombinedMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identCombinedMetered)
+
// Assert that identity with different IMSI matches.
- templateUmts.assertMatches(identImsi2)
- templateAll.assertMatches(identImsi2)
- templateUnknown.assertDoesNotMatch(identImsi2)
+ // Verify metered template.
+ templateUmtsMetered.assertMatches(identImsi2UmtsMetered)
+ templateAllMetered.assertMatches(identImsi2UmtsMetered)
+ templateUnknownMetered.assertDoesNotMatch(identImsi2UmtsMetered)
+ // Verify non-metered template.
+ templateUmtsNonMetered.assertMatches(identImsi2UmtsNonMetered)
+ templateAllNonMetered.assertMatches(identImsi2UmtsNonMetered)
+ templateUnknownNonMetered.assertDoesNotMatch(identImsi2UmtsNonMetered)
+ // Verify that the same RAT but different meteredness should not match.
+ templateUmtsNonMetered.assertDoesNotMatch(identImsi2UmtsMetered)
+ templateAllNonMetered.assertDoesNotMatch(identImsi2UmtsMetered)
+
// Assert that wifi identity does not match.
- templateUmts.assertDoesNotMatch(identWifi)
- templateAll.assertDoesNotMatch(identWifi)
- templateUnknown.assertDoesNotMatch(identWifi)
+ templateUmtsMetered.assertDoesNotMatch(identWifi)
+ templateUnknownMetered.assertDoesNotMatch(identWifi)
+ templateUmtsNonMetered.assertDoesNotMatch(identWifi)
+ templateUnknownNonMetered.assertDoesNotMatch(identWifi)
}
@Test
@@ -365,4 +495,45 @@
identSsid = TEST_SSID1)
matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI_WILDCARD, identSsid = TEST_SSID1)
}
+
+ @Test
+ fun testNormalize() {
+ var mergedImsiList = listOf(arrayOf(TEST_IMSI1, TEST_IMSI2))
+ val identMobileImsi1 = buildNetworkIdentity(mockContext,
+ buildMobileNetworkState(TEST_IMSI1), false /* defaultNetwork */,
+ TelephonyManager.NETWORK_TYPE_UMTS)
+ val identMobileImsi2 = buildNetworkIdentity(mockContext,
+ buildMobileNetworkState(TEST_IMSI2), false /* defaultNetwork */,
+ TelephonyManager.NETWORK_TYPE_UMTS)
+ val identMobileImsi3 = buildNetworkIdentity(mockContext,
+ buildMobileNetworkState(TEST_IMSI3), false /* defaultNetwork */,
+ TelephonyManager.NETWORK_TYPE_UMTS)
+ val identWifiImsi1Ssid1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0)
+ val identWifiImsi2Ssid1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_SSID1), true, 0)
+ val identWifiImsi3Ssid1 = buildNetworkIdentity(
+ mockContext, buildWifiNetworkState(TEST_IMSI3, TEST_SSID1), true, 0)
+
+ normalize(buildTemplateMobileAll(TEST_IMSI1), mergedImsiList).also {
+ it.assertMatches(identMobileImsi1)
+ it.assertMatches(identMobileImsi2)
+ it.assertDoesNotMatch(identMobileImsi3)
+ }
+ normalize(buildTemplateCarrierMetered(TEST_IMSI1), mergedImsiList).also {
+ it.assertMatches(identMobileImsi1)
+ it.assertMatches(identMobileImsi2)
+ it.assertDoesNotMatch(identMobileImsi3)
+ }
+ normalize(buildTemplateWifi(TEST_SSID1, TEST_IMSI1), mergedImsiList).also {
+ it.assertMatches(identWifiImsi1Ssid1)
+ it.assertMatches(identWifiImsi2Ssid1)
+ it.assertDoesNotMatch(identWifiImsi3Ssid1)
+ }
+ normalize(buildTemplateMobileWildcard(), mergedImsiList).also {
+ it.assertMatches(identMobileImsi1)
+ it.assertMatches(identMobileImsi2)
+ it.assertMatches(identMobileImsi3)
+ }
+ }
}
diff --git a/tests/unit/java/android/net/nsd/NsdManagerTest.java b/tests/unit/java/android/net/nsd/NsdManagerTest.java
index de77d23..30b8fcd 100644
--- a/tests/unit/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/unit/java/android/net/nsd/NsdManagerTest.java
@@ -20,38 +20,32 @@
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.Context;
import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import androidx.test.filters.SmallTest;
-import com.android.internal.util.AsyncChannel;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
-import com.android.testutils.HandlerUtils;
+import com.android.testutils.ExceptionUtils;
-import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -67,9 +61,10 @@
@Mock Context mContext;
@Mock INsdManager mService;
- MockServiceHandler mServiceHandler;
+ @Mock INsdServiceConnector mServiceConn;
NsdManager mManager;
+ INsdManagerCallback mCallback;
long mTimeoutMs = 200; // non-final so that tests can adjust the value.
@@ -77,91 +72,85 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mServiceHandler = spy(MockServiceHandler.create(mContext));
- doReturn(new Messenger(mServiceHandler)).when(mService).getMessenger();
- }
-
- @After
- public void tearDown() throws Exception {
- HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs);
- mServiceHandler.chan.disconnect();
- mServiceHandler.stop();
- if (mManager != null) {
- mManager.disconnect();
- }
+ doReturn(mServiceConn).when(mService).connect(any());
+ mManager = new NsdManager(mContext, mService);
+ final ArgumentCaptor<INsdManagerCallback> cbCaptor = ArgumentCaptor.forClass(
+ INsdManagerCallback.class);
+ verify(mService).connect(cbCaptor.capture());
+ mCallback = cbCaptor.getValue();
}
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testResolveServiceS() {
- mManager = makeNsdManagerS();
+ public void testResolveServiceS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
doTestResolveService();
}
@Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testResolveServicePreS() {
- mManager = makeNsdManagerPreS();
+ public void testResolveServicePreS() throws Exception {
+ verify(mServiceConn).startDaemon();
doTestResolveService();
}
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testDiscoverServiceS() {
- mManager = makeNsdManagerS();
+ public void testDiscoverServiceS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
doTestDiscoverService();
}
@Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testDiscoverServicePreS() {
- mManager = makeNsdManagerPreS();
+ public void testDiscoverServicePreS() throws Exception {
+ verify(mServiceConn).startDaemon();
doTestDiscoverService();
}
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testParallelResolveServiceS() {
- mManager = makeNsdManagerS();
+ public void testParallelResolveServiceS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
doTestParallelResolveService();
}
@Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testParallelResolveServicePreS() {
- mManager = makeNsdManagerPreS();
+ public void testParallelResolveServicePreS() throws Exception {
+ verify(mServiceConn).startDaemon();
doTestParallelResolveService();
}
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testInvalidCallsS() {
- mManager = makeNsdManagerS();
+ public void testInvalidCallsS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
doTestInvalidCalls();
}
@Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testInvalidCallsPreS() {
- mManager = makeNsdManagerPreS();
+ public void testInvalidCallsPreS() throws Exception {
+ verify(mServiceConn).startDaemon();
doTestInvalidCalls();
}
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testRegisterServiceS() {
- mManager = makeNsdManagerS();
+ public void testRegisterServiceS() throws Exception {
+ verify(mServiceConn, never()).startDaemon();
doTestRegisterService();
}
@Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testRegisterServicePreS() {
- mManager = makeNsdManagerPreS();
+ public void testRegisterServicePreS() throws Exception {
+ verify(mServiceConn).startDaemon();
doTestRegisterService();
}
- public void doTestResolveService() {
+ private void doTestResolveService() throws Exception {
NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -169,18 +158,19 @@
NsdManager.ResolveListener listener = mock(NsdManager.ResolveListener.class);
manager.resolveService(request, listener);
- int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE);
+ int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
int err = 33;
- sendResponse(NsdManager.RESOLVE_SERVICE_FAILED, err, key1, null);
+ mCallback.onResolveServiceFailed(key1, err);
verify(listener, timeout(mTimeoutMs).times(1)).onResolveFailed(request, err);
manager.resolveService(request, listener);
- int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE);
- sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply);
+ int key2 = getRequestKey(req ->
+ verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
+ mCallback.onResolveServiceSucceeded(key2, reply);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
}
- public void doTestParallelResolveService() {
+ private void doTestParallelResolveService() throws Exception {
NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
@@ -190,19 +180,20 @@
NsdManager.ResolveListener listener2 = mock(NsdManager.ResolveListener.class);
manager.resolveService(request, listener1);
- int key1 = verifyRequest(NsdManager.RESOLVE_SERVICE);
+ int key1 = getRequestKey(req -> verify(mServiceConn).resolveService(req.capture(), any()));
manager.resolveService(request, listener2);
- int key2 = verifyRequest(NsdManager.RESOLVE_SERVICE);
+ int key2 = getRequestKey(req ->
+ verify(mServiceConn, times(2)).resolveService(req.capture(), any()));
- sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key2, reply);
- sendResponse(NsdManager.RESOLVE_SERVICE_SUCCEEDED, 0, key1, reply);
+ mCallback.onResolveServiceSucceeded(key2, reply);
+ mCallback.onResolveServiceSucceeded(key1, reply);
verify(listener1, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
verify(listener2, timeout(mTimeoutMs).times(1)).onServiceResolved(reply);
}
- public void doTestRegisterService() {
+ private void doTestRegisterService() throws Exception {
NsdManager manager = mManager;
NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type");
@@ -214,40 +205,43 @@
// Register two services
manager.registerService(request1, PROTOCOL, listener1);
- int key1 = verifyRequest(NsdManager.REGISTER_SERVICE);
+ int key1 = getRequestKey(req -> verify(mServiceConn).registerService(req.capture(), any()));
manager.registerService(request2, PROTOCOL, listener2);
- int key2 = verifyRequest(NsdManager.REGISTER_SERVICE);
+ int key2 = getRequestKey(req ->
+ verify(mServiceConn, times(2)).registerService(req.capture(), any()));
// First reques fails, second request succeeds
- sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key2, request2);
+ mCallback.onRegisterServiceSucceeded(key2, request2);
verify(listener2, timeout(mTimeoutMs).times(1)).onServiceRegistered(request2);
int err = 1;
- sendResponse(NsdManager.REGISTER_SERVICE_FAILED, err, key1, request1);
+ mCallback.onRegisterServiceFailed(key1, err);
verify(listener1, timeout(mTimeoutMs).times(1)).onRegistrationFailed(request1, err);
// Client retries first request, it succeeds
manager.registerService(request1, PROTOCOL, listener1);
- int key3 = verifyRequest(NsdManager.REGISTER_SERVICE);
+ int key3 = getRequestKey(req ->
+ verify(mServiceConn, times(3)).registerService(req.capture(), any()));
- sendResponse(NsdManager.REGISTER_SERVICE_SUCCEEDED, 0, key3, request1);
+ mCallback.onRegisterServiceSucceeded(key3, request1);
verify(listener1, timeout(mTimeoutMs).times(1)).onServiceRegistered(request1);
// First request is unregistered, it succeeds
manager.unregisterService(listener1);
- int key3again = verifyRequest(NsdManager.UNREGISTER_SERVICE);
+ int key3again = getRequestKey(req -> verify(mServiceConn).unregisterService(req.capture()));
assertEquals(key3, key3again);
- sendResponse(NsdManager.UNREGISTER_SERVICE_SUCCEEDED, 0, key3again, null);
+ mCallback.onUnregisterServiceSucceeded(key3again);
verify(listener1, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request1);
// Second request is unregistered, it fails
manager.unregisterService(listener2);
- int key2again = verifyRequest(NsdManager.UNREGISTER_SERVICE);
+ int key2again = getRequestKey(req ->
+ verify(mServiceConn, times(2)).unregisterService(req.capture()));
assertEquals(key2, key2again);
- sendResponse(NsdManager.UNREGISTER_SERVICE_FAILED, err, key2again, null);
+ mCallback.onUnregisterServiceFailed(key2again, err);
verify(listener2, timeout(mTimeoutMs).times(1)).onUnregistrationFailed(request2, err);
// TODO: do not unregister listener until service is unregistered
@@ -260,7 +254,7 @@
//verify(listener2, timeout(mTimeoutMs).times(1)).onServiceUnregistered(request2);
}
- public void doTestDiscoverService() {
+ private void doTestDiscoverService() throws Exception {
NsdManager manager = mManager;
NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type");
@@ -271,69 +265,73 @@
// Client registers for discovery, request fails
manager.discoverServices("a_type", PROTOCOL, listener);
- int key1 = verifyRequest(NsdManager.DISCOVER_SERVICES);
+ int key1 = getRequestKey(req ->
+ verify(mServiceConn).discoverServices(req.capture(), any()));
int err = 1;
- sendResponse(NsdManager.DISCOVER_SERVICES_FAILED, err, key1, null);
+ mCallback.onDiscoverServicesFailed(key1, err);
verify(listener, timeout(mTimeoutMs).times(1)).onStartDiscoveryFailed("a_type", err);
// Client retries, request succeeds
manager.discoverServices("a_type", PROTOCOL, listener);
- int key2 = verifyRequest(NsdManager.DISCOVER_SERVICES);
+ int key2 = getRequestKey(req ->
+ verify(mServiceConn, times(2)).discoverServices(req.capture(), any()));
- sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key2, reply1);
+ mCallback.onDiscoverServicesStarted(key2, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
// mdns notifies about services
- sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply1);
+ mCallback.onServiceFound(key2, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply1);
- sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply2);
+ mCallback.onServiceFound(key2, reply2);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceFound(reply2);
- sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply2);
+ mCallback.onServiceLost(key2, reply2);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply2);
// Client unregisters its listener
manager.stopServiceDiscovery(listener);
- int key2again = verifyRequest(NsdManager.STOP_DISCOVERY);
+ int key2again = getRequestKey(req -> verify(mServiceConn).stopDiscovery(req.capture()));
assertEquals(key2, key2again);
// TODO: unregister listener immediately and stop notifying it about services
// Notifications are still passed to the client's listener
- sendResponse(NsdManager.SERVICE_LOST, 0, key2, reply1);
+ mCallback.onServiceLost(key2, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onServiceLost(reply1);
// Client is notified of complete unregistration
- sendResponse(NsdManager.STOP_DISCOVERY_SUCCEEDED, 0, key2again, "a_type");
+ mCallback.onStopDiscoverySucceeded(key2again);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStopped("a_type");
// Notifications are not passed to the client anymore
- sendResponse(NsdManager.SERVICE_FOUND, 0, key2, reply3);
+ mCallback.onServiceFound(key2, reply3);
verify(listener, timeout(mTimeoutMs).times(0)).onServiceLost(reply3);
// Client registers for service discovery
reset(listener);
manager.discoverServices("a_type", PROTOCOL, listener);
- int key3 = verifyRequest(NsdManager.DISCOVER_SERVICES);
+ int key3 = getRequestKey(req ->
+ verify(mServiceConn, times(3)).discoverServices(req.capture(), any()));
- sendResponse(NsdManager.DISCOVER_SERVICES_STARTED, 0, key3, reply1);
+ mCallback.onDiscoverServicesStarted(key3, reply1);
verify(listener, timeout(mTimeoutMs).times(1)).onDiscoveryStarted("a_type");
// Client unregisters immediately, it fails
manager.stopServiceDiscovery(listener);
- int key3again = verifyRequest(NsdManager.STOP_DISCOVERY);
+ int key3again = getRequestKey(req ->
+ verify(mServiceConn, times(2)).stopDiscovery(req.capture()));
assertEquals(key3, key3again);
err = 2;
- sendResponse(NsdManager.STOP_DISCOVERY_FAILED, err, key3again, "a_type");
+ mCallback.onStopDiscoveryFailed(key3again, err);
verify(listener, timeout(mTimeoutMs).times(1)).onStopDiscoveryFailed("a_type", err);
// New notifications are not passed to the client anymore
- sendResponse(NsdManager.SERVICE_FOUND, 0, key3, reply1);
+ mCallback.onServiceFound(key3, reply1);
verify(listener, timeout(mTimeoutMs).times(0)).onServiceFound(reply1);
}
@@ -398,77 +396,10 @@
}
}
- NsdManager makeNsdManagerS() {
- // Expect we'll get 2 AsyncChannel related msgs.
- return makeManager(2);
- }
-
- NsdManager makeNsdManagerPreS() {
- // Expect we'll get 3 msgs. 2 AsyncChannel related msgs + 1 additional daemon startup msg.
- return makeManager(3);
- }
-
- NsdManager makeManager(int expectedMsgCount) {
- NsdManager manager = new NsdManager(mContext, mService);
- // Acknowledge first two messages connecting the AsyncChannel.
- verify(mServiceHandler, timeout(mTimeoutMs).times(expectedMsgCount)).handleMessage(any());
-
- reset(mServiceHandler);
- assertNotNull(mServiceHandler.chan);
- return manager;
- }
-
- int verifyRequest(int expectedMessageType) {
- HandlerUtils.waitForIdle(mServiceHandler, mTimeoutMs);
- verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any());
- reset(mServiceHandler);
- Message received = mServiceHandler.getLastMessage();
- assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what));
- return received.arg2;
- }
-
- void sendResponse(int replyType, int arg, int key, Object obj) {
- mServiceHandler.chan.sendMessage(replyType, arg, key, obj);
- }
-
- // Implements the server side of AsyncChannel connection protocol
- public static class MockServiceHandler extends Handler {
- public final Context context;
- public AsyncChannel chan;
- public Message lastMessage;
-
- MockServiceHandler(Looper l, Context c) {
- super(l);
- context = c;
- }
-
- synchronized Message getLastMessage() {
- return lastMessage;
- }
-
- synchronized void setLastMessage(Message msg) {
- lastMessage = obtainMessage();
- lastMessage.copyFrom(msg);
- }
-
- @Override
- public void handleMessage(Message msg) {
- setLastMessage(msg);
- if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) {
- chan = new AsyncChannel();
- chan.connect(context, this, msg.replyTo);
- chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
- }
- }
-
- void stop() {
- getLooper().quitSafely();
- }
-
- static MockServiceHandler create(Context context) {
- HandlerThread t = new HandlerThread("mock-service-handler");
- t.start();
- return new MockServiceHandler(t.getLooper(), context);
- }
+ int getRequestKey(ExceptionUtils.ThrowingConsumer<ArgumentCaptor<Integer>> verifier)
+ throws Exception {
+ final ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
+ verifier.accept(captor);
+ return captor.getValue();
}
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 1b3496f..044ff02 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -38,7 +38,6 @@
import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.content.pm.PackageManager.FEATURE_WIFI_DIRECT;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
-import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
@@ -125,6 +124,7 @@
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
+import static com.android.server.ConnectivityService.MAX_NETWORK_REQUESTS_PER_SYSTEM_UID;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_MOBILE_DATA_PREFERERRED;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_OEM;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_PROFILE;
@@ -150,6 +150,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -157,7 +158,6 @@
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
-import static org.mockito.ArgumentMatchers.startsWith;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
@@ -203,6 +203,7 @@
import android.location.LocationManager;
import android.net.CaptivePortalData;
import android.net.ConnectionInfo;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.PacketKeepalive;
@@ -239,6 +240,7 @@
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkPolicyManager;
import android.net.NetworkPolicyManager.NetworkPolicyCallback;
+import android.net.NetworkProvider;
import android.net.NetworkRequest;
import android.net.NetworkScore;
import android.net.NetworkSpecifier;
@@ -246,6 +248,8 @@
import android.net.NetworkStateSnapshot;
import android.net.NetworkTestResultParcelable;
import android.net.OemNetworkPreferences;
+import android.net.PacProxyManager;
+import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.QosCallbackException;
import android.net.QosFilter;
@@ -284,6 +288,7 @@
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
@@ -309,17 +314,21 @@
import androidx.test.filters.SmallTest;
import com.android.connectivity.resources.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.ArrayTrackRecord;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LocationPermissionChecker;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
import com.android.server.ConnectivityService.NetworkRequestInfo;
+import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
+import com.android.server.connectivity.ConnectivityFlags;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
@@ -335,6 +344,7 @@
import com.android.testutils.HandlerUtils;
import com.android.testutils.RecorderCallback.CallbackEntry;
import com.android.testutils.TestableNetworkCallback;
+import com.android.testutils.TestableNetworkOfferCallback;
import org.junit.After;
import org.junit.Before;
@@ -459,7 +469,7 @@
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
private HandlerThread mVMSHandlerThread;
- private ConnectivityService.Dependencies mDeps;
+ private ConnectivityServiceDependencies mDeps;
private ConnectivityService mService;
private WrappedConnectivityManager mCm;
private TestNetworkAgentWrapper mWiFiNetworkAgent;
@@ -469,6 +479,7 @@
private Context mContext;
private NetworkPolicyCallback mPolicyCallback;
private WrappedMultinetworkPolicyTracker mPolicyTracker;
+ private ProxyTracker mProxyTracker;
private HandlerThread mAlarmManagerThread;
private TestNetIdManager mNetIdManager;
private QosCallbackMockHelper mQosCallbackMockHelper;
@@ -497,13 +508,12 @@
@Mock LocationManager mLocationManager;
@Mock AppOpsManager mAppOpsManager;
@Mock TelephonyManager mTelephonyManager;
- @Mock MockableSystemProperties mSystemProperties;
@Mock EthernetManager mEthernetManager;
@Mock NetworkPolicyManager mNetworkPolicyManager;
@Mock VpnProfileStore mVpnProfileStore;
@Mock SystemConfigManager mSystemConfigManager;
@Mock Resources mResources;
- @Mock ProxyTracker mProxyTracker;
+ @Mock PacProxyManager mPacProxyManager;
// BatteryStatsManager is final and cannot be mocked with regular mockito, so just mock the
// underlying binder calls.
@@ -540,19 +550,22 @@
private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
// Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
+ // For permissions granted across the board, the key is only the permission name.
+ // For permissions only granted to a combination of uid/pid, the key
+ // is "<permission name>,<pid>,<uid>". PID+UID permissons have priority over generic ones.
private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
MockContext(Context base, ContentProvider settingsProvider) {
super(base);
mInternalResources = spy(base.getResources());
- when(mInternalResources.getStringArray(com.android.internal.R.array.networkAttributes))
- .thenReturn(new String[] {
- "wifi,1,1,1,-1,true",
- "mobile,0,0,0,-1,true",
- "mobile_mms,2,0,2,60000,true",
- "mobile_supl,3,0,2,60000,true",
- });
+ doReturn(new String[] {
+ "wifi,1,1,1,-1,true",
+ "mobile,0,0,0,-1,true",
+ "mobile_mms,2,0,2,60000,true",
+ "mobile_supl,3,0,2,60000,true",
+ }).when(mInternalResources)
+ .getStringArray(com.android.internal.R.array.networkAttributes);
mContentResolver = new MockContentResolver();
mContentResolver.addProvider(Settings.AUTHORITY, settingsProvider);
@@ -602,6 +615,7 @@
if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager;
if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
if (Context.BATTERY_STATS_SERVICE.equals(name)) return mBatteryStatsManager;
+ if (Context.PAC_PROXY_SERVICE.equals(name)) return mPacProxyManager;
return super.getSystemService(name);
}
@@ -641,30 +655,40 @@
return mPackageManager;
}
- private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
- final Integer granted = mMockedPermissions.get(permission);
- return granted != null ? granted : ifAbsent.get();
+ private int checkMockedPermission(String permission, int pid, int uid,
+ Supplier<Integer> ifAbsent) {
+ final Integer granted = mMockedPermissions.get(permission + "," + pid + "," + uid);
+ if (null != granted) {
+ return granted;
+ }
+ final Integer allGranted = mMockedPermissions.get(permission);
+ if (null != allGranted) {
+ return allGranted;
+ }
+ return ifAbsent.get();
}
@Override
public int checkPermission(String permission, int pid, int uid) {
- return checkMockedPermission(
- permission, () -> super.checkPermission(permission, pid, uid));
+ return checkMockedPermission(permission, pid, uid,
+ () -> super.checkPermission(permission, pid, uid));
}
@Override
public int checkCallingOrSelfPermission(String permission) {
- return checkMockedPermission(
- permission, () -> super.checkCallingOrSelfPermission(permission));
+ return checkMockedPermission(permission, Process.myPid(), Process.myUid(),
+ () -> super.checkCallingOrSelfPermission(permission));
}
@Override
public void enforceCallingOrSelfPermission(String permission, String message) {
- final Integer granted = mMockedPermissions.get(permission);
- if (granted == null) {
- super.enforceCallingOrSelfPermission(permission, message);
- return;
- }
+ final Integer granted = checkMockedPermission(permission,
+ Process.myPid(), Process.myUid(),
+ () -> {
+ super.enforceCallingOrSelfPermission(permission, message);
+ // enforce will crash if the permission is not granted
+ return PERMISSION_GRANTED;
+ });
if (!granted.equals(PERMISSION_GRANTED)) {
throw new SecurityException("[Test] permission denied: " + permission);
@@ -674,6 +698,8 @@
/**
* Mock checks for the specified permission, and have them behave as per {@code granted}.
*
+ * This will apply across the board no matter what the checked UID and PID are.
+ *
* <p>Passing null reverts to default behavior, which does a real permission check on the
* test package.
* @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
@@ -683,6 +709,21 @@
mMockedPermissions.put(permission, granted);
}
+ /**
+ * Mock checks for the specified permission, and have them behave as per {@code granted}.
+ *
+ * This will only apply to the passed UID and PID.
+ *
+ * <p>Passing null reverts to default behavior, which does a real permission check on the
+ * test package.
+ * @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
+ * {@link PackageManager#PERMISSION_DENIED}.
+ */
+ public void setPermission(String permission, int pid, int uid, Integer granted) {
+ final String key = permission + "," + pid + "," + uid;
+ mMockedPermissions.put(key, granted);
+ }
+
@Override
public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
@NonNull IntentFilter filter, @Nullable String broadcastPermission,
@@ -783,17 +824,22 @@
private String mRedirectUrl;
TestNetworkAgentWrapper(int transport) throws Exception {
- this(transport, new LinkProperties(), null);
+ this(transport, new LinkProperties(), null /* ncTemplate */, null /* provider */);
}
TestNetworkAgentWrapper(int transport, LinkProperties linkProperties)
throws Exception {
- this(transport, linkProperties, null);
+ this(transport, linkProperties, null /* ncTemplate */, null /* provider */);
}
private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
NetworkCapabilities ncTemplate) throws Exception {
- super(transport, linkProperties, ncTemplate, mServiceContext);
+ this(transport, linkProperties, ncTemplate, null /* provider */);
+ }
+
+ private TestNetworkAgentWrapper(int transport, LinkProperties linkProperties,
+ NetworkCapabilities ncTemplate, NetworkProvider provider) throws Exception {
+ super(transport, linkProperties, ncTemplate, provider, mServiceContext);
// Waits for the NetworkAgent to be registered, which includes the creation of the
// NetworkMonitor.
@@ -802,9 +848,40 @@
HandlerUtils.waitForIdle(ConnectivityThread.get(), TIMEOUT_MS);
}
+ class TestInstrumentedNetworkAgent extends InstrumentedNetworkAgent {
+ TestInstrumentedNetworkAgent(NetworkAgentWrapper wrapper, LinkProperties lp,
+ NetworkAgentConfig nac, NetworkProvider provider) {
+ super(wrapper, lp, nac, provider);
+ }
+
+ @Override
+ public void networkStatus(int status, String redirectUrl) {
+ mRedirectUrl = redirectUrl;
+ mNetworkStatusReceived.open();
+ }
+
+ @Override
+ public void onNetworkCreated() {
+ super.onNetworkCreated();
+ if (mCreatedCallback != null) mCreatedCallback.run();
+ }
+
+ @Override
+ public void onNetworkUnwanted() {
+ super.onNetworkUnwanted();
+ if (mUnwantedCallback != null) mUnwantedCallback.run();
+ }
+
+ @Override
+ public void onNetworkDestroyed() {
+ super.onNetworkDestroyed();
+ if (mDisconnectedCallback != null) mDisconnectedCallback.run();
+ }
+ }
+
@Override
protected InstrumentedNetworkAgent makeNetworkAgent(LinkProperties linkProperties,
- NetworkAgentConfig nac) throws Exception {
+ NetworkAgentConfig nac, NetworkProvider provider) throws Exception {
mNetworkMonitor = mock(INetworkMonitor.class);
final Answer validateAnswer = inv -> {
@@ -824,31 +901,7 @@
nmCbCaptor.capture());
final InstrumentedNetworkAgent na =
- new InstrumentedNetworkAgent(this, linkProperties, nac) {
- @Override
- public void networkStatus(int status, String redirectUrl) {
- mRedirectUrl = redirectUrl;
- mNetworkStatusReceived.open();
- }
-
- @Override
- public void onNetworkCreated() {
- super.onNetworkCreated();
- if (mCreatedCallback != null) mCreatedCallback.run();
- }
-
- @Override
- public void onNetworkUnwanted() {
- super.onNetworkUnwanted();
- if (mUnwantedCallback != null) mUnwantedCallback.run();
- }
-
- @Override
- public void onNetworkDestroyed() {
- super.onNetworkDestroyed();
- if (mDisconnectedCallback != null) mDisconnectedCallback.run();
- }
- };
+ new TestInstrumentedNetworkAgent(this, linkProperties, nac, provider);
assertEquals(na.getNetwork().netId, nmNetworkCaptor.getValue().netId);
mNmCallbacks = nmCbCaptor.getValue();
@@ -1530,11 +1583,11 @@
}
private <T> T doAsUid(final int uid, @NonNull final Supplier<T> what) {
- doReturn(uid).when(mDeps).getCallingUid();
+ mDeps.setCallingUid(uid);
try {
return what.get();
} finally {
- returnRealCallingUid();
+ mDeps.setCallingUid(null);
}
}
@@ -1564,15 +1617,21 @@
}
private void withPermission(String permission, ExceptionalRunnable r) throws Exception {
- if (mServiceContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
- r.run();
- return;
- }
try {
mServiceContext.setPermission(permission, PERMISSION_GRANTED);
r.run();
} finally {
- mServiceContext.setPermission(permission, PERMISSION_DENIED);
+ mServiceContext.setPermission(permission, null);
+ }
+ }
+
+ private void withPermission(String permission, int pid, int uid, ExceptionalRunnable r)
+ throws Exception {
+ try {
+ mServiceContext.setPermission(permission, pid, uid, PERMISSION_GRANTED);
+ r.run();
+ } finally {
+ mServiceContext.setPermission(permission, pid, uid, null);
}
}
@@ -1605,21 +1664,21 @@
MockitoAnnotations.initMocks(this);
- when(mUserManager.getAliveUsers()).thenReturn(asList(PRIMARY_USER_INFO));
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(asList(PRIMARY_USER_HANDLE));
- when(mUserManager.getUserInfo(PRIMARY_USER)).thenReturn(PRIMARY_USER_INFO);
+ doReturn(asList(PRIMARY_USER_INFO)).when(mUserManager).getAliveUsers();
+ doReturn(asList(PRIMARY_USER_HANDLE)).when(mUserManager).getUserHandles(anyBoolean());
+ doReturn(PRIMARY_USER_INFO).when(mUserManager).getUserInfo(PRIMARY_USER);
// canHaveRestrictedProfile does not take a userId. It applies to the userId of the context
// it was started from, i.e., PRIMARY_USER.
- when(mUserManager.canHaveRestrictedProfile()).thenReturn(true);
- when(mUserManager.getUserInfo(RESTRICTED_USER)).thenReturn(RESTRICTED_USER_INFO);
+ doReturn(true).when(mUserManager).canHaveRestrictedProfile();
+ doReturn(RESTRICTED_USER_INFO).when(mUserManager).getUserInfo(RESTRICTED_USER);
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
- when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
- .thenReturn(applicationInfo);
- when(mPackageManager.getTargetSdkVersion(anyString()))
- .thenReturn(applicationInfo.targetSdkVersion);
- when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
+ doReturn(applicationInfo).when(mPackageManager)
+ .getApplicationInfoAsUser(anyString(), anyInt(), any());
+ doReturn(applicationInfo.targetSdkVersion).when(mPackageManager)
+ .getTargetSdkVersion(anyString());
+ doReturn(new int[0]).when(mSystemConfigManager).getSystemPermissionUids(anyString());
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
@@ -1647,8 +1706,15 @@
mCsHandlerThread = new HandlerThread("TestConnectivityService");
mVMSHandlerThread = new HandlerThread("TestVpnManagerService");
- mDeps = makeDependencies();
- returnRealCallingUid();
+ mProxyTracker = new ProxyTracker(mServiceContext, mock(Handler.class),
+ 16 /* EVENT_PROXY_HAS_CHANGED */);
+
+ initMockedResources();
+ final Context mockResContext = mock(Context.class);
+ doReturn(mResources).when(mockResContext).getResources();
+ ConnectivityResources.setResourcesContextForTest(mockResContext);
+ mDeps = new ConnectivityServiceDependencies(mockResContext);
+
mService = new ConnectivityService(mServiceContext,
mMockDnsResolver,
mock(IpConnectivityLog.class),
@@ -1656,7 +1722,6 @@
mDeps);
mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
- verify(mDeps).makeMultinetworkPolicyTracker(any(), any(), any());
final ArgumentCaptor<NetworkPolicyCallback> policyCallbackCaptor =
ArgumentCaptor.forClass(NetworkPolicyCallback.class);
@@ -1680,33 +1745,7 @@
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
}
- private void returnRealCallingUid() {
- doAnswer((invocationOnMock) -> Binder.getCallingUid()).when(mDeps).getCallingUid();
- }
-
- private ConnectivityService.Dependencies makeDependencies() {
- doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
- final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class);
- doReturn(mCsHandlerThread).when(deps).makeHandlerThread();
- doReturn(mNetIdManager).when(deps).makeNetIdManager();
- doReturn(mNetworkStack).when(deps).getNetworkStack();
- doReturn(mSystemProperties).when(deps).getSystemProperties();
- doReturn(mProxyTracker).when(deps).makeProxyTracker(any(), any());
- doReturn(true).when(deps).queryUserAccess(anyInt(), any(), any());
- doAnswer(inv -> {
- mPolicyTracker = new WrappedMultinetworkPolicyTracker(
- inv.getArgument(0), inv.getArgument(1), inv.getArgument(2));
- return mPolicyTracker;
- }).when(deps).makeMultinetworkPolicyTracker(any(), any(), any());
- doReturn(true).when(deps).getCellular464XlatEnabled();
- doAnswer(inv ->
- new LocationPermissionChecker(inv.getArgument(0)) {
- @Override
- protected int getCurrentUser() {
- return runAsShell(CREATE_USERS, () -> super.getCurrentUser());
- }
- }).when(deps).makeLocationPermissionChecker(any());
-
+ private void initMockedResources() {
doReturn(60000).when(mResources).getInteger(R.integer.config_networkTransitionTimeout);
doReturn("").when(mResources).getString(R.string.config_networkCaptivePortalServerUrl);
doReturn(new String[]{ WIFI_WOL_IFNAME }).when(mResources).getStringArray(
@@ -1719,7 +1758,8 @@
R.array.config_protectedNetworks);
// We don't test the actual notification value strings, so just return an empty array.
// It doesn't matter what the values are as long as it's not null.
- doReturn(new String[0]).when(mResources).getStringArray(R.array.network_switch_type_name);
+ doReturn(new String[0]).when(mResources)
+ .getStringArray(R.array.network_switch_type_name);
doReturn(R.array.config_networkSupportedKeepaliveCount).when(mResources)
.getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any());
@@ -1728,22 +1768,170 @@
doReturn(R.integer.config_networkAvoidBadWifi).when(mResources)
.getIdentifier(eq("config_networkAvoidBadWifi"), eq("integer"), any());
doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+ doReturn(true).when(mResources)
+ .getBoolean(R.bool.config_cellular_radio_timesharing_capable);
+ }
- final ConnectivityResources connRes = mock(ConnectivityResources.class);
- doReturn(mResources).when(connRes).get();
- doReturn(connRes).when(deps).getResources(any());
+ class ConnectivityServiceDependencies extends ConnectivityService.Dependencies {
+ final ConnectivityResources mConnRes;
+ @Mock final MockableSystemProperties mSystemProperties;
- final Context mockResContext = mock(Context.class);
- doReturn(mResources).when(mockResContext).getResources();
- ConnectivityResources.setResourcesContextForTest(mockResContext);
+ ConnectivityServiceDependencies(final Context mockResContext) {
+ mSystemProperties = mock(MockableSystemProperties.class);
+ doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false);
- doAnswer(inv -> {
- final PendingIntent a = inv.getArgument(0);
- final PendingIntent b = inv.getArgument(1);
+ mConnRes = new ConnectivityResources(mockResContext);
+ }
+
+ @Override
+ public MockableSystemProperties getSystemProperties() {
+ return mSystemProperties;
+ }
+
+ @Override
+ public HandlerThread makeHandlerThread() {
+ return mCsHandlerThread;
+ }
+
+ @Override
+ public NetworkStackClientBase getNetworkStack() {
+ return mNetworkStack;
+ }
+
+ @Override
+ public ProxyTracker makeProxyTracker(final Context context, final Handler handler) {
+ return mProxyTracker;
+ }
+
+ @Override
+ public NetIdManager makeNetIdManager() {
+ return mNetIdManager;
+ }
+
+ @Override
+ public boolean queryUserAccess(final int uid, final Network network,
+ final ConnectivityService cs) {
+ return true;
+ }
+
+ @Override
+ public MultinetworkPolicyTracker makeMultinetworkPolicyTracker(final Context c,
+ final Handler h, final Runnable r) {
+ if (null != mPolicyTracker) {
+ throw new IllegalStateException("Multinetwork policy tracker already initialized");
+ }
+ mPolicyTracker = new WrappedMultinetworkPolicyTracker(mServiceContext, h, r);
+ return mPolicyTracker;
+ }
+
+ @Override
+ public ConnectivityResources getResources(final Context ctx) {
+ return mConnRes;
+ }
+
+ @Override
+ public LocationPermissionChecker makeLocationPermissionChecker(final Context context) {
+ return new LocationPermissionChecker(context) {
+ @Override
+ protected int getCurrentUser() {
+ return runAsShell(CREATE_USERS, () -> super.getCurrentUser());
+ }
+ };
+ }
+
+ @Override
+ public boolean intentFilterEquals(final PendingIntent a, final PendingIntent b) {
return runAsShell(GET_INTENT_SENDER_INTENT, () -> a.intentFilterEquals(b));
- }).when(deps).intentFilterEquals(any(), any());
+ }
- return deps;
+ @GuardedBy("this")
+ private Integer mCallingUid = null;
+
+ @Override
+ public int getCallingUid() {
+ synchronized (this) {
+ if (null != mCallingUid) return mCallingUid;
+ return super.getCallingUid();
+ }
+ }
+
+ // Pass null for the real calling UID
+ public void setCallingUid(final Integer uid) {
+ synchronized (this) {
+ mCallingUid = uid;
+ }
+ }
+
+ @GuardedBy("this")
+ private boolean mCellular464XlatEnabled = true;
+
+ @Override
+ public boolean getCellular464XlatEnabled() {
+ synchronized (this) {
+ return mCellular464XlatEnabled;
+ }
+ }
+
+ public void setCellular464XlatEnabled(final boolean enabled) {
+ synchronized (this) {
+ mCellular464XlatEnabled = enabled;
+ }
+ }
+
+ @GuardedBy("this")
+ private Integer mConnectionOwnerUid = null;
+
+ @Override
+ public int getConnectionOwnerUid(final int protocol, final InetSocketAddress local,
+ final InetSocketAddress remote) {
+ synchronized (this) {
+ if (null != mConnectionOwnerUid) return mConnectionOwnerUid;
+ return super.getConnectionOwnerUid(protocol, local, remote);
+ }
+ }
+
+ // Pass null to get the production implementation of getConnectionOwnerUid
+ public void setConnectionOwnerUid(final Integer uid) {
+ synchronized (this) {
+ mConnectionOwnerUid = uid;
+ }
+ }
+
+ final class ReportedInterfaces {
+ public final Context context;
+ public final String iface;
+ public final int[] transportTypes;
+ ReportedInterfaces(final Context c, final String i, final int[] t) {
+ context = c;
+ iface = i;
+ transportTypes = t;
+ }
+
+ public boolean contentEquals(final Context c, final String i, final int[] t) {
+ return Objects.equals(context, c) && Objects.equals(iface, i)
+ && Arrays.equals(transportTypes, t);
+ }
+ }
+
+ final ArrayTrackRecord<ReportedInterfaces> mReportedInterfaceHistory =
+ new ArrayTrackRecord<>();
+
+ @Override
+ public void reportNetworkInterfaceForTransports(final Context context, final String iface,
+ final int[] transportTypes) {
+ mReportedInterfaceHistory.add(new ReportedInterfaces(context, iface, transportTypes));
+ super.reportNetworkInterfaceForTransports(context, iface, transportTypes);
+ }
+
+ @Override
+ public boolean isFeatureEnabled(Context context, String name, boolean defaultEnabled) {
+ switch (name) {
+ case ConnectivityFlags.NO_REMATCH_ALL_REQUESTS_ON_REGISTER:
+ return true;
+ default:
+ return super.isFeatureEnabled(context, name, defaultEnabled);
+ }
+ }
}
private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
@@ -1808,36 +1996,34 @@
final String myPackageName = mContext.getPackageName();
final PackageInfo myPackageInfo = mContext.getPackageManager().getPackageInfo(
myPackageName, PackageManager.GET_PERMISSIONS);
- when(mPackageManager.getPackagesForUid(Binder.getCallingUid())).thenReturn(
- new String[] {myPackageName});
- when(mPackageManager.getPackageInfoAsUser(eq(myPackageName), anyInt(),
- eq(UserHandle.getCallingUserId()))).thenReturn(myPackageInfo);
+ doReturn(new String[] {myPackageName}).when(mPackageManager)
+ .getPackagesForUid(Binder.getCallingUid());
+ doReturn(myPackageInfo).when(mPackageManager).getPackageInfoAsUser(
+ eq(myPackageName), anyInt(), eq(UserHandle.getCallingUserId()));
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- asList(new PackageInfo[] {
- buildPackageInfo(/* SYSTEM */ false, APP1_UID),
- buildPackageInfo(/* SYSTEM */ false, APP2_UID),
- buildPackageInfo(/* SYSTEM */ false, VPN_UID)
- }));
+ doReturn(asList(new PackageInfo[] {
+ buildPackageInfo(/* SYSTEM */ false, APP1_UID),
+ buildPackageInfo(/* SYSTEM */ false, APP2_UID),
+ buildPackageInfo(/* SYSTEM */ false, VPN_UID)
+ })).when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
// Create a fake always-on VPN package.
final int userId = UserHandle.getCallingUserId();
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = Build.VERSION_CODES.R; // Always-on supported in N+.
- when(mPackageManager.getApplicationInfoAsUser(eq(ALWAYS_ON_PACKAGE), anyInt(),
- eq(userId))).thenReturn(applicationInfo);
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfoAsUser(
+ eq(ALWAYS_ON_PACKAGE), anyInt(), eq(userId));
// Minimal mocking to keep Vpn#isAlwaysOnPackageSupported happy.
ResolveInfo rInfo = new ResolveInfo();
rInfo.serviceInfo = new ServiceInfo();
rInfo.serviceInfo.metaData = new Bundle();
final List<ResolveInfo> services = asList(new ResolveInfo[]{rInfo});
- when(mPackageManager.queryIntentServicesAsUser(any(), eq(PackageManager.GET_META_DATA),
- eq(userId))).thenReturn(services);
- when(mPackageManager.getPackageUidAsUser(TEST_PACKAGE_NAME, userId))
- .thenReturn(Process.myUid());
- when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, userId))
- .thenReturn(VPN_UID);
+ doReturn(services).when(mPackageManager).queryIntentServicesAsUser(
+ any(), eq(PackageManager.GET_META_DATA), eq(userId));
+ doReturn(Process.myUid()).when(mPackageManager).getPackageUidAsUser(
+ TEST_PACKAGE_NAME, userId);
+ doReturn(VPN_UID).when(mPackageManager).getPackageUidAsUser(ALWAYS_ON_PACKAGE, userId);
}
private void verifyActiveNetwork(int transport) {
@@ -1954,6 +2140,39 @@
return expected;
}
+ private ExpectedBroadcast expectProxyChangeAction(ProxyInfo proxy) {
+ return registerPacProxyBroadcastThat(intent -> {
+ final ProxyInfo actualProxy = (ProxyInfo) intent.getExtra(Proxy.EXTRA_PROXY_INFO,
+ ProxyInfo.buildPacProxy(Uri.EMPTY));
+ return proxy.equals(actualProxy);
+ });
+ }
+
+ private ExpectedBroadcast registerPacProxyBroadcast() {
+ return registerPacProxyBroadcastThat(intent -> true);
+ }
+
+ private ExpectedBroadcast registerPacProxyBroadcastThat(
+ @NonNull final Predicate<Intent> filter) {
+ final IntentFilter intentFilter = new IntentFilter(Proxy.PROXY_CHANGE_ACTION);
+ // AtomicReference allows receiver to access expected even though it is constructed later.
+ final AtomicReference<ExpectedBroadcast> expectedRef = new AtomicReference<>();
+ final BroadcastReceiver receiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ final ProxyInfo proxy = (ProxyInfo) intent.getExtra(
+ Proxy.EXTRA_PROXY_INFO, ProxyInfo.buildPacProxy(Uri.EMPTY));
+ Log.d(TAG, "Receive PROXY_CHANGE_ACTION, proxy = " + proxy);
+ if (filter.test(intent)) {
+ expectedRef.get().complete(intent);
+ }
+ }
+ };
+ final ExpectedBroadcast expected = new ExpectedBroadcast(receiver);
+ expectedRef.set(expected);
+ mServiceContext.registerReceiver(receiver, intentFilter);
+ return expected;
+ }
+
private boolean extraInfoInBroadcastHasExpectedNullness(NetworkInfo ni) {
final DetailedState state = ni.getDetailedState();
if (state == DetailedState.CONNECTED && ni.getExtraInfo() == null) return false;
@@ -2206,10 +2425,25 @@
deathRecipient.get().binderDied();
// Wait for the release message to be processed.
waitForIdle();
+ // After waitForIdle(), the message was processed and the service didn't crash.
}
+ // TODO : migrate to @Parameterized
@Test
- public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
+ public void testValidatedCellularOutscoresUnvalidatedWiFi_CanTimeShare() throws Exception {
+ // The behavior of this test should be the same whether the radio can time share or not.
+ doTestValidatedCellularOutscoresUnvalidatedWiFi(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testValidatedCellularOutscoresUnvalidatedWiFi_CannotTimeShare() throws Exception {
+ doTestValidatedCellularOutscoresUnvalidatedWiFi(false);
+ }
+
+ public void doTestValidatedCellularOutscoresUnvalidatedWiFi(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated WiFi
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2243,8 +2477,21 @@
verifyNoNetwork();
}
+ // TODO : migrate to @Parameterized
@Test
- public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
+ public void testUnvalidatedWifiOutscoresUnvalidatedCellular_CanTimeShare() throws Exception {
+ doTestUnvalidatedWifiOutscoresUnvalidatedCellular(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testUnvalidatedWifiOutscoresUnvalidatedCellular_CannotTimeShare() throws Exception {
+ doTestUnvalidatedWifiOutscoresUnvalidatedCellular(false);
+ }
+
+ public void doTestUnvalidatedWifiOutscoresUnvalidatedCellular(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2269,8 +2516,21 @@
verifyNoNetwork();
}
+ // TODO : migrate to @Parameterized
@Test
- public void testUnlingeringDoesNotValidate() throws Exception {
+ public void testUnlingeringDoesNotValidate_CanTimeShare() throws Exception {
+ doTestUnlingeringDoesNotValidate(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testUnlingeringDoesNotValidate_CannotTimeShare() throws Exception {
+ doTestUnlingeringDoesNotValidate(false);
+ }
+
+ public void doTestUnlingeringDoesNotValidate(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2297,8 +2557,134 @@
NET_CAPABILITY_VALIDATED));
}
+ // TODO : migrate to @Parameterized
@Test
- public void testCellularOutscoresWeakWifi() throws Exception {
+ public void testRequestMigrationToSameTransport_CanTimeShare() throws Exception {
+ // Simulate a device where the cell radio is capable of time sharing
+ mService.mCellularRadioTimesharingCapable = true;
+ doTestRequestMigrationToSameTransport(TRANSPORT_CELLULAR, true);
+ doTestRequestMigrationToSameTransport(TRANSPORT_WIFI, true);
+ doTestRequestMigrationToSameTransport(TRANSPORT_ETHERNET, true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testRequestMigrationToSameTransport_CannotTimeShare() throws Exception {
+ // Simulate a device where the cell radio is not capable of time sharing
+ mService.mCellularRadioTimesharingCapable = false;
+ doTestRequestMigrationToSameTransport(TRANSPORT_CELLULAR, false);
+ doTestRequestMigrationToSameTransport(TRANSPORT_WIFI, true);
+ doTestRequestMigrationToSameTransport(TRANSPORT_ETHERNET, true);
+ }
+
+ public void doTestRequestMigrationToSameTransport(final int transport,
+ final boolean expectLingering) throws Exception {
+ // To speed up tests the linger delay is very short by default in tests but this
+ // test needs to make sure the delay is not incurred so a longer value is safer (it
+ // reduces the risk that a bug exists but goes undetected). The alarm manager in the test
+ // throws and crashes CS if this is set to anything more than the below constant though.
+ mService.mLingerDelayMs = UNREASONABLY_LONG_ALARM_WAIT_MS;
+
+ final TestNetworkCallback generalCb = new TestNetworkCallback();
+ final TestNetworkCallback defaultCb = new TestNetworkCallback();
+ mCm.registerNetworkCallback(
+ new NetworkRequest.Builder().addTransportType(transport | transport).build(),
+ generalCb);
+ mCm.registerDefaultNetworkCallback(defaultCb);
+
+ // Bring up net agent 1
+ final TestNetworkAgentWrapper net1 = new TestNetworkAgentWrapper(transport);
+ net1.connect(true);
+ // Make sure the default request is on net 1
+ generalCb.expectAvailableThenValidatedCallbacks(net1);
+ defaultCb.expectAvailableThenValidatedCallbacks(net1);
+
+ // Bring up net 2 with primary and mms
+ final TestNetworkAgentWrapper net2 = new TestNetworkAgentWrapper(transport);
+ net2.addCapability(NET_CAPABILITY_MMS);
+ net2.setScore(new NetworkScore.Builder().setTransportPrimary(true).build());
+ net2.connect(true);
+
+ // Make sure the default request goes to net 2
+ generalCb.expectAvailableCallbacksUnvalidated(net2);
+ if (expectLingering) {
+ generalCb.expectCallback(CallbackEntry.LOSING, net1);
+ }
+ generalCb.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, net2);
+ defaultCb.expectAvailableDoubleValidatedCallbacks(net2);
+
+ // Make sure cell 1 is unwanted immediately if the radio can't time share, but only
+ // after some delay if it can.
+ if (expectLingering) {
+ net1.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS); // always incurs the timeout
+ generalCb.assertNoCallback();
+ // assertNotDisconnected waited for TEST_CALLBACK_TIMEOUT_MS, so waiting for the
+ // linger period gives TEST_CALLBACK_TIMEOUT_MS time for the event to process.
+ net1.expectDisconnected(UNREASONABLY_LONG_ALARM_WAIT_MS);
+ } else {
+ net1.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
+ }
+ net1.disconnect();
+ generalCb.expectCallback(CallbackEntry.LOST, net1);
+
+ // Remove primary from net 2
+ net2.setScore(new NetworkScore.Builder().build());
+ // Request MMS
+ final TestNetworkCallback mmsCallback = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build(),
+ mmsCallback);
+ mmsCallback.expectAvailableCallbacksValidated(net2);
+
+ // Bring up net 3 with primary but without MMS
+ final TestNetworkAgentWrapper net3 = new TestNetworkAgentWrapper(transport);
+ net3.setScore(new NetworkScore.Builder().setTransportPrimary(true).build());
+ net3.connect(true);
+
+ // Make sure default goes to net 3, but the MMS request doesn't
+ generalCb.expectAvailableThenValidatedCallbacks(net3);
+ defaultCb.expectAvailableDoubleValidatedCallbacks(net3);
+ mmsCallback.assertNoCallback();
+ net2.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS); // Always incurs the timeout
+
+ // Revoke MMS request and make sure net 2 is torn down with the appropriate delay
+ mCm.unregisterNetworkCallback(mmsCallback);
+ if (expectLingering) {
+ // If the radio can time share, the linger delay hasn't elapsed yet, so apps will
+ // get LOSING. If the radio can't time share, this is a hard loss, since the last
+ // request keeping up this network has been removed and the network isn't lingering
+ // for any other request.
+ generalCb.expectCallback(CallbackEntry.LOSING, net2);
+ net2.assertNotDisconnected(TEST_CALLBACK_TIMEOUT_MS);
+ generalCb.assertNoCallback();
+ net2.expectDisconnected(UNREASONABLY_LONG_ALARM_WAIT_MS);
+ } else {
+ net2.expectDisconnected(TEST_CALLBACK_TIMEOUT_MS);
+ }
+ net2.disconnect();
+ generalCb.expectCallback(CallbackEntry.LOST, net2);
+ defaultCb.assertNoCallback();
+
+ net3.disconnect();
+ mCm.unregisterNetworkCallback(defaultCb);
+ mCm.unregisterNetworkCallback(generalCb);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testCellularOutscoresWeakWifi_CanTimeShare() throws Exception {
+ // The behavior of this test should be the same whether the radio can time share or not.
+ doTestCellularOutscoresWeakWifi(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testCellularOutscoresWeakWifi_CannotTimeShare() throws Exception {
+ doTestCellularOutscoresWeakWifi(false);
+ }
+
+ public void doTestCellularOutscoresWeakWifi(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2323,8 +2709,21 @@
verifyActiveNetwork(TRANSPORT_WIFI);
}
+ // TODO : migrate to @Parameterized
@Test
- public void testReapingNetwork() throws Exception {
+ public void testReapingNetwork_CanTimeShare() throws Exception {
+ doTestReapingNetwork(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testReapingNetwork_CannotTimeShare() throws Exception {
+ doTestReapingNetwork(false);
+ }
+
+ public void doTestReapingNetwork(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up WiFi without NET_CAPABILITY_INTERNET.
// Expect it to be torn down immediately because it satisfies no requests.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -2352,8 +2751,21 @@
mWiFiNetworkAgent.expectDisconnected();
}
+ // TODO : migrate to @Parameterized
@Test
- public void testCellularFallback() throws Exception {
+ public void testCellularFallback_CanTimeShare() throws Exception {
+ doTestCellularFallback(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testCellularFallback_CannotTimeShare() throws Exception {
+ doTestCellularFallback(false);
+ }
+
+ public void doTestCellularFallback(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up validated cellular.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2390,8 +2802,21 @@
verifyActiveNetwork(TRANSPORT_WIFI);
}
+ // TODO : migrate to @Parameterized
@Test
- public void testWiFiFallback() throws Exception {
+ public void testWiFiFallback_CanTimeShare() throws Exception {
+ doTestWiFiFallback(true);
+ }
+
+ // TODO : migrate to @Parameterized
+ @Test
+ public void testWiFiFallback_CannotTimeShare() throws Exception {
+ doTestWiFiFallback(false);
+ }
+
+ public void doTestWiFiFallback(
+ final boolean cellRadioTimesharingCapable) throws Exception {
+ mService.mCellularRadioTimesharingCapable = cellRadioTimesharingCapable;
// Test bringing up unvalidated WiFi.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
ExpectedBroadcast b = registerConnectivityBroadcast(1);
@@ -2907,9 +3332,8 @@
private void grantUsingBackgroundNetworksPermissionForUid(
final int uid, final String packageName) throws Exception {
- when(mPackageManager.getPackageInfo(
- eq(packageName), eq(GET_PERMISSIONS | MATCH_ANY_USER)))
- .thenReturn(buildPackageInfo(true /* hasSystemPermission */, uid));
+ doReturn(buildPackageInfo(true /* hasSystemPermission */, uid)).when(mPackageManager)
+ .getPackageInfo(eq(packageName), eq(GET_PERMISSIONS));
mService.mPermissionMonitor.onPackageAdded(packageName, uid);
}
@@ -4865,9 +5289,6 @@
@Test
public void testAvoidBadWifiSetting() throws Exception {
- final ContentResolver cr = mServiceContext.getContentResolver();
- final String settingName = ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI;
-
doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
testAvoidBadWifiConfig_ignoreSettings();
@@ -4875,7 +5296,123 @@
testAvoidBadWifiConfig_controlledBySettings();
}
- @Ignore("Refactoring in progress b/178071397")
+ @Test
+ public void testOffersAvoidsBadWifi() throws Exception {
+ // Normal mode : the carrier doesn't restrict moving away from bad wifi.
+ // This has getAvoidBadWifi return true.
+ doReturn(1).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+ // Don't request cell separately for the purposes of this test.
+ setAlwaysOnNetworks(false);
+
+ final NetworkProvider cellProvider = new NetworkProvider(mServiceContext,
+ mCsHandlerThread.getLooper(), "Cell provider");
+ final NetworkProvider wifiProvider = new NetworkProvider(mServiceContext,
+ mCsHandlerThread.getLooper(), "Wifi provider");
+
+ mCm.registerNetworkProvider(cellProvider);
+ mCm.registerNetworkProvider(wifiProvider);
+
+ final NetworkScore cellScore = new NetworkScore.Builder().build();
+ final NetworkScore wifiScore = new NetworkScore.Builder().build();
+ final NetworkCapabilities defaultCaps = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .build();
+ final NetworkCapabilities cellCaps = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .build();
+ final NetworkCapabilities wifiCaps = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
+ .build();
+ final TestableNetworkOfferCallback cellCallback = new TestableNetworkOfferCallback(
+ TIMEOUT_MS /* timeout */, TEST_CALLBACK_TIMEOUT_MS /* noCallbackTimeout */);
+ final TestableNetworkOfferCallback wifiCallback = new TestableNetworkOfferCallback(
+ TIMEOUT_MS /* timeout */, TEST_CALLBACK_TIMEOUT_MS /* noCallbackTimeout */);
+
+ // Offer callbacks will run on the CS handler thread in this test.
+ cellProvider.registerNetworkOffer(cellScore, cellCaps, r -> r.run(), cellCallback);
+ wifiProvider.registerNetworkOffer(wifiScore, wifiCaps, r -> r.run(), wifiCallback);
+
+ // Both providers see the default request.
+ cellCallback.expectOnNetworkNeeded(defaultCaps);
+ wifiCallback.expectOnNetworkNeeded(defaultCaps);
+
+ // Listen to cell and wifi to know when agents are finished processing
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.registerNetworkCallback(cellRequest, cellNetworkCallback);
+ final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ mCm.registerNetworkCallback(wifiRequest, wifiNetworkCallback);
+
+ // Cell connects and validates.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR,
+ new LinkProperties(), null /* ncTemplate */, cellProvider);
+ mCellNetworkAgent.connect(true);
+ cellNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ cellCallback.assertNoCallback();
+ wifiCallback.assertNoCallback();
+
+ // Bring up wifi. At first it's invalidated, so cell is still needed.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI,
+ new LinkProperties(), null /* ncTemplate */, wifiProvider);
+ mWiFiNetworkAgent.connect(false);
+ wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cellCallback.assertNoCallback();
+ wifiCallback.assertNoCallback();
+
+ // Wifi validates. Cell is no longer needed, because it's outscored.
+ mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */);
+ // Have CS reconsider the network (see testPartialConnectivity)
+ mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
+ wifiNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ cellCallback.expectOnNetworkUnneeded(defaultCaps);
+ wifiCallback.assertNoCallback();
+
+ // Wifi is no longer validated. Cell is needed again.
+ mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+ mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
+ wifiNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ cellCallback.expectOnNetworkNeeded(defaultCaps);
+ wifiCallback.assertNoCallback();
+
+ // Disconnect wifi and pretend the carrier restricts moving away from bad wifi.
+ mWiFiNetworkAgent.disconnect();
+ wifiNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ // This has getAvoidBadWifi return false. This test doesn't change the value of the
+ // associated setting.
+ doReturn(0).when(mResources).getInteger(R.integer.config_networkAvoidBadWifi);
+ mPolicyTracker.reevaluate();
+ waitForIdle();
+
+ // Connect wifi again, cell is needed until wifi validates.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI,
+ new LinkProperties(), null /* ncTemplate */, wifiProvider);
+ mWiFiNetworkAgent.connect(false);
+ wifiNetworkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ cellCallback.assertNoCallback();
+ wifiCallback.assertNoCallback();
+ mWiFiNetworkAgent.setNetworkValid(true /* isStrictMode */);
+ mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), true);
+ wifiNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ cellCallback.expectOnNetworkUnneeded(defaultCaps);
+ wifiCallback.assertNoCallback();
+
+ // Wifi loses validation. Because the device doesn't avoid bad wifis, cell is
+ // not needed.
+ mWiFiNetworkAgent.setNetworkInvalid(true /* isStrictMode */);
+ mCm.reportNetworkConnectivity(mWiFiNetworkAgent.getNetwork(), false);
+ wifiNetworkCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ cellCallback.assertNoCallback();
+ wifiCallback.assertNoCallback();
+ }
+
@Test
public void testAvoidBadWifi() throws Exception {
final ContentResolver cr = mServiceContext.getContentResolver();
@@ -6185,16 +6722,16 @@
mCm.unregisterNetworkCallback(networkCallback);
}
- private void expectNotifyNetworkStatus(List<Network> networks, String defaultIface,
+ private void expectNotifyNetworkStatus(List<Network> defaultNetworks, String defaultIface,
Integer vpnUid, String vpnIfname, List<String> underlyingIfaces) throws Exception {
- ArgumentCaptor<List<Network>> networksCaptor = ArgumentCaptor.forClass(List.class);
+ ArgumentCaptor<List<Network>> defaultNetworksCaptor = ArgumentCaptor.forClass(List.class);
ArgumentCaptor<List<UnderlyingNetworkInfo>> vpnInfosCaptor =
ArgumentCaptor.forClass(List.class);
- verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(networksCaptor.capture(),
+ verify(mStatsManager, atLeastOnce()).notifyNetworkStatus(defaultNetworksCaptor.capture(),
any(List.class), eq(defaultIface), vpnInfosCaptor.capture());
- assertSameElements(networks, networksCaptor.getValue());
+ assertSameElements(defaultNetworks, defaultNetworksCaptor.getValue());
List<UnderlyingNetworkInfo> infos = vpnInfosCaptor.getValue();
if (vpnUid != null) {
@@ -6210,8 +6747,8 @@
}
private void expectNotifyNetworkStatus(
- List<Network> networks, String defaultIface) throws Exception {
- expectNotifyNetworkStatus(networks, defaultIface, null, null, List.of());
+ List<Network> defaultNetworks, String defaultIface) throws Exception {
+ expectNotifyNetworkStatus(defaultNetworks, defaultIface, null, null, List.of());
}
@Test
@@ -6867,6 +7404,15 @@
initialCaps.addTransportType(TRANSPORT_VPN);
initialCaps.addCapability(NET_CAPABILITY_INTERNET);
initialCaps.removeCapability(NET_CAPABILITY_NOT_VPN);
+ final ArrayList<Network> emptyUnderlyingNetworks = new ArrayList<Network>();
+ final ArrayList<Network> underlyingNetworksContainMobile = new ArrayList<Network>();
+ underlyingNetworksContainMobile.add(mobile);
+ final ArrayList<Network> underlyingNetworksContainWifi = new ArrayList<Network>();
+ underlyingNetworksContainWifi.add(wifi);
+ final ArrayList<Network> underlyingNetworksContainMobileAndMobile =
+ new ArrayList<Network>();
+ underlyingNetworksContainMobileAndMobile.add(mobile);
+ underlyingNetworksContainMobileAndMobile.add(wifi);
final NetworkCapabilities withNoUnderlying = new NetworkCapabilities();
withNoUnderlying.addCapability(NET_CAPABILITY_INTERNET);
@@ -6875,17 +7421,20 @@
withNoUnderlying.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
withNoUnderlying.addTransportType(TRANSPORT_VPN);
withNoUnderlying.removeCapability(NET_CAPABILITY_NOT_VPN);
+ withNoUnderlying.setUnderlyingNetworks(emptyUnderlyingNetworks);
final NetworkCapabilities withMobileUnderlying = new NetworkCapabilities(withNoUnderlying);
withMobileUnderlying.addTransportType(TRANSPORT_CELLULAR);
withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
withMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_SUSPENDED);
withMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
+ withMobileUnderlying.setUnderlyingNetworks(underlyingNetworksContainMobile);
final NetworkCapabilities withWifiUnderlying = new NetworkCapabilities(withNoUnderlying);
withWifiUnderlying.addTransportType(TRANSPORT_WIFI);
withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
withWifiUnderlying.setLinkUpstreamBandwidthKbps(20);
+ withWifiUnderlying.setUnderlyingNetworks(underlyingNetworksContainWifi);
final NetworkCapabilities withWifiAndMobileUnderlying =
new NetworkCapabilities(withNoUnderlying);
@@ -6895,6 +7444,7 @@
withWifiAndMobileUnderlying.removeCapability(NET_CAPABILITY_NOT_ROAMING);
withWifiAndMobileUnderlying.setLinkDownstreamBandwidthKbps(10);
withWifiAndMobileUnderlying.setLinkUpstreamBandwidthKbps(20);
+ withWifiAndMobileUnderlying.setUnderlyingNetworks(underlyingNetworksContainMobileAndMobile);
final NetworkCapabilities initialCapsNotMetered = new NetworkCapabilities(initialCaps);
initialCapsNotMetered.addCapability(NET_CAPABILITY_NOT_METERED);
@@ -6902,40 +7452,61 @@
NetworkCapabilities caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{}, initialCapsNotMetered, caps);
assertEquals(withNoUnderlying, caps);
+ assertEquals(0, new ArrayList<>(caps.getUnderlyingNetworks()).size());
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null}, initialCapsNotMetered, caps);
assertEquals(withNoUnderlying, caps);
+ assertEquals(0, new ArrayList<>(caps.getUnderlyingNetworks()).size());
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{mobile}, initialCapsNotMetered, caps);
assertEquals(withMobileUnderlying, caps);
+ assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
+ caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCapsNotMetered, caps);
assertEquals(withWifiUnderlying, caps);
+ assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
withWifiUnderlying.removeCapability(NET_CAPABILITY_NOT_METERED);
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{wifi}, initialCaps, caps);
assertEquals(withWifiUnderlying, caps);
+ assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{mobile, wifi}, initialCaps, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
+ assertEquals(2, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(1));
withWifiUnderlying.addCapability(NET_CAPABILITY_NOT_METERED);
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
initialCapsNotMetered, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
+ assertEquals(2, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(1));
caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(new Network[]{null, mobile, null, wifi},
initialCapsNotMetered, caps);
assertEquals(withWifiAndMobileUnderlying, caps);
+ assertEquals(2, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(mobile, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(1));
+ caps = new NetworkCapabilities(initialCaps);
mService.applyUnderlyingCapabilities(null, initialCapsNotMetered, caps);
assertEquals(withWifiUnderlying, caps);
+ assertEquals(1, new ArrayList<>(caps.getUnderlyingNetworks()).size());
+ assertEquals(wifi, new ArrayList<>(caps.getUnderlyingNetworks()).get(0));
}
@Test
@@ -6944,51 +7515,78 @@
final NetworkRequest request = new NetworkRequest.Builder()
.removeCapability(NET_CAPABILITY_NOT_VPN).build();
- mCm.registerNetworkCallback(request, callback);
+ runAsShell(NETWORK_SETTINGS, () -> {
+ mCm.registerNetworkCallback(request, callback);
- // Bring up a VPN that specifies an underlying network that does not exist yet.
- // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist yet,
- // (and doing so is difficult without using reflection) but it's good to test that the code
- // behaves approximately correctly.
- mMockVpn.establishForMyUid(false, true, false);
- assertUidRangesUpdatedForMyUid(true);
- final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
- mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
- callback.expectAvailableCallbacksUnvalidated(mMockVpn);
- assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
- .hasTransport(TRANSPORT_VPN));
- assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
- .hasTransport(TRANSPORT_WIFI));
+ // Bring up a VPN that specifies an underlying network that does not exist yet.
+ // Note: it's sort of meaningless for a VPN app to declare a network that doesn't exist
+ // yet, (and doing so is difficult without using reflection) but it's good to test that
+ // the code behaves approximately correctly.
+ mMockVpn.establishForMyUid(false, true, false);
+ callback.expectAvailableCallbacksUnvalidated(mMockVpn);
+ assertUidRangesUpdatedForMyUid(true);
+ final Network wifiNetwork = new Network(mNetIdManager.peekNextNetId());
+ mMockVpn.setUnderlyingNetworks(new Network[]{wifiNetwork});
+ // onCapabilitiesChanged() should be called because
+ // NetworkCapabilities#mUnderlyingNetworks is updated.
+ CallbackEntry ce = callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED,
+ mMockVpn);
+ final NetworkCapabilities vpnNc1 = ((CallbackEntry.CapabilitiesChanged) ce).getCaps();
+ // Since the wifi network hasn't brought up,
+ // ConnectivityService#applyUnderlyingCapabilities cannot find it. Update
+ // NetworkCapabilities#mUnderlyingNetworks to an empty array, and it will be updated to
+ // the correct underlying networks once the wifi network brings up. But this case
+ // shouldn't happen in reality since no one could get the network which hasn't brought
+ // up. For the empty array of underlying networks, it should be happened for 2 cases,
+ // the first one is that the VPN app declares an empty array for its underlying
+ // networks, the second one is that the underlying networks are torn down.
+ //
+ // It shouldn't be null since the null value means the underlying networks of this
+ // network should follow the default network.
+ final ArrayList<Network> underlyingNetwork = new ArrayList<>();
+ assertEquals(underlyingNetwork, vpnNc1.getUnderlyingNetworks());
+ // Since the wifi network isn't exist, applyUnderlyingCapabilities()
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertFalse(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
- // Make that underlying network connect, and expect to see its capabilities immediately
- // reflected in the VPN's capabilities.
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
- mWiFiNetworkAgent.connect(false);
- // TODO: the callback for the VPN happens before any callbacks are called for the wifi
- // network that has just connected. There appear to be two issues here:
- // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities() for
- // it returns non-null (which happens very early, during handleRegisterNetworkAgent).
- // This is not correct because that that point the network is not connected and cannot
- // pass any traffic.
- // 2. When a network connects, updateNetworkInfo propagates underlying network capabilities
- // before rematching networks.
- // Given that this scenario can't really happen, this is probably fine for now.
- callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
- callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
- .hasTransport(TRANSPORT_VPN));
- assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
- .hasTransport(TRANSPORT_WIFI));
+ // Make that underlying network connect, and expect to see its capabilities immediately
+ // reflected in the VPN's capabilities.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ assertEquals(wifiNetwork, mWiFiNetworkAgent.getNetwork());
+ mWiFiNetworkAgent.connect(false);
+ // TODO: the callback for the VPN happens before any callbacks are called for the wifi
+ // network that has just connected. There appear to be two issues here:
+ // 1. The VPN code will accept an underlying network as soon as getNetworkCapabilities()
+ // for it returns non-null (which happens very early, during
+ // handleRegisterNetworkAgent).
+ // This is not correct because that that point the network is not connected and
+ // cannot pass any traffic.
+ // 2. When a network connects, updateNetworkInfo propagates underlying network
+ // capabilities before rematching networks.
+ // Given that this scenario can't really happen, this is probably fine for now.
+ ce = callback.expectCallback(CallbackEntry.NETWORK_CAPS_UPDATED, mMockVpn);
+ final NetworkCapabilities vpnNc2 = ((CallbackEntry.CapabilitiesChanged) ce).getCaps();
+ // The wifi network is brought up, NetworkCapabilities#mUnderlyingNetworks is updated to
+ // it.
+ underlyingNetwork.add(wifiNetwork);
+ assertEquals(underlyingNetwork, vpnNc2.getUnderlyingNetworks());
+ callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_VPN));
+ assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
+ .hasTransport(TRANSPORT_WIFI));
- // Disconnect the network, and expect to see the VPN capabilities change accordingly.
- mWiFiNetworkAgent.disconnect();
- callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
- callback.expectCapabilitiesThat(mMockVpn, (nc) ->
- nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
+ // Disconnect the network, and expect to see the VPN capabilities change accordingly.
+ mWiFiNetworkAgent.disconnect();
+ callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
+ callback.expectCapabilitiesThat(mMockVpn, (nc) ->
+ nc.getTransportTypes().length == 1 && nc.hasTransport(TRANSPORT_VPN));
- mMockVpn.disconnect();
- mCm.unregisterNetworkCallback(callback);
+ mMockVpn.disconnect();
+ mCm.unregisterNetworkCallback(callback);
+ });
}
private void assertGetNetworkInfoOfGetActiveNetworkIsConnected(boolean expectedConnectivity) {
@@ -7634,8 +8232,8 @@
callback.expectCapabilitiesThat(mWiFiNetworkAgent, (caps)
-> caps.hasCapability(NET_CAPABILITY_VALIDATED));
- when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
- .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
+ doReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)).when(mPackageManager)
+ .getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER);
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
@@ -7719,10 +8317,10 @@
assertNotNull(mCm.getActiveNetworkForUid(restrictedUid));
// Start the restricted profile, and check that the UID within it loses network access.
- when(mPackageManager.getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER))
- .thenReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID));
- when(mUserManager.getAliveUsers()).thenReturn(asList(PRIMARY_USER_INFO,
- RESTRICTED_USER_INFO));
+ doReturn(UserHandle.getUid(RESTRICTED_USER, VPN_UID)).when(mPackageManager)
+ .getPackageUidAsUser(ALWAYS_ON_PACKAGE, RESTRICTED_USER);
+ doReturn(asList(PRIMARY_USER_INFO, RESTRICTED_USER_INFO)).when(mUserManager)
+ .getAliveUsers();
// TODO: check that VPN app within restricted profile still has access, etc.
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(RESTRICTED_USER));
@@ -7732,7 +8330,7 @@
assertNull(mCm.getActiveNetworkForUid(restrictedUid));
// Stop the restricted profile, and check that the UID within it has network access again.
- when(mUserManager.getAliveUsers()).thenReturn(asList(PRIMARY_USER_INFO));
+ doReturn(asList(PRIMARY_USER_INFO)).when(mUserManager).getAliveUsers();
// Send a USER_REMOVED broadcast and expect to lose the UID range for the restricted user.
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
@@ -7818,8 +8416,8 @@
mMockVpn.disconnect();
}
- @Test
- public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() throws Exception {
+ @Test
+ public void testIsActiveNetworkMeteredOverVpnSpecifyingUnderlyingNetworks() throws Exception {
// Returns true by default when no network is available.
assertTrue(mCm.isActiveNetworkMetered());
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
@@ -8384,10 +8982,56 @@
mCm.unregisterNetworkCallback(vpnDefaultCallbackAsUid);
}
+ @Test
+ public void testVpnExcludesOwnUid() throws Exception {
+ // required for registerDefaultNetworkCallbackForUid.
+ mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
+
+ // Connect Wi-Fi.
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true /* validated */);
+
+ // Connect a VPN that excludes its UID from its UID ranges.
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(VPN_IFNAME);
+ final int myUid = Process.myUid();
+ final Set<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(0, myUid - 1));
+ ranges.add(new UidRange(myUid + 1, UserHandle.PER_USER_RANGE - 1));
+ mMockVpn.setUnderlyingNetworks(new Network[]{mWiFiNetworkAgent.getNetwork()});
+ mMockVpn.establish(lp, myUid, ranges);
+
+ // Wait for validation before registering callbacks.
+ waitForIdle();
+
+ final int otherUid = myUid + 1;
+ final Handler h = new Handler(ConnectivityThread.getInstanceLooper());
+ final TestNetworkCallback otherUidCb = new TestNetworkCallback();
+ final TestNetworkCallback defaultCb = new TestNetworkCallback();
+ final TestNetworkCallback perUidCb = new TestNetworkCallback();
+ registerDefaultNetworkCallbackAsUid(otherUidCb, otherUid);
+ mCm.registerDefaultNetworkCallback(defaultCb, h);
+ doAsUid(Process.SYSTEM_UID,
+ () -> mCm.registerDefaultNetworkCallbackForUid(myUid, perUidCb, h));
+
+ otherUidCb.expectAvailableCallbacksValidated(mMockVpn);
+ // BUG (b/195265065): the default network for the VPN app is actually Wi-Fi, not the VPN.
+ defaultCb.expectAvailableCallbacksValidated(mMockVpn);
+ perUidCb.expectAvailableCallbacksValidated(mMockVpn);
+ // getActiveNetwork is not affected by this bug.
+ assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetworkForUid(myUid + 1));
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
+ assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(myUid));
+
+ doAsUid(otherUid, () -> mCm.unregisterNetworkCallback(otherUidCb));
+ mCm.unregisterNetworkCallback(defaultCb);
+ doAsUid(Process.SYSTEM_UID, () -> mCm.unregisterNetworkCallback(perUidCb));
+ }
+
private void setupLegacyLockdownVpn() {
final String profileName = "testVpnProfile";
final byte[] profileTag = profileName.getBytes(StandardCharsets.UTF_8);
- when(mVpnProfileStore.get(Credentials.LOCKDOWN_VPN)).thenReturn(profileTag);
+ doReturn(profileTag).when(mVpnProfileStore).get(Credentials.LOCKDOWN_VPN);
final VpnProfile profile = new VpnProfile(profileName);
profile.name = "My VPN";
@@ -8395,7 +9039,7 @@
profile.dnsServers = "8.8.8.8";
profile.type = VpnProfile.TYPE_IPSEC_XAUTH_PSK;
final byte[] encodedProfile = profile.encode();
- when(mVpnProfileStore.get(Credentials.VPN + profileName)).thenReturn(encodedProfile);
+ doReturn(encodedProfile).when(mVpnProfileStore).get(Credentials.VPN + profileName);
}
private void establishLegacyLockdownVpn(Network underlying) throws Exception {
@@ -8677,18 +9321,20 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+ final ArrayTrackRecord<ReportedInterfaces>.ReadHead readHead =
+ mDeps.mReportedInterfaceHistory.newReadHead();
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
cellLp.getInterfaceName(),
- new int[] { TRANSPORT_CELLULAR });
+ new int[] { TRANSPORT_CELLULAR })));
final LinkProperties wifiLp = new LinkProperties();
wifiLp.setInterfaceName("wifi0");
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
mWiFiNetworkAgent.connect(true);
waitForIdle();
- verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
wifiLp.getInterfaceName(),
- new int[] { TRANSPORT_WIFI });
+ new int[] { TRANSPORT_WIFI })));
mCellNetworkAgent.disconnect();
mWiFiNetworkAgent.disconnect();
@@ -8697,9 +9343,9 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
cellLp.getInterfaceName(),
- new int[] { TRANSPORT_CELLULAR });
+ new int[] { TRANSPORT_CELLULAR })));
mCellNetworkAgent.disconnect();
}
@@ -8782,9 +9428,11 @@
assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default);
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
- verify(mDeps).reportNetworkInterfaceForTransports(mServiceContext,
+ final ArrayTrackRecord<ReportedInterfaces>.ReadHead readHead =
+ mDeps.mReportedInterfaceHistory.newReadHead();
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
cellLp.getInterfaceName(),
- new int[] { TRANSPORT_CELLULAR });
+ new int[] { TRANSPORT_CELLULAR })));
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -8803,15 +9451,15 @@
// Make sure BatteryStats was not told about any v4- interfaces, as none should have
// come online yet.
waitForIdle();
- verify(mDeps, never())
- .reportNetworkInterfaceForTransports(eq(mServiceContext), startsWith("v4-"), any());
+ assertNull(readHead.poll(0 /* timeout */, ri -> mServiceContext.equals(ri.context)
+ && ri.iface != null && ri.iface.startsWith("v4-")));
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
reset(mMockDnsResolver);
- when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfigParcel(myIpv4));
+ doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
+ .interfaceGetCfg(CLAT_MOBILE_IFNAME);
// Remove IPv4 address. Expect prefix discovery to be started again.
cellLp.removeLinkAddress(myIpv4);
@@ -8856,13 +9504,13 @@
assertTrue(CollectionUtils.contains(resolvrParams.servers, "8.8.8.8"));
for (final LinkProperties stackedLp : stackedLpsAfterChange) {
- verify(mDeps).reportNetworkInterfaceForTransports(
- mServiceContext, stackedLp.getInterfaceName(),
- new int[] { TRANSPORT_CELLULAR });
+ assertNotNull(readHead.poll(TIMEOUT_MS, ri -> ri.contentEquals(mServiceContext,
+ stackedLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR })));
}
reset(mMockNetd);
- when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfigParcel(myIpv4));
+ doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
+ .interfaceGetCfg(CLAT_MOBILE_IFNAME);
// Change the NAT64 prefix without first removing it.
// Expect clatd to be stopped and started with the new prefix.
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
@@ -8910,8 +9558,8 @@
verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
reset(mMockDnsResolver);
- when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfigParcel(myIpv4));
+ doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
+ .interfaceGetCfg(CLAT_MOBILE_IFNAME);
// Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
@@ -8965,8 +9613,8 @@
// Test disconnecting a network that is running 464xlat.
// Connect a network with a NAT64 prefix.
- when(mMockNetd.interfaceGetCfg(CLAT_MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfigParcel(myIpv4));
+ doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
+ .interfaceGetCfg(CLAT_MOBILE_IFNAME);
cellLp.setNat64Prefix(kNat64Prefix);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(false /* validated */);
@@ -9175,7 +9823,7 @@
@Test
public void testWith464XlatDisable() throws Exception {
- doReturn(false).when(mDeps).getCellular464XlatEnabled();
+ mDeps.setCellular464XlatEnabled(false);
final TestNetworkCallback callback = new TestNetworkCallback();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
@@ -9335,7 +9983,7 @@
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
final Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
- when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo);
+ mProxyTracker.setGlobalProxy(testProxyInfo);
assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork));
}
@@ -9511,6 +10159,19 @@
}
@Test
+ public void testStartVpnProfileFromDiffPackage() throws Exception {
+ final String notMyVpnPkg = "com.not.my.vpn";
+ assertThrows(
+ SecurityException.class, () -> mVpnManagerService.startVpnProfile(notMyVpnPkg));
+ }
+
+ @Test
+ public void testStopVpnProfileFromDiffPackage() throws Exception {
+ final String notMyVpnPkg = "com.not.my.vpn";
+ assertThrows(SecurityException.class, () -> mVpnManagerService.stopVpnProfile(notMyVpnPkg));
+ }
+
+ @Test
public void testUidUpdateChangesInterfaceFilteringRule() throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
@@ -9598,16 +10259,16 @@
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = targetSdk;
- when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
- .thenReturn(applicationInfo);
- when(mPackageManager.getTargetSdkVersion(any())).thenReturn(targetSdk);
+ doReturn(applicationInfo).when(mPackageManager)
+ .getApplicationInfoAsUser(anyString(), anyInt(), any());
+ doReturn(targetSdk).when(mPackageManager).getTargetSdkVersion(any());
- when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
+ doReturn(locationToggle).when(mLocationManager).isLocationEnabledForUser(any());
if (op != null) {
- when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()),
- eq(mContext.getPackageName()), eq(getAttributionTag()), anyString()))
- .thenReturn(AppOpsManager.MODE_ALLOWED);
+ doReturn(AppOpsManager.MODE_ALLOWED).when(mAppOpsManager).noteOp(
+ eq(op), eq(Process.myUid()), eq(mContext.getPackageName()),
+ eq(getAttributionTag()), anyString());
}
if (perm != null) {
@@ -9629,7 +10290,7 @@
int callerUid, boolean includeLocationSensitiveInfo,
boolean shouldMakeCopyWithLocationSensitiveFieldsParcelable) {
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions()).thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION).when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -9786,8 +10447,8 @@
mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_GRANTED);
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions())
- .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS)
+ .when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -9805,8 +10466,8 @@
mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED);
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions())
- .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_LOCAL_MAC_ADDRESS)
+ .when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -9825,8 +10486,8 @@
mServiceContext.setPermission(NETWORK_SETTINGS, PERMISSION_GRANTED);
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions())
- .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS)
+ .when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -9844,8 +10505,8 @@
mServiceContext.setPermission(Manifest.permission.LOCAL_MAC_ADDRESS, PERMISSION_DENIED);
final TransportInfo transportInfo = mock(TransportInfo.class);
- when(transportInfo.getApplicableRedactions())
- .thenReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS);
+ doReturn(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_NETWORK_SETTINGS)
+ .when(transportInfo).getApplicableRedactions();
final NetworkCapabilities netCap =
new NetworkCapabilities().setTransportInfo(transportInfo);
@@ -9932,7 +10593,7 @@
@NonNull TestNetworkCallback wifiNetworkCallback, int actualOwnerUid,
@NonNull TransportInfo actualTransportInfo, int expectedOwnerUid,
@NonNull TransportInfo expectedTransportInfo) throws Exception {
- when(mPackageManager.getTargetSdkVersion(anyString())).thenReturn(Build.VERSION_CODES.S);
+ doReturn(Build.VERSION_CODES.S).when(mPackageManager).getTargetSdkVersion(anyString());
final NetworkCapabilities ncTemplate =
new NetworkCapabilities()
.addTransportType(TRANSPORT_WIFI)
@@ -10020,7 +10681,7 @@
final UnderlyingNetworkInfo underlyingNetworkInfo =
new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<>());
mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo);
- doReturn(42).when(mDeps).getConnectionOwnerUid(anyInt(), any(), any());
+ mDeps.setConnectionOwnerUid(42);
}
private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
@@ -10162,7 +10823,7 @@
public void testRegisterUnregisterConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest wifiRequest =
new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
- when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+ doReturn(mIBinder).when(mConnectivityDiagnosticsCallback).asBinder();
mService.registerConnectivityDiagnosticsCallback(
mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
@@ -10185,7 +10846,7 @@
public void testRegisterDuplicateConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest wifiRequest =
new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI).build();
- when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+ doReturn(mIBinder).when(mConnectivityDiagnosticsCallback).asBinder();
mService.registerConnectivityDiagnosticsCallback(
mConnectivityDiagnosticsCallback, wifiRequest, mContext.getPackageName());
@@ -10207,6 +10868,35 @@
assertTrue(mService.mConnectivityDiagnosticsCallbacks.containsKey(mIBinder));
}
+ @Test(expected = NullPointerException.class)
+ public void testRegisterConnectivityDiagnosticsCallbackNullCallback() {
+ mService.registerConnectivityDiagnosticsCallback(
+ null /* callback */,
+ new NetworkRequest.Builder().build(),
+ mContext.getPackageName());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testRegisterConnectivityDiagnosticsCallbackNullNetworkRequest() {
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback,
+ null /* request */,
+ mContext.getPackageName());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testRegisterConnectivityDiagnosticsCallbackNullPackageName() {
+ mService.registerConnectivityDiagnosticsCallback(
+ mConnectivityDiagnosticsCallback,
+ new NetworkRequest.Builder().build(),
+ null /* callingPackageName */);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testUnregisterConnectivityDiagnosticsCallbackNullPackageName() {
+ mService.unregisterConnectivityDiagnosticsCallback(null /* callback */);
+ }
+
public NetworkAgentInfo fakeMobileNai(NetworkCapabilities nc) {
final NetworkCapabilities cellNc = new NetworkCapabilities.Builder(nc)
.addTransportType(TRANSPORT_CELLULAR).build();
@@ -10224,6 +10914,14 @@
return fakeNai(wifiNc, info);
}
+ private NetworkAgentInfo fakeVpnNai(NetworkCapabilities nc) {
+ final NetworkCapabilities vpnNc = new NetworkCapabilities.Builder(nc)
+ .addTransportType(TRANSPORT_VPN).build();
+ final NetworkInfo info = new NetworkInfo(TYPE_VPN, 0 /* subtype */,
+ ConnectivityManager.getNetworkTypeName(TYPE_VPN), "" /* subtypeName */);
+ return fakeNai(vpnNc, info);
+ }
+
private NetworkAgentInfo fakeNai(NetworkCapabilities nc, NetworkInfo networkInfo) {
return new NetworkAgentInfo(null, new Network(NET_ID), networkInfo, new LinkProperties(),
nc, new NetworkScore.Builder().setLegacyInt(0).build(),
@@ -10358,6 +11056,36 @@
}
@Test
+ public void testUnderlyingNetworksWillBeSetInNetworkAgentInfoConstructor() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastT());
+ final Network network1 = new Network(100);
+ final Network network2 = new Network(101);
+ final List<Network> underlyingNetworks = new ArrayList<>();
+ final NetworkCapabilities ncWithEmptyUnderlyingNetworks = new NetworkCapabilities.Builder()
+ .setUnderlyingNetworks(underlyingNetworks)
+ .build();
+ final NetworkAgentInfo vpnNaiWithEmptyUnderlyingNetworks =
+ fakeVpnNai(ncWithEmptyUnderlyingNetworks);
+ assertEquals(underlyingNetworks,
+ Arrays.asList(vpnNaiWithEmptyUnderlyingNetworks.declaredUnderlyingNetworks));
+
+ underlyingNetworks.add(network1);
+ underlyingNetworks.add(network2);
+ final NetworkCapabilities ncWithUnderlyingNetworks = new NetworkCapabilities.Builder()
+ .setUnderlyingNetworks(underlyingNetworks)
+ .build();
+ final NetworkAgentInfo vpnNaiWithUnderlyingNetwokrs = fakeVpnNai(ncWithUnderlyingNetworks);
+ assertEquals(underlyingNetworks,
+ Arrays.asList(vpnNaiWithUnderlyingNetwokrs.declaredUnderlyingNetworks));
+
+ final NetworkCapabilities ncWithoutUnderlyingNetworks = new NetworkCapabilities.Builder()
+ .build();
+ final NetworkAgentInfo vpnNaiWithoutUnderlyingNetwokrs =
+ fakeVpnNai(ncWithoutUnderlyingNetworks);
+ assertNull(vpnNaiWithoutUnderlyingNetwokrs.declaredUnderlyingNetworks);
+ }
+
+ @Test
public void testRegisterConnectivityDiagnosticsCallbackCallsOnConnectivityReport()
throws Exception {
// Set up the Network, which leads to a ConnectivityReport being cached for the network.
@@ -10371,7 +11099,7 @@
callback.assertNoCallback();
final NetworkRequest request = new NetworkRequest.Builder().build();
- when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+ doReturn(mIBinder).when(mConnectivityDiagnosticsCallback).asBinder();
mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
@@ -10387,7 +11115,7 @@
private void setUpConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().build();
- when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
+ doReturn(mIBinder).when(mConnectivityDiagnosticsCallback).asBinder();
mServiceContext.setPermission(NETWORK_STACK, PERMISSION_GRANTED);
@@ -10409,6 +11137,12 @@
mCellNetworkAgent.connect(true);
callback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
callback.assertNoCallback();
+
+ // Make sure a report is sent and that the caps are suitably redacted.
+ verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
+ .onConnectivityReportAvailable(argThat(report ->
+ areConnDiagCapsRedacted(report.getNetworkCapabilities())));
+ reset(mConnectivityDiagnosticsCallback);
}
private boolean areConnDiagCapsRedacted(NetworkCapabilities nc) {
@@ -10422,17 +11156,6 @@
}
@Test
- public void testConnectivityDiagnosticsCallbackOnConnectivityReportAvailable()
- throws Exception {
- setUpConnectivityDiagnosticsCallback();
-
- // Verify onConnectivityReport fired
- verify(mConnectivityDiagnosticsCallback, timeout(TIMEOUT_MS))
- .onConnectivityReportAvailable(argThat(report ->
- areConnDiagCapsRedacted(report.getNetworkCapabilities())));
- }
-
- @Test
public void testConnectivityDiagnosticsCallbackOnDataStallSuspected() throws Exception {
setUpConnectivityDiagnosticsCallback();
@@ -10449,9 +11172,6 @@
public void testConnectivityDiagnosticsCallbackOnConnectivityReported() throws Exception {
setUpConnectivityDiagnosticsCallback();
- // reset to ignore callbacks from setup
- reset(mConnectivityDiagnosticsCallback);
-
final Network n = mCellNetworkAgent.getNetwork();
final boolean hasConnectivity = true;
mService.reportNetworkConnectivity(n, hasConnectivity);
@@ -10483,9 +11203,6 @@
throws Exception {
setUpConnectivityDiagnosticsCallback();
- // reset to ignore callbacks from setup
- reset(mConnectivityDiagnosticsCallback);
-
// report known Connectivity from a different uid. Verify that network is not re-validated
// and this callback is not notified.
final Network n = mCellNetworkAgent.getNetwork();
@@ -10517,6 +11234,24 @@
areConnDiagCapsRedacted(report.getNetworkCapabilities())));
}
+ @Test(expected = NullPointerException.class)
+ public void testSimulateDataStallNullNetwork() {
+ mService.simulateDataStall(
+ DataStallReport.DETECTION_METHOD_DNS_EVENTS,
+ 0L /* timestampMillis */,
+ null /* network */,
+ new PersistableBundle());
+ }
+
+ @Test(expected = NullPointerException.class)
+ public void testSimulateDataStallNullPersistableBundle() {
+ mService.simulateDataStall(
+ DataStallReport.DETECTION_METHOD_DNS_EVENTS,
+ 0L /* timestampMillis */,
+ mock(Network.class),
+ null /* extras */);
+ }
+
@Test
public void testRouteAddDeleteUpdate() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().build();
@@ -10656,6 +11391,7 @@
assertNull(mService.getProxyForNetwork(null));
assertNull(mCm.getDefaultProxy());
+ final ExpectedBroadcast b1 = registerPacProxyBroadcast();
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
@@ -10665,9 +11401,10 @@
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
// VPN is connected but proxy is not set, so there is no need to send proxy broadcast.
- verify(mProxyTracker, never()).sendProxyBroadcast();
+ b1.expectNoBroadcast(500);
// Update to new range which is old range minus APP1, i.e. only APP2
+ final ExpectedBroadcast b2 = registerPacProxyBroadcast();
final Set<UidRange> newRanges = new HashSet<>(asList(
new UidRange(vpnRange.start, APP1_UID - 1),
new UidRange(APP1_UID + 1, vpnRange.stop)));
@@ -10678,37 +11415,37 @@
assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID);
// Uid has changed but proxy is not set, so there is no need to send proxy broadcast.
- verify(mProxyTracker, never()).sendProxyBroadcast();
+ b2.expectNoBroadcast(500);
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+ final ExpectedBroadcast b3 = registerPacProxyBroadcast();
lp.setHttpProxy(testProxyInfo);
mMockVpn.sendLinkProperties(lp);
waitForIdle();
// Proxy is set, so send a proxy broadcast.
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
- reset(mProxyTracker);
+ b3.expectBroadcast();
+ final ExpectedBroadcast b4 = registerPacProxyBroadcast();
mMockVpn.setUids(vpnRanges);
waitForIdle();
// Uid has changed and proxy is already set, so send a proxy broadcast.
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
- reset(mProxyTracker);
+ b4.expectBroadcast();
+ final ExpectedBroadcast b5 = registerPacProxyBroadcast();
// Proxy is removed, send a proxy broadcast.
lp.setHttpProxy(null);
mMockVpn.sendLinkProperties(lp);
waitForIdle();
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
- reset(mProxyTracker);
+ b5.expectBroadcast();
// Proxy is added in WiFi(default network), setDefaultProxy will be called.
final LinkProperties wifiLp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork());
assertNotNull(wifiLp);
+ final ExpectedBroadcast b6 = expectProxyChangeAction(testProxyInfo);
wifiLp.setHttpProxy(testProxyInfo);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
- verify(mProxyTracker, times(1)).setDefaultProxy(eq(testProxyInfo));
- reset(mProxyTracker);
+ b6.expectBroadcast();
}
@Test
@@ -10727,18 +11464,21 @@
lp.setHttpProxy(testProxyInfo);
final UidRange vpnRange = PRIMARY_UIDRANGE;
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
+ final ExpectedBroadcast b1 = registerPacProxyBroadcast();
mMockVpn.setOwnerAndAdminUid(VPN_UID);
mMockVpn.registerAgent(false, vpnRanges, lp);
// In any case, the proxy broadcast won't be sent before VPN goes into CONNECTED state.
// Otherwise, the app that calls ConnectivityManager#getDefaultProxy() when it receives the
// proxy broadcast will get null.
- verify(mProxyTracker, never()).sendProxyBroadcast();
+ b1.expectNoBroadcast(500);
+
+ final ExpectedBroadcast b2 = registerPacProxyBroadcast();
mMockVpn.connect(true /* validated */, true /* hasInternet */, false /* isStrictMode */);
waitForIdle();
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
// Vpn is connected with proxy, so the proxy broadcast will be sent to inform the apps to
// update their proxy data.
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
+ b2.expectBroadcast();
}
@Test
@@ -10767,10 +11507,10 @@
final LinkProperties cellularLp = new LinkProperties();
cellularLp.setInterfaceName(MOBILE_IFNAME);
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
+ final ExpectedBroadcast b = registerPacProxyBroadcast();
cellularLp.setHttpProxy(testProxyInfo);
mCellNetworkAgent.sendLinkProperties(cellularLp);
- waitForIdle();
- verify(mProxyTracker, times(1)).sendProxyBroadcast();
+ b.expectBroadcast();
}
@Test
@@ -10889,8 +11629,8 @@
final Pair<IQosCallback, IBinder> pair = createQosCallback();
mCallback = pair.first;
- when(mFilter.getNetwork()).thenReturn(network);
- when(mFilter.validate()).thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(network).when(mFilter).getNetwork();
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE).when(mFilter).validate();
mAgentWrapper = mCellNetworkAgent;
}
@@ -10912,8 +11652,8 @@
private Pair<IQosCallback, IBinder> createQosCallback() {
final IQosCallback callback = mock(IQosCallback.class);
final IBinder binder = mock(Binder.class);
- when(callback.asBinder()).thenReturn(binder);
- when(binder.isBinderAlive()).thenReturn(true);
+ doReturn(binder).when(callback).asBinder();
+ doReturn(true).when(binder).isBinderAlive();
return new Pair<>(callback, binder);
}
@@ -10923,8 +11663,8 @@
mQosCallbackMockHelper = new QosCallbackMockHelper();
final NetworkAgentWrapper wrapper = mQosCallbackMockHelper.mAgentWrapper;
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
@@ -10947,8 +11687,8 @@
public void testQosCallbackNoRegistrationOnValidationError() throws Exception {
mQosCallbackMockHelper = new QosCallbackMockHelper();
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED)
+ .when(mQosCallbackMockHelper.mFilter).validate();
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
waitForIdle();
@@ -10962,8 +11702,8 @@
final int sessionId = 10;
final int qosCallbackId = 1;
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
waitForIdle();
@@ -10992,8 +11732,8 @@
final int sessionId = 10;
final int qosCallbackId = 1;
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
waitForIdle();
@@ -11020,8 +11760,8 @@
public void testQosCallbackTooManyRequests() throws Exception {
mQosCallbackMockHelper = new QosCallbackMockHelper();
- when(mQosCallbackMockHelper.mFilter.validate())
- .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
for (int i = 0; i < 100; i++) {
final Pair<IQosCallback, IBinder> pair = createQosCallback();
@@ -11052,8 +11792,8 @@
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.uid = uid;
try {
- when(mPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(), eq(user)))
- .thenReturn(applicationInfo);
+ doReturn(applicationInfo).when(mPackageManager).getApplicationInfoAsUser(
+ eq(packageName), anyInt(), eq(user));
} catch (Exception e) {
fail(e.getMessage());
}
@@ -11062,13 +11802,12 @@
private void mockGetApplicationInfoThrowsNameNotFound(@NonNull final String packageName,
@NonNull final UserHandle user)
throws Exception {
- when(mPackageManager.getApplicationInfoAsUser(eq(packageName), anyInt(), eq(user)))
- .thenThrow(new PackageManager.NameNotFoundException(packageName));
+ doThrow(new PackageManager.NameNotFoundException(packageName)).when(
+ mPackageManager).getApplicationInfoAsUser(eq(packageName), anyInt(), eq(user));
}
private void mockHasSystemFeature(@NonNull final String featureName, final boolean hasFeature) {
- when(mPackageManager.hasSystemFeature(eq(featureName)))
- .thenReturn(hasFeature);
+ doReturn(hasFeature).when(mPackageManager).hasSystemFeature(eq(featureName));
}
private Range<Integer> getNriFirstUidRange(@NonNull final NetworkRequestInfo nri) {
@@ -11268,8 +12007,8 @@
// Arrange users
final int secondUserTestPackageUid = UserHandle.getUid(SECONDARY_USER, TEST_PACKAGE_UID);
final int thirdUserTestPackageUid = UserHandle.getUid(TERTIARY_USER, TEST_PACKAGE_UID);
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE, SECONDARY_USER_HANDLE, TERTIARY_USER_HANDLE));
+ doReturn(asList(PRIMARY_USER_HANDLE, SECONDARY_USER_HANDLE, TERTIARY_USER_HANDLE))
+ .when(mUserManager).getUserHandles(anyBoolean());
// Arrange PackageManager mocks testing for users who have and don't have a package.
mockGetApplicationInfoThrowsNameNotFound(TEST_PACKAGE_NAME, PRIMARY_USER_HANDLE);
@@ -12215,8 +12954,8 @@
// Arrange users
final int secondUser = 10;
final UserHandle secondUserHandle = new UserHandle(secondUser);
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE, secondUserHandle));
+ doReturn(asList(PRIMARY_USER_HANDLE, secondUserHandle)).when(mUserManager)
+ .getUserHandles(anyBoolean());
// Arrange PackageManager mocks
final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
@@ -12256,8 +12995,7 @@
// Arrange users
final int secondUser = 10;
final UserHandle secondUserHandle = new UserHandle(secondUser);
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE));
+ doReturn(asList(PRIMARY_USER_HANDLE)).when(mUserManager).getUserHandles(anyBoolean());
// Arrange PackageManager mocks
final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
@@ -12284,8 +13022,8 @@
false /* shouldDestroyNetwork */);
// Send a broadcast indicating a user was added.
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE, secondUserHandle));
+ doReturn(asList(PRIMARY_USER_HANDLE, secondUserHandle)).when(mUserManager)
+ .getUserHandles(anyBoolean());
final Intent addedIntent = new Intent(ACTION_USER_ADDED);
addedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser));
processBroadcast(addedIntent);
@@ -12297,8 +13035,7 @@
false /* shouldDestroyNetwork */);
// Send a broadcast indicating a user was removed.
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(
- asList(PRIMARY_USER_HANDLE));
+ doReturn(asList(PRIMARY_USER_HANDLE)).when(mUserManager).getUserHandles(anyBoolean());
final Intent removedIntent = new Intent(ACTION_USER_REMOVED);
removedIntent.putExtra(Intent.EXTRA_USER, UserHandle.of(secondUser));
processBroadcast(removedIntent);
@@ -12837,21 +13574,26 @@
assertLength(2, snapshots);
assertContainsAll(snapshots, cellSnapshot, wifiSnapshot);
- // Set cellular as suspended, verify the snapshots will not contain suspended networks.
- // TODO: Consider include SUSPENDED networks, which should be considered as
- // temporary shortage of connectivity of a connected network.
+ // Set cellular as suspended, verify the snapshots will contain suspended networks.
mCellNetworkAgent.suspend();
waitForIdle();
+ final NetworkCapabilities cellSuspendedNc =
+ mCm.getNetworkCapabilities(mCellNetworkAgent.getNetwork());
+ assertFalse(cellSuspendedNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
+ final NetworkStateSnapshot cellSuspendedSnapshot = new NetworkStateSnapshot(
+ mCellNetworkAgent.getNetwork(), cellSuspendedNc, cellLp,
+ null, ConnectivityManager.TYPE_MOBILE);
snapshots = mCm.getAllNetworkStateSnapshots();
- assertLength(1, snapshots);
- assertEquals(wifiSnapshot, snapshots.get(0));
+ assertLength(2, snapshots);
+ assertContainsAll(snapshots, cellSuspendedSnapshot, wifiSnapshot);
- // Disconnect wifi, verify the snapshots contain nothing.
+ // Disconnect wifi, verify the snapshots contain only cellular.
mWiFiNetworkAgent.disconnect();
waitForIdle();
snapshots = mCm.getAllNetworkStateSnapshots();
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
- assertLength(0, snapshots);
+ assertLength(1, snapshots);
+ assertEquals(cellSuspendedSnapshot, snapshots.get(0));
mCellNetworkAgent.resume();
waitForIdle();
@@ -13400,17 +14142,45 @@
@Test
public void testProfileNetworkPrefCountsRequestsCorrectlyOnSet() throws Exception {
final UserHandle testHandle = setupEnterpriseNetwork();
- testRequestCountLimits(() -> {
- // Set initially to test the limit prior to having existing requests.
- final TestOnCompleteListener listener = new TestOnCompleteListener();
- mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
- Runnable::run, listener);
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ // Leave one request available so the profile preference can be set.
+ testRequestCountLimits(1 /* countToLeaveAvailable */, () -> {
+ withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ Process.myPid(), Process.myUid(), () -> {
+ // Set initially to test the limit prior to having existing requests.
+ mCm.setProfileNetworkPreference(testHandle,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
+ Runnable::run, listener);
+ });
listener.expectOnComplete();
- // re-set so as to test the limit as part of replacing existing requests.
- mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
- Runnable::run, listener);
+ // Simulate filing requests as some app on the work profile
+ final int otherAppUid = UserHandle.getUid(TEST_WORK_PROFILE_USER_ID,
+ UserHandle.getAppId(Process.myUid() + 1));
+ final int remainingCount = ConnectivityService.MAX_NETWORK_REQUESTS_PER_UID
+ - mService.mNetworkRequestCounter.mUidToNetworkRequestCount.get(otherAppUid)
+ - 1;
+ final NetworkCallback[] callbacks = new NetworkCallback[remainingCount];
+ doAsUid(otherAppUid, () -> {
+ for (int i = 0; i < remainingCount; ++i) {
+ callbacks[i] = new TestableNetworkCallback();
+ mCm.registerDefaultNetworkCallback(callbacks[i]);
+ }
+ });
+
+ withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ Process.myPid(), Process.myUid(), () -> {
+ // re-set so as to test the limit as part of replacing existing requests.
+ mCm.setProfileNetworkPreference(testHandle,
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE, Runnable::run, listener);
+ });
listener.expectOnComplete();
+
+ doAsUid(otherAppUid, () -> {
+ for (final NetworkCallback callback : callbacks) {
+ mCm.unregisterNetworkCallback(callback);
+ }
+ });
});
}
@@ -13422,39 +14192,45 @@
mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
@OemNetworkPreferences.OemNetworkPreference final int networkPref =
OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
- testRequestCountLimits(() -> {
- // Set initially to test the limit prior to having existing requests.
- final TestOemListenerCallback listener = new TestOemListenerCallback();
- mService.setOemNetworkPreference(
- createDefaultOemNetworkPreferences(networkPref), listener);
- listener.expectOnComplete();
+ // Leave one request available so the OEM preference can be set.
+ testRequestCountLimits(1 /* countToLeaveAvailable */, () ->
+ withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> {
+ // Set initially to test the limit prior to having existing requests.
+ final TestOemListenerCallback listener = new TestOemListenerCallback();
+ mService.setOemNetworkPreference(
+ createDefaultOemNetworkPreferences(networkPref), listener);
+ listener.expectOnComplete();
- // re-set so as to test the limit as part of replacing existing requests.
- mService.setOemNetworkPreference(
- createDefaultOemNetworkPreferences(networkPref), listener);
- listener.expectOnComplete();
- });
+ // re-set so as to test the limit as part of replacing existing requests.
+ mService.setOemNetworkPreference(
+ createDefaultOemNetworkPreferences(networkPref), listener);
+ listener.expectOnComplete();
+ }));
}
- private void testRequestCountLimits(@NonNull final Runnable r) throws Exception {
+ private void testRequestCountLimits(final int countToLeaveAvailable,
+ @NonNull final ExceptionalRunnable r) throws Exception {
final ArraySet<TestNetworkCallback> callbacks = new ArraySet<>();
try {
final int requestCount = mService.mSystemNetworkRequestCounter
.mUidToNetworkRequestCount.get(Process.myUid());
- // The limit is hit when total requests <= limit.
- final int maxCount =
- ConnectivityService.MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - requestCount;
+ // The limit is hit when total requests = limit - 1, and exceeded with a crash when
+ // total requests >= limit.
+ final int countToFile =
+ MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - requestCount - countToLeaveAvailable;
// Need permission so registerDefaultNetworkCallback uses mSystemNetworkRequestCounter
withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> {
- for (int i = 1; i < maxCount - 1; i++) {
+ for (int i = 1; i < countToFile; i++) {
final TestNetworkCallback cb = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(cb);
callbacks.add(cb);
}
-
- // Code to run to check if it triggers a max request count limit error.
- r.run();
+ assertEquals(MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - 1 - countToLeaveAvailable,
+ mService.mSystemNetworkRequestCounter
+ .mUidToNetworkRequestCount.get(Process.myUid()));
});
+ // Code to run to check if it triggers a max request count limit error.
+ r.run();
} finally {
for (final TestNetworkCallback cb : callbacks) {
mCm.unregisterNetworkCallback(cb);
@@ -13699,15 +14475,18 @@
public void testMobileDataPreferredUidsChangedCountsRequestsCorrectlyOnSet() throws Exception {
ConnectivitySettingsManager.setMobileDataPreferredUids(mServiceContext,
Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)));
- testRequestCountLimits(() -> {
- // Set initially to test the limit prior to having existing requests.
- mService.updateMobileDataPreferredUids();
- waitForIdle();
+ // Leave one request available so MDO preference set up above can be set.
+ testRequestCountLimits(1 /* countToLeaveAvailable */, () ->
+ withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ Process.myPid(), Process.myUid(), () -> {
+ // Set initially to test the limit prior to having existing requests.
+ mService.updateMobileDataPreferredUids();
+ waitForIdle();
- // re-set so as to test the limit as part of replacing existing requests.
- mService.updateMobileDataPreferredUids();
- waitForIdle();
- });
+ // re-set so as to test the limit as part of replacing existing requests
+ mService.updateMobileDataPreferredUids();
+ waitForIdle();
+ }));
}
@Test
@@ -13848,7 +14627,7 @@
// callback.
final int[] uids2 = new int[] { TEST_WORK_PROFILE_APP_UID };
final UidRangeParcel[] uidRanges2 = toUidRangeStableParcels(uidRangesForUids(uids2));
- when(mUserManager.getUserHandles(anyBoolean())).thenReturn(Arrays.asList(testHandle));
+ doReturn(Arrays.asList(testHandle)).when(mUserManager).getUserHandles(anyBoolean());
setupSetOemNetworkPreferenceForPreferenceTest(
networkPref, uidRanges2, "com.android.test", testHandle);
mDefaultNetworkCallback.assertNoCallback();
@@ -13886,4 +14665,11 @@
mDefaultNetworkCallback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
mDefaultNetworkCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
}
+
+ @Test
+ public void testRequestRouteToHostAddress_PackageDoesNotBelongToCaller() {
+ assertThrows(SecurityException.class, () -> mService.requestRouteToHostAddress(
+ ConnectivityManager.TYPE_NONE, null /* hostAddress */, "com.not.package.owner",
+ null /* callingAttributionTag */));
+ }
}
diff --git a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
index 5bbbe40..45f3d3c 100644
--- a/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -60,6 +60,7 @@
import android.os.Binder;
import android.os.Build;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.system.Os;
import android.test.mock.MockContext;
import android.util.ArraySet;
@@ -188,9 +189,15 @@
}
}
+ private IpSecService.Dependencies makeDependencies() throws RemoteException {
+ final IpSecService.Dependencies deps = mock(IpSecService.Dependencies.class);
+ when(deps.getNetdInstance(mTestContext)).thenReturn(mMockNetd);
+ return deps;
+ }
+
INetd mMockNetd;
PackageManager mMockPkgMgr;
- IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+ IpSecService.Dependencies mDeps;
IpSecService mIpSecService;
Network fakeNetwork = new Network(0xAB);
int mUid = Os.getuid();
@@ -219,11 +226,8 @@
public void setUp() throws Exception {
mMockNetd = mock(INetd.class);
mMockPkgMgr = mock(PackageManager.class);
- mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mTestContext, mMockIpSecSrvConfig);
-
- // Injecting mock netd
- when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
+ mDeps = makeDependencies();
+ mIpSecService = new IpSecService(mTestContext, mDeps);
// PackageManager should always return true (feature flag tests in IpSecServiceTest)
when(mMockPkgMgr.hasSystemFeature(anyString())).thenReturn(true);
diff --git a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
index 6957d51..5c7ca6f 100644
--- a/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceRefcountedResourceTest.java
@@ -57,14 +57,14 @@
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class IpSecServiceRefcountedResourceTest {
Context mMockContext;
- IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+ IpSecService.Dependencies mMockDeps;
IpSecService mIpSecService;
@Before
public void setUp() throws Exception {
mMockContext = mock(Context.class);
- mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
+ mMockDeps = mock(IpSecService.Dependencies.class);
+ mIpSecService = new IpSecService(mMockContext, mMockDeps);
}
private void assertResourceState(
diff --git a/tests/unit/java/com/android/server/IpSecServiceTest.java b/tests/unit/java/com/android/server/IpSecServiceTest.java
index fabd6f1..7e6b157 100644
--- a/tests/unit/java/com/android/server/IpSecServiceTest.java
+++ b/tests/unit/java/com/android/server/IpSecServiceTest.java
@@ -46,6 +46,7 @@
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.Process;
+import android.os.RemoteException;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
@@ -122,24 +123,22 @@
Context mMockContext;
INetd mMockNetd;
- IpSecService.IpSecServiceConfiguration mMockIpSecSrvConfig;
+ IpSecService.Dependencies mDeps;
IpSecService mIpSecService;
@Before
public void setUp() throws Exception {
mMockContext = mock(Context.class);
mMockNetd = mock(INetd.class);
- mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class);
- mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig);
-
- // Injecting mock netd
- when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd);
+ mDeps = makeDependencies();
+ mIpSecService = new IpSecService(mMockContext, mDeps);
+ assertNotNull(mIpSecService);
}
- @Test
- public void testIpSecServiceCreate() throws InterruptedException {
- IpSecService ipSecSrv = IpSecService.create(mMockContext);
- assertNotNull(ipSecSrv);
+ private IpSecService.Dependencies makeDependencies() throws RemoteException {
+ final IpSecService.Dependencies deps = mock(IpSecService.Dependencies.class);
+ when(deps.getNetdInstance(mMockContext)).thenReturn(mMockNetd);
+ return deps;
}
@Test
@@ -611,7 +610,7 @@
public void testOpenUdpEncapSocketTagsSocket() throws Exception {
IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class);
IpSecService testIpSecService = new IpSecService(
- mMockContext, mMockIpSecSrvConfig, mockTagger);
+ mMockContext, mDeps, mockTagger);
IpSecUdpEncapResponse udpEncapResp =
testIpSecService.openUdpEncapsulationSocket(0, new Binder());
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 4d2970a..6d1d765 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -20,6 +20,7 @@
import static libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -33,14 +34,19 @@
import android.compat.testing.PlatformCompatChangeRule;
import android.content.ContentResolver;
import android.content.Context;
+import android.net.nsd.INsdManagerCallback;
+import android.net.nsd.INsdServiceConnector;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
import com.android.server.NsdService.DaemonConnection;
@@ -56,11 +62,15 @@
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
+import org.mockito.AdditionalAnswers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
+import java.util.LinkedList;
+import java.util.Queue;
+
// TODOs:
// - test client can send requests and receive replies
// - test NSD_ON ENABLE/DISABLED listening
@@ -73,16 +83,30 @@
private static final long CLEANUP_DELAY_MS = 500;
private static final long TIMEOUT_MS = 500;
+ // Records INsdManagerCallback created when NsdService#connect is called.
+ // Only accessed on the test thread, since NsdService#connect is called by the NsdManager
+ // constructor called on the test thread.
+ private final Queue<INsdManagerCallback> mCreatedCallbacks = new LinkedList<>();
+
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
@Mock Context mContext;
@Mock ContentResolver mResolver;
- @Mock NsdService.NsdSettings mSettings;
NativeCallbackReceiver mDaemonCallback;
@Spy DaemonConnection mDaemon = new DaemonConnection(mDaemonCallback);
HandlerThread mThread;
TestHandler mHandler;
+ private static class LinkToDeathRecorder extends Binder {
+ IBinder.DeathRecipient mDr;
+
+ @Override
+ public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
+ super.linkToDeath(recipient, flags);
+ mDr = recipient;
+ }
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -103,26 +127,29 @@
@Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testPreSClients() {
- when(mSettings.isEnabled()).thenReturn(true);
+ public void testPreSClients() throws Exception {
NsdService service = makeService();
// Pre S client connected, the daemon should be started.
- NsdManager client1 = connectClient(service);
+ connectClient(service);
waitForIdle();
+ final INsdManagerCallback cb1 = getCallback();
+ final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
verify(mDaemon, times(1)).maybeStart();
verifyDaemonCommands("start-service");
- NsdManager client2 = connectClient(service);
+ connectClient(service);
waitForIdle();
+ final INsdManagerCallback cb2 = getCallback();
+ final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
verify(mDaemon, times(1)).maybeStart();
- client1.disconnect();
+ deathRecipient1.binderDied();
// Still 1 client remains, daemon shouldn't be stopped.
waitForIdle();
verify(mDaemon, never()).maybeStop();
- client2.disconnect();
+ deathRecipient2.binderDied();
// All clients are disconnected, the daemon should be stopped.
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
verifyDaemonCommands("stop-service");
@@ -130,43 +157,49 @@
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testNoDaemonStartedWhenClientsConnect() {
- when(mSettings.isEnabled()).thenReturn(true);
-
- NsdService service = makeService();
+ public void testNoDaemonStartedWhenClientsConnect() throws Exception {
+ final NsdService service = makeService();
// Creating an NsdManager will not cause any cmds executed, which means
// no daemon is started.
- NsdManager client1 = connectClient(service);
+ connectClient(service);
waitForIdle();
verify(mDaemon, never()).execute(any());
+ final INsdManagerCallback cb1 = getCallback();
+ final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
// Creating another NsdManager will not cause any cmds executed.
- NsdManager client2 = connectClient(service);
+ connectClient(service);
waitForIdle();
verify(mDaemon, never()).execute(any());
+ final INsdManagerCallback cb2 = getCallback();
+ final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
// If there is no active request, try to clean up the daemon
// every time the client disconnects.
- client1.disconnect();
+ deathRecipient1.binderDied();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
reset(mDaemon);
- client2.disconnect();
+ deathRecipient2.binderDied();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
+ }
- client1.disconnect();
- client2.disconnect();
+ private IBinder.DeathRecipient verifyLinkToDeath(INsdManagerCallback cb)
+ throws Exception {
+ final IBinder.DeathRecipient dr = ((LinkToDeathRecorder) cb.asBinder()).mDr;
+ assertNotNull(dr);
+ return dr;
}
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testClientRequestsAreGCedAtDisconnection() {
- when(mSettings.isEnabled()).thenReturn(true);
-
+ public void testClientRequestsAreGCedAtDisconnection() throws Exception {
NsdService service = makeService();
- NsdManager client = connectClient(service);
+ NsdManager client = connectClient(service);
waitForIdle();
+ final INsdManagerCallback cb1 = getCallback();
+ final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
verify(mDaemon, never()).maybeStart();
verify(mDaemon, never()).execute(any());
@@ -195,20 +228,16 @@
verifyDaemonCommand("resolve 4 a_name a_type local.");
// Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
- client.disconnect();
+ deathRecipient.binderDied();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
// checks that request are cleaned
verifyDaemonCommands("stop-register 2", "stop-discover 3",
"stop-resolve 4", "stop-service");
-
- client.disconnect();
}
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
- public void testCleanupDelayNoRequestActive() {
- when(mSettings.isEnabled()).thenReturn(true);
-
+ public void testCleanupDelayNoRequestActive() throws Exception {
NsdService service = makeService();
NsdManager client = connectClient(service);
@@ -218,6 +247,8 @@
client.registerService(request, PROTOCOL, listener1);
waitForIdle();
verify(mDaemon, times(1)).maybeStart();
+ final INsdManagerCallback cb1 = getCallback();
+ final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
verifyDaemonCommands("start-service", "register 2 a_name a_type 2201");
client.unregisterService(listener1);
@@ -226,7 +257,7 @@
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
verifyDaemonCommand("stop-service");
reset(mDaemon);
- client.disconnect();
+ deathRecipient.binderDied();
// Client disconnects, after CLEANUP_DELAY_MS, maybeStop the daemon.
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
}
@@ -240,12 +271,28 @@
mDaemonCallback = callback;
return mDaemon;
};
- NsdService service = new NsdService(mContext, mSettings,
- mHandler, supplier, CLEANUP_DELAY_MS);
+ final NsdService service = new NsdService(mContext, mHandler, supplier, CLEANUP_DELAY_MS) {
+ @Override
+ public INsdServiceConnector connect(INsdManagerCallback baseCb) {
+ // Wrap the callback in a transparent mock, to mock asBinder returning a
+ // LinkToDeathRecorder. This will allow recording the binder death recipient
+ // registered on the callback. Use a transparent mock and not a spy as the actual
+ // implementation class is not public and cannot be spied on by Mockito.
+ final INsdManagerCallback cb = mock(INsdManagerCallback.class,
+ AdditionalAnswers.delegatesTo(baseCb));
+ doReturn(new LinkToDeathRecorder()).when(cb).asBinder();
+ mCreatedCallbacks.add(cb);
+ return super.connect(cb);
+ }
+ };
verify(mDaemon, never()).execute(any(String.class));
return service;
}
+ private INsdManagerCallback getCallback() {
+ return mCreatedCallbacks.remove();
+ }
+
NsdManager connectClient(NsdService service) {
return new NsdManager(mContext, service);
}
diff --git a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
index 9ef558f..24aecdb 100644
--- a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
@@ -43,6 +43,7 @@
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.net.ConnectivitySettingsManager;
import android.net.IDnsResolver;
@@ -106,8 +107,14 @@
@Mock IDnsResolver mMockDnsResolver;
private void assertResolverOptionsEquals(
- @NonNull ResolverOptionsParcel actual,
- @NonNull ResolverOptionsParcel expected) {
+ @Nullable ResolverOptionsParcel actual,
+ @Nullable ResolverOptionsParcel expected) {
+ if (actual == null) {
+ assertNull(expected);
+ return;
+ } else {
+ assertNotNull(expected);
+ }
assertEquals(actual.hosts, expected.hosts);
assertEquals(actual.tcMode, expected.tcMode);
assertEquals(actual.enforceDnsUid, expected.enforceDnsUid);
@@ -365,7 +372,7 @@
expectedParams.tlsName = "";
expectedParams.tlsServers = new String[]{"3.3.3.3", "4.4.4.4"};
expectedParams.transportTypes = TEST_TRANSPORT_TYPES;
- expectedParams.resolverOptions = new ResolverOptionsParcel();
+ expectedParams.resolverOptions = null;
assertResolverParamsEquals(actualParams, expectedParams);
}
diff --git a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
index f358726..aa4c4e3 100644
--- a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -109,8 +109,8 @@
mNai.linkProperties = new LinkProperties();
mNai.linkProperties.setInterfaceName(BASE_IFACE);
- mNai.networkInfo = new NetworkInfo(null);
- mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+ mNai.networkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */,
+ null /* typeName */, null /* subtypeName */);
mNai.networkCapabilities = new NetworkCapabilities();
markNetworkConnected();
when(mNai.connService()).thenReturn(mConnectivity);
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
index c1059b3..2cf5d8e 100644
--- a/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -27,6 +27,7 @@
import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.SIGN_IN;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.clearInvocations;
@@ -39,9 +40,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -49,11 +55,19 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;
+import android.os.Bundle;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
+import android.widget.TextView;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiSelector;
import com.android.connectivity.resources.R;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
@@ -84,6 +98,7 @@
private static final String TEST_EXTRA_INFO = "extra";
private static final int TEST_NOTIF_ID = 101;
private static final String TEST_NOTIF_TAG = NetworkNotificationManager.tagFor(TEST_NOTIF_ID);
+ private static final long TEST_TIMEOUT_MS = 10_000L;
static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities();
@@ -102,6 +117,25 @@
VPN_CAPABILITIES.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
}
+ /**
+ * Test activity that shows the action it was started with on screen, and dismisses when the
+ * text is tapped.
+ */
+ public static class TestDialogActivity extends Activity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setTurnScreenOn(true);
+ getSystemService(KeyguardManager.class).requestDismissKeyguard(
+ this, null /* callback */);
+
+ final TextView txt = new TextView(this);
+ txt.setText(getIntent().getAction());
+ txt.setOnClickListener(e -> finish());
+ setContentView(txt);
+ }
+ }
+
@Mock Context mCtx;
@Mock Resources mResources;
@Mock DisplayMetrics mDisplayMetrics;
@@ -345,4 +379,82 @@
mManager.clearNotification(id, PARTIAL_CONNECTIVITY);
verify(mNotificationManager, never()).cancel(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId));
}
+
+ @Test
+ public void testNotifyNoInternetAsDialogWhenHighPriority() throws Exception {
+ doReturn(true).when(mResources).getBoolean(
+ R.bool.config_notifyNoInternetAsDialogWhenHighPriority);
+
+ mManager.showNotification(TEST_NOTIF_ID, NETWORK_SWITCH, mWifiNai, mCellNai, null, false);
+ // Non-"no internet" notifications are not affected
+ verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(NETWORK_SWITCH.eventId), any());
+
+ final Instrumentation instr = InstrumentationRegistry.getInstrumentation();
+ final Context ctx = instr.getContext();
+ final String testAction = "com.android.connectivity.coverage.TEST_DIALOG";
+ final Intent intent = new Intent(testAction)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .setClassName(ctx.getPackageName(), TestDialogActivity.class.getName());
+ final PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0 /* requestCode */,
+ intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
+
+ mManager.showNotification(TEST_NOTIF_ID, NO_INTERNET, mWifiNai, null /* switchToNai */,
+ pendingIntent, true /* highPriority */);
+
+ // Previous notifications are still dismissed
+ verify(mNotificationManager).cancel(TEST_NOTIF_TAG, NETWORK_SWITCH.eventId);
+
+ // Verify that the activity is shown (the activity shows the action on screen)
+ final UiObject actionText = UiDevice.getInstance(instr).findObject(
+ new UiSelector().text(testAction));
+ assertTrue("Activity not shown", actionText.waitForExists(TEST_TIMEOUT_MS));
+
+ // Tapping the text should dismiss the dialog
+ actionText.click();
+ assertTrue("Activity not dismissed", actionText.waitUntilGone(TEST_TIMEOUT_MS));
+
+ // Verify no NO_INTERNET notification was posted
+ verify(mNotificationManager, never()).notify(any(), eq(NO_INTERNET.eventId), any());
+ }
+
+ private void doNotificationTextTest(NotificationType type, @StringRes int expectedTitleRes,
+ String expectedTitleArg, @StringRes int expectedContentRes) {
+ final String expectedTitle = "title " + expectedTitleArg;
+ final String expectedContent = "expected content";
+ doReturn(expectedTitle).when(mResources).getString(expectedTitleRes, expectedTitleArg);
+ doReturn(expectedContent).when(mResources).getString(expectedContentRes);
+
+ mManager.showNotification(TEST_NOTIF_ID, type, mWifiNai, mCellNai, null, false);
+ final ArgumentCaptor<Notification> notifCap = ArgumentCaptor.forClass(Notification.class);
+
+ verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(type.eventId),
+ notifCap.capture());
+ final Notification notif = notifCap.getValue();
+
+ assertEquals(expectedTitle, notif.extras.getString(Notification.EXTRA_TITLE));
+ assertEquals(expectedContent, notif.extras.getString(Notification.EXTRA_TEXT));
+ }
+
+ @Test
+ public void testNotificationText_NoInternet() {
+ doNotificationTextTest(NO_INTERNET,
+ R.string.wifi_no_internet, TEST_EXTRA_INFO,
+ R.string.wifi_no_internet_detailed);
+ }
+
+ @Test
+ public void testNotificationText_Partial() {
+ doNotificationTextTest(PARTIAL_CONNECTIVITY,
+ R.string.network_partial_connectivity, TEST_EXTRA_INFO,
+ R.string.network_partial_connectivity_detailed);
+ }
+
+ @Test
+ public void testNotificationText_PartialAsNoInternet() {
+ doReturn(true).when(mResources).getBoolean(
+ R.bool.config_partialConnectivityNotifiedAsNoInternet);
+ doNotificationTextTest(PARTIAL_CONNECTIVITY,
+ R.string.wifi_no_internet, TEST_EXTRA_INFO,
+ R.string.wifi_no_internet_detailed);
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index a9749c4..99ef80b 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -32,6 +32,7 @@
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
import static android.net.INetd.PERMISSION_INTERNET;
+import static android.net.INetd.PERMISSION_NETWORK;
import static android.net.INetd.PERMISSION_NONE;
import static android.net.INetd.PERMISSION_SYSTEM;
import static android.net.INetd.PERMISSION_UNINSTALLED;
@@ -39,8 +40,7 @@
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.Process.SYSTEM_UID;
-import static com.android.server.connectivity.PermissionMonitor.NETWORK;
-import static com.android.server.connectivity.PermissionMonitor.SYSTEM;
+import static com.android.server.connectivity.PermissionMonitor.isHigherNetworkPermission;
import static junit.framework.Assert.fail;
@@ -55,12 +55,13 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.intThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -80,12 +81,14 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.util.ArraySet;
import android.util.SparseIntArray;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import com.android.net.module.util.CollectionUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -98,11 +101,8 @@
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
-import java.util.ArrayList;
+import java.lang.reflect.Array;
import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -110,13 +110,22 @@
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class PermissionMonitorTest {
- private static final UserHandle MOCK_USER1 = UserHandle.of(0);
- private static final UserHandle MOCK_USER2 = UserHandle.of(1);
- private static final int MOCK_UID1 = 10001;
- private static final int MOCK_UID2 = 10086;
- private static final int SYSTEM_UID1 = 1000;
- private static final int SYSTEM_UID2 = 1008;
- private static final int VPN_UID = 10002;
+ private static final int MOCK_USER_ID1 = 0;
+ private static final int MOCK_USER_ID2 = 1;
+ private static final UserHandle MOCK_USER1 = UserHandle.of(MOCK_USER_ID1);
+ private static final UserHandle MOCK_USER2 = UserHandle.of(MOCK_USER_ID2);
+ private static final int MOCK_APPID1 = 10001;
+ private static final int MOCK_APPID2 = 10086;
+ private static final int SYSTEM_APPID1 = 1100;
+ private static final int SYSTEM_APPID2 = 1108;
+ private static final int VPN_APPID = 10002;
+ private static final int MOCK_UID11 = MOCK_USER1.getUid(MOCK_APPID1);
+ private static final int MOCK_UID12 = MOCK_USER1.getUid(MOCK_APPID2);
+ private static final int SYSTEM_APP_UID11 = MOCK_USER1.getUid(SYSTEM_APPID1);
+ private static final int VPN_UID = MOCK_USER1.getUid(VPN_APPID);
+ private static final int MOCK_UID21 = MOCK_USER2.getUid(MOCK_APPID1);
+ private static final int MOCK_UID22 = MOCK_USER2.getUid(MOCK_APPID2);
+ private static final int SYSTEM_APP_UID21 = MOCK_USER2.getUid(SYSTEM_APPID1);
private static final String REAL_SYSTEM_PACKAGE_NAME = "android";
private static final String MOCK_PACKAGE1 = "appName1";
private static final String MOCK_PACKAGE2 = "appName2";
@@ -146,8 +155,7 @@
MockitoAnnotations.initMocks(this);
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
- when(mUserManager.getUserHandles(eq(true))).thenReturn(
- Arrays.asList(new UserHandle[] { MOCK_USER1, MOCK_USER2 }));
+ doReturn(List.of(MOCK_USER1)).when(mUserManager).getUserHandles(eq(true));
when(mContext.getSystemServiceName(SystemConfigManager.class))
.thenReturn(Context.SYSTEM_CONFIG_SERVICE);
when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE))
@@ -157,22 +165,22 @@
doCallRealMethod().when(mContext).getSystemService(SystemConfigManager.class);
}
when(mSystemConfigManager.getSystemPermissionUids(anyString())).thenReturn(new int[0]);
- final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
- doReturn(UserHandle.ALL).when(asUserCtx).getUser();
- when(mContext.createContextAsUser(eq(UserHandle.ALL), anyInt())).thenReturn(asUserCtx);
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+ doAnswer(invocation -> {
+ final Object[] args = invocation.getArguments();
+ final Context asUserCtx = mock(Context.class, AdditionalAnswers.delegatesTo(mContext));
+ final UserHandle user = (UserHandle) args[0];
+ doReturn(user).when(asUserCtx).getUser();
+ return asUserCtx;
+ }).when(mContext).createContextAsUser(any(), anyInt());
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of());
+ // Set DEVICE_INITIAL_SDK_INT to Q that SYSTEM_UID won't have restricted network permission
+ // by default.
+ doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
- mPermissionMonitor = spy(new PermissionMonitor(mContext, mNetdService, mDeps));
+ mPermissionMonitor = new PermissionMonitor(mContext, mNetdService, mDeps);
mNetdMonitor = new NetdMonitor(mNetdService);
- when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(/* empty app list */ null);
- mPermissionMonitor.startMonitoring();
- }
-
- private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion, int uid,
- String... permissions) {
- return hasRestrictedNetworkPermission(
- partition, targetSdkVersion, "" /* packageName */, uid, permissions);
+ doReturn(List.of()).when(mPackageManager).getInstalledPackagesAsUser(anyInt(), anyInt());
}
private boolean hasRestrictedNetworkPermission(String partition, int targetSdkVersion,
@@ -223,13 +231,44 @@
private static PackageInfo buildPackageInfo(String packageName, int uid,
String... permissions) {
- final PackageInfo pkgInfo;
- pkgInfo = systemPackageInfoWithPermissions(permissions);
+ final PackageInfo pkgInfo = systemPackageInfoWithPermissions(permissions);
pkgInfo.packageName = packageName;
pkgInfo.applicationInfo.uid = uid;
return pkgInfo;
}
+ // TODO: Move this method to static lib.
+ private static @NonNull <T> T[] appendElement(Class<T> kind, @Nullable T[] array, T element) {
+ final T[] result;
+ if (array != null) {
+ result = Arrays.copyOf(array, array.length + 1);
+ } else {
+ result = (T[]) Array.newInstance(kind, 1);
+ }
+ result[result.length - 1] = element;
+ return result;
+ }
+
+ private void buildAndMockPackageInfoWithPermissions(String packageName, int uid,
+ String... permissions) throws Exception {
+ final PackageInfo packageInfo = buildPackageInfo(packageName, uid, permissions);
+ // This will return the wrong UID for the package when queried with other users.
+ doReturn(packageInfo).when(mPackageManager)
+ .getPackageInfo(eq(packageName), anyInt() /* flag */);
+ final String[] oldPackages = mPackageManager.getPackagesForUid(uid);
+ // If it's duplicated package, no need to set it again.
+ if (CollectionUtils.contains(oldPackages, packageName)) return;
+
+ // Combine the package if this uid is shared with other packages.
+ final String[] newPackages = appendElement(String.class, oldPackages, packageName);
+ doReturn(newPackages).when(mPackageManager).getPackagesForUid(eq(uid));
+ }
+
+ private void addPackage(String packageName, int uid, String... permissions) throws Exception {
+ buildAndMockPackageInfoWithPermissions(packageName, uid, permissions);
+ mPermissionMonitor.onPackageAdded(packageName, uid);
+ }
+
@Test
public void testHasPermission() {
PackageInfo app = systemPackageInfoWithPermissions();
@@ -298,80 +337,90 @@
@Test
public void testHasRestrictedNetworkPermission() {
- assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
- assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, NETWORK_STACK));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, MOCK_UID1, PERMISSION_MAINLINE_NETWORK_STACK));
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_WIFI_STATE));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, MOCK_PACKAGE1, MOCK_UID11,
+ PERMISSION_MAINLINE_NETWORK_STACK));
- assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
}
@Test
public void testHasRestrictedNetworkPermissionSystemUid() {
doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
- assertTrue(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_INTERNAL));
+ PARTITION_SYSTEM, VERSION_P, SYSTEM_PACKAGE1, SYSTEM_UID));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_P, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ PARTITION_SYSTEM, VERSION_P, SYSTEM_PACKAGE1, SYSTEM_UID, CONNECTIVITY_INTERNAL));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_P, SYSTEM_PACKAGE1, SYSTEM_UID,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS));
doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
- assertFalse(hasRestrictedNetworkPermission(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_INTERNAL));
+ PARTITION_SYSTEM, VERSION_Q, SYSTEM_PACKAGE1, SYSTEM_UID));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_SYSTEM, VERSION_Q, SYSTEM_PACKAGE1, SYSTEM_UID, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ PARTITION_SYSTEM, VERSION_Q, SYSTEM_PACKAGE1, SYSTEM_UID,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS));
}
@Test
public void testHasRestrictedNetworkPermissionVendorApp() {
- assertTrue(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_NETWORK_STATE));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_UID1, NETWORK_STACK));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, NETWORK_STACK));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_UID1, CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_P, MOCK_UID1, CHANGE_WIFI_STATE));
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS));
+ assertTrue(hasRestrictedNetworkPermission(
+ PARTITION_VENDOR, VERSION_P, MOCK_PACKAGE1, MOCK_UID11, CHANGE_WIFI_STATE));
- assertFalse(hasRestrictedNetworkPermission(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_UID1, CHANGE_NETWORK_STATE));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
+ assertFalse(hasRestrictedNetworkPermission(
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
}
@Test
public void testHasRestrictedNetworkPermissionUidAllowedOnRestrictedNetworks() {
- mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
- new ArraySet<>(new Integer[] { MOCK_UID1 }));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID11));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE));
assertTrue(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID1, CONNECTIVITY_INTERNAL));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE1, MOCK_UID11, CONNECTIVITY_INTERNAL));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID12));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID12, CHANGE_NETWORK_STATE));
assertFalse(hasRestrictedNetworkPermission(
- PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID2, CONNECTIVITY_INTERNAL));
+ PARTITION_VENDOR, VERSION_Q, MOCK_PACKAGE2, MOCK_UID12, CONNECTIVITY_INTERNAL));
}
@@ -388,27 +437,27 @@
doReturn(VERSION_P).when(mDeps).getDeviceFirstSdkInt();
assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
- assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID11));
+ assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID11));
assertTrue(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID11));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID11));
doReturn(VERSION_Q).when(mDeps).getDeviceFirstSdkInt();
assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, SYSTEM_UID));
assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID1));
- assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_P, MOCK_UID11));
+ assertTrue(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_P, MOCK_UID11));
assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, SYSTEM_UID));
assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_SYSTEM, VERSION_Q, MOCK_UID11));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_VENDOR, VERSION_Q, MOCK_UID11));
assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, SYSTEM_UID));
assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, SYSTEM_UID));
- assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID1));
- assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID1));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_OEM, VERSION_Q, MOCK_UID11));
+ assertFalse(wouldBeCarryoverPackage(PARTITION_PRODUCT, VERSION_Q, MOCK_UID11));
}
private boolean wouldBeUidAllowedOnRestrictedNetworks(int uid) {
@@ -419,32 +468,26 @@
@Test
public void testIsAppAllowedOnRestrictedNetworks() {
- mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(new ArraySet<>());
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of());
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
- mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
- new ArraySet<>(new Integer[] { MOCK_UID1 }));
- assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID11));
+ assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
- mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
- new ArraySet<>(new Integer[] { MOCK_UID2 }));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
- assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(MOCK_UID12));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+ assertTrue(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
- mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(
- new ArraySet<>(new Integer[] { 123 }));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID1));
- assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID2));
+ mPermissionMonitor.updateUidsAllowedOnRestrictedNetworks(Set.of(123));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID11));
+ assertFalse(wouldBeUidAllowedOnRestrictedNetworks(MOCK_UID12));
}
private void assertBackgroundPermission(boolean hasPermission, String name, int uid,
String... permissions) throws Exception {
- when(mPackageManager.getPackageInfo(eq(name), anyInt()))
- .thenReturn(packageInfoWithPermissions(
- REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM));
- mPermissionMonitor.onPackageAdded(name, uid);
+ addPackage(name, uid, permissions);
assertEquals(hasPermission, mPermissionMonitor.hasUseBackgroundNetworksPermission(uid));
}
@@ -456,34 +499,35 @@
assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, CHANGE_NETWORK_STATE);
assertBackgroundPermission(true, SYSTEM_PACKAGE1, SYSTEM_UID, NETWORK_STACK);
- assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID1));
- assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID1);
- assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID1,
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID11));
+ assertBackgroundPermission(false, MOCK_PACKAGE1, MOCK_UID11);
+ assertBackgroundPermission(true, MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
- assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID2));
- assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2);
- assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID2,
+ assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(MOCK_UID12));
+ assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID12);
+ assertBackgroundPermission(false, MOCK_PACKAGE2, MOCK_UID12,
CONNECTIVITY_INTERNAL);
- assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID2, NETWORK_STACK);
+ assertBackgroundPermission(true, MOCK_PACKAGE2, MOCK_UID12, NETWORK_STACK);
}
private class NetdMonitor {
- private final HashMap<Integer, Boolean> mUidsNetworkPermission = new HashMap<>();
- private final HashMap<Integer, Integer> mAppIdsTrafficPermission = new HashMap<>();
+ private final SparseIntArray mUidsNetworkPermission = new SparseIntArray();
+ private final SparseIntArray mAppIdsTrafficPermission = new SparseIntArray();
+ private static final int DOES_NOT_EXIST = -2;
NetdMonitor(INetd mockNetd) throws Exception {
// Add hook to verify and track result of networkSetPermission.
doAnswer((InvocationOnMock invocation) -> {
final Object[] args = invocation.getArguments();
- final Boolean isSystem = args[0].equals(PERMISSION_SYSTEM);
+ final int permission = (int) args[0];
for (final int uid : (int[]) args[1]) {
// TODO: Currently, permission monitor will send duplicate commands for each uid
// corresponding to each user. Need to fix that and uncomment below test.
// if (mApps.containsKey(uid) && mApps.get(uid) == isSystem) {
// fail("uid " + uid + " is already set to " + isSystem);
// }
- mUidsNetworkPermission.put(uid, isSystem);
+ mUidsNetworkPermission.put(uid, permission);
}
return null;
}).when(mockNetd).networkSetPermissionForUser(anyInt(), any(int[].class));
@@ -497,7 +541,7 @@
// if (!mApps.containsKey(uid)) {
// fail("uid " + uid + " does not exist.");
// }
- mUidsNetworkPermission.remove(uid);
+ mUidsNetworkPermission.delete(uid);
}
return null;
}).when(mockNetd).networkClearPermissionForUser(any(int[].class));
@@ -513,11 +557,11 @@
}).when(mockNetd).trafficSetNetPermForUids(anyInt(), any(int[].class));
}
- public void expectNetworkPerm(Boolean permission, UserHandle[] users, int... appIds) {
+ public void expectNetworkPerm(int permission, UserHandle[] users, int... appIds) {
for (final UserHandle user : users) {
for (final int appId : appIds) {
final int uid = user.getUid(appId);
- if (!mUidsNetworkPermission.containsKey(uid)) {
+ if (mUidsNetworkPermission.get(uid, DOES_NOT_EXIST) == DOES_NOT_EXIST) {
fail("uid " + uid + " does not exist.");
}
if (mUidsNetworkPermission.get(uid) != permission) {
@@ -531,7 +575,7 @@
for (final UserHandle user : users) {
for (final int appId : appIds) {
final int uid = user.getUid(appId);
- if (mUidsNetworkPermission.containsKey(uid)) {
+ if (mUidsNetworkPermission.get(uid, DOES_NOT_EXIST) != DOES_NOT_EXIST) {
fail("uid " + uid + " has listed permissions, expected none.");
}
}
@@ -540,7 +584,7 @@
public void expectTrafficPerm(int permission, int... appIds) {
for (final int appId : appIds) {
- if (!mAppIdsTrafficPermission.containsKey(appId)) {
+ if (mAppIdsTrafficPermission.get(appId, DOES_NOT_EXIST) == DOES_NOT_EXIST) {
fail("appId " + appId + " does not exist.");
}
if (mAppIdsTrafficPermission.get(appId) != permission) {
@@ -553,290 +597,278 @@
@Test
public void testUserAndPackageAddRemove() throws Exception {
- // MOCK_UID1: MOCK_PACKAGE1 only has network permission.
- // SYSTEM_UID: SYSTEM_PACKAGE1 has system permission.
- // SYSTEM_UID: SYSTEM_PACKAGE2 only has network permission.
- doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(any(),
- eq(SYSTEM_PACKAGE1));
- doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
- eq(SYSTEM_PACKAGE2));
- doReturn(NETWORK).when(mPermissionMonitor).highestPermissionForUid(any(),
- eq(MOCK_PACKAGE1));
- doReturn(SYSTEM).when(mPermissionMonitor).highestPermissionForUid(eq(SYSTEM), anyString());
+ // MOCK_UID11: MOCK_PACKAGE1 only has network permission.
+ // SYSTEM_APP_UID11: SYSTEM_PACKAGE1 has system permission.
+ // SYSTEM_APP_UID11: SYSTEM_PACKAGE2 only has network permission.
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+ buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE1, SYSTEM_APP_UID11,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ buildAndMockPackageInfoWithPermissions(SYSTEM_PACKAGE2, SYSTEM_APP_UID11,
+ CHANGE_NETWORK_STATE);
- // Add SYSTEM_PACKAGE2, expect only have network permission.
+ // Add user MOCK_USER1.
mPermissionMonitor.onUserAdded(MOCK_USER1);
- addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_UID);
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, SYSTEM_UID);
+ // Add SYSTEM_PACKAGE2, expect only have network permission.
+ addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE2, SYSTEM_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
+ SYSTEM_APPID1);
- // Add SYSTEM_PACKAGE1, expect permission escalate.
- addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_UID);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, SYSTEM_UID);
+ // Add SYSTEM_PACKAGE1, expect permission upgrade.
+ addPackageForUsers(new UserHandle[]{MOCK_USER1}, SYSTEM_PACKAGE1, SYSTEM_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
+ SYSTEM_APPID1);
+ final List<PackageInfo> pkgs = List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID21,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+ buildPackageInfo(SYSTEM_PACKAGE2, SYSTEM_APP_UID21, CHANGE_NETWORK_STATE));
+ doReturn(pkgs).when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS),
+ eq(MOCK_USER_ID2));
+ // Add user MOCK_USER2.
mPermissionMonitor.onUserAdded(MOCK_USER2);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
- SYSTEM_UID);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ SYSTEM_APPID1);
// Remove SYSTEM_PACKAGE2, expect keep system permission.
- when(mPackageManager.getPackagesForUid(MOCK_USER1.getUid(SYSTEM_UID)))
- .thenReturn(new String[]{SYSTEM_PACKAGE1});
- when(mPackageManager.getPackagesForUid(MOCK_USER2.getUid(SYSTEM_UID)))
- .thenReturn(new String[]{SYSTEM_PACKAGE1});
+ doReturn(new String[]{SYSTEM_PACKAGE1}).when(mPackageManager)
+ .getPackagesForUid(intThat(uid -> UserHandle.getAppId(uid) == SYSTEM_APPID1));
removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2},
- SYSTEM_PACKAGE2, SYSTEM_UID);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
- SYSTEM_UID);
+ SYSTEM_PACKAGE2, SYSTEM_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ SYSTEM_APPID1);
// Add SYSTEM_PACKAGE2, expect keep system permission.
- addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_UID);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
- SYSTEM_UID);
+ addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_PACKAGE2,
+ SYSTEM_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ SYSTEM_APPID1);
- addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
- SYSTEM_UID);
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2},
- MOCK_UID1);
+ // Add MOCK_PACKAGE1
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID21, CHANGE_NETWORK_STATE);
+ addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ SYSTEM_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ MOCK_APPID1);
- // Remove MOCK_UID1, expect no permission left for all user.
- when(mPackageManager.getPackagesForUid(MOCK_USER1.getUid(MOCK_UID1)))
- .thenReturn(new String[]{});
- when(mPackageManager.getPackagesForUid(MOCK_USER2.getUid(MOCK_UID1)))
- .thenReturn(new String[]{});
- mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_UID1);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+ // Remove MOCK_PACKAGE1, expect no permission left for all user.
+ doReturn(new String[]{}).when(mPackageManager)
+ .getPackagesForUid(intThat(uid -> UserHandle.getAppId(uid) == MOCK_APPID1));
+ removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1);
// Remove SYSTEM_PACKAGE1, expect permission downgrade.
- when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{SYSTEM_PACKAGE2});
+ when(mPackageManager.getPackagesForUid(
+ intThat(uid -> UserHandle.getAppId(uid) == SYSTEM_APPID1)))
+ .thenReturn(new String[]{SYSTEM_PACKAGE2});
removePackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2},
- SYSTEM_PACKAGE1, SYSTEM_UID);
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2},
- SYSTEM_UID);
+ SYSTEM_PACKAGE1, SYSTEM_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1, MOCK_USER2},
+ SYSTEM_APPID1);
mPermissionMonitor.onUserRemoved(MOCK_USER1);
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER2}, SYSTEM_UID);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER2},
+ SYSTEM_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, SYSTEM_APPID1);
// Remove all packages, expect no permission left.
- when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(new String[]{});
- removePackageForUsers(new UserHandle[]{MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_UID);
- mNetdMonitor.expectNoNetworkPerm(
- new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID, MOCK_UID1);
+ when(mPackageManager.getPackagesForUid(
+ intThat(uid -> UserHandle.getAppId(uid) == SYSTEM_APPID1)))
+ .thenReturn(new String[]{});
+ removePackageForUsers(new UserHandle[]{MOCK_USER2}, SYSTEM_PACKAGE2, SYSTEM_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_APPID1,
+ MOCK_APPID1);
- // Remove last user, expect no redundant clearPermission is invoked.
+ // Remove last user, expect no permission change.
mPermissionMonitor.onUserRemoved(MOCK_USER2);
- mNetdMonitor.expectNoNetworkPerm(
- new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_UID, MOCK_UID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, SYSTEM_APPID1,
+ MOCK_APPID1);
}
@Test
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_UID1, CHANGE_NETWORK_STATE,
- CONNECTIVITY_USE_RESTRICTED_NETWORKS),
- buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
- buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2),
- buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
+ doReturn(List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
+ CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+ buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID12),
+ buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
mPermissionMonitor.startMonitoring();
- // Every app on user 0 except MOCK_UID2 are under VPN.
- final Set<UidRange> vpnRange1 = new HashSet<>(Arrays.asList(new UidRange[] {
- new UidRange(0, MOCK_UID2 - 1),
- new UidRange(MOCK_UID2 + 1, UserHandle.PER_USER_RANGE - 1)}));
- final Set<UidRange> vpnRange2 = Collections.singleton(new UidRange(MOCK_UID2, MOCK_UID2));
+ // Every app on user 0 except MOCK_UID12 are under VPN.
+ final Set<UidRange> vpnRange1 = Set.of(
+ new UidRange(0, MOCK_UID12 - 1),
+ new UidRange(MOCK_UID12 + 1, UserHandle.PER_USER_RANGE - 1));
+ final Set<UidRange> vpnRange2 = Set.of(new UidRange(MOCK_UID12, MOCK_UID12));
- // When VPN is connected, expect a rule to be set up for user app MOCK_UID1
+ // When VPN is connected, expect a rule to be set up for user app MOCK_UID11
mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange1, VPN_UID);
- verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"),
- aryEq(new int[] {MOCK_UID1}));
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID11}));
reset(mNetdService);
- // When MOCK_UID1 package is uninstalled and reinstalled, expect Netd to be updated
- mPermissionMonitor.onPackageRemoved(
- MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1));
- verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
- mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1));
- verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"),
- aryEq(new int[] {MOCK_UID1}));
+ // When MOCK_UID11 package is uninstalled and reinstalled, expect Netd to be updated
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+ verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID11}));
+ mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_UID11);
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID11}));
reset(mNetdService);
// During VPN uid update (vpnRange1 -> vpnRange2), ConnectivityService first deletes the
// old UID rules then adds the new ones. Expect netd to be updated
mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange1, VPN_UID);
- verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
+ verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID11}));
mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange2, VPN_UID);
- verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"),
- aryEq(new int[] {MOCK_UID2}));
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID12}));
reset(mNetdService);
// When VPN is disconnected, expect rules to be torn down
mPermissionMonitor.onVpnUidRangesRemoved("tun0", vpnRange2, VPN_UID);
- verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID2}));
+ verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID12}));
assertNull(mPermissionMonitor.getVpnUidRanges("tun0"));
}
@Test
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_UID1, CHANGE_NETWORK_STATE,
- NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS),
- buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)));
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
+ doReturn(List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
+ NETWORK_STACK, CONNECTIVITY_USE_RESTRICTED_NETWORKS),
+ buildPackageInfo(SYSTEM_PACKAGE2, VPN_UID)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
mPermissionMonitor.startMonitoring();
- final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(MOCK_USER1));
+ final Set<UidRange> vpnRange = Set.of(UidRange.createForUser(MOCK_USER1),
+ UidRange.createForUser(MOCK_USER2));
mPermissionMonitor.onVpnUidRangesAdded("tun0", vpnRange, VPN_UID);
// Newly-installed package should have uid rules added
- mPermissionMonitor.onPackageAdded(MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1));
- verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"),
- aryEq(new int[] {MOCK_UID1}));
+ addPackageForUsers(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_PACKAGE1, MOCK_APPID1);
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID11}));
+ verify(mNetdService).firewallAddUidInterfaceRules(eq("tun0"), aryEq(new int[]{MOCK_UID21}));
// Removed package should have its uid rules removed
- mPermissionMonitor.onPackageRemoved(
- MOCK_PACKAGE1, MOCK_USER1.getUid(MOCK_UID1));
- verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[] {MOCK_UID1}));
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+ verify(mNetdService).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID11}));
+ verify(mNetdService, never()).firewallRemoveUidInterfaceRules(aryEq(new int[]{MOCK_UID21}));
}
// Normal package add/remove operations will trigger multiple intent for uids corresponding to
// each user. To simulate generic package operations, the onPackageAdded/Removed will need to be
// called multiple times with the uid corresponding to each user.
- private void addPackageForUsers(UserHandle[] users, String packageName, int uid) {
+ private void addPackageForUsers(UserHandle[] users, String packageName, int appId) {
for (final UserHandle user : users) {
- mPermissionMonitor.onPackageAdded(packageName, user.getUid(uid));
+ mPermissionMonitor.onPackageAdded(packageName, user.getUid(appId));
}
}
- private void removePackageForUsers(UserHandle[] users, String packageName, int uid) {
+ private void removePackageForUsers(UserHandle[] users, String packageName, int appId) {
for (final UserHandle user : users) {
- mPermissionMonitor.onPackageRemoved(packageName, user.getUid(uid));
+ mPermissionMonitor.onPackageRemoved(packageName, user.getUid(appId));
}
}
@Test
public void testPackagePermissionUpdate() throws Exception {
- // MOCK_UID1: MOCK_PACKAGE1 only has internet permission.
- // MOCK_UID2: MOCK_PACKAGE2 does not have any permission.
- // SYSTEM_UID1: SYSTEM_PACKAGE1 has internet permission and update device stats permission.
- // SYSTEM_UID2: SYSTEM_PACKAGE2 has only update device stats permission.
+ // MOCK_APPID1: MOCK_PACKAGE1 only has internet permission.
+ // MOCK_APPID2: MOCK_PACKAGE2 does not have any permission.
+ // SYSTEM_APPID1: SYSTEM_PACKAGE1 has internet permission and update device stats permission
+ // SYSTEM_APPID2: SYSTEM_PACKAGE2 has only update device stats permission.
SparseIntArray netdPermissionsAppIds = new SparseIntArray();
- netdPermissionsAppIds.put(MOCK_UID1, PERMISSION_INTERNET);
- netdPermissionsAppIds.put(MOCK_UID2, PERMISSION_NONE);
- netdPermissionsAppIds.put(SYSTEM_UID1, PERMISSION_TRAFFIC_ALL);
- netdPermissionsAppIds.put(SYSTEM_UID2, PERMISSION_UPDATE_DEVICE_STATS);
+ netdPermissionsAppIds.put(MOCK_APPID1, PERMISSION_INTERNET);
+ netdPermissionsAppIds.put(MOCK_APPID2, PERMISSION_NONE);
+ netdPermissionsAppIds.put(SYSTEM_APPID1, PERMISSION_TRAFFIC_ALL);
+ netdPermissionsAppIds.put(SYSTEM_APPID2, PERMISSION_UPDATE_DEVICE_STATS);
// Send the permission information to netd, expect permission updated.
- mPermissionMonitor.sendPackagePermissionsToNetd(netdPermissionsAppIds);
+ mPermissionMonitor.sendAppIdsTrafficPermission(netdPermissionsAppIds);
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_UID2);
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, SYSTEM_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, SYSTEM_UID2);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID2);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, SYSTEM_APPID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, SYSTEM_APPID2);
- // Update permission of MOCK_UID1, expect new permission show up.
- mPermissionMonitor.sendPackagePermissionsForUid(MOCK_UID1, PERMISSION_TRAFFIC_ALL);
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ // Update permission of MOCK_APPID1, expect new permission show up.
+ mPermissionMonitor.sendPackagePermissionsForAppId(MOCK_APPID1, PERMISSION_TRAFFIC_ALL);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
- // Change permissions of SYSTEM_UID2, expect new permission show up and old permission
+ // Change permissions of SYSTEM_APPID2, expect new permission show up and old permission
// revoked.
- mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID2, PERMISSION_INTERNET);
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, SYSTEM_UID2);
+ mPermissionMonitor.sendPackagePermissionsForAppId(SYSTEM_APPID2, PERMISSION_INTERNET);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, SYSTEM_APPID2);
- // Revoke permission from SYSTEM_UID1, expect no permission stored.
- mPermissionMonitor.sendPackagePermissionsForUid(SYSTEM_UID1, PERMISSION_NONE);
- mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, SYSTEM_UID1);
- }
-
- private PackageInfo setPackagePermissions(String packageName, int uid, String[] permissions)
- throws Exception {
- PackageInfo packageInfo = packageInfoWithPermissions(
- REQUESTED_PERMISSION_GRANTED, permissions, PARTITION_SYSTEM);
- when(mPackageManager.getPackageInfo(eq(packageName), anyInt())).thenReturn(packageInfo);
- when(mPackageManager.getPackagesForUid(eq(uid))).thenReturn(new String[]{packageName});
- return packageInfo;
- }
-
- private PackageInfo addPackage(String packageName, int uid, String[] permissions)
- throws Exception {
- PackageInfo packageInfo = setPackagePermissions(packageName, uid, permissions);
- mPermissionMonitor.onPackageAdded(packageName, uid);
- return packageInfo;
+ // Revoke permission from SYSTEM_APPID1, expect no permission stored.
+ mPermissionMonitor.sendPackagePermissionsForAppId(SYSTEM_APPID1, PERMISSION_NONE);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, SYSTEM_APPID1);
}
@Test
public void testPackageInstall() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
- addPackage(MOCK_PACKAGE2, MOCK_UID2, new String[] {INTERNET});
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_UID2);
+ addPackage(MOCK_PACKAGE2, MOCK_UID12, INTERNET);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID2);
}
@Test
public void testPackageInstallSharedUid() throws Exception {
- PackageInfo packageInfo1 = addPackage(MOCK_PACKAGE1, MOCK_UID1,
- new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
- // Install another package with the same uid and no permissions should not cause the UID to
- // lose permissions.
- PackageInfo packageInfo2 = systemPackageInfoWithPermissions();
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
- when(mPackageManager.getPackagesForUid(MOCK_UID1))
- .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
- mPermissionMonitor.onPackageAdded(MOCK_PACKAGE2, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ // Install another package with the same uid and no permissions should not cause the app id
+ // to lose permissions.
+ addPackage(MOCK_PACKAGE2, MOCK_UID11);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
}
@Test
public void testPackageUninstallBasic() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
- mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_UID1);
+ when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
}
@Test
public void testPackageRemoveThenAdd() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{});
- mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_UID1);
+ when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
- addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
}
@Test
public void testPackageUpdate() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {});
- mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1);
- addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET});
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
}
@Test
public void testPackageUninstallWithMultiplePackages() throws Exception {
- addPackage(MOCK_PACKAGE1, MOCK_UID1, new String[] {INTERNET, UPDATE_DEVICE_STATS});
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
- // Mock another package with the same uid but different permissions.
- PackageInfo packageInfo2 = systemPackageInfoWithPermissions(INTERNET);
- when(mPackageManager.getPackageInfo(eq(MOCK_PACKAGE2), anyInt())).thenReturn(packageInfo2);
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{
- MOCK_PACKAGE2});
+ // Install another package with the same uid but different permissions.
+ addPackage(MOCK_PACKAGE2, MOCK_UID11, INTERNET);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID11);
- mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_UID1);
+ // Uninstall MOCK_PACKAGE1 and expect only INTERNET permission left.
+ when(mPackageManager.getPackagesForUid(eq(MOCK_UID11)))
+ .thenReturn(new String[]{MOCK_PACKAGE2});
+ mPermissionMonitor.onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
}
@Test
@@ -853,15 +885,14 @@
@Test
public void testUpdateUidPermissionsFromSystemConfig() throws Exception {
- when(mPackageManager.getInstalledPackages(anyInt())).thenReturn(new ArrayList<>());
when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET)))
- .thenReturn(new int[]{ MOCK_UID1, MOCK_UID2 });
+ .thenReturn(new int[]{ MOCK_UID11, MOCK_UID12 });
when(mSystemConfigManager.getSystemPermissionUids(eq(UPDATE_DEVICE_STATS)))
- .thenReturn(new int[]{ MOCK_UID2 });
+ .thenReturn(new int[]{ MOCK_UID12 });
mPermissionMonitor.startMonitoring();
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID2);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID2);
}
private BroadcastReceiver expectBroadcastReceiver(String... actions) {
@@ -881,25 +912,26 @@
@Test
public void testIntentReceiver() throws Exception {
+ mPermissionMonitor.startMonitoring();
final BroadcastReceiver receiver = expectBroadcastReceiver(
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REMOVED);
// Verify receiving PACKAGE_ADDED intent.
final Intent addedIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
- addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
- setPackagePermissions(MOCK_PACKAGE1, MOCK_UID1,
- new String[] { INTERNET, UPDATE_DEVICE_STATS });
+ addedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID11);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, INTERNET,
+ UPDATE_DEVICE_STATS);
receiver.onReceive(mContext, addedIntent);
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
// Verify receiving PACKAGE_REMOVED intent.
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(null);
+ when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
final Intent removedIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
Uri.fromParts("package", MOCK_PACKAGE1, null /* fragment */));
- removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID1);
+ removedIntent.putExtra(Intent.EXTRA_UID, MOCK_UID11);
receiver.onReceive(mContext, removedIntent);
- mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_UID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
}
private ContentObserver expectRegisterContentObserver(Uri expectedUri) {
@@ -910,167 +942,167 @@
return captor.getValue();
}
- private void buildAndMockPackageInfoWithPermissions(String packageName, int uid,
- String... permissions) throws Exception {
- final PackageInfo packageInfo = setPackagePermissions(packageName, uid, permissions);
- packageInfo.packageName = packageName;
- packageInfo.applicationInfo.uid = uid;
- }
-
@Test
public void testUidsAllowedOnRestrictedNetworksChanged() throws Exception {
+ mPermissionMonitor.startMonitoring();
final ContentObserver contentObserver = expectRegisterContentObserver(
Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
- mPermissionMonitor.onUserAdded(MOCK_USER1);
// Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12);
- // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
+ // MOCK_UID11 is listed in setting that allow to use restricted networks, MOCK_UID11
// should have SYSTEM permission.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
- new ArraySet<>(new Integer[] { MOCK_UID1 }));
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID11));
contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_UID2);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
- // MOCK_UID2 is listed in setting that allow to use restricted networks, MOCK_UID2
- // should have SYSTEM permission but MOCK_UID1 should revoke permission.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
- new ArraySet<>(new Integer[] { MOCK_UID2 }));
+ // MOCK_UID12 is listed in setting that allow to use restricted networks, MOCK_UID12
+ // should have SYSTEM permission but MOCK_UID11 should revoke permission.
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID12));
contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_UID2);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_UID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID2);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1);
// No uid lists in setting, should revoke permission from all uids.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of());
contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_UID1, MOCK_UID2);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1, MOCK_APPID2);
}
@Test
public void testUidsAllowedOnRestrictedNetworksChangedWithSharedUid() throws Exception {
+ mPermissionMonitor.startMonitoring();
final ContentObserver contentObserver = expectRegisterContentObserver(
Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
- mPermissionMonitor.onUserAdded(MOCK_USER1);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1);
- when(mPackageManager.getPackagesForUid(MOCK_UID1))
- .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID11);
- // MOCK_PACKAGE1 have CHANGE_NETWORK_STATE, MOCK_UID1 should have NETWORK permission.
- addPackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1);
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
+ // MOCK_PACKAGE1 have CHANGE_NETWORK_STATE, MOCK_UID11 should have NETWORK permission.
+ addPackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
- // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
+ // MOCK_UID11 is listed in setting that allow to use restricted networks, MOCK_UID11
// should upgrade to SYSTEM permission.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
- new ArraySet<>(new Integer[] { MOCK_UID1 }));
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID11));
contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
- // No app lists in setting, MOCK_UID1 should downgrade to NETWORK permission.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+ // No app lists in setting, MOCK_UID11 should downgrade to NETWORK permission.
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of());
contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
- // MOCK_PACKAGE1 removed, should revoke permission from MOCK_UID1.
- when(mPackageManager.getPackagesForUid(MOCK_UID1)).thenReturn(new String[]{MOCK_PACKAGE2});
- removePackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_UID1);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_UID1);
+ // MOCK_PACKAGE1 removed, should revoke permission from MOCK_UID11.
+ when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{MOCK_PACKAGE2});
+ removePackageForUsers(new UserHandle[]{MOCK_USER1}, MOCK_PACKAGE1, MOCK_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1);
}
@Test
public void testUidsAllowedOnRestrictedNetworksChangedWithMultipleUsers() throws Exception {
+ mPermissionMonitor.startMonitoring();
final ContentObserver contentObserver = expectRegisterContentObserver(
Settings.Global.getUriFor(UIDS_ALLOWED_ON_RESTRICTED_NETWORKS));
- // One user MOCK_USER1
- mPermissionMonitor.onUserAdded(MOCK_USER1);
- // Prepare PackageInfo for MOCK_PACKAGE1 and MOCK_PACKAGE2.
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2);
+ // Prepare PackageInfo for MOCK_APPID1 and MOCK_APPID2 in MOCK_USER1.
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12);
- // MOCK_UID1 is listed in setting that allow to use restricted networks, MOCK_UID1
- // in MOCK_USER1 should have SYSTEM permission and MOCK_UID2 has no permissions.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
- new ArraySet<>(new Integer[] { MOCK_UID1 }));
+ // MOCK_UID11 is listed in setting that allow to use restricted networks, MOCK_UID11 should
+ // have SYSTEM permission and MOCK_UID12 has no permissions.
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of(MOCK_UID11));
contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_UID2);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
// Add user MOCK_USER2.
+ final List<PackageInfo> pkgs = List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID21));
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID22);
+ doReturn(pkgs).when(mPackageManager)
+ .getInstalledPackagesAsUser(eq(GET_PERMISSIONS), eq(MOCK_USER_ID2));
mPermissionMonitor.onUserAdded(MOCK_USER2);
- // MOCK_UID1 in both users should all have SYSTEM permission and MOCK_UID2 has no
- // permissions in either user.
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_UID2);
+ // MOCK_APPID1 in MOCK_USER1 should have SYSTEM permission but in MOCK_USER2 should have no
+ // permissions. And MOCK_APPID2 has no permissions in either users.
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER2}, MOCK_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID2);
- // MOCK_UID2 is listed in setting that allow to use restricted networks, MOCK_UID2
- // in both users should have SYSTEM permission and MOCK_UID1 has no permissions.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(
- new ArraySet<>(new Integer[] { MOCK_UID2 }));
+ // MOCK_UID22 is listed in setting that allow to use restricted networks,
+ // MOCK_APPID2 in MOCK_USER2 should have SYSTEM permission but in MOCK_USER1 should have no
+ // permissions. And MOCK_APPID1 has no permissions in either users.
+ doReturn(Set.of(MOCK_UID22)).when(mDeps).getUidsAllowedOnRestrictedNetworks(any());
contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_UID2);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_UID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER2},
+ MOCK_APPID2);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1, MOCK_USER2}, MOCK_APPID1);
// Remove user MOCK_USER1
mPermissionMonitor.onUserRemoved(MOCK_USER1);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER2}, MOCK_UID2);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER2}, MOCK_UID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER2},
+ MOCK_APPID2);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER2}, MOCK_APPID1);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID2);
// No uid lists in setting, should revoke permission from all uids.
- when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(new ArraySet<>());
+ when(mDeps.getUidsAllowedOnRestrictedNetworks(any())).thenReturn(Set.of());
contentObserver.onChange(true /* selfChange */);
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER2}, MOCK_UID1, MOCK_UID2);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER2}, MOCK_APPID1, MOCK_APPID2);
}
@Test
public void testOnExternalApplicationsAvailable() throws Exception {
- final BroadcastReceiver receiver = expectBroadcastReceiver(
- Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-
// Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
// and have different uids. There has no permission for both uids.
- when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1));
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
- buildPackageInfo(MOCK_PACKAGE2, MOCK_UID2)));
+ doReturn(List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID12)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
mPermissionMonitor.startMonitoring();
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_UID1, MOCK_UID2);
- mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_UID1, MOCK_UID2);
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1, MOCK_APPID2);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1, MOCK_APPID2);
+ final BroadcastReceiver receiver = expectBroadcastReceiver(
+ Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
// Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST,
new String[] { MOCK_PACKAGE1 , MOCK_PACKAGE2});
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12, CHANGE_NETWORK_STATE,
UPDATE_DEVICE_STATS);
receiver.onReceive(mContext, externalIntent);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_UID2);
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, MOCK_UID2);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID2);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, MOCK_APPID2);
}
@Test
public void testOnExternalApplicationsAvailable_AppsNotRegisteredOnStartMonitoring()
throws Exception {
+ mPermissionMonitor.startMonitoring();
final BroadcastReceiver receiver = expectBroadcastReceiver(
Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- // One user MOCK_USER1
- mPermissionMonitor.onUserAdded(MOCK_USER1);
-
// Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
// and have different uids. There has no permission for both uids.
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS, INTERNET);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID2, CHANGE_NETWORK_STATE,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID12, CHANGE_NETWORK_STATE,
UPDATE_DEVICE_STATS);
// Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
@@ -1078,69 +1110,78 @@
externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST,
new String[] { MOCK_PACKAGE1 , MOCK_PACKAGE2});
receiver.onReceive(mContext, externalIntent);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_UID2);
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, MOCK_UID2);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID2);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, MOCK_APPID2);
}
@Test
public void testOnExternalApplicationsAvailableWithSharedUid()
throws Exception {
+ // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
+ // storage and shared on MOCK_UID11. There has no permission for MOCK_UID11.
+ doReturn(List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID11)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
+ mPermissionMonitor.startMonitoring();
+ mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_APPID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1);
+
final BroadcastReceiver receiver = expectBroadcastReceiver(
Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-
- // Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
- // storage and shared on MOCK_UID1. There has no permission for MOCK_UID1.
- when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1));
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
- buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1)));
- mPermissionMonitor.startMonitoring();
- mNetdMonitor.expectNoNetworkPerm(new UserHandle[]{MOCK_USER1}, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_UID1);
-
// Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1});
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1, CHANGE_NETWORK_STATE);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, UPDATE_DEVICE_STATS);
- when(mPackageManager.getPackagesForUid(MOCK_UID1))
- .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID11, UPDATE_DEVICE_STATS);
receiver.onReceive(mContext, externalIntent);
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, MOCK_UID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_UPDATE_DEVICE_STATS, MOCK_APPID1);
}
@Test
public void testOnExternalApplicationsAvailableWithSharedUid_DifferentStorage()
throws Exception {
+ // Initial the permission state. MOCK_PACKAGE1 is installed on external storage and
+ // MOCK_PACKAGE2 is installed on device. These two packages are shared on MOCK_UID11.
+ // MOCK_UID11 has NETWORK and INTERNET permissions.
+ doReturn(List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID11),
+ buildPackageInfo(MOCK_PACKAGE2, MOCK_UID11, CHANGE_NETWORK_STATE, INTERNET)))
+ .when(mPackageManager).getInstalledPackagesAsUser(eq(GET_PERMISSIONS), anyInt());
+ mPermissionMonitor.startMonitoring();
+ mNetdMonitor.expectNetworkPerm(PERMISSION_NETWORK, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
+
final BroadcastReceiver receiver = expectBroadcastReceiver(
Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
-
- // Initial the permission state. MOCK_PACKAGE1 is installed on external storage and
- // MOCK_PACKAGE2 is installed on device. These two packages are shared on MOCK_UID1.
- // MOCK_UID1 has NETWORK and INTERNET permissions.
- when(mUserManager.getUserHandles(eq(true))).thenReturn(List.of(MOCK_USER1));
- when(mPackageManager.getInstalledPackages(eq(GET_PERMISSIONS | MATCH_ANY_USER))).thenReturn(
- List.of(buildPackageInfo(MOCK_PACKAGE1, MOCK_UID1),
- buildPackageInfo(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE,
- INTERNET)));
- mPermissionMonitor.startMonitoring();
- mNetdMonitor.expectNetworkPerm(NETWORK, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_UID1);
-
// Verify receiving EXTERNAL_APPLICATIONS_AVAILABLE intent and update permission to netd.
final Intent externalIntent = new Intent(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
externalIntent.putExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST, new String[] {MOCK_PACKAGE1});
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID1,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE1, MOCK_UID11,
CONNECTIVITY_USE_RESTRICTED_NETWORKS, UPDATE_DEVICE_STATS);
- buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID1, CHANGE_NETWORK_STATE,
+ buildAndMockPackageInfoWithPermissions(MOCK_PACKAGE2, MOCK_UID11, CHANGE_NETWORK_STATE,
INTERNET);
- when(mPackageManager.getPackagesForUid(MOCK_UID1))
- .thenReturn(new String[]{MOCK_PACKAGE1, MOCK_PACKAGE2});
receiver.onReceive(mContext, externalIntent);
- mNetdMonitor.expectNetworkPerm(SYSTEM, new UserHandle[]{MOCK_USER1}, MOCK_UID1);
- mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_UID1);
+ mNetdMonitor.expectNetworkPerm(PERMISSION_SYSTEM, new UserHandle[]{MOCK_USER1},
+ MOCK_APPID1);
+ mNetdMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
+ }
+
+ @Test
+ public void testIsHigherNetworkPermission() {
+ assertFalse(isHigherNetworkPermission(PERMISSION_NONE, PERMISSION_NONE));
+ assertFalse(isHigherNetworkPermission(PERMISSION_NONE, PERMISSION_NETWORK));
+ assertFalse(isHigherNetworkPermission(PERMISSION_NONE, PERMISSION_SYSTEM));
+ assertTrue(isHigherNetworkPermission(PERMISSION_NETWORK, PERMISSION_NONE));
+ assertFalse(isHigherNetworkPermission(PERMISSION_NETWORK, PERMISSION_NETWORK));
+ assertFalse(isHigherNetworkPermission(PERMISSION_NETWORK, PERMISSION_SYSTEM));
+ assertTrue(isHigherNetworkPermission(PERMISSION_SYSTEM, PERMISSION_NONE));
+ assertTrue(isHigherNetworkPermission(PERMISSION_SYSTEM, PERMISSION_NETWORK));
+ assertFalse(isHigherNetworkPermission(PERMISSION_SYSTEM, PERMISSION_SYSTEM));
}
}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index b706090..fd9aefa 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import static android.Manifest.permission.BIND_VPN_SERVICE;
import static android.content.pm.UserInfo.FLAG_ADMIN;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
@@ -31,12 +32,14 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.after;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doCallRealMethod;
@@ -70,6 +73,7 @@
import android.net.IpPrefix;
import android.net.IpSecManager;
import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.Network;
@@ -86,6 +90,7 @@
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.INetworkManagementService;
+import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -102,6 +107,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.modules.utils.build.SdkLevel;
import com.android.server.IpSecService;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -118,6 +124,7 @@
import java.io.BufferedWriter;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileWriter;
import java.io.IOException;
import java.net.Inet4Address;
@@ -851,6 +858,81 @@
}
@Test
+ public void testStartOpAndFinishOpWillBeCalledWhenPlatformVpnIsOnAndOff() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastT());
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ verify(mAppOps).noteOpNoThrow(
+ eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ verify(mAppOps).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ // Add a small delay to make sure that startOp is only called once.
+ verify(mAppOps, after(100).times(1)).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ // Check that the startOp is not called with OPSTR_ESTABLISH_VPN_SERVICE.
+ verify(mAppOps, never()).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+ // Add a small delay to double confirm that startOp is only called once.
+ verify(mAppOps, after(100)).finishOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */);
+ }
+
+ @Test
+ public void testStartOpWithSeamlessHandover() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastT());
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_VPN);
+ assertTrue(vpn.prepare(TEST_VPN_PKG, null, VpnManager.TYPE_VPN_SERVICE));
+ final VpnConfig config = new VpnConfig();
+ config.user = "VpnTest";
+ config.addresses.add(new LinkAddress("192.0.2.2/32"));
+ config.mtu = 1450;
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ final ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.permission = BIND_VPN_SERVICE;
+ resolveInfo.serviceInfo = serviceInfo;
+ when(mPackageManager.resolveService(any(), anyInt())).thenReturn(resolveInfo);
+ when(mContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(true);
+ vpn.establish(config);
+ verify(mAppOps, times(1)).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ // Call establish() twice with the same config, it should match seamless handover case and
+ // startOp() shouldn't be called again.
+ vpn.establish(config);
+ verify(mAppOps, times(1)).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_SERVICE),
+ eq(Process.myUid()),
+ eq(TEST_VPN_PKG),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ }
+
+ @Test
public void testSetPackageAuthorizationVpnService() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
@@ -1197,6 +1279,32 @@
public boolean isInterfacePresent(final Vpn vpn, final String iface) {
return true;
}
+
+ @Override
+ public ParcelFileDescriptor adoptFd(Vpn vpn, int mtu) {
+ return new ParcelFileDescriptor(new FileDescriptor());
+ }
+
+ @Override
+ public int jniCreate(Vpn vpn, int mtu) {
+ // Pick a random positive number as fd to return.
+ return 345;
+ }
+
+ @Override
+ public String jniGetName(Vpn vpn, int fd) {
+ return TEST_IFACE_NAME;
+ }
+
+ @Override
+ public int jniSetAddresses(Vpn vpn, String interfaze, String addresses) {
+ if (addresses == null) return 0;
+ // Return the number of addresses.
+ return addresses.split(" ").length;
+ }
+
+ @Override
+ public void setBlocking(FileDescriptor fd, boolean blocking) {}
}
/**
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
index e35104e..416549c 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -37,7 +37,9 @@
import android.app.usage.NetworkStatsManager;
import android.net.DataUsageRequest;
import android.net.NetworkIdentity;
+import android.net.NetworkIdentitySet;
import android.net.NetworkStats;
+import android.net.NetworkStatsAccess;
import android.net.NetworkTemplate;
import android.os.Build;
import android.os.ConditionVariable;
@@ -72,7 +74,7 @@
*/
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S)
public class NetworkStatsObserversTest {
private static final String TEST_IFACE = "test0";
private static final String TEST_IFACE2 = "test1";
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index ab76460..4948e66 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -523,7 +523,7 @@
public void testUidStatsAcrossNetworks() throws Exception {
// pretend first mobile network comes online
expectDefaultSettings();
- NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1)};
+ NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildMobileState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -554,7 +554,7 @@
// disappearing, to verify we don't count backwards.
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
- states = new NetworkStateSnapshot[] {buildMobile3gState(IMSI_2)};
+ states = new NetworkStateSnapshot[] {buildMobileState(IMSI_2)};
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
.insertEntry(TEST_IFACE, 2048L, 16L, 512L, 4L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
@@ -657,13 +657,16 @@
@Test
public void testMobileStatsByRatType() throws Exception {
final NetworkTemplate template3g =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS,
+ METERED_YES);
final NetworkTemplate template4g =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_LTE,
+ METERED_YES);
final NetworkTemplate template5g =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR,
+ METERED_YES);
final NetworkStateSnapshot[] states =
- new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)};
+ new NetworkStateSnapshot[]{buildMobileState(IMSI_1)};
// 3G network comes online.
expectNetworkStatsSummary(buildEmptyStats());
@@ -730,6 +733,45 @@
}
@Test
+ public void testMobileStatsMeteredness() throws Exception {
+ // Create metered 5g template.
+ final NetworkTemplate templateMetered5g =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR,
+ METERED_YES);
+ // Create non-metered 5g template
+ final NetworkTemplate templateNonMetered5g =
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_NR, METERED_NO);
+
+ expectDefaultSettings();
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+
+ // Pretend that 5g mobile network comes online
+ final NetworkStateSnapshot[] mobileStates =
+ new NetworkStateSnapshot[] {buildMobileState(IMSI_1), buildMobileState(TEST_IFACE2,
+ IMSI_1, true /* isTemporarilyNotMetered */, false /* isRoaming */)};
+ setMobileRatTypeAndWaitForIdle(TelephonyManager.NETWORK_TYPE_NR);
+ mService.notifyNetworkStatus(NETWORKS_MOBILE, mobileStates,
+ getActiveIface(mobileStates), new UnderlyingNetworkInfo[0]);
+
+ // Create some traffic
+ // Note that all traffic from NetworkManagementService is tagged as METERED_NO, ROAMING_NO
+ // and DEFAULT_NETWORK_YES, because these three properties aren't tracked at that layer.
+ // They are layered on top by inspecting the iface properties.
+ incrementCurrentTime(HOUR_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 128L, 2L, 128L, 2L, 0L)
+ .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, METERED_YES, ROAMING_NO,
+ DEFAULT_NETWORK_YES, 256, 3L, 128L, 5L, 0L));
+ forcePollAndWaitForIdle();
+
+ // Verify service recorded history.
+ assertUidTotal(templateMetered5g, UID_RED, 128L, 2L, 128L, 2L, 0);
+ assertUidTotal(templateNonMetered5g, UID_RED, 256, 3L, 128L, 5L, 0);
+ }
+
+ @Test
public void testMobileStatsOemManaged() throws Exception {
final NetworkTemplate templateOemPaid = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
/*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
@@ -1112,7 +1154,8 @@
// pretend that network comes online
expectDefaultSettings();
NetworkStateSnapshot[] states =
- new NetworkStateSnapshot[] {buildMobile3gState(IMSI_1, true /* isRoaming */)};
+ new NetworkStateSnapshot[] {buildMobileState(TEST_IFACE, IMSI_1,
+ false /* isTemporarilyNotMetered */, true /* isRoaming */)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1151,7 +1194,7 @@
// pretend first mobile network comes online
expectDefaultSettings();
final NetworkStateSnapshot[] states =
- new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)};
+ new NetworkStateSnapshot[]{buildMobileState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1478,13 +1521,15 @@
// Build 3G template, type unknown template to get stats while network type is unknown
// and type all template to get the sum of all network type stats.
final NetworkTemplate template3g =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS,
+ METERED_YES);
final NetworkTemplate templateUnknown =
- buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN);
+ buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ METERED_YES);
final NetworkTemplate templateAll =
- buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL);
+ buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL, METERED_YES);
final NetworkStateSnapshot[] states =
- new NetworkStateSnapshot[]{buildMobile3gState(IMSI_1)};
+ new NetworkStateSnapshot[]{buildMobileState(IMSI_1)};
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(buildEmptyStats());
@@ -1561,7 +1606,7 @@
// Pretend mobile network comes online, but wifi is the default network.
expectDefaultSettings();
NetworkStateSnapshot[] states = new NetworkStateSnapshot[]{
- buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobile3gState(IMSI_1)};
+ buildWifiState(true /*isMetered*/, TEST_IFACE2), buildMobileState(IMSI_1)};
expectNetworkStatsUidDetail(buildEmptyStats());
mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states),
new UnderlyingNetworkInfo[0]);
@@ -1580,7 +1625,7 @@
// Verify mobile summary is not changed by the operation count.
final NetworkTemplate templateMobile =
- buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL);
+ buildTemplateMobileWithRatType(null, NETWORK_TYPE_ALL, METERED_YES);
final NetworkStats statsMobile = mSession.getSummaryForAllUid(
templateMobile, Long.MIN_VALUE, Long.MAX_VALUE, true);
assertValues(statsMobile, IFACE_ALL, UID_RED, SET_ALL, TAG_NONE, METERED_ALL, ROAMING_ALL,
@@ -1655,6 +1700,8 @@
return states[0].getLinkProperties().getInterfaceName();
}
+ // TODO: These expect* methods are used to have NetworkStatsService returns the given stats
+ // instead of expecting anything. Therefore, these methods should be renamed properly.
private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
expectNetworkStatsSummaryDev(summary.clone());
expectNetworkStatsSummaryXt(summary.clone());
@@ -1744,15 +1791,21 @@
return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, subscriberId, TYPE_WIFI);
}
- private static NetworkStateSnapshot buildMobile3gState(String subscriberId) {
- return buildMobile3gState(subscriberId, false /* isRoaming */);
+ private static NetworkStateSnapshot buildMobileState(String subscriberId) {
+ return buildMobileState(TEST_IFACE, subscriberId, false /* isTemporarilyNotMetered */,
+ false /* isRoaming */);
}
- private static NetworkStateSnapshot buildMobile3gState(String subscriberId, boolean isRoaming) {
+ private static NetworkStateSnapshot buildMobileState(String iface, String subscriberId,
+ boolean isTemporarilyNotMetered, boolean isRoaming) {
final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(TEST_IFACE);
+ prop.setInterfaceName(iface);
final NetworkCapabilities capabilities = new NetworkCapabilities();
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
+
+ if (isTemporarilyNotMetered) {
+ capabilities.addCapability(
+ NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ }
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
return new NetworkStateSnapshot(
diff --git a/tests/unit/jni/Android.bp b/tests/unit/jni/Android.bp
index 1c1ba9e..fe971e7 100644
--- a/tests/unit/jni/Android.bp
+++ b/tests/unit/jni/Android.bp
@@ -13,6 +13,8 @@
"-Wthread-safety",
],
+ header_libs: ["bpf_connectivity_headers"],
+
srcs: [
":lib_networkStatsFactory_native",
"test_onload.cpp",
diff --git a/tests/unit/vpn-jarjar-rules.txt b/tests/unit/vpn-jarjar-rules.txt
new file mode 100644
index 0000000..16661b9
--- /dev/null
+++ b/tests/unit/vpn-jarjar-rules.txt
@@ -0,0 +1,4 @@
+# Only keep classes imported by ConnectivityServiceTest
+keep com.android.server.VpnManagerService
+keep com.android.server.connectivity.Vpn
+keep com.android.server.connectivity.VpnProfileStore
\ No newline at end of file