Merge "Move fromStableParcelable to KeepaliveController"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index be0e040..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"
}
]
},
@@ -33,6 +36,9 @@
"options": [
{
"exclude-annotation": "com.android.testutils.SkipPresubmit"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.RequiresDevice"
}
]
},
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index e4ce615..a7ae21a 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -81,6 +81,7 @@
"libnativehelper_compat_libc++",
],
static_libs: [
+ "libbpfmapjni",
"libnetjniutils",
],
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/jni/com_android_networkstack_tethering_BpfMap.cpp b/Tethering/jni/com_android_networkstack_tethering_BpfMap.cpp
deleted file mode 100644
index 260dbc1..0000000
--- a/Tethering/jni/com_android_networkstack_tethering_BpfMap.cpp
+++ /dev/null
@@ -1,136 +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));
-
- if (fd < 0) jniThrowErrnoException(env, "bpfFdGet", errno);
-
- 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/onload.cpp b/Tethering/jni/onload.cpp
index 02e602d..fbae615 100644
--- a/Tethering/jni/onload.cpp
+++ b/Tethering/jni/onload.cpp
@@ -23,7 +23,7 @@
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);
@@ -36,7 +36,8 @@
if (register_android_net_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 cf49683..bee928d 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -46,8 +46,6 @@
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;
@@ -67,6 +65,7 @@
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;
@@ -767,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.",
@@ -785,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/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 1df3e58..4a6b9aa 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -61,6 +61,7 @@
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;
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/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 1559f3b..c942899 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -1140,9 +1140,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) {
diff --git a/Tethering/tests/integration/Android.bp b/Tethering/tests/integration/Android.bp
index c7f5527..7e8c65b 100644
--- a/Tethering/tests/integration/Android.bp
+++ b/Tethering/tests/integration/Android.bp
@@ -77,7 +77,7 @@
defaults: ["TetheringIntegrationTestsDefaults"],
test_suites: [
"device-tests",
- "mts",
+ "mts-tethering",
],
compile_multilib: "both",
jarjar_rules: ":NetworkStackJarJarRules",
@@ -129,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: [
diff --git a/Tethering/tests/privileged/Android.bp b/Tethering/tests/privileged/Android.bp
index 75fdd6e..e27c811 100644
--- a/Tethering/tests/privileged/Android.bp
+++ b/Tethering/tests/privileged/Android.bp
@@ -42,7 +42,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/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 f97270c..184d045 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
@@ -31,6 +31,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;
diff --git a/Tethering/tests/unit/Android.bp b/Tethering/tests/unit/Android.bp
index 0eb682b..85169f9 100644
--- a/Tethering/tests/unit/Android.bp
+++ b/Tethering/tests/unit/Android.bp
@@ -97,7 +97,7 @@
platform_apis: true,
test_suites: [
"device-tests",
- "mts",
+ "mts-tethering",
],
defaults: ["TetheringTestsDefaults"],
compile_multilib: "both",
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index ef4330a..86ff7ac 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -99,10 +99,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;
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 acc042b..c5969d2 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -97,6 +97,7 @@
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;
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 3bda264..9e88890 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -4047,7 +4047,8 @@
// Temporary help to debug b/194394697 ; TODO : remove this function when the
// bug is fixed.
dumpAllRequestInfoLogsToLogcat();
- throw new IllegalStateException("This NRI is already registered. " + nri);
+ throw new IllegalStateException("This NRI is already registered. New : " + nri
+ + ", existing : " + n);
}
}
}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 4c6b669..da2715e 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -51,6 +51,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;
@@ -120,6 +121,9 @@
@GuardedBy("this")
private final Set<Integer> mUidsAllowedOnRestrictedNetworks = new ArraySet<>();
+ 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) {
@@ -485,6 +489,32 @@
}
/**
+ * 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.
*
* @param packageName The name of the new package.
@@ -494,7 +524,8 @@
*/
public synchronized void onPackageAdded(@NonNull final String packageName, final int uid) {
final int appId = UserHandle.getAppId(uid);
- sendPackagePermissionsForAppId(appId, getPermissionForUid(uid));
+ final int trafficPerm = getPermissionForUid(uid);
+ sendPackagePermissionsForAppId(appId, trafficPerm);
// 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).
@@ -513,6 +544,10 @@
// updateVpnUid() depends on mApps 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 int highestUidNetworkPermission(int uid) {
@@ -541,7 +576,8 @@
*/
public synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
final int appId = UserHandle.getAppId(uid);
- sendPackagePermissionsForAppId(appId, getPermissionForUid(uid));
+ final int trafficPerm = getPermissionForUid(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() in
@@ -552,7 +588,12 @@
mAllApps.remove(appId);
}
+ final int currentPermission = mApps.get(appId, 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 == PERMISSION_SYSTEM) {
// An app with this UID still has the SYSTEM permission.
// Therefore, this UID must already have the SYSTEM permission.
@@ -560,7 +601,7 @@
return;
}
// If the permissions of this UID have not changed, do nothing.
- if (permission == mApps.get(appId, PERMISSION_NONE)) return;
+ if (permission == currentPermission) return;
final SparseIntArray apps = new SparseIntArray();
if (permission != PERMISSION_NONE) {
@@ -838,6 +879,8 @@
// Step3. Update or revoke permission for uids with netd.
update(mUsers, updatedUids, true /* add */);
update(mUsers, removedUids, false /* add */);
+ mPermissionUpdateLogs.log("Setting change: update=" + updatedUids
+ + ", remove=" + removedUids);
}
private synchronized void onExternalApplicationsAvailable(String[] pkgList) {
@@ -865,6 +908,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/tests/common/Android.bp b/tests/common/Android.bp
index 9b136d5..c4c72c8 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",
diff --git a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
index 80951ca..6b2a1ee 100644
--- a/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/BatteryStatsManagerTest.java
@@ -41,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;
@@ -94,7 +94,7 @@
// 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.
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 594000b..579be15 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -155,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;
@@ -168,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;
@@ -559,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));
@@ -1425,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));
@@ -1632,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));
@@ -1682,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"
@@ -1728,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));
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/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index c54a11e..bc50d0d 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -159,7 +159,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;
@@ -314,6 +313,7 @@
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;
@@ -326,6 +326,7 @@
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.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
@@ -352,11 +353,8 @@
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
-import org.mockito.MockingDetails;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
-import org.mockito.exceptions.misusing.UnfinishedStubbingException;
import org.mockito.stubbing.Answer;
import java.io.FileDescriptor;
@@ -469,7 +467,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;
@@ -507,7 +505,6 @@
@Mock LocationManager mLocationManager;
@Mock AppOpsManager mAppOpsManager;
@Mock TelephonyManager mTelephonyManager;
- @Mock MockableSystemProperties mSystemProperties;
@Mock EthernetManager mEthernetManager;
@Mock NetworkPolicyManager mNetworkPolicyManager;
@Mock VpnProfileStore mVpnProfileStore;
@@ -1582,11 +1579,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);
}
}
@@ -1705,8 +1702,13 @@
mCsHandlerThread = new HandlerThread("TestConnectivityService");
mVMSHandlerThread = new HandlerThread("TestVpnManagerService");
- mDeps = makeDependencies();
- returnRealCallingUid();
+
+ 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),
@@ -1714,7 +1716,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);
@@ -1738,41 +1739,7 @@
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
}
- private void returnRealCallingUid() {
- try {
- doAnswer((invocationOnMock) -> Binder.getCallingUid()).when(mDeps).getCallingUid();
- } catch (UnfinishedStubbingException e) {
- final MockingDetails details = Mockito.mockingDetails(mDeps);
- Log.e("ConnectivityServiceTest", "UnfinishedStubbingException,"
- + " Stubbings: " + TextUtils.join(", ", details.getStubbings())
- + " Invocations: " + details.printInvocations(), e);
- throw e;
- }
- }
-
- 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(
@@ -1785,7 +1752,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());
@@ -1796,22 +1764,158 @@
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);
+ }
}
private static void initAlarmManager(final AlarmManager am, final Handler alarmHandler) {
@@ -5136,9 +5240,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();
@@ -9171,18 +9272,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();
@@ -9191,9 +9294,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();
}
@@ -9276,9 +9379,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);
@@ -9297,8 +9402,8 @@
// 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);
@@ -9350,9 +9455,9 @@
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);
doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
@@ -9669,7 +9774,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();
@@ -10527,7 +10632,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)
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) {}
}
/**