Merge "Update networksecurity resource owner" into main
diff --git a/bpf/tests/mts/bpf_existence_test.cpp b/bpf/tests/mts/bpf_existence_test.cpp
index 2cfa546..0ecda3d 100644
--- a/bpf/tests/mts/bpf_existence_test.cpp
+++ b/bpf/tests/mts/bpf_existence_test.cpp
@@ -21,7 +21,6 @@
#include <string>
#include <android-base/properties.h>
-#include <android-modules-utils/sdk_level.h>
#include <android/api-level.h>
#include <bpf/BpfUtils.h>
@@ -32,11 +31,6 @@
using std::string;
using android::bpf::isAtLeastKernelVersion;
-using android::modules::sdklevel::IsAtLeastR;
-using android::modules::sdklevel::IsAtLeastS;
-using android::modules::sdklevel::IsAtLeastT;
-using android::modules::sdklevel::IsAtLeastU;
-using android::modules::sdklevel::IsAtLeastV;
#define PLATFORM "/sys/fs/bpf/"
#define TETHERING "/sys/fs/bpf/tethering/"
@@ -48,10 +42,15 @@
class BpfExistenceTest : public ::testing::Test {
};
-//ToDo: replace isAtLeast25Q2 with IsAtLeastB once sdk_level have been upgraded to 36 on aosp/main
const bool unreleased = (android::base::GetProperty("ro.build.version.codename", "REL") != "REL");
-const int api_level = unreleased ? __ANDROID_API_FUTURE__ : android_get_device_api_level();
-const bool isAtLeast25Q2 = (api_level > __ANDROID_API_V__);
+const int api_level = unreleased ? 10000 : android_get_device_api_level();
+const bool isAtLeastR = (api_level >= 30);
+const bool isAtLeastS = (api_level >= 31);
+// Sv2 is 32
+const bool isAtLeastT = (api_level >= 33);
+const bool isAtLeastU = (api_level >= 34);
+const bool isAtLeastV = (api_level >= 35);
+const bool isAtLeast25Q2 = (api_level >= 36);
// Part of Android R platform (for 4.9+), but mainlined in S
static const set<string> PLATFORM_ONLY_IN_R = {
@@ -194,33 +193,33 @@
// and for the presence of mainline stuff.
// Note: Q is no longer supported by mainline
- ASSERT_TRUE(IsAtLeastR());
+ ASSERT_TRUE(isAtLeastR);
// R can potentially run on pre-4.9 kernel non-eBPF capable devices.
- DO_EXPECT(IsAtLeastR() && !IsAtLeastS() && isAtLeastKernelVersion(4, 9, 0), PLATFORM_ONLY_IN_R);
+ DO_EXPECT(isAtLeastR && !isAtLeastS && isAtLeastKernelVersion(4, 9, 0), PLATFORM_ONLY_IN_R);
// S requires Linux Kernel 4.9+ and thus requires eBPF support.
- if (IsAtLeastS()) ASSERT_TRUE(isAtLeastKernelVersion(4, 9, 0));
- DO_EXPECT(IsAtLeastS(), MAINLINE_FOR_S_PLUS);
+ if (isAtLeastS) ASSERT_TRUE(isAtLeastKernelVersion(4, 9, 0));
+ DO_EXPECT(isAtLeastS, MAINLINE_FOR_S_PLUS);
// Nothing added or removed in SCv2.
// T still only requires Linux Kernel 4.9+.
- DO_EXPECT(IsAtLeastT(), MAINLINE_FOR_T_PLUS);
- DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(4, 14, 0), MAINLINE_FOR_T_4_14_PLUS);
- DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(4, 19, 0), MAINLINE_FOR_T_4_19_PLUS);
- DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_T_5_10_PLUS);
- DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
+ DO_EXPECT(isAtLeastT, MAINLINE_FOR_T_PLUS);
+ DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 14, 0), MAINLINE_FOR_T_4_14_PLUS);
+ DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(4, 19, 0), MAINLINE_FOR_T_4_19_PLUS);
+ DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_T_5_10_PLUS);
+ DO_EXPECT(isAtLeastT && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
// U requires Linux Kernel 4.14+, but nothing (as yet) added or removed in U.
- if (IsAtLeastU()) ASSERT_TRUE(isAtLeastKernelVersion(4, 14, 0));
- DO_EXPECT(IsAtLeastU(), MAINLINE_FOR_U_PLUS);
- DO_EXPECT(IsAtLeastU() && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_U_5_10_PLUS);
+ if (isAtLeastU) ASSERT_TRUE(isAtLeastKernelVersion(4, 14, 0));
+ DO_EXPECT(isAtLeastU, MAINLINE_FOR_U_PLUS);
+ DO_EXPECT(isAtLeastU && isAtLeastKernelVersion(5, 10, 0), MAINLINE_FOR_U_5_10_PLUS);
// V requires Linux Kernel 4.19+, but nothing (as yet) added or removed in V.
- if (IsAtLeastV()) ASSERT_TRUE(isAtLeastKernelVersion(4, 19, 0));
- DO_EXPECT(IsAtLeastV(), MAINLINE_FOR_V_PLUS);
- DO_EXPECT(IsAtLeastV() && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_V_5_4_PLUS);
+ if (isAtLeastV) ASSERT_TRUE(isAtLeastKernelVersion(4, 19, 0));
+ DO_EXPECT(isAtLeastV, MAINLINE_FOR_V_PLUS);
+ DO_EXPECT(isAtLeastV && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_V_5_4_PLUS);
if (isAtLeast25Q2) ASSERT_TRUE(isAtLeastKernelVersion(5, 4, 0));
DO_EXPECT(isAtLeast25Q2, MAINLINE_FOR_25Q2_PLUS);
diff --git a/framework/src/android/net/TestNetworkManager.java b/framework/src/android/net/TestNetworkManager.java
index 416c6de..cbc7a4f 100644
--- a/framework/src/android/net/TestNetworkManager.java
+++ b/framework/src/android/net/TestNetworkManager.java
@@ -23,8 +23,10 @@
import android.os.IBinder;
import android.os.RemoteException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import java.util.Objects;
/**
@@ -196,45 +198,6 @@
}
/**
- * Create a tap interface for testing purposes
- *
- * @param linkAddrs an array of LinkAddresses to assign to the TAP interface
- * @return A TestNetworkInterface representing the underlying TAP interface. Close the contained
- * ParcelFileDescriptor to tear down the TAP interface.
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
- @NonNull
- public TestNetworkInterface createTapInterface(@NonNull LinkAddress[] linkAddrs) {
- try {
- return mService.createInterface(TAP, CARRIER_UP, BRING_UP, USE_IPV6_PROV_DELAY,
- linkAddrs, null /* iface */);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Create a tap interface for testing purposes
- *
- * @param bringUp whether to bring up the interface before returning it.
- *
- * @return A ParcelFileDescriptor of the underlying TAP interface. Close this to tear down the
- * TAP interface.
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
- @NonNull
- public TestNetworkInterface createTapInterface(boolean bringUp) {
- try {
- return mService.createInterface(TAP, CARRIER_UP, bringUp, USE_IPV6_PROV_DELAY,
- NO_ADDRS, null /* iface */);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Create a tap interface with a given interface name for testing purposes
*
* @param bringUp whether to bring up the interface before returning it.
@@ -258,26 +221,6 @@
}
/**
- * Create a tap interface with or without carrier for testing purposes.
- *
- * Note: setting carrierUp = false is not supported until kernel version 6.0.
- *
- * @param carrierUp whether the created interface has a carrier or not.
- * @param bringUp whether to bring up the interface before returning it.
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
- @NonNull
- public TestNetworkInterface createTapInterface(boolean carrierUp, boolean bringUp) {
- try {
- return mService.createInterface(TAP, carrierUp, bringUp, USE_IPV6_PROV_DELAY, NO_ADDRS,
- null /* iface */);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Create a tap interface for testing purposes.
*
* Note: setting carrierUp = false is not supported until kernel version 6.0.
@@ -300,27 +243,6 @@
}
/**
- * Create a tap interface for testing purposes.
- *
- * @param disableIpv6ProvisioningDelay whether to disable DAD and RS delay.
- * @param linkAddrs an array of LinkAddresses to assign to the TAP interface
- * @return A TestNetworkInterface representing the underlying TAP interface. Close the contained
- * ParcelFileDescriptor to tear down the TAP interface.
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
- @NonNull
- public TestNetworkInterface createTapInterface(boolean disableIpv6ProvisioningDelay,
- @NonNull LinkAddress[] linkAddrs) {
- try {
- return mService.createInterface(TAP, CARRIER_UP, BRING_UP, disableIpv6ProvisioningDelay,
- linkAddrs, null /* iface */);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Enable / disable carrier on TestNetworkInterface
*
* Note: TUNSETCARRIER is not supported until kernel version 5.0.
@@ -337,4 +259,110 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Represents a request to create a tun/tap interface for testing.
+ *
+ * @hide
+ */
+ public static class TestInterfaceRequest {
+ public final boolean isTun;
+ public final boolean hasCarrier;
+ public final boolean bringUp;
+ public final boolean disableIpv6ProvDelay;
+ @Nullable public final String ifname;
+ public final LinkAddress[] linkAddresses;
+
+ private TestInterfaceRequest(boolean isTun, boolean hasCarrier, boolean bringUp,
+ boolean disableProvDelay, @Nullable String ifname, LinkAddress[] linkAddresses) {
+ this.isTun = isTun;
+ this.hasCarrier = hasCarrier;
+ this.bringUp = bringUp;
+ this.disableIpv6ProvDelay = disableProvDelay;
+ this.ifname = ifname;
+ this.linkAddresses = linkAddresses;
+ }
+
+ /**
+ * Builder class for TestInterfaceRequest
+ *
+ * Defaults to a tap interface with carrier that has been brought up.
+ */
+ public static class Builder {
+ private boolean mIsTun = false;
+ private boolean mHasCarrier = true;
+ private boolean mBringUp = true;
+ private boolean mDisableIpv6ProvDelay = false;
+ @Nullable private String mIfname;
+ private List<LinkAddress> mLinkAddresses = new ArrayList<>();
+
+ /** Create tun interface. */
+ public Builder setTun() {
+ mIsTun = true;
+ return this;
+ }
+
+ /** Create tap interface. */
+ public Builder setTap() {
+ mIsTun = false;
+ return this;
+ }
+
+ /** Configure whether the interface has carrier. */
+ public Builder setHasCarrier(boolean hasCarrier) {
+ mHasCarrier = hasCarrier;
+ return this;
+ }
+
+ /** Configure whether the interface should be brought up. */
+ public Builder setBringUp(boolean bringUp) {
+ mBringUp = bringUp;
+ return this;
+ }
+
+ /** Disable DAD and RS delay. */
+ public Builder setDisableIpv6ProvisioningDelay(boolean disableProvDelay) {
+ mDisableIpv6ProvDelay = disableProvDelay;
+ return this;
+ }
+
+ /** Set the interface name. */
+ public Builder setInterfaceName(@Nullable String ifname) {
+ mIfname = ifname;
+ return this;
+ }
+
+ /** The addresses to configure on the interface. */
+ public Builder addLinkAddress(LinkAddress la) {
+ mLinkAddresses.add(la);
+ return this;
+ }
+
+ /** Build TestInterfaceRequest */
+ public TestInterfaceRequest build() {
+ return new TestInterfaceRequest(mIsTun, mHasCarrier, mBringUp,
+ mDisableIpv6ProvDelay, mIfname, mLinkAddresses.toArray(new LinkAddress[0]));
+ }
+ }
+ }
+
+ /**
+ * Create a TestNetworkInterface (tun or tap) for testing purposes.
+ *
+ * @param request The request describing the interface to create.
+ * @return A TestNetworkInterface representing the underlying tun/tap interface. Close the
+ * contained ParcelFileDescriptor to tear down the tun/tap interface.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
+ @NonNull
+ public TestNetworkInterface createTestInterface(@NonNull TestInterfaceRequest request) {
+ try {
+ // TODO: Make TestInterfaceRequest parcelable and pass it instead.
+ return mService.createInterface(request.isTun, request.hasCarrier, request.bringUp,
+ request.disableIpv6ProvDelay, request.linkAddresses, request.ifname);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
index 0536263..317854b 100644
--- a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
+++ b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
@@ -143,7 +143,7 @@
* @hide
*/
@ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
public static final long RESTRICT_LOCAL_NETWORK = 365139289L;
private ConnectivityCompatChanges() {
diff --git a/networksecurity/TEST_MAPPING b/networksecurity/TEST_MAPPING
index 448ee84..f75bf9a 100644
--- a/networksecurity/TEST_MAPPING
+++ b/networksecurity/TEST_MAPPING
@@ -1,4 +1,9 @@
{
+ "tethering-mainline-presubmit": [
+ {
+ "name": "NetworkSecurityUnitTests"
+ }
+ ],
"presubmit": [
{
"name": "CtsNetSecConfigCertificateTransparencyTestCases"
diff --git a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
index 1212e29..d91bd11 100644
--- a/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
+++ b/service-t/src/com/android/server/connectivity/mdns/MdnsSocketProvider.java
@@ -564,7 +564,6 @@
// Never try mDNS on cellular, or on interfaces with incompatible flags
if (CollectionUtils.contains(transports, TRANSPORT_CELLULAR)
|| iface.isLoopback()
- || iface.isPointToPoint()
|| iface.isVirtual()
|| !iface.isUp()) {
return false;
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 27f039f..5c5f4ca 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -183,11 +183,14 @@
import com.android.net.module.util.NetworkStatsUtils;
import com.android.net.module.util.PermissionUtils;
import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.SkDestroyListener;
import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.S32;
import com.android.net.module.util.Struct.U8;
import com.android.net.module.util.bpf.CookieTagMapKey;
import com.android.net.module.util.bpf.CookieTagMapValue;
+import com.android.net.module.util.netlink.InetDiagMessage;
+import com.android.net.module.util.netlink.StructInetDiagSockId;
import com.android.networkstack.apishim.BroadcastOptionsShimImpl;
import com.android.networkstack.apishim.ConstantsShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
@@ -215,6 +218,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Collect and persist detailed network statistics, and provide this data to
@@ -725,7 +729,14 @@
mTrafficStatsUidCache = null;
}
- mSkDestroyListener = mDeps.makeSkDestroyListener(mCookieTagMap, mHandler);
+ mSkDestroyListener = mDeps.makeSkDestroyListener((message) -> {
+ final StructInetDiagSockId sockId = message.inetDiagMsg.id;
+ try {
+ mCookieTagMap.deleteEntry(new CookieTagMapKey(sockId.cookie));
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Failed to delete CookieTagMap entry for " + sockId.cookie + ": " + e);
+ }
+ }, mHandler);
mHandler.post(mSkDestroyListener::start);
}
@@ -947,10 +958,10 @@
}
/** Create a new SkDestroyListener. */
- public SkDestroyListener makeSkDestroyListener(
- IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap, Handler handler) {
- return new SkDestroyListener(
- cookieTagMap, handler, new SharedLog(MAX_SOCKET_DESTROY_LISTENER_LOGS, TAG));
+ public SkDestroyListener makeSkDestroyListener(Consumer<InetDiagMessage> consumer,
+ Handler handler) {
+ return SkDestroyListener.makeSkDestroyListener(consumer, handler,
+ new SharedLog(MAX_SOCKET_DESTROY_LISTENER_LOGS, TAG));
}
/**
diff --git a/service-t/src/com/android/server/net/SkDestroyListener.java b/service-t/src/com/android/server/net/SkDestroyListener.java
deleted file mode 100644
index a6cc2b5..0000000
--- a/service-t/src/com/android/server/net/SkDestroyListener.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.net;
-
-import static android.system.OsConstants.NETLINK_INET_DIAG;
-
-import android.os.Handler;
-import android.system.ErrnoException;
-
-import com.android.net.module.util.IBpfMap;
-import com.android.net.module.util.SharedLog;
-import com.android.net.module.util.bpf.CookieTagMapKey;
-import com.android.net.module.util.bpf.CookieTagMapValue;
-import com.android.net.module.util.ip.NetlinkMonitor;
-import com.android.net.module.util.netlink.InetDiagMessage;
-import com.android.net.module.util.netlink.NetlinkMessage;
-import com.android.net.module.util.netlink.StructInetDiagSockId;
-
-import java.io.PrintWriter;
-
-/**
- * Monitor socket destroy and delete entry from cookie tag bpf map.
- */
-public class SkDestroyListener extends NetlinkMonitor {
- private static final int SKNLGRP_INET_TCP_DESTROY = 1;
- private static final int SKNLGRP_INET_UDP_DESTROY = 2;
- private static final int SKNLGRP_INET6_TCP_DESTROY = 3;
- private static final int SKNLGRP_INET6_UDP_DESTROY = 4;
-
- // TODO: if too many sockets are closed too quickly, this can overflow the socket buffer, and
- // some entries in mCookieTagMap will not be freed. In order to fix this it would be needed to
- // periodically dump all sockets and remove the tag entries for sockets that have been closed.
- // For now, set a large-enough buffer that hundreds of sockets can be closed without getting
- // ENOBUFS and leaking mCookieTagMap entries.
- private static final int SOCK_RCV_BUF_SIZE = 512 * 1024;
-
- private final IBpfMap<CookieTagMapKey, CookieTagMapValue> mCookieTagMap;
-
- SkDestroyListener(final IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap,
- final Handler handler, final SharedLog log) {
- super(handler, log, "SkDestroyListener", NETLINK_INET_DIAG,
- 1 << (SKNLGRP_INET_TCP_DESTROY - 1)
- | 1 << (SKNLGRP_INET_UDP_DESTROY - 1)
- | 1 << (SKNLGRP_INET6_TCP_DESTROY - 1)
- | 1 << (SKNLGRP_INET6_UDP_DESTROY - 1),
- SOCK_RCV_BUF_SIZE);
- mCookieTagMap = cookieTagMap;
- }
-
- @Override
- public void processNetlinkMessage(final NetlinkMessage nlMsg, final long whenMs) {
- if (!(nlMsg instanceof InetDiagMessage)) {
- mLog.e("Received non InetDiagMessage");
- return;
- }
- final StructInetDiagSockId sockId = ((InetDiagMessage) nlMsg).inetDiagMsg.id;
- try {
- mCookieTagMap.deleteEntry(new CookieTagMapKey(sockId.cookie));
- } catch (ErrnoException e) {
- mLog.e("Failed to delete CookieTagMap entry for " + sockId.cookie + ": " + e);
- }
- }
-
- /**
- * Dump the contents of SkDestroyListener log.
- */
- public void dump(PrintWriter pw) {
- mLog.reverseDump(pw);
- }
-}
diff --git a/service/libconnectivity/include/connectivity_native.h b/service/libconnectivity/include/connectivity_native.h
index f4676a9..f264b68 100644
--- a/service/libconnectivity/include/connectivity_native.h
+++ b/service/libconnectivity/include/connectivity_native.h
@@ -20,12 +20,6 @@
#include <sys/cdefs.h>
#include <netinet/in.h>
-// For branches that do not yet have __ANDROID_API_U__ defined, like module
-// release branches.
-#ifndef __ANDROID_API_U__
-#define __ANDROID_API_U__ 34
-#endif
-
__BEGIN_DECLS
/**
@@ -41,7 +35,7 @@
*
* @param port Int corresponding to port number.
*/
-int AConnectivityNative_blockPortForBind(in_port_t port) __INTRODUCED_IN(__ANDROID_API_U__);
+int AConnectivityNative_blockPortForBind(in_port_t port) __INTRODUCED_IN(34);
/**
* Unblocks a port that has previously been blocked.
@@ -54,7 +48,7 @@
*
* @param port Int corresponding to port number.
*/
-int AConnectivityNative_unblockPortForBind(in_port_t port) __INTRODUCED_IN(__ANDROID_API_U__);
+int AConnectivityNative_unblockPortForBind(in_port_t port) __INTRODUCED_IN(34);
/**
* Unblocks all ports that have previously been blocked.
@@ -64,7 +58,7 @@
* - EPERM if the UID of the client doesn't have network stack permission
* - Other errors as per https://man7.org/linux/man-pages/man2/bpf.2.html
*/
-int AConnectivityNative_unblockAllPortsForBind() __INTRODUCED_IN(__ANDROID_API_U__);
+int AConnectivityNative_unblockAllPortsForBind() __INTRODUCED_IN(34);
/**
* Gets the list of ports that have been blocked.
@@ -79,7 +73,7 @@
* blocked ports, which may be larger than the ports array that was filled.
*/
int AConnectivityNative_getPortsBlockedForBind(in_port_t* _Nonnull ports, size_t* _Nonnull count)
- __INTRODUCED_IN(__ANDROID_API_U__);
+ __INTRODUCED_IN(34);
__END_DECLS
diff --git a/service/native/libs/libclat/Android.bp b/service/native/libs/libclat/Android.bp
index 6c1c2c4..9554bd8 100644
--- a/service/native/libs/libclat/Android.bp
+++ b/service/native/libs/libclat/Android.bp
@@ -47,6 +47,7 @@
srcs: [
"clatutils_test.cpp",
],
+ stl: "libc++_static",
header_libs: [
"bpf_connectivity_headers",
],
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index ebb8de1..7c0c223 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -38,16 +38,16 @@
import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
-import static android.net.INetd.PERMISSION_INTERNET;
-import static android.net.INetd.PERMISSION_NONE;
-import static android.net.INetd.PERMISSION_UNINSTALLED;
-import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
import static android.system.OsConstants.EINVAL;
import static android.system.OsConstants.ENODEV;
import static android.system.OsConstants.ENOENT;
import static android.system.OsConstants.EOPNOTSUPP;
import static com.android.server.ConnectivityStatsLog.NETWORK_BPF_MAP_INFO;
+import static com.android.server.connectivity.NetworkPermissions.PERMISSION_NONE;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_INTERNET;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UNINSTALLED;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS;
import android.app.StatsManager;
import android.content.Context;
@@ -139,8 +139,8 @@
private static IBpfMap<U32, Bool> sLocalNetBlockedUidMap = null;
private static final List<Pair<Integer, String>> PERMISSION_LIST = Arrays.asList(
- Pair.create(PERMISSION_INTERNET, "PERMISSION_INTERNET"),
- Pair.create(PERMISSION_UPDATE_DEVICE_STATS, "PERMISSION_UPDATE_DEVICE_STATS")
+ Pair.create(TRAFFIC_PERMISSION_INTERNET, "PERMISSION_INTERNET"),
+ Pair.create(TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS, "PERMISSION_UPDATE_DEVICE_STATS")
);
/**
@@ -870,7 +870,8 @@
}
// Remove the entry if package is uninstalled or uid has only INTERNET permission.
- if (permissions == PERMISSION_UNINSTALLED || permissions == PERMISSION_INTERNET) {
+ if (permissions == TRAFFIC_PERMISSION_UNINSTALLED
+ || permissions == TRAFFIC_PERMISSION_INTERNET) {
for (final int uid : uids) {
try {
sUidPermissionMap.deleteEntry(new S32(uid));
@@ -1043,10 +1044,10 @@
// Key of uid permission map is appId
// TODO: Rename map name
final U8 permissions = sUidPermissionMap.getValue(new S32(appId));
- return permissions != null ? permissions.val : PERMISSION_INTERNET;
+ return permissions != null ? permissions.val : TRAFFIC_PERMISSION_INTERNET;
} catch (ErrnoException e) {
Log.wtf(TAG, "Failed to get permission for uid: " + uid);
- return PERMISSION_INTERNET;
+ return TRAFFIC_PERMISSION_INTERNET;
}
}
@@ -1195,7 +1196,7 @@
if (permissionMask == PERMISSION_NONE) {
return "PERMISSION_NONE";
}
- if (permissionMask == PERMISSION_UNINSTALLED) {
+ if (permissionMask == TRAFFIC_PERMISSION_UNINSTALLED) {
// PERMISSION_UNINSTALLED should never appear in the map
return "PERMISSION_UNINSTALLED error!";
}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index abe4056..6dbb1d8 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -564,6 +564,7 @@
// The Context is created for UserHandle.ALL.
private final Context mUserAllContext;
private final Dependencies mDeps;
+ private final PermissionMonitor.Dependencies mPermissionMonitorDeps;
private final ConnectivityFlags mFlags;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -1808,12 +1809,13 @@
public ConnectivityService(Context context) {
this(context, getDnsResolver(context), new IpConnectivityLog(),
INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE)),
- new Dependencies());
+ new Dependencies(), new PermissionMonitor.Dependencies());
}
@VisibleForTesting
protected ConnectivityService(Context context, IDnsResolver dnsresolver,
- IpConnectivityLog logger, INetd netd, Dependencies deps) {
+ IpConnectivityLog logger, INetd netd, Dependencies deps,
+ PermissionMonitor.Dependencies mPermDeps) {
if (DBG) log("ConnectivityService starting up");
mDeps = Objects.requireNonNull(deps, "missing Dependencies");
@@ -1886,8 +1888,10 @@
mNetd = netd;
mBpfNetMaps = mDeps.getBpfNetMaps(mContext, netd);
mHandlerThread = mDeps.makeHandlerThread("ConnectivityServiceThread");
+ mPermissionMonitorDeps = mPermDeps;
mPermissionMonitor =
- new PermissionMonitor(mContext, mNetd, mBpfNetMaps, mHandlerThread);
+ new PermissionMonitor(mContext, mNetd, mBpfNetMaps, mPermissionMonitorDeps,
+ mHandlerThread);
mHandlerThread.start();
mHandler = new InternalHandler(mHandlerThread.getLooper());
mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
@@ -9804,42 +9808,61 @@
removeLocalAddressesFromBpfMap(iface, MULTICAST_AND_BROADCAST_PREFIXES, oldLp);
}
- final CompareResult<LinkAddress> linkAddressDiff = new CompareResult<>(
- oldLp != null ? oldLp.getLinkAddresses() : null,
- newLp != null ? newLp.getLinkAddresses() : null);
+ // The both list contain current link properties + stacked links for new and old LP.
+ List<LinkProperties> newLinkProperties = new ArrayList<>();
+ List<LinkProperties> oldLinkProperties = new ArrayList<>();
- List<IpPrefix> unicastLocalPrefixesToBeAdded = new ArrayList<>();
- List<IpPrefix> unicastLocalPrefixesToBeRemoved = new ArrayList<>();
-
- // Finding the list of local network prefixes that needs to be added
if (newLp != null) {
- for (LinkAddress linkAddress : newLp.getLinkAddresses()) {
+ newLinkProperties.add(newLp);
+ newLinkProperties.addAll(newLp.getStackedLinks());
+ }
+ if (oldLp != null) {
+ oldLinkProperties.add(oldLp);
+ oldLinkProperties.addAll(oldLp.getStackedLinks());
+ }
+
+ // map contains interface name to list of local network prefixes added because of change
+ // in link properties
+ Map<String, List<IpPrefix>> prefixesAddedForInterface = new ArrayMap<>();
+
+ final CompareResult<LinkProperties> linkPropertiesDiff = new CompareResult<>(
+ oldLinkProperties, newLinkProperties);
+
+ for (LinkProperties linkProperty : linkPropertiesDiff.added) {
+ List<IpPrefix> unicastLocalPrefixesToBeAdded = new ArrayList<>();
+ for (LinkAddress linkAddress : linkProperty.getLinkAddresses()) {
unicastLocalPrefixesToBeAdded.addAll(
getLocalNetworkPrefixesForAddress(linkAddress));
}
- }
+ addLocalAddressesToBpfMap(linkProperty.getInterfaceName(),
+ unicastLocalPrefixesToBeAdded, linkProperty);
- for (LinkAddress linkAddress : linkAddressDiff.removed) {
- unicastLocalPrefixesToBeRemoved.addAll(getLocalNetworkPrefixesForAddress(linkAddress));
- }
-
- // If newLp is not null, adding local network prefixes using interface name of newLp
- if (newLp != null) {
- addLocalAddressesToBpfMap(newLp.getInterfaceName(),
- new ArrayList<>(unicastLocalPrefixesToBeAdded), newLp);
- }
- if (oldLp != null) {
- // excluding removal of ip prefixes that needs to be added for newLp, but also
- // removed for oldLp.
- if (newLp != null && Objects.equals(oldLp.getInterfaceName(),
- newLp.getInterfaceName())) {
- unicastLocalPrefixesToBeRemoved.removeAll(unicastLocalPrefixesToBeAdded);
+ // adding iterface name -> ip prefixes that we added to map
+ if (!prefixesAddedForInterface.containsKey(linkProperty.getInterfaceName())) {
+ prefixesAddedForInterface.put(linkProperty.getInterfaceName(), new ArrayList<>());
}
- // removing ip local network prefixes because of change in link addresses.
- removeLocalAddressesFromBpfMap(oldLp.getInterfaceName(),
- new ArrayList<>(unicastLocalPrefixesToBeRemoved), oldLp);
+ prefixesAddedForInterface.get(linkProperty.getInterfaceName())
+ .addAll(unicastLocalPrefixesToBeAdded);
}
+ for (LinkProperties linkProperty : linkPropertiesDiff.removed) {
+ List<IpPrefix> unicastLocalPrefixesToBeRemoved = new ArrayList<>();
+ List<IpPrefix> unicastLocalPrefixesAdded = prefixesAddedForInterface.getOrDefault(
+ linkProperty.getInterfaceName(), new ArrayList<>());
+
+ for (LinkAddress linkAddress : linkProperty.getLinkAddresses()) {
+ unicastLocalPrefixesToBeRemoved.addAll(
+ getLocalNetworkPrefixesForAddress(linkAddress));
+ }
+
+ // This is to ensure if 10.0.10.0/24 was added and 10.0.11.0/24 was removed both will
+ // still populate the same prefix of 10.0.0.0/8, which mean we should not remove the
+ // prefix because of removal of 10.0.11.0/24
+ unicastLocalPrefixesToBeRemoved.removeAll(unicastLocalPrefixesAdded);
+
+ removeLocalAddressesFromBpfMap(linkProperty.getInterfaceName(),
+ new ArrayList<>(unicastLocalPrefixesToBeRemoved), linkProperty);
+ }
}
/**
diff --git a/service/src/com/android/server/L2capNetworkProvider.java b/service/src/com/android/server/L2capNetworkProvider.java
index 441f084..814a068 100644
--- a/service/src/com/android/server/L2capNetworkProvider.java
+++ b/service/src/com/android/server/L2capNetworkProvider.java
@@ -16,7 +16,7 @@
package com.android.server;
-import static android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN;
+import static android.content.pm.PackageManager.FEATURE_BLUETOOTH_LE;
import static android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_ANY;
import static android.net.L2capNetworkSpecifier.PSM_ANY;
import static android.net.L2capNetworkSpecifier.ROLE_CLIENT;
@@ -30,7 +30,6 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.RES_ID_MATCH_ALL_RESERVATIONS;
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
-import static android.content.pm.PackageManager.FEATURE_BLUETOOTH_LE;
import static android.system.OsConstants.F_GETFL;
import static android.system.OsConstants.F_SETFL;
import static android.system.OsConstants.O_NONBLOCK;
@@ -50,10 +49,8 @@
import android.net.NetworkProvider.NetworkOfferCallback;
import android.net.NetworkRequest;
import android.net.NetworkScore;
-import android.net.NetworkSpecifier;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.system.Os;
import android.util.ArrayMap;
@@ -61,8 +58,10 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.HandlerUtils;
import com.android.net.module.util.ServiceConnectivityJni;
import com.android.server.net.L2capNetwork;
+import com.android.server.net.L2capPacketForwarder;
import java.io.IOException;
import java.util.ArrayList;
@@ -78,7 +77,6 @@
// BLUETOOTH_CONNECT permission.
NetworkCapabilities.Builder.withoutDefaultCapabilities()
.addTransportType(TRANSPORT_BLUETOOTH)
- // TODO: remove NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED.
.addCapability(NET_CAPABILITY_NOT_BANDWIDTH_CONSTRAINED)
.addCapability(NET_CAPABILITY_NOT_CONGESTED)
.addCapability(NET_CAPABILITY_NOT_METERED)
@@ -129,12 +127,7 @@
}
// TODO: consider moving this into L2capNetworkSpecifier as #isValidServerReservation().
- private boolean isValidL2capSpecifier(@Nullable NetworkSpecifier spec) {
- if (spec == null) return false;
- // If spec is not null, L2capNetworkSpecifier#canBeSatisfiedBy() guarantees the
- // specifier is of type L2capNetworkSpecifier.
- final L2capNetworkSpecifier l2capSpec = (L2capNetworkSpecifier) spec;
-
+ private boolean isValidL2capServerSpecifier(L2capNetworkSpecifier l2capSpec) {
// The ROLE_SERVER offer can be satisfied by a ROLE_ANY request.
if (l2capSpec.getRole() != ROLE_SERVER) return false;
@@ -152,9 +145,13 @@
@Override
public void onNetworkNeeded(NetworkRequest request) {
- Log.d(TAG, "New reservation request: " + request);
- if (!isValidL2capSpecifier(request.getNetworkSpecifier())) {
- Log.w(TAG, "Ignoring invalid reservation request: " + request);
+ // The NetworkSpecifier is guaranteed to be either null or an L2capNetworkSpecifier, so
+ // this cast is safe.
+ final L2capNetworkSpecifier specifier =
+ (L2capNetworkSpecifier) request.getNetworkSpecifier();
+ if (specifier == null) return;
+ if (!isValidL2capServerSpecifier(specifier)) {
+ Log.i(TAG, "Ignoring invalid reservation request: " + request);
return;
}
@@ -230,6 +227,7 @@
}
private void destroyAndUnregisterReservedOffer(ReservedServerOffer reservedOffer) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
// Ensure the offer still exists if this was posted on the handler.
if (!mReservedServerOffers.contains(reservedOffer)) return;
mReservedServerOffers.remove(reservedOffer);
@@ -239,36 +237,17 @@
}
@Nullable
- private static ParcelFileDescriptor createTunInterface(String ifname) {
- final ParcelFileDescriptor fd;
- try {
- fd = ParcelFileDescriptor.adoptFd(
- ServiceConnectivityJni.createTunTap(
- true /*isTun*/, true /*hasCarrier*/, true /*setIffMulticast*/, ifname));
- ServiceConnectivityJni.bringUpInterface(ifname);
- // TODO: consider adding a parameter to createTunTap() (or the Builder that should
- // be added) to configure i/o blocking.
- final int flags = Os.fcntlInt(fd.getFileDescriptor(), F_GETFL, 0);
- Os.fcntlInt(fd.getFileDescriptor(), F_SETFL, flags & ~O_NONBLOCK);
- } catch (Exception e) {
- // Note: createTunTap currently throws an IllegalStateException on failure.
- // TODO: native functions should throw ErrnoException.
- Log.e(TAG, "Failed to create tun interface", e);
- return null;
- }
- return fd;
- }
-
- @Nullable
private L2capNetwork createL2capNetwork(BluetoothSocket socket, NetworkCapabilities caps,
L2capNetwork.ICallback cb) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
final String ifname = TUN_IFNAME + String.valueOf(sTunIndex++);
- final ParcelFileDescriptor tunFd = createTunInterface(ifname);
+ final ParcelFileDescriptor tunFd = mDeps.createTunInterface(ifname);
if (tunFd == null) {
return null;
}
- return new L2capNetwork(mHandler, mContext, mProvider, ifname, socket, tunFd, caps, cb);
+ return L2capNetwork.create(
+ mHandler, mContext, mProvider, ifname, socket, tunFd, caps, mDeps, cb);
}
private static void closeBluetoothSocket(BluetoothSocket socket) {
@@ -294,22 +273,26 @@
private volatile boolean mIsRunning = true;
public AcceptThread(BluetoothServerSocket serverSocket) {
+ super("L2capNetworkProvider-AcceptThread");
mServerSocket = serverSocket;
}
private void postDestroyAndUnregisterReservedOffer() {
+ // Called on AcceptThread
mHandler.post(() -> {
destroyAndUnregisterReservedOffer(ReservedServerOffer.this);
});
}
private void postCreateServerNetwork(BluetoothSocket connectedSocket) {
+ // Called on AcceptThread
mHandler.post(() -> {
final boolean success = createServerNetwork(connectedSocket);
if (!success) closeBluetoothSocket(connectedSocket);
});
}
+ @Override
public void run() {
while (mIsRunning) {
final BluetoothSocket connectedSocket;
@@ -329,6 +312,7 @@
}
public void tearDown() {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
mIsRunning = false;
try {
// BluetoothServerSocket.close() is thread-safe.
@@ -345,6 +329,7 @@
}
private boolean createServerNetwork(BluetoothSocket socket) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
// It is possible the offer went away.
if (!mReservedServerOffers.contains(this)) return false;
@@ -355,17 +340,19 @@
final L2capNetwork network = createL2capNetwork(socket, mReservedCapabilities,
new L2capNetwork.ICallback() {
- @Override
- public void onError(L2capNetwork network) {
- destroyAndUnregisterReservedOffer(ReservedServerOffer.this);
- }
- @Override
- public void onNetworkUnwanted(L2capNetwork network) {
- // Leave reservation in place.
- final boolean networkExists = mL2capNetworks.remove(network);
- if (!networkExists) return; // already torn down.
- network.tearDown();
- }
+ @Override
+ public void onError(L2capNetwork network) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
+ destroyAndUnregisterReservedOffer(ReservedServerOffer.this);
+ }
+ @Override
+ public void onNetworkUnwanted(L2capNetwork network) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
+ // Leave reservation in place.
+ final boolean networkExists = mL2capNetworks.remove(network);
+ if (!networkExists) return; // already torn down.
+ network.tearDown();
+ }
});
if (network == null) {
@@ -400,6 +387,7 @@
/** Called when the reservation goes away and the reserved offer must be torn down. */
public void tearDown() {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
mAcceptThread.tearDown();
for (L2capNetwork network : mL2capNetworks) {
network.tearDown();
@@ -449,10 +437,12 @@
private volatile boolean mIsAborted = false;
public ConnectThread(L2capNetworkSpecifier specifier, BluetoothSocket socket) {
+ super("L2capNetworkProvider-ConnectThread");
mSpecifier = specifier;
mSocket = socket;
}
+ @Override
public void run() {
try {
mSocket.connect();
@@ -472,6 +462,7 @@
}
public void abort() {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
mIsAborted = true;
// Closing the BluetoothSocket is the only way to unblock connect() because it calls
// shutdown on the underlying (connected) SOCK_SEQPACKET.
@@ -487,6 +478,7 @@
private boolean createClientNetwork(L2capNetworkSpecifier specifier,
BluetoothSocket socket) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
// Check whether request still exists
final ClientRequestInfo cri = mClientNetworkRequests.get(specifier);
if (cri == null) return false;
@@ -497,18 +489,20 @@
final L2capNetwork network = createL2capNetwork(socket, caps,
new L2capNetwork.ICallback() {
- // TODO: do not send onUnavailable() after the network has become available. The
- // right thing to do here is to tearDown the network (if it still exists, because
- // note that the request might have already been removed in the meantime, so
- // `network` cannot be used directly.
- @Override
- public void onError(L2capNetwork network) {
- declareAllNetworkRequestsUnfulfillable(specifier);
- }
- @Override
- public void onNetworkUnwanted(L2capNetwork network) {
- declareAllNetworkRequestsUnfulfillable(specifier);
- }
+ // TODO: do not send onUnavailable() after the network has become available. The
+ // right thing to do here is to tearDown the network (if it still exists,
+ // because note that the request might have already been removed in the
+ // meantime, so `network` cannot be used directly.
+ @Override
+ public void onError(L2capNetwork network) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
+ declareAllNetworkRequestsUnfulfillable(specifier);
+ }
+ @Override
+ public void onNetworkUnwanted(L2capNetwork network) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
+ declareAllNetworkRequestsUnfulfillable(specifier);
+ }
});
if (network == null) return false;
@@ -516,12 +510,7 @@
return true;
}
- private boolean isValidL2capSpecifier(@Nullable NetworkSpecifier spec) {
- if (spec == null) return false;
-
- // If not null, guaranteed to be L2capNetworkSepcifier.
- final L2capNetworkSpecifier l2capSpec = (L2capNetworkSpecifier) spec;
-
+ private boolean isValidL2capClientSpecifier(L2capNetworkSpecifier l2capSpec) {
// The ROLE_CLIENT offer can be satisfied by a ROLE_ANY request.
if (l2capSpec.getRole() != ROLE_CLIENT) return false;
@@ -541,14 +530,16 @@
@Override
public void onNetworkNeeded(NetworkRequest request) {
- Log.d(TAG, "New client network request: " + request);
- if (!isValidL2capSpecifier(request.getNetworkSpecifier())) {
- Log.w(TAG, "Ignoring invalid client request: " + request);
+ // The NetworkSpecifier is guaranteed to be either null or an L2capNetworkSpecifier, so
+ // this cast is safe.
+ final L2capNetworkSpecifier requestSpecifier =
+ (L2capNetworkSpecifier) request.getNetworkSpecifier();
+ if (requestSpecifier == null) return;
+ if (!isValidL2capClientSpecifier(requestSpecifier)) {
+ Log.i(TAG, "Ignoring invalid client request: " + request);
return;
}
- final L2capNetworkSpecifier requestSpecifier =
- (L2capNetworkSpecifier) request.getNetworkSpecifier();
// Check whether this exact request is already being tracked.
final ClientRequestInfo cri = mClientNetworkRequests.get(requestSpecifier);
if (cri != null) {
@@ -623,6 +614,7 @@
* Only call this when all associated NetworkRequests have been released.
*/
private void releaseClientNetworkRequest(ClientRequestInfo cri) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
mClientNetworkRequests.remove(cri.specifier);
if (cri.connectThread.isAlive()) {
// Note that if ConnectThread succeeds between calling #isAlive() and #abort(), the
@@ -638,6 +630,7 @@
}
private void declareAllNetworkRequestsUnfulfillable(L2capNetworkSpecifier specifier) {
+ HandlerUtils.ensureRunningOnHandlerThread(mHandler);
final ClientRequestInfo cri = mClientNetworkRequests.get(specifier);
if (cri == null) return;
@@ -656,13 +649,43 @@
thread.start();
return thread;
}
+
+ /** Create a tun interface configured for blocking i/o */
+ @Nullable
+ public ParcelFileDescriptor createTunInterface(String ifname) {
+ final ParcelFileDescriptor fd;
+ try {
+ fd = ParcelFileDescriptor.adoptFd(ServiceConnectivityJni.createTunTap(
+ true /*isTun*/,
+ true /*hasCarrier*/,
+ true /*setIffMulticast*/,
+ ifname));
+ ServiceConnectivityJni.bringUpInterface(ifname);
+ // TODO: consider adding a parameter to createTunTap() (or the Builder that should
+ // be added) to configure i/o blocking.
+ final int flags = Os.fcntlInt(fd.getFileDescriptor(), F_GETFL, 0);
+ Os.fcntlInt(fd.getFileDescriptor(), F_SETFL, flags & ~O_NONBLOCK);
+ } catch (Exception e) {
+ // Note: createTunTap currently throws an IllegalStateException on failure.
+ // TODO: native functions should throw ErrnoException.
+ Log.e(TAG, "Failed to create tun interface", e);
+ return null;
+ }
+ return fd;
+ }
+
+ /** Create an L2capPacketForwarder and start forwarding */
+ public L2capPacketForwarder createL2capPacketForwarder(Handler handler,
+ ParcelFileDescriptor tunFd, BluetoothSocket socket, boolean compressHeaders,
+ L2capPacketForwarder.ICallback cb) {
+ return new L2capPacketForwarder(handler, tunFd, socket, compressHeaders, cb);
+ }
}
public L2capNetworkProvider(Context context) {
this(new Dependencies(), context);
}
- @VisibleForTesting
public L2capNetworkProvider(Dependencies deps, Context context) {
mDeps = deps;
mContext = context;
diff --git a/service/src/com/android/server/connectivity/NetworkPermissions.java b/service/src/com/android/server/connectivity/NetworkPermissions.java
new file mode 100644
index 0000000..9543d8f
--- /dev/null
+++ b/service/src/com/android/server/connectivity/NetworkPermissions.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2025 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.net.INetd;
+
+/**
+ * A wrapper class for managing network and traffic permissions.
+ *
+ * This class encapsulates permissions represented as a bitmask, as defined in INetd.aidl
+ * and used within PermissionMonitor.java. It distinguishes between two types of permissions:
+ *
+ * 1. Network Permissions: These permissions, declared in INetd.aidl, are used
+ * by the Android platform's network daemon (system/netd) to control network
+ * management
+ *
+ * 2. Traffic Permissions: These permissions are used internally by PermissionMonitor.java and
+ * BpfNetMaps.java to manage fine-grained network traffic filtering and control.
+ *
+ * This wrapper ensures that no new permission definitions, here or in aidl, conflict with any
+ * existing permissions. This prevents unintended interactions or overrides.
+ *
+ * @hide
+ */
+public class NetworkPermissions {
+
+ /*
+ * Below are network permissions declared in INetd.aidl and used by the platform. Using these is
+ * equivalent to using the values in android.net.INetd.
+ */
+ public static final int PERMISSION_NONE = INetd.PERMISSION_NONE; /* 0 */
+ public static final int PERMISSION_NETWORK = INetd.PERMISSION_NETWORK; /* 1 */
+ public static final int PERMISSION_SYSTEM = INetd.PERMISSION_SYSTEM; /* 2 */
+
+ /*
+ * Below are traffic permissions used by PermissionMonitor and BpfNetMaps.
+ */
+
+ /**
+ * PERMISSION_UNINSTALLED is used when an app is uninstalled from the device. All internet
+ * related permissions need to be cleaned.
+ */
+ public static final int TRAFFIC_PERMISSION_UNINSTALLED = -1;
+
+ /**
+ * PERMISSION_INTERNET indicates that the app can create AF_INET and AF_INET6 sockets.
+ */
+ public static final int TRAFFIC_PERMISSION_INTERNET = 4;
+
+ /**
+ * PERMISSION_UPDATE_DEVICE_STATS is used for system UIDs and privileged apps
+ * that have the UPDATE_DEVICE_STATS permission.
+ */
+ public static final int TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS = 8;
+
+ /**
+ * TRAFFIC_PERMISSION_SDKSANDBOX_LOCALHOST indicates if an SdkSandbox UID will be allowed
+ * to connect to localhost. For non SdkSandbox UIDs this bit is a no-op.
+ */
+ public static final int TRAFFIC_PERMISSION_SDKSANDBOX_LOCALHOST = 16;
+}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 737e27a..5de5f61 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -19,27 +19,29 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.INTERNET;
+import static android.Manifest.permission.NEARBY_WIFI_DEVICES;
import static android.Manifest.permission.NETWORK_STACK;
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.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;
-import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.connectivity.ConnectivityCompatChanges.RESTRICT_LOCAL_NETWORK;
import static android.os.Process.INVALID_UID;
import static android.os.Process.SYSTEM_UID;
+import static com.android.server.connectivity.NetworkPermissions.PERMISSION_NETWORK;
+import static com.android.server.connectivity.NetworkPermissions.PERMISSION_NONE;
+import static com.android.server.connectivity.NetworkPermissions.PERMISSION_SYSTEM;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_INTERNET;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UNINSTALLED;
+import static com.android.server.connectivity.NetworkPermissions.TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS;
import static com.android.net.module.util.CollectionUtils.toIntArray;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.compat.CompatChanges;
+import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -62,6 +64,7 @@
import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.PermissionManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -72,7 +75,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.modules.utils.build.SdkLevel;
-import com.android.net.flags.Flags;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.apishim.ProcessShimImpl;
@@ -99,6 +101,8 @@
private final PackageManager mPackageManager;
private final UserManager mUserManager;
private final SystemConfigManager mSystemConfigManager;
+ private final PermissionManager mPermissionManager;
+ private final PermissionChangeListener mPermissionChangeListener;
private final INetd mNetd;
private final Dependencies mDeps;
private final Context mContext;
@@ -230,6 +234,12 @@
context.getContentResolver().registerContentObserver(
uri, notifyForDescendants, observer);
}
+
+ public boolean shouldEnforceLocalNetRestrictions(int uid) {
+ // TODO(b/394567896): Update compat change checks for enforcement
+ return BpfNetMaps.isAtLeast25Q2() &&
+ CompatChanges.isChangeEnabled(RESTRICT_LOCAL_NETWORK, uid);
+ }
}
private static class MultiSet<T> {
@@ -270,13 +280,15 @@
}
@VisibleForTesting
- PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd,
+ public PermissionMonitor(@NonNull final Context context, @NonNull final INetd netd,
@NonNull final BpfNetMaps bpfNetMaps,
@NonNull final Dependencies deps,
@NonNull final HandlerThread thread) {
mPackageManager = context.getPackageManager();
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mSystemConfigManager = context.getSystemService(SystemConfigManager.class);
+ mPermissionManager = context.getSystemService(PermissionManager.class);
+ mPermissionChangeListener = new PermissionChangeListener();
mNetd = netd;
mDeps = deps;
mContext = context;
@@ -287,7 +299,29 @@
// This listener should finish registration by the time the system has completed
// boot setup such that any changes to runtime permissions for local network
// restrictions can only occur after this registration has completed.
- mPackageManager.addOnPermissionsChangeListener(new PermissionChangeListener());
+ mPackageManager.addOnPermissionsChangeListener(mPermissionChangeListener);
+ }
+ }
+
+ @VisibleForTesting
+ void setLocalNetworkPermissions(final int uid, @Nullable final String packageName) {
+ if (!mDeps.shouldEnforceLocalNetRestrictions(uid)) return;
+
+ final AttributionSource attributionSource =
+ new AttributionSource.Builder(uid).setPackageName(packageName).build();
+ final int permissionState = mPermissionManager.checkPermissionForPreflight(
+ NEARBY_WIFI_DEVICES, attributionSource);
+ if (permissionState == PermissionManager.PERMISSION_GRANTED) {
+ mBpfNetMaps.removeUidFromLocalNetBlockMap(attributionSource.getUid());
+ } else {
+ mBpfNetMaps.addUidToLocalNetBlockMap(attributionSource.getUid());
+ }
+ if (hasSdkSandbox(uid)){
+ // SDKs in the SDK RT cannot hold runtime permissions
+ final int sdkSandboxUid = sProcessShim.toSdkSandboxUid(uid);
+ if (!mBpfNetMaps.isUidBlockedFromUsingLocalNetwork(sdkSandboxUid)) {
+ mBpfNetMaps.addUidToLocalNetBlockMap(sdkSandboxUid);
+ }
}
}
@@ -351,6 +385,7 @@
uidsPerm.put(sdkSandboxUid, permission);
}
}
+ setLocalNetworkPermissions(uid, app.packageName);
}
return uidsPerm;
}
@@ -405,7 +440,7 @@
final SparseIntArray appIdsPerm = new SparseIntArray();
for (final int uid : mSystemConfigManager.getSystemPermissionUids(INTERNET)) {
final int appId = UserHandle.getAppId(uid);
- final int permission = appIdsPerm.get(appId) | PERMISSION_INTERNET;
+ final int permission = appIdsPerm.get(appId) | TRAFFIC_PERMISSION_INTERNET;
appIdsPerm.put(appId, permission);
if (hasSdkSandbox(appId)) {
appIdsPerm.put(sProcessShim.toSdkSandboxUid(appId), permission);
@@ -413,7 +448,7 @@
}
for (final int uid : mSystemConfigManager.getSystemPermissionUids(UPDATE_DEVICE_STATS)) {
final int appId = UserHandle.getAppId(uid);
- final int permission = appIdsPerm.get(appId) | PERMISSION_UPDATE_DEVICE_STATS;
+ final int permission = appIdsPerm.get(appId) | TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS;
appIdsPerm.put(appId, permission);
if (hasSdkSandbox(appId)) {
appIdsPerm.put(sProcessShim.toSdkSandboxUid(appId), permission);
@@ -598,7 +633,7 @@
final List<PackageInfo> apps = getInstalledPackagesAsUser(user);
- // Save all apps
+ // Save all apps in mAllApps
updateAllApps(apps);
// Uids network permissions
@@ -635,6 +670,11 @@
final int uid = allUids.keyAt(i);
if (user.equals(UserHandle.getUserHandleForUid(uid))) {
mUidToNetworkPerm.delete(uid);
+ if (mDeps.shouldEnforceLocalNetRestrictions(uid)) {
+ mBpfNetMaps.removeUidFromLocalNetBlockMap(uid);
+ if (hasSdkSandbox(uid)) mBpfNetMaps.removeUidFromLocalNetBlockMap(
+ sProcessShim.toSdkSandboxUid(uid));
+ }
removedUids.put(uid, allUids.valueAt(i));
}
}
@@ -656,7 +696,7 @@
final int appId = removedUserAppIds.keyAt(i);
// Need to clear permission if the removed appId is not found in the array.
if (appIds.indexOfKey(appId) < 0) {
- appIds.put(appId, PERMISSION_UNINSTALLED);
+ appIds.put(appId, TRAFFIC_PERMISSION_UNINSTALLED);
}
}
sendAppIdsTrafficPermission(appIds);
@@ -708,7 +748,7 @@
}
} else {
// The last package of this uid is removed from device. Clean the package up.
- permission = PERMISSION_UNINSTALLED;
+ permission = TRAFFIC_PERMISSION_UNINSTALLED;
}
return permission;
}
@@ -751,13 +791,13 @@
return "NETWORK";
case PERMISSION_SYSTEM:
return "SYSTEM";
- case PERMISSION_INTERNET:
+ case TRAFFIC_PERMISSION_INTERNET:
return "INTERNET";
- case PERMISSION_UPDATE_DEVICE_STATS:
+ case TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS:
return "UPDATE_DEVICE_STATS";
- case (PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS):
+ case (TRAFFIC_PERMISSION_INTERNET | TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS):
return "ALL";
- case PERMISSION_UNINSTALLED:
+ case TRAFFIC_PERMISSION_UNINSTALLED:
return "UNINSTALLED";
default:
return "UNKNOWN";
@@ -776,7 +816,7 @@
// (PERMISSION_UNINSTALLED), remove the appId from the array. Otherwise, update the latest
// permission to the appId.
final int appId = UserHandle.getAppId(uid);
- if (uidTrafficPerm == PERMISSION_UNINSTALLED) {
+ if (uidTrafficPerm == TRAFFIC_PERMISSION_UNINSTALLED) {
userTrafficPerms.delete(appId);
} else {
userTrafficPerms.put(appId, uidTrafficPerm);
@@ -794,7 +834,7 @@
installed = true;
}
}
- return installed ? permission : PERMISSION_UNINSTALLED;
+ return installed ? permission : TRAFFIC_PERMISSION_UNINSTALLED;
}
/**
@@ -829,6 +869,7 @@
}
sendUidsNetworkPermission(apps, true /* add */);
}
+ setLocalNetworkPermissions(uid, packageName);
// If the newly-installed package falls within some VPN's uid range, update Netd with it.
// This needs to happen after the mUidToNetworkPerm update above, since
@@ -873,6 +914,11 @@
synchronized void onPackageRemoved(@NonNull final String packageName, final int uid) {
// Update uid permission.
updateAppIdTrafficPermission(uid);
+ if (BpfNetMaps.isAtLeast25Q2()) {
+ mBpfNetMaps.removeUidFromLocalNetBlockMap(uid);
+ if (hasSdkSandbox(uid)) mBpfNetMaps.removeUidFromLocalNetBlockMap(
+ sProcessShim.toSdkSandboxUid(uid));
+ }
// Get the appId permission from all users then send the latest permission to netd.
final int appId = UserHandle.getAppId(uid);
final int appIdTrafficPerm = getAppIdTrafficPermission(appId);
@@ -931,11 +977,11 @@
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i].equals(INTERNET)
&& ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
- permissions |= PERMISSION_INTERNET;
+ permissions |= TRAFFIC_PERMISSION_INTERNET;
}
if (requestedPermissions[i].equals(UPDATE_DEVICE_STATS)
&& ((requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED) != 0)) {
- permissions |= PERMISSION_UPDATE_DEVICE_STATS;
+ permissions |= TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS;
}
}
return permissions;
@@ -1174,19 +1220,19 @@
for (int i = 0; i < netdPermissionsAppIds.size(); i++) {
int permissions = netdPermissionsAppIds.valueAt(i);
switch(permissions) {
- case (PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS):
+ case (TRAFFIC_PERMISSION_INTERNET | TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS):
allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
- case PERMISSION_INTERNET:
+ case TRAFFIC_PERMISSION_INTERNET:
internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
- case PERMISSION_UPDATE_DEVICE_STATS:
+ case TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS:
updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
case PERMISSION_NONE:
noPermissionAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
- case PERMISSION_UNINSTALLED:
+ case TRAFFIC_PERMISSION_UNINSTALLED:
uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i));
break;
default:
@@ -1198,15 +1244,15 @@
// TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids()
if (allPermissionAppIds.size() != 0) {
mBpfNetMaps.setNetPermForUids(
- PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS,
+ TRAFFIC_PERMISSION_INTERNET | TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS,
toIntArray(allPermissionAppIds));
}
if (internetPermissionAppIds.size() != 0) {
- mBpfNetMaps.setNetPermForUids(PERMISSION_INTERNET,
+ mBpfNetMaps.setNetPermForUids(TRAFFIC_PERMISSION_INTERNET,
toIntArray(internetPermissionAppIds));
}
if (updateStatsPermissionAppIds.size() != 0) {
- mBpfNetMaps.setNetPermForUids(PERMISSION_UPDATE_DEVICE_STATS,
+ mBpfNetMaps.setNetPermForUids(TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS,
toIntArray(updateStatsPermissionAppIds));
}
if (noPermissionAppIds.size() != 0) {
@@ -1214,7 +1260,7 @@
toIntArray(noPermissionAppIds));
}
if (uninstalledAppIds.size() != 0) {
- mBpfNetMaps.setNetPermForUids(PERMISSION_UNINSTALLED,
+ mBpfNetMaps.setNetPermForUids(TRAFFIC_PERMISSION_UNINSTALLED,
toIntArray(uninstalledAppIds));
}
} catch (RemoteException | ServiceSpecificException e) {
@@ -1325,16 +1371,7 @@
private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener {
@Override
public void onPermissionsChanged(int uid) {
- // RESTRICT_LOCAL_NETWORK is a compat change that is enabled when developers manually
- // opt-in to this change, or when the app's targetSdkVersion is greater than 36.
- // The RESTRICT_LOCAL_NETWORK compat change is used here instead of the
- // Flags.restrictLocalNetwork() is used to offer the feature to devices, but it will
- // only be enforced when develoeprs choose to enable it.
- // TODO(b/394567896): Update compat change checks
- if (CompatChanges.isChangeEnabled(RESTRICT_LOCAL_NETWORK, uid)
- && BpfNetMaps.isAtLeast25Q2()) {
- // TODO(b/388803658): Update network permissions and record change
- }
+ setLocalNetworkPermissions(uid, null);
}
}
}
diff --git a/service/src/com/android/server/net/L2capNetwork.java b/service/src/com/android/server/net/L2capNetwork.java
index 594d273..c7417f9 100644
--- a/service/src/com/android/server/net/L2capNetwork.java
+++ b/service/src/com/android/server/net/L2capNetwork.java
@@ -38,14 +38,14 @@
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import com.android.server.L2capNetworkProvider;
+
public class L2capNetwork {
private static final NetworkScore NETWORK_SCORE = new NetworkScore.Builder().build();
private final String mLogTag;
private final Handler mHandler;
- private final String mIfname;
private final L2capPacketForwarder mForwarder;
private final NetworkCapabilities mNetworkCapabilities;
- private final L2capIpClient mIpClient;
private final NetworkAgent mNetworkAgent;
/** IpClient wrapper to handle IPv6 link-local provisioning for L2CAP tun.
@@ -59,7 +59,7 @@
@Nullable
private IpClientManager mIpClient;
@Nullable
- private LinkProperties mLinkProperties;
+ private volatile LinkProperties mLinkProperties;
L2capIpClient(String logTag, Context context, String ifname) {
mLogTag = logTag;
@@ -74,11 +74,24 @@
@Override
public void onProvisioningSuccess(LinkProperties lp) {
- Log.d(mLogTag, "Successfully provisionined l2cap tun: " + lp);
+ Log.d(mLogTag, "Successfully provisioned l2cap tun: " + lp);
mLinkProperties = lp;
mOnProvisioningSuccessCv.open();
}
+ @Override
+ public void onProvisioningFailure(LinkProperties lp) {
+ Log.i(mLogTag, "Failed to provision l2cap tun: " + lp);
+ mLinkProperties = null;
+ mOnProvisioningSuccessCv.open();
+ }
+
+ /**
+ * Starts IPv6 link-local provisioning.
+ *
+ * @return LinkProperties on success, null on failure.
+ */
+ @Nullable
public LinkProperties start() {
mOnIpClientCreatedCv.block();
// mIpClient guaranteed non-null.
@@ -102,32 +115,28 @@
void onNetworkUnwanted(L2capNetwork network);
}
- public L2capNetwork(Handler handler, Context context, NetworkProvider provider, String ifname,
- BluetoothSocket socket, ParcelFileDescriptor tunFd,
- NetworkCapabilities networkCapabilities, ICallback cb) {
- // TODO: add a check that this constructor is invoked on the handler thread.
- mLogTag = String.format("L2capNetwork[%s]", ifname);
+ public L2capNetwork(String logTag, Handler handler, Context context, NetworkProvider provider,
+ BluetoothSocket socket, ParcelFileDescriptor tunFd, NetworkCapabilities nc,
+ LinkProperties lp, L2capNetworkProvider.Dependencies deps, ICallback cb) {
+ mLogTag = logTag;
mHandler = handler;
- mIfname = ifname;
+ mNetworkCapabilities = nc;
- // Guaranteed non-null.
- final L2capNetworkSpecifier spec =
- (L2capNetworkSpecifier) networkCapabilities.getNetworkSpecifier();
+ final L2capNetworkSpecifier spec = (L2capNetworkSpecifier) nc.getNetworkSpecifier();
final boolean compressHeaders = spec.getHeaderCompression() == HEADER_COMPRESSION_6LOWPAN;
- mForwarder = new L2capPacketForwarder(handler, tunFd, socket, compressHeaders, () -> {
+
+ mForwarder = deps.createL2capPacketForwarder(handler, tunFd, socket, compressHeaders,
+ () -> {
// TODO: add a check that this callback is invoked on the handler thread.
cb.onError(L2capNetwork.this);
});
- mNetworkCapabilities = networkCapabilities;
- mIpClient = new L2capIpClient(mLogTag, context, ifname);
- final LinkProperties linkProperties = mIpClient.start();
final NetworkAgentConfig config = new NetworkAgentConfig.Builder().build();
mNetworkAgent = new NetworkAgent(context, mHandler.getLooper(), mLogTag,
- networkCapabilities, linkProperties, NETWORK_SCORE, config, provider) {
+ nc, lp, NETWORK_SCORE, config, provider) {
@Override
public void onNetworkUnwanted() {
- Log.i(mLogTag, mIfname + ": Network is unwanted");
+ Log.i(mLogTag, "Network is unwanted");
// TODO: add a check that this callback is invoked on the handler thread.
cb.onNetworkUnwanted(L2capNetwork.this);
}
@@ -136,6 +145,25 @@
mNetworkAgent.markConnected();
}
+ /** Create an L2capNetwork or return null on failure. */
+ @Nullable
+ public static L2capNetwork create(Handler handler, Context context, NetworkProvider provider,
+ String ifname, BluetoothSocket socket, ParcelFileDescriptor tunFd,
+ NetworkCapabilities nc, L2capNetworkProvider.Dependencies deps, ICallback cb) {
+ // TODO: add a check that this function is invoked on the handler thread.
+ final String logTag = String.format("L2capNetwork[%s]", ifname);
+
+ // L2capIpClient#start() blocks until provisioning either succeeds (and returns
+ // LinkProperties) or fails (and returns null).
+ // Note that since L2capNetwork is using IPv6 link-local provisioning the most likely
+ // (only?) failure mode is due to the interface disappearing.
+ final LinkProperties lp = new L2capIpClient(logTag, context, ifname).start();
+ if (lp == null) return null;
+
+ return new L2capNetwork(
+ logTag, handler, context, provider, socket, tunFd, nc, lp, deps, cb);
+ }
+
/** Get the NetworkCapabilities used for this Network */
public NetworkCapabilities getNetworkCapabilities() {
return mNetworkCapabilities;
diff --git a/service/src/com/android/server/net/L2capPacketForwarder.java b/service/src/com/android/server/net/L2capPacketForwarder.java
index 00faecf..737cb9c 100644
--- a/service/src/com/android/server/net/L2capPacketForwarder.java
+++ b/service/src/com/android/server/net/L2capPacketForwarder.java
@@ -233,6 +233,7 @@
L2capThread(IReadWriteFd readFd, IReadWriteFd writeFd, boolean isIngress,
boolean compressHeaders) {
+ super("L2capNetworkProvider-ForwarderThread");
mLogTag = isIngress ? "L2capForwarderThread-Ingress" : "L2capForwarderThread-Egress";
mReadFd = readFd;
mWriteFd = writeFd;
diff --git a/staticlibs/device/com/android/net/module/util/SkDestroyListener.java b/staticlibs/device/com/android/net/module/util/SkDestroyListener.java
new file mode 100644
index 0000000..c7c2829
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/SkDestroyListener.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.net.module.util;
+
+import static android.system.OsConstants.NETLINK_INET_DIAG;
+
+import android.os.Handler;
+
+import com.android.net.module.util.ip.NetlinkMonitor;
+import com.android.net.module.util.netlink.InetDiagMessage;
+import com.android.net.module.util.netlink.NetlinkMessage;
+
+import java.io.PrintWriter;
+import java.util.function.Consumer;
+
+/**
+ * Monitor socket destroy and delete entry from cookie tag bpf map.
+ */
+public class SkDestroyListener extends NetlinkMonitor {
+ private static final int SKNLGRP_INET_TCP_DESTROY = 1;
+ private static final int SKNLGRP_INET_UDP_DESTROY = 2;
+ private static final int SKNLGRP_INET6_TCP_DESTROY = 3;
+ private static final int SKNLGRP_INET6_UDP_DESTROY = 4;
+
+ // TODO: if too many sockets are closed too quickly, this can overflow the socket buffer, and
+ // some entries in mCookieTagMap will not be freed. In order to fix this it would be needed to
+ // periodically dump all sockets and remove the tag entries for sockets that have been closed.
+ // For now, set a large-enough buffer that hundreds of sockets can be closed without getting
+ // ENOBUFS and leaking mCookieTagMap entries.
+ private static final int SOCK_RCV_BUF_SIZE = 512 * 1024;
+
+ private final Consumer<InetDiagMessage> mSkDestroyCallback;
+
+ /**
+ * Return SkDestroyListener that monitor both TCP and UDP socket destroy
+ *
+ * @param consumer The consumer that processes InetDiagMessage
+ * @param handler The Handler on which to poll for messages
+ * @param log A SharedLog to log to.
+ * @return SkDestroyListener
+ */
+ public static SkDestroyListener makeSkDestroyListener(final Consumer<InetDiagMessage> consumer,
+ final Handler handler, final SharedLog log) {
+ return makeSkDestroyListener(consumer, true /* monitorTcpSocket */,
+ true /* monitorUdpSocket */, handler, log);
+ }
+
+ /**
+ * Return SkDestroyListener that monitor socket destroy
+ *
+ * @param consumer The consumer that processes InetDiagMessage
+ * @param monitorTcpSocket {@code true} to monitor TCP socket destroy
+ * @param monitorUdpSocket {@code true} to monitor UDP socket destroy
+ * @param handler The Handler on which to poll for messages
+ * @param log A SharedLog to log to.
+ * @return SkDestroyListener
+ */
+ public static SkDestroyListener makeSkDestroyListener(final Consumer<InetDiagMessage> consumer,
+ final boolean monitorTcpSocket, final boolean monitorUdpSocket,
+ final Handler handler, final SharedLog log) {
+ if (!monitorTcpSocket && !monitorUdpSocket) {
+ throw new IllegalArgumentException(
+ "Both monitorTcpSocket and monitorUdpSocket can not be false");
+ }
+ int bindGroups = 0;
+ if (monitorTcpSocket) {
+ bindGroups |= 1 << (SKNLGRP_INET_TCP_DESTROY - 1)
+ | 1 << (SKNLGRP_INET6_TCP_DESTROY - 1);
+ }
+ if (monitorUdpSocket) {
+ bindGroups |= 1 << (SKNLGRP_INET_UDP_DESTROY - 1)
+ | 1 << (SKNLGRP_INET6_UDP_DESTROY - 1);
+ }
+ return new SkDestroyListener(consumer, bindGroups, handler, log);
+ }
+
+ private SkDestroyListener(final Consumer<InetDiagMessage> consumer, final int bindGroups,
+ final Handler handler, final SharedLog log) {
+ super(handler, log, "SkDestroyListener", NETLINK_INET_DIAG,
+ bindGroups, SOCK_RCV_BUF_SIZE);
+ mSkDestroyCallback = consumer;
+ }
+
+ @Override
+ public void processNetlinkMessage(final NetlinkMessage nlMsg, final long whenMs) {
+ if (!(nlMsg instanceof InetDiagMessage)) {
+ mLog.e("Received non InetDiagMessage");
+ return;
+ }
+ mSkDestroyCallback.accept((InetDiagMessage) nlMsg);
+ }
+
+ /**
+ * Dump the contents of SkDestroyListener log.
+ */
+ public void dump(PrintWriter pw) {
+ mLog.reverseDump(pw);
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/netlink/InetDiagMessage.java b/staticlibs/device/com/android/net/module/util/netlink/InetDiagMessage.java
index fecaa09..c9a89ec 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/InetDiagMessage.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/InetDiagMessage.java
@@ -309,16 +309,18 @@
}
private static void sendNetlinkDestroyRequest(FileDescriptor fd, int proto,
- InetDiagMessage diagMsg) throws InterruptedIOException, ErrnoException {
+ StructInetDiagSockId id, short family, int state)
+ throws InterruptedIOException, ErrnoException {
+ // TODO: Investigate if it's fine to always set 0 to state and remove state from the arg
final byte[] destroyMsg = InetDiagMessage.inetDiagReqV2(
proto,
- diagMsg.inetDiagMsg.id,
- diagMsg.inetDiagMsg.idiag_family,
+ id,
+ family,
SOCK_DESTROY,
(short) (StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_ACK),
0 /* pad */,
0 /* idiagExt */,
- 1 << diagMsg.inetDiagMsg.idiag_state
+ state
);
NetlinkUtils.sendMessage(fd, destroyMsg, 0, destroyMsg.length, IO_TIMEOUT_MS);
NetlinkUtils.receiveNetlinkAck(fd);
@@ -343,7 +345,8 @@
Consumer<InetDiagMessage> handleNlDumpMsg = (diagMsg) -> {
if (filter.test(diagMsg)) {
try {
- sendNetlinkDestroyRequest(destroyFd, proto, diagMsg);
+ sendNetlinkDestroyRequest(destroyFd, proto, diagMsg.inetDiagMsg.id,
+ diagMsg.inetDiagMsg.idiag_family, 1 << diagMsg.inetDiagMsg.idiag_state);
destroyedSockets.getAndIncrement();
} catch (InterruptedIOException | ErrnoException e) {
if (!(e instanceof ErrnoException
@@ -484,6 +487,30 @@
Log.d(TAG, "Destroyed live tcp sockets for uids=" + ownerUids + " in " + durationMs + "ms");
}
+ /**
+ * Close the udp socket which can be uniquely identified with the cookie and other information.
+ */
+ public static void destroyUdpSocket(final InetSocketAddress src, final InetSocketAddress dst,
+ final int ifIndex, final long cookie)
+ throws ErrnoException, SocketException, InterruptedIOException {
+ FileDescriptor fd = null;
+
+ try {
+ fd = NetlinkUtils.createNetLinkInetDiagSocket();
+ connectToKernel(fd);
+ final int family = (src.getAddress() instanceof Inet6Address) ? AF_INET6 : AF_INET;
+ final StructInetDiagSockId id = new StructInetDiagSockId(
+ src,
+ dst,
+ ifIndex,
+ cookie
+ );
+ sendNetlinkDestroyRequest(fd, IPPROTO_UDP, id, (short) family, 0 /* state */);
+ } finally {
+ closeSocketQuietly(fd);
+ }
+ }
+
@Override
public String toString() {
return "InetDiagMessage{ "
diff --git a/tests/unit/java/com/android/server/net/SkDestroyListenerTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/SkDestroyListenerTest.kt
similarity index 89%
rename from tests/unit/java/com/android/server/net/SkDestroyListenerTest.kt
rename to staticlibs/tests/unit/src/com/android/net/module/util/SkDestroyListenerTest.kt
index 18785e5..e4b47fe 100644
--- a/tests/unit/java/com/android/server/net/SkDestroyListenerTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/SkDestroyListenerTest.kt
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.server.net
+package com.android.net.module.util
import android.os.Handler
import android.os.HandlerThread
-import com.android.net.module.util.SharedLog
+import com.android.net.module.util.SkDestroyListener.makeSkDestroyListener
import com.android.testutils.DevSdkIgnoreRunner
import java.io.PrintWriter
import org.junit.After
@@ -54,7 +54,7 @@
doReturn(sharedLog).`when`(sharedLog).forSubComponent(any())
val handler = Handler(handlerThread.looper)
- val skDestroylistener = SkDestroyListener(null /* cookieTagMap */, handler, sharedLog)
+ val skDestroylistener = makeSkDestroyListener({} /* consumer */, handler, sharedLog)
val pw = PrintWriter(System.out)
skDestroylistener.dump(pw)
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt
index 7b970d3..0b239b4 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/SetFeatureFlagsRule.kt
@@ -68,10 +68,15 @@
* If any `@FeatureFlag` annotation is found, it passes every feature flag's name
* and enabled state into the user-specified lambda to apply custom actions.
*/
+ private val parameterizedRegexp = Regex("\\[\\d+\\]$")
override fun apply(base: Statement, description: Description): Statement {
return object : Statement() {
override fun evaluate() {
- val testMethod = description.testClass.getMethod(description.methodName)
+ // If the same class also uses Parameterized, depending on evaluation order the
+ // method names here may be synthetic method names, where [0] [1] or so are added
+ // at the end of the method name. Find the original method name.
+ val methodName = description.methodName.replace(parameterizedRegexp, "")
+ val testMethod = description.testClass.getMethod(methodName)
val featureFlagAnnotations = testMethod.getAnnotationsByType(
FeatureFlag::class.java
)
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index 8e790ca..c6a1b09 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -19,8 +19,6 @@
package android.net.cts
-import android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG
-import android.Manifest.permission.WRITE_DEVICE_CONFIG
import android.content.pm.PackageManager.FEATURE_AUTOMOTIVE
import android.content.pm.PackageManager.FEATURE_LEANBACK
import android.content.pm.PackageManager.FEATURE_WIFI
@@ -55,8 +53,6 @@
import android.os.SystemProperties
import android.os.UserManager
import android.platform.test.annotations.AppModeFull
-import android.provider.DeviceConfig
-import android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY
import android.system.Os
import android.system.OsConstants
import android.system.OsConstants.AF_INET6
@@ -90,7 +86,6 @@
import com.android.testutils.RecorderCallback.CallbackEntry.LinkPropertiesChanged
import com.android.testutils.SkipPresubmit
import com.android.testutils.TestableNetworkCallback
-import com.android.testutils.runAsShell
import com.android.testutils.waitForIdle
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
@@ -116,7 +111,6 @@
private const val TAG = "ApfIntegrationTest"
private const val TIMEOUT_MS = 2000L
-private const val APF_NEW_RA_FILTER_VERSION = "apf_new_ra_filter_version"
private const val POLLING_INTERVAL_MS: Int = 100
private const val RCV_BUFFER_SIZE = 1480
private const val PING_HEADER_LENGTH = 8
@@ -192,16 +186,6 @@
Thread.sleep(1000)
// TODO: check that there is no active wifi network. Otherwise, ApfFilter has already been
// created.
- // APF adb cmds are only implemented in ApfFilter.java. Enable experiment to prevent
- // LegacyApfFilter.java from being used.
- runAsShell(WRITE_DEVICE_CONFIG, WRITE_ALLOWLISTED_DEVICE_CONFIG) {
- DeviceConfig.setProperty(
- NAMESPACE_CONNECTIVITY,
- APF_NEW_RA_FILTER_VERSION,
- "1", // value => force enabled
- false // makeDefault
- )
- }
}
@AfterClass
diff --git a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
index 1de4cf9..ceccf0b 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -44,6 +44,7 @@
import android.net.RouteInfo
import android.net.TestNetworkInterface
import android.net.TestNetworkManager
+import android.net.TestNetworkManager.TestInterfaceRequest
import android.net.cts.util.CtsNetUtils.TestNetworkCallback
import android.os.HandlerThread
import android.os.SystemClock
@@ -164,7 +165,11 @@
// Only statically configure the IPv4 address; for IPv6, use the SLAAC generated
// address.
- iface = tnm.createTapInterface(arrayOf(LinkAddress(LOCAL_IPV4_ADDRESS, IP4_PREFIX_LEN)))
+ val req = TestInterfaceRequest.Builder()
+ .setTap()
+ .addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, IP4_PREFIX_LEN))
+ .build()
+ iface = tnm.createTestInterface(req)
assertNotNull(iface)
}
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 06f2075..9f32132 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -51,6 +51,7 @@
import android.net.StaticIpConfiguration
import android.net.TestNetworkInterface
import android.net.TestNetworkManager
+import android.net.TestNetworkManager.TestInterfaceRequest
import android.net.cts.EthernetManagerTest.EthernetStateListener.CallbackEntry.EthernetStateChanged
import android.net.cts.EthernetManagerTest.EthernetStateListener.CallbackEntry.InterfaceStateChanged
import android.os.Build
@@ -169,7 +170,12 @@
// false, it is subsequently disabled. This means that the interface may briefly get
// link. With IPv6 provisioning delays (RS delay and DAD) disabled, this can cause
// tests that expect no network to come up when hasCarrier is false to become flaky.
- tnm.createTapInterface(hasCarrier, false /* bringUp */)
+ val req = TestInterfaceRequest.Builder()
+ .setTap()
+ .setHasCarrier(hasCarrier)
+ .setBringUp(false)
+ .build()
+ tnm.createTestInterface(req)
}
val mtu = tapInterface.mtu
packetReader = PollPacketReader(
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 cc9445d..437eb81 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -62,6 +62,7 @@
import com.android.server.connectivity.ConnectivityResources
import com.android.server.connectivity.MockableSystemProperties
import com.android.server.connectivity.MultinetworkPolicyTracker
+import com.android.server.connectivity.PermissionMonitor
import com.android.server.connectivity.ProxyTracker
import com.android.server.connectivity.SatelliteAccessController
import com.android.testutils.DevSdkIgnoreRunner
@@ -222,7 +223,7 @@
}
private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
- context, dnsResolver, log, netd, deps)
+ context, dnsResolver, log, netd, deps, PermissionMonitorDependencies())
private inner class TestDependencies : ConnectivityService.Dependencies() {
override fun getNetworkStack() = networkStackClient
@@ -277,6 +278,10 @@
override fun makeL2capNetworkProvider(context: Context) = null
}
+ private inner class PermissionMonitorDependencies : PermissionMonitor.Dependencies() {
+ override fun shouldEnforceLocalNetRestrictions(uid: Int) = false
+ }
+
@After
fun tearDown() {
nsInstrumentation.clearAllState()
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 1562dad..b7aa387 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -154,6 +154,7 @@
import static android.net.Proxy.PROXY_CHANGE_ACTION;
import static android.net.RouteInfo.RTN_UNREACHABLE;
import static android.net.connectivity.ConnectivityCompatChanges.NETWORK_BLOCKED_WITHOUT_INTERNET_PERMISSION;
+import static android.net.connectivity.ConnectivityCompatChanges.RESTRICT_LOCAL_NETWORK;
import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_ADDED;
import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.PREFIX_OPERATION_REMOVED;
import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALIDATION_RESULT_FAILURE;
@@ -257,6 +258,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.admin.DevicePolicyManager;
+import android.app.compat.CompatChanges;
import android.app.usage.NetworkStatsManager;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.BroadcastReceiver;
@@ -426,6 +428,7 @@
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.SatelliteAccessController;
@@ -594,6 +597,7 @@
private MockContext mServiceContext;
private HandlerThread mCsHandlerThread;
private ConnectivityServiceDependencies mDeps;
+ private PermissionMonitorDependencies mPermDeps;
private AutomaticOnOffKeepaliveTrackerDependencies mAutoOnOffKeepaliveDependencies;
private ConnectivityService mService;
private WrappedConnectivityManager mCm;
@@ -1921,6 +1925,7 @@
doReturn(mResources).when(mockResContext).getResources();
ConnectivityResources.setResourcesContextForTest(mockResContext);
mDeps = new ConnectivityServiceDependencies(mockResContext);
+ mPermDeps = new PermissionMonitorDependencies();
doReturn(true).when(mMockKeepaliveTrackerDependencies)
.isAddressTranslationEnabled(mServiceContext);
doReturn(new ConnectivityResources(mockResContext)).when(mMockKeepaliveTrackerDependencies)
@@ -1933,7 +1938,7 @@
mMockDnsResolver,
mock(IpConnectivityLog.class),
mMockNetd,
- mDeps);
+ mDeps, mPermDeps);
mService.mLingerDelayMs = TEST_LINGER_DELAY_MS;
mService.mNascentDelayMs = TEST_NASCENT_DELAY_MS;
@@ -2389,6 +2394,13 @@
}
}
+ static class PermissionMonitorDependencies extends PermissionMonitor.Dependencies {
+ @Override
+ public boolean shouldEnforceLocalNetRestrictions(int uid) {
+ return false;
+ }
+ }
+
private class AutomaticOnOffKeepaliveTrackerDependencies
extends AutomaticOnOffKeepaliveTracker.Dependencies {
AutomaticOnOffKeepaliveTrackerDependencies(Context context) {
diff --git a/tests/unit/java/com/android/server/connectivity/NetworkPermissionsTest.kt b/tests/unit/java/com/android/server/connectivity/NetworkPermissionsTest.kt
new file mode 100644
index 0000000..8a9d288
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivity/NetworkPermissionsTest.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2025 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.net.INetd
+import android.os.Build
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
+import com.android.testutils.DevSdkIgnoreRunner
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(DevSdkIgnoreRunner::class)
+@IgnoreUpTo(Build.VERSION_CODES.TIRAMISU)
+class NetworkPermissionsTest {
+ @Test
+ fun test_networkTrafficPerms_correctValues() {
+ assertEquals(NetworkPermissions.PERMISSION_NONE, INetd.PERMISSION_NONE) /* 0 */
+ assertEquals(NetworkPermissions.PERMISSION_NETWORK, INetd.PERMISSION_NETWORK) /* 1 */
+ assertEquals(NetworkPermissions.PERMISSION_SYSTEM, INetd.PERMISSION_SYSTEM) /* 2 */
+ assertEquals(NetworkPermissions.TRAFFIC_PERMISSION_INTERNET, 4)
+ assertEquals(NetworkPermissions.TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS, 8)
+ assertEquals(NetworkPermissions.TRAFFIC_PERMISSION_UNINSTALLED, -1)
+ assertEquals(NetworkPermissions.TRAFFIC_PERMISSION_SDKSANDBOX_LOCALHOST, 16)
+ }
+
+ @Test
+ fun test_noOverridesInFlags() {
+ val permsList = listOf(
+ NetworkPermissions.PERMISSION_NONE,
+ NetworkPermissions.PERMISSION_NETWORK,
+ NetworkPermissions.PERMISSION_SYSTEM,
+ NetworkPermissions.TRAFFIC_PERMISSION_INTERNET,
+ NetworkPermissions.TRAFFIC_PERMISSION_UPDATE_DEVICE_STATS,
+ NetworkPermissions.TRAFFIC_PERMISSION_SDKSANDBOX_LOCALHOST,
+ NetworkPermissions.TRAFFIC_PERMISSION_UNINSTALLED
+ )
+ assertFalse(hasDuplicates(permsList))
+ }
+
+ fun hasDuplicates(list: List<Int>): Boolean {
+ return list.distinct().size != list.size
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
index 55c68b7..ec9c6b0 100644
--- a/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/PermissionMonitorTest.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
import static android.Manifest.permission.INTERNET;
+import static android.Manifest.permission.NEARBY_WIFI_DEVICES;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
@@ -31,6 +32,7 @@
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_REQUIRED;
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.net.ConnectivitySettingsManager.UIDS_ALLOWED_ON_RESTRICTED_NETWORKS;
import static android.net.INetd.PERMISSION_INTERNET;
import static android.net.INetd.PERMISSION_NETWORK;
@@ -39,16 +41,20 @@
import static android.net.INetd.PERMISSION_UNINSTALLED;
import static android.net.INetd.PERMISSION_UPDATE_DEVICE_STATS;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+import static android.net.connectivity.ConnectivityCompatChanges.RESTRICT_LOCAL_NETWORK;
import static android.os.Process.SYSTEM_UID;
+import static android.permission.PermissionManager.PERMISSION_GRANTED;
import static com.android.server.connectivity.PermissionMonitor.isHigherNetworkPermission;
import static com.android.testutils.TestPermissionUtil.runAsShell;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -68,6 +74,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -85,7 +93,9 @@
import android.os.SystemConfigManager;
import android.os.UserHandle;
import android.os.UserManager;
+import android.permission.PermissionManager;
import android.provider.Settings;
+import android.util.ArraySet;
import android.util.SparseIntArray;
import androidx.annotation.NonNull;
@@ -102,9 +112,13 @@
import com.android.testutils.DevSdkIgnoreRunner;
import com.android.testutils.HandlerUtils;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
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.AdditionalAnswers;
import org.mockito.ArgumentCaptor;
@@ -121,6 +135,8 @@
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class PermissionMonitorTest {
+ @Rule
+ public TestRule compatChangeRule = new PlatformCompatChangeRule();
private static final int MOCK_USER_ID1 = 0;
private static final int MOCK_USER_ID2 = 1;
private static final int MOCK_USER_ID3 = 2;
@@ -162,9 +178,14 @@
private static final int PERMISSION_TRAFFIC_ALL =
PERMISSION_INTERNET | PERMISSION_UPDATE_DEVICE_STATS;
private static final int TIMEOUT_MS = 2_000;
+ // The ACCESS_LOCAL_NETWORK permission is not available yet. For the time being, use
+ // NEARBY_WIFI_DEVICES as a means to develop, for expediency.
+ // TODO(b/375236298): remove this constant when the ACCESS_LOCAL_NETWORK permission is defined.
+ private static final String ACCESS_LOCAL_NETWORK = NEARBY_WIFI_DEVICES;
@Mock private Context mContext;
@Mock private PackageManager mPackageManager;
+ @Mock private PermissionManager mPermissionManager;
@Mock private INetd mNetdService;
@Mock private UserManager mUserManager;
@Mock private PermissionMonitor.Dependencies mDeps;
@@ -183,6 +204,7 @@
when(mContext.getPackageManager()).thenReturn(mPackageManager);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
doReturn(List.of(MOCK_USER1)).when(mUserManager).getUserHandles(eq(true));
+ when(mContext.getSystemService(PermissionManager.class)).thenReturn(mPermissionManager);
when(mContext.getSystemServiceName(SystemConfigManager.class))
.thenReturn(Context.SYSTEM_CONFIG_SERVICE);
when(mContext.getSystemService(Context.SYSTEM_CONFIG_SERVICE))
@@ -295,19 +317,28 @@
return result;
}
- private void buildAndMockPackageInfoWithPermissions(String packageName, int uid,
+ private PackageInfo 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 */);
+ if (BpfNetMaps.isAtLeast25Q2()) {
+ // Runtime permission checks for local net restrictions were introduced in 25Q2
+ for (String permission : permissions) {
+ doReturn(PERMISSION_GRANTED).when(mPermissionManager).checkPermissionForPreflight(
+ eq(permission),
+ argThat(attributionSource -> attributionSource.getUid() == uid));
+ }
+ }
final String[] oldPackages = mPackageManager.getPackagesForUid(uid);
// If it's duplicated package, no need to set it again.
- if (CollectionUtils.contains(oldPackages, packageName)) return;
+ if (CollectionUtils.contains(oldPackages, packageName)) return packageInfo;
// 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));
+ return packageInfo;
}
private void startMonitoring() {
@@ -342,7 +373,7 @@
private void addPackage(String packageName, int uid, String... permissions) throws Exception {
buildAndMockPackageInfoWithPermissions(packageName, uid, permissions);
- processOnHandlerThread(() -> mPermissionMonitor.onPackageAdded(packageName, uid));
+ onPackageAdded(packageName, uid);
}
private void removePackage(String packageName, int uid) {
@@ -354,7 +385,12 @@
final String[] newPackages = Arrays.stream(oldPackages).filter(e -> !e.equals(packageName))
.toArray(String[]::new);
doReturn(newPackages).when(mPackageManager).getPackagesForUid(eq(uid));
- processOnHandlerThread(() -> mPermissionMonitor.onPackageRemoved(packageName, uid));
+ if (BpfNetMaps.isAtLeast25Q2()){
+ // Runtime permission checks for local net restrictions were introduced in 25Q2
+ doReturn(PERMISSION_DENIED).when(mPermissionManager).checkPermissionForPreflight(
+ anyString(), argThat(as -> as.getUid() == uid));
+ }
+ onPackageRemoved(packageName, uid);
}
@Test
@@ -585,6 +621,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testHasUseBackgroundNetworksPermission() throws Exception {
assertFalse(mPermissionMonitor.hasUseBackgroundNetworksPermission(SYSTEM_UID));
assertBackgroundPermission(false, SYSTEM_PACKAGE1, SYSTEM_UID);
@@ -606,6 +643,7 @@
private class BpfMapMonitor {
private final SparseIntArray mAppIdsTrafficPermission = new SparseIntArray();
+ private final ArraySet<Integer> mLocalNetBlockedUids = new ArraySet<>();
private static final int DOES_NOT_EXIST = -2;
BpfMapMonitor(BpfNetMaps mockBpfmap) throws Exception {
@@ -618,6 +656,18 @@
}
return null;
}).when(mockBpfmap).setNetPermForUids(anyInt(), any(int[].class));
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ final int uid = (int) args[0];
+ mLocalNetBlockedUids.add(uid);
+ return null;
+ }).when(mockBpfmap).addUidToLocalNetBlockMap(anyInt());
+ doAnswer((InvocationOnMock invocation) -> {
+ final Object[] args = invocation.getArguments();
+ final int uid = (int) args[0];
+ mLocalNetBlockedUids.remove(uid);
+ return null;
+ }).when(mockBpfmap).removeUidFromLocalNetBlockMap(anyInt());
}
public void expectTrafficPerm(int permission, Integer... appIds) {
@@ -642,6 +692,18 @@
}
}
}
+
+ public boolean hasLocalNetPermissions(int uid) {
+ return !mLocalNetBlockedUids.contains(uid);
+ }
+
+ public boolean isUidPresentInLocalNetBlockMap(int uid) {
+ return mLocalNetBlockedUids.contains(uid);
+ }
+
+ public boolean hasBlockedLocalNetForSandboxUid(int sandboxUid) {
+ return mLocalNetBlockedUids.contains(sandboxUid);
+ }
}
private class NetdMonitor {
@@ -725,6 +787,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testUserAndPackageAddRemove() throws Exception {
// MOCK_UID11: MOCK_PACKAGE1 only has network permission.
// SYSTEM_APP_UID11: SYSTEM_PACKAGE1 has system permission.
@@ -814,6 +877,48 @@
MOCK_APPID1);
}
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
+ public void testLocalNetRestrictions_onUserAdded() throws Exception {
+ assumeTrue(BpfNetMaps.isAtLeast25Q2());
+ doReturn(true).when(mDeps).shouldEnforceLocalNetRestrictions(anyInt());
+ when(mPermissionManager.checkPermissionForPreflight(
+ anyString(), any(AttributionSource.class))).thenReturn(PERMISSION_DENIED);
+ final PackageInfo packageInfo = buildAndMockPackageInfoWithPermissions(
+ MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+ // Set package for all users on devices
+ doReturn(List.of(packageInfo)).when(mPackageManager)
+ .getInstalledPackagesAsUser(anyInt(), eq(MOCK_USER1.getIdentifier()));
+ onUserAdded(MOCK_USER1);
+
+ assertFalse(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID11));
+ if (hasSdkSandbox(MOCK_UID11)) {
+ assertTrue(mBpfMapMonitor.hasBlockedLocalNetForSandboxUid(
+ mProcessShim.toSdkSandboxUid(MOCK_UID11)));
+ }
+ }
+
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
+ public void testLocalNetRestrictions_onUserRemoved() throws Exception {
+ assumeTrue(BpfNetMaps.isAtLeast25Q2());
+ doReturn(true).when(mDeps).shouldEnforceLocalNetRestrictions(anyInt());
+ when(mPermissionManager.checkPermissionForPreflight(
+ anyString(), any(AttributionSource.class))).thenReturn(PERMISSION_DENIED);
+ final PackageInfo packageInfo = buildAndMockPackageInfoWithPermissions(
+ MOCK_PACKAGE1, MOCK_UID11, CHANGE_NETWORK_STATE);
+ // Set package for all users on devices
+ doReturn(List.of(packageInfo)).when(mPackageManager)
+ .getInstalledPackagesAsUser(anyInt(), eq(MOCK_USER1.getIdentifier()));
+ onUserAdded(MOCK_USER1);
+ assertFalse(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID11));
+
+ onUserRemoved(MOCK_USER1);
+ assertFalse(mBpfMapMonitor.isUidPresentInLocalNetBlockMap(MOCK_UID11));
+ }
+
private void doTestUidFilteringDuringVpnConnectDisconnectAndUidUpdates(@Nullable String ifName)
throws Exception {
doReturn(List.of(
@@ -860,11 +965,13 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdates() throws Exception {
doTestUidFilteringDuringVpnConnectDisconnectAndUidUpdates("tun0");
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testUidFilteringDuringVpnConnectDisconnectAndUidUpdatesWithWildcard()
throws Exception {
doTestUidFilteringDuringVpnConnectDisconnectAndUidUpdates(null /* ifName */);
@@ -897,16 +1004,19 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testUidFilteringDuringPackageInstallAndUninstall() throws Exception {
doTestUidFilteringDuringPackageInstallAndUninstall("tun0");
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testUidFilteringDuringPackageInstallAndUninstallWithWildcard() throws Exception {
doTestUidFilteringDuringPackageInstallAndUninstall(null /* ifName */);
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testLockdownUidFilteringWithLockdownEnableDisable() {
doReturn(List.of(
buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
@@ -938,6 +1048,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testLockdownUidFilteringWithLockdownEnableDisableWithMultiAdd() {
doReturn(List.of(
buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
@@ -979,6 +1090,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testLockdownUidFilteringWithLockdownEnableDisableWithMultiAddAndOverlap() {
doReturn(List.of(buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
CONNECTIVITY_USE_RESTRICTED_NETWORKS),
@@ -1039,6 +1151,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testLockdownUidFilteringWithLockdownEnableDisableWithDuplicates() {
doReturn(List.of(
buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
@@ -1073,6 +1186,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testLockdownUidFilteringWithInstallAndUnInstall() {
doReturn(List.of(
buildPackageInfo(SYSTEM_PACKAGE1, SYSTEM_APP_UID11, CHANGE_NETWORK_STATE,
@@ -1109,15 +1223,13 @@
// called multiple times with the uid corresponding to each user.
private void addPackageForUsers(UserHandle[] users, String packageName, int appId) {
for (final UserHandle user : users) {
- processOnHandlerThread(() ->
- mPermissionMonitor.onPackageAdded(packageName, user.getUid(appId)));
+ onPackageAdded(packageName, user.getUid(appId));
}
}
private void removePackageForUsers(UserHandle[] users, String packageName, int appId) {
for (final UserHandle user : users) {
- processOnHandlerThread(() ->
- mPermissionMonitor.onPackageRemoved(packageName, user.getUid(appId)));
+ onPackageRemoved(packageName, user.getUid(appId));
}
}
@@ -1165,6 +1277,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testPackageInstall() throws Exception {
addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mBpfMapMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
@@ -1173,7 +1286,25 @@
mBpfMapMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID2);
}
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
+ public void testLocalNetRestrictions_onPackageInstall() throws Exception {
+ assumeTrue(BpfNetMaps.isAtLeast25Q2());
+ doReturn(true).when(mDeps).shouldEnforceLocalNetRestrictions(anyInt());
+ when(mPermissionManager.checkPermissionForPreflight(
+ anyString(), any(AttributionSource.class))).thenReturn(PERMISSION_DENIED);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
+ assertFalse(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID11));
+
+ addPackage(MOCK_PACKAGE2, MOCK_UID12, ACCESS_LOCAL_NETWORK);
+ assertTrue(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID12));
+ if (hasSdkSandbox(MOCK_UID12)) assertTrue(mBpfMapMonitor.hasBlockedLocalNetForSandboxUid(
+ mProcessShim.toSdkSandboxUid(MOCK_UID12)));
+ }
+
+ @Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testPackageInstallSharedUid() throws Exception {
addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mBpfMapMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
@@ -1185,6 +1316,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testPackageUninstallBasic() throws Exception {
addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mBpfMapMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
@@ -1194,7 +1326,24 @@
mBpfMapMonitor.expectTrafficPerm(PERMISSION_UNINSTALLED, MOCK_APPID1);
}
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
+ public void testLocalNetRestrictions_onPackageUninstall() throws Exception {
+ assumeTrue(BpfNetMaps.isAtLeast25Q2());
+ doReturn(true).when(mDeps).shouldEnforceLocalNetRestrictions(anyInt());
+ when(mPermissionManager.checkPermissionForPreflight(
+ anyString(), any(AttributionSource.class))).thenReturn(PERMISSION_DENIED);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, ACCESS_LOCAL_NETWORK);
+ assertTrue(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID11));
+
+ when(mPackageManager.getPackagesForUid(MOCK_UID11)).thenReturn(new String[]{});
+ onPackageRemoved(MOCK_PACKAGE1, MOCK_UID11);
+ assertFalse(mBpfMapMonitor.isUidPresentInLocalNetBlockMap(MOCK_UID11));
+ }
+
+ @Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testPackageRemoveThenAdd() throws Exception {
addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mBpfMapMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
@@ -1207,7 +1356,30 @@
mBpfMapMonitor.expectTrafficPerm(PERMISSION_INTERNET, MOCK_APPID1);
}
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
+ public void testLocalNetRestrictions_onPackageRemoveThenAdd() throws Exception {
+ assumeTrue(BpfNetMaps.isAtLeast25Q2());
+ doReturn(true).when(mDeps).shouldEnforceLocalNetRestrictions(anyInt());
+ when(mPermissionManager.checkPermissionForPreflight(
+ anyString(), any(AttributionSource.class))).thenReturn(PERMISSION_DENIED);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, ACCESS_LOCAL_NETWORK);
+ assertTrue(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID11));
+ if (hasSdkSandbox(MOCK_UID12)) assertTrue(mBpfMapMonitor.hasBlockedLocalNetForSandboxUid(
+ mProcessShim.toSdkSandboxUid(MOCK_UID11)));
+
+ removePackage(MOCK_PACKAGE1, MOCK_UID11);
+ assertFalse(mBpfMapMonitor.isUidPresentInLocalNetBlockMap(MOCK_UID11));
+
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
+ assertFalse(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID11));
+ if (hasSdkSandbox(MOCK_UID12)) assertTrue(mBpfMapMonitor.hasBlockedLocalNetForSandboxUid(
+ mProcessShim.toSdkSandboxUid(MOCK_UID11)));
+ }
+
+ @Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testPackageUpdate() throws Exception {
addPackage(MOCK_PACKAGE1, MOCK_UID11);
mBpfMapMonitor.expectTrafficPerm(PERMISSION_NONE, MOCK_APPID1);
@@ -1217,6 +1389,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testPackageUninstallWithMultiplePackages() throws Exception {
addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET, UPDATE_DEVICE_STATS);
mBpfMapMonitor.expectTrafficPerm(PERMISSION_TRAFFIC_ALL, MOCK_APPID1);
@@ -1248,6 +1421,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testUpdateUidPermissionsFromSystemConfig() throws Exception {
when(mSystemConfigManager.getSystemPermissionUids(eq(INTERNET)))
.thenReturn(new int[]{ MOCK_UID11, MOCK_UID12 });
@@ -1287,6 +1461,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testIntentReceiver() throws Exception {
startMonitoring();
final BroadcastReceiver receiver = expectBroadcastReceiver(
@@ -1325,6 +1500,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testUidsAllowedOnRestrictedNetworksChanged() throws Exception {
startMonitoring();
final ContentObserver contentObserver = expectRegisterContentObserver(
@@ -1357,6 +1533,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testUidsAllowedOnRestrictedNetworksChangedWithSharedUid() throws Exception {
startMonitoring();
final ContentObserver contentObserver = expectRegisterContentObserver(
@@ -1390,6 +1567,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testUidsAllowedOnRestrictedNetworksChangedWithMultipleUsers() throws Exception {
startMonitoring();
final ContentObserver contentObserver = expectRegisterContentObserver(
@@ -1444,6 +1622,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testOnExternalApplicationsAvailable() throws Exception {
// Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
// and have different uids. There has no permission for both uids.
@@ -1475,6 +1654,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testOnExternalApplicationsAvailable_AppsNotRegisteredOnStartMonitoring()
throws Exception {
startMonitoring();
@@ -1502,6 +1682,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testOnExternalApplicationsAvailableWithSharedUid()
throws Exception {
// Initial the permission state. MOCK_PACKAGE1 and MOCK_PACKAGE2 are installed on external
@@ -1528,6 +1709,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testOnExternalApplicationsAvailableWithSharedUid_DifferentStorage()
throws Exception {
// Initial the permission state. MOCK_PACKAGE1 is installed on external storage and
@@ -1570,6 +1752,38 @@
assertFalse(isHigherNetworkPermission(PERMISSION_SYSTEM, PERMISSION_SYSTEM));
}
+ @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+ @Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
+ public void testLocalNetRestrictions_setPermChanges() throws Exception {
+ assumeTrue(BpfNetMaps.isAtLeast25Q2());
+ doReturn(true).when(mDeps).shouldEnforceLocalNetRestrictions(anyInt());
+ when(mPermissionManager.checkPermissionForPreflight(
+ anyString(), any(AttributionSource.class))).thenReturn(PERMISSION_DENIED);
+ addPackage(MOCK_PACKAGE1, MOCK_UID11, INTERNET);
+ assertFalse(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID11));
+
+ // Mock permission grant
+ when(mPermissionManager.checkPermissionForPreflight(
+ eq(ACCESS_LOCAL_NETWORK),
+ argThat(attributionSource -> attributionSource.getUid() == MOCK_UID11)))
+ .thenReturn(PERMISSION_GRANTED);
+ mPermissionMonitor.setLocalNetworkPermissions(MOCK_UID11, null);
+ assertTrue(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID11));
+ if (hasSdkSandbox(MOCK_UID12)) assertTrue(mBpfMapMonitor.hasBlockedLocalNetForSandboxUid(
+ mProcessShim.toSdkSandboxUid(MOCK_UID11)));
+
+ // Mock permission denied
+ when(mPermissionManager.checkPermissionForPreflight(
+ eq(ACCESS_LOCAL_NETWORK),
+ argThat(attributionSource -> attributionSource.getUid() == MOCK_UID11)))
+ .thenReturn(PERMISSION_DENIED);
+ mPermissionMonitor.setLocalNetworkPermissions(MOCK_UID11, null);
+ assertFalse(mBpfMapMonitor.hasLocalNetPermissions(MOCK_UID11));
+ if (hasSdkSandbox(MOCK_UID12)) assertTrue(mBpfMapMonitor.hasBlockedLocalNetForSandboxUid(
+ mProcessShim.toSdkSandboxUid(MOCK_UID11)));
+ }
+
private void prepareMultiUserPackages() {
// MOCK_USER1 has installed 3 packages
// mockApp1 has no permission and share MOCK_APPID1.
@@ -1602,7 +1816,7 @@
private void addUserAndVerifyAppIdsPermissions(UserHandle user, int appId1Perm,
int appId2Perm, int appId3Perm) {
- processOnHandlerThread(() -> mPermissionMonitor.onUserAdded(user));
+ onUserAdded(user);
mBpfMapMonitor.expectTrafficPerm(appId1Perm, MOCK_APPID1);
mBpfMapMonitor.expectTrafficPerm(appId2Perm, MOCK_APPID2);
mBpfMapMonitor.expectTrafficPerm(appId3Perm, MOCK_APPID3);
@@ -1610,13 +1824,14 @@
private void removeUserAndVerifyAppIdsPermissions(UserHandle user, int appId1Perm,
int appId2Perm, int appId3Perm) {
- processOnHandlerThread(() -> mPermissionMonitor.onUserRemoved(user));
+ onUserRemoved(user);
mBpfMapMonitor.expectTrafficPerm(appId1Perm, MOCK_APPID1);
mBpfMapMonitor.expectTrafficPerm(appId2Perm, MOCK_APPID2);
mBpfMapMonitor.expectTrafficPerm(appId3Perm, MOCK_APPID3);
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testAppIdsTrafficPermission_UserAddedRemoved() {
prepareMultiUserPackages();
@@ -1650,6 +1865,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testAppIdsTrafficPermission_Multiuser_PackageAdded() throws Exception {
// Add two users with empty package list.
onUserAdded(MOCK_USER1);
@@ -1720,6 +1936,7 @@
}
@Test
+ @EnableCompatChanges(RESTRICT_LOCAL_NETWORK)
public void testAppIdsTrafficPermission_Multiuser_PackageRemoved() throws Exception {
// Add two users with empty package list.
onUserAdded(MOCK_USER1);
diff --git a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
index 1cc9985..f763bae 100644
--- a/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
+++ b/tests/unit/java/com/android/server/connectivity/mdns/MdnsSocketProviderTest.java
@@ -610,6 +610,7 @@
@Test
public void testSocketCreatedForMulticastInterface() throws Exception {
+ doReturn(true).when(mTestNetworkIfaceWrapper).isPointToPoint();
doReturn(true).when(mTestNetworkIfaceWrapper).supportsMulticast();
startMonitoringSockets();
@@ -621,18 +622,6 @@
}
@Test
- public void testNoSocketCreatedForPTPInterface() throws Exception {
- doReturn(true).when(mTestNetworkIfaceWrapper).isPointToPoint();
- startMonitoringSockets();
-
- final TestSocketCallback testCallback = new TestSocketCallback();
- runOnHandler(() -> mSocketProvider.requestSocket(TEST_NETWORK, testCallback));
-
- postNetworkAvailable(TRANSPORT_BLUETOOTH);
- testCallback.expectedNoCallback();
- }
-
- @Test
public void testNoSocketCreatedForVPNInterface() throws Exception {
// VPN interfaces generally also have IFF_POINTOPOINT, but even if they don't, they should
// not be included even with TRANSPORT_WIFI.
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
index d44bd0a..489c3ad 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSL2capProviderTest.kt
@@ -20,42 +20,37 @@
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothServerSocket
import android.bluetooth.BluetoothSocket
-import android.content.Context
import android.net.L2capNetworkSpecifier
import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN
import android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_NONE
import android.net.L2capNetworkSpecifier.ROLE_SERVER
-import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED
import android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED
import android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH
-import android.net.NetworkProvider
-import android.net.NetworkProvider.NetworkOfferCallback
import android.net.NetworkRequest
-import android.net.NetworkScore
import android.net.NetworkSpecifier
import android.os.Build
import android.os.HandlerThread
-import android.os.Looper
-import com.android.server.L2capNetworkProvider
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import com.android.testutils.DevSdkIgnoreRunner
import com.android.testutils.RecorderCallback.CallbackEntry.Reserved
+import com.android.testutils.RecorderCallback.CallbackEntry.Unavailable
import com.android.testutils.TestableNetworkCallback
+import com.android.testutils.waitForIdle
import java.io.IOException
-import java.util.concurrent.Executor
+import java.util.Optional
import java.util.concurrent.LinkedBlockingQueue
import kotlin.test.assertEquals
+import kotlin.test.assertFalse
import kotlin.test.assertNull
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.doThrow
import org.mockito.Mockito.mock
-import org.mockito.MockitoAnnotations
private const val PSM = 0x85
private val REMOTE_MAC = byteArrayOf(1, 2, 3, 4, 5, 6)
@@ -67,14 +62,19 @@
@RunWith(DevSdkIgnoreRunner::class)
@IgnoreUpTo(Build.VERSION_CODES.R)
+@DevSdkIgnoreRunner.MonitorThreadLeak
class CSL2capProviderTest : CSTest() {
private val btAdapter = mock<BluetoothAdapter>()
private val btServerSocket = mock<BluetoothServerSocket>()
private val btSocket = mock<BluetoothSocket>()
private val providerDeps = mock<L2capNetworkProvider.Dependencies>()
- private val acceptQueue = LinkedBlockingQueue<BluetoothSocket>()
+ // BlockingQueue does not support put(null) operations, as null is used as an internal sentinel
+ // value. Therefore, use Optional<BluetoothSocket> where an empty optional signals the
+ // BluetoothServerSocket#close() operation.
+ private val acceptQueue = LinkedBlockingQueue<Optional<BluetoothSocket>>()
private val handlerThread = HandlerThread("CSL2capProviderTest thread").apply { start() }
+ private val registeredCallbacks = ArrayList<TestableNetworkCallback>()
// Requires Dependencies mock to be setup before creation.
private lateinit var provider: L2capNetworkProvider
@@ -83,15 +83,16 @@
fun innerSetUp() {
doReturn(btAdapter).`when`(bluetoothManager).getAdapter()
doReturn(btServerSocket).`when`(btAdapter).listenUsingInsecureL2capChannel()
- doReturn(PSM).`when`(btServerSocket).getPsm();
+ doReturn(PSM).`when`(btServerSocket).getPsm()
doAnswer {
val sock = acceptQueue.take()
- sock ?: throw IOException()
+ if (sock == null || !sock.isPresent()) throw IOException()
+ sock.get()
}.`when`(btServerSocket).accept()
doAnswer {
- acceptQueue.put(null)
+ acceptQueue.put(Optional.empty())
}.`when`(btServerSocket).close()
doReturn(handlerThread).`when`(providerDeps).getHandlerThread()
@@ -101,16 +102,30 @@
@After
fun innerTearDown() {
+ // Unregistering a callback which has previously been unregistered by virtue of receiving
+ // onUnavailable is a noop.
+ registeredCallbacks.forEach { cm.unregisterNetworkCallback(it) }
+ // Wait for CS handler idle, meaning the unregisterNetworkCallback has been processed and
+ // L2capNetworkProvider has been notified.
+ waitForIdle()
+
+ // While quitSafely() effectively waits for idle, it is not enough, because the tear down
+ // path itself posts on the handler thread. This means that waitForIdle() needs to run
+ // twice. The first time, to ensure all active threads have been joined, and the second time
+ // to run all associated clean up actions.
+ handlerThread.waitForIdle(HANDLER_TIMEOUT_MS)
handlerThread.quitSafely()
handlerThread.join()
}
private fun reserveNetwork(nr: NetworkRequest) = TestableNetworkCallback().also {
cm.reserveNetwork(nr, csHandler, it)
+ registeredCallbacks.add(it)
}
private fun requestNetwork(nr: NetworkRequest) = TestableNetworkCallback().also {
cm.requestNetwork(nr, it, csHandler)
+ registeredCallbacks.add(it)
}
private fun NetworkRequest.copyWithSpecifier(specifier: NetworkSpecifier): NetworkRequest {
@@ -188,4 +203,42 @@
nr = REQUEST.copyWithSpecifier(specifier)
reserveNetwork(nr).assertNoCallback()
}
+
+ @Test
+ fun testBluetoothException_listenUsingInsecureL2capChannelThrows() {
+ doThrow(IOException()).`when`(btAdapter).listenUsingInsecureL2capChannel()
+ var specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+ .build()
+ var nr = REQUEST.copyWithSpecifier(specifier)
+ reserveNetwork(nr).expect<Unavailable>()
+
+ doReturn(btServerSocket).`when`(btAdapter).listenUsingInsecureL2capChannel()
+ reserveNetwork(nr).expect<Reserved>()
+ }
+
+ @Test
+ fun testBluetoothException_acceptThrows() {
+ doThrow(IOException()).`when`(btServerSocket).accept()
+ var specifier = L2capNetworkSpecifier.Builder()
+ .setRole(ROLE_SERVER)
+ .setHeaderCompression(HEADER_COMPRESSION_6LOWPAN)
+ .build()
+ var nr = REQUEST.copyWithSpecifier(specifier)
+ val cb = reserveNetwork(nr)
+ cb.expect<Reserved>()
+ cb.expect<Unavailable>()
+
+ // BluetoothServerSocket#close() puts Optional.empty() on the acceptQueue.
+ acceptQueue.clear()
+ doAnswer {
+ val sock = acceptQueue.take()
+ assertFalse(sock.isPresent())
+ throw IOException() // to indicate the socket was closed.
+ }.`when`(btServerSocket).accept()
+ val cb2 = reserveNetwork(nr)
+ cb2.expect<Reserved>()
+ cb2.assertNoCallback()
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
index 83fff87..3583f84 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
@@ -54,9 +54,7 @@
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
import org.mockito.Mockito.timeout
-import org.mockito.Mockito.times
import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
private const val TIMEOUT_MS = 200L
private const val MEDIUM_TIMEOUT_MS = 1_000L
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalNetworkProtectionTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalNetworkProtectionTest.kt
index 73e7515..5bf6e04 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSLocalNetworkProtectionTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalNetworkProtectionTest.kt
@@ -190,6 +190,106 @@
}
@Test
+ fun testStackedLinkPropertiesWithDifferentLinkAddresses_AddressAddedInBpfMap() {
+ val nr = nr(TRANSPORT_WIFI)
+ val cb = TestableNetworkCallback()
+ cm.requestNetwork(nr, cb)
+
+ val wifiLp = lp(WIFI_IFNAME, LOCAL_IPV6_LINK_ADDRESS)
+ val wifiLp2 = lp(WIFI_IFNAME_2, LOCAL_IPV4_LINK_ADDRRESS_1)
+ // Adding stacked link
+ wifiLp.addStackedLink(wifiLp2)
+ val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
+ wifiAgent.connect()
+ cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
+
+ // Multicast and Broadcast address should always be populated in local_net_access map
+ verifyPopulationOfMulticastAndBroadcastAddress()
+ // Verifying IPv6 address should be populated in local_net_access map
+ verify(bpfNetMaps).addLocalNetAccess(
+ eq(PREFIX_LENGTH_IPV6 + LOCAL_IPV6_IP_ADDRESS_PREFIX.getPrefixLength()),
+ eq(WIFI_IFNAME),
+ eq(LOCAL_IPV6_IP_ADDRESS_PREFIX.getAddress()),
+ eq(0),
+ eq(0),
+ eq(false)
+ )
+
+ // Multicast and Broadcast address should always be populated on stacked link
+ // in local_net_access map
+ verifyPopulationOfMulticastAndBroadcastAddress(WIFI_IFNAME_2)
+ // Verifying IPv4 matching prefix(10.0.0.0/8) should be populated as part of stacked link
+ // in local_net_access map
+ verify(bpfNetMaps).addLocalNetAccess(
+ eq(PREFIX_LENGTH_IPV4 + 8),
+ eq(WIFI_IFNAME_2),
+ eq(InetAddresses.parseNumericAddress("10.0.0.0")),
+ eq(0),
+ eq(0),
+ eq(false)
+ )
+ // As both addresses are in stacked links, so no address should be removed from the map.
+ verify(bpfNetMaps, never()).removeLocalNetAccess(any(), any(), any(), any(), any())
+ }
+
+ @Test
+ fun testRemovingStackedLinkProperties_AddressRemovedInBpfMap() {
+ val nr = nr(TRANSPORT_WIFI)
+ val cb = TestableNetworkCallback()
+ cm.requestNetwork(nr, cb)
+
+ val wifiLp = lp(WIFI_IFNAME, LOCAL_IPV6_LINK_ADDRESS)
+ val wifiLp2 = lp(WIFI_IFNAME_2, LOCAL_IPV4_LINK_ADDRRESS_1)
+ // populating stacked link
+ wifiLp.addStackedLink(wifiLp2)
+ val wifiAgent = Agent(nc = wifiNc, lp = wifiLp)
+ wifiAgent.connect()
+ cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
+
+ // Multicast and Broadcast address should always be populated in local_net_access map
+ verifyPopulationOfMulticastAndBroadcastAddress()
+ // Verifying IPv6 address should be populated in local_net_access map
+ verify(bpfNetMaps).addLocalNetAccess(
+ eq(PREFIX_LENGTH_IPV6 + LOCAL_IPV6_IP_ADDRESS_PREFIX.getPrefixLength()),
+ eq(WIFI_IFNAME),
+ eq(LOCAL_IPV6_IP_ADDRESS_PREFIX.getAddress()),
+ eq(0),
+ eq(0),
+ eq(false)
+ )
+
+ // Multicast and Broadcast address should always be populated on stacked link
+ // in local_net_access map
+ verifyPopulationOfMulticastAndBroadcastAddress(WIFI_IFNAME_2)
+ // Verifying IPv4 matching prefix(10.0.0.0/8) should be populated as part of stacked link
+ // in local_net_access map
+ verify(bpfNetMaps).addLocalNetAccess(
+ eq(PREFIX_LENGTH_IPV4 + 8),
+ eq(WIFI_IFNAME_2),
+ eq(InetAddresses.parseNumericAddress("10.0.0.0")),
+ eq(0),
+ eq(0),
+ eq(false)
+ )
+ // As both addresses are in stacked links, so no address should be removed from the map.
+ verify(bpfNetMaps, never()).removeLocalNetAccess(any(), any(), any(), any(), any())
+
+ // replacing link properties without stacked links
+ val wifiLp_3 = lp(WIFI_IFNAME, LOCAL_IPV6_LINK_ADDRESS)
+ wifiAgent.sendLinkProperties(wifiLp_3)
+ cb.expect<LinkPropertiesChanged>(wifiAgent.network)
+
+ // As both stacked links is removed, 10.0.0.0/8 should be removed from local_net_access map.
+ verify(bpfNetMaps).removeLocalNetAccess(
+ eq(PREFIX_LENGTH_IPV4 + 8),
+ eq(WIFI_IFNAME_2),
+ eq(InetAddresses.parseNumericAddress("10.0.0.0")),
+ eq(0),
+ eq(0)
+ )
+ }
+
+ @Test
fun testChangeLinkPropertiesWithLinkAddressesInSameRange_AddressIntactInBpfMap() {
val nr = nr(TRANSPORT_WIFI)
val cb = TestableNetworkCallback()
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
index 350a140..557bfd6 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTest.kt
@@ -72,6 +72,7 @@
import com.android.server.connectivity.MultinetworkPolicyTracker
import com.android.server.connectivity.MultinetworkPolicyTrackerTestDependencies
import com.android.server.connectivity.NetworkRequestStateStatsMetrics
+import com.android.server.connectivity.PermissionMonitor
import com.android.server.connectivity.ProxyTracker
import com.android.server.connectivity.SatelliteAccessController
import com.android.testutils.visibleOnHandlerThread
@@ -217,6 +218,7 @@
val destroySocketsWrapper = mock<DestroySocketsWrapper>()
val deps = CSDeps()
+ val permDeps = PermDeps()
// Initializations that start threads are done from setUp to avoid thread leak
lateinit var alarmHandlerThread: HandlerThread
@@ -253,7 +255,9 @@
alarmHandlerThread = HandlerThread("TestAlarmManager").also { it.start() }
alarmManager = makeMockAlarmManager(alarmHandlerThread)
- service = makeConnectivityService(context, netd, deps).also { it.systemReadyInternal() }
+ service = makeConnectivityService(context, netd, deps, permDeps).also {
+ it.systemReadyInternal()
+ }
cm = ConnectivityManager(context, service)
// csHandler initialization must be after makeConnectivityService since ConnectivityService
// constructor starts csHandlerThread
@@ -399,6 +403,10 @@
override fun makeL2capNetworkProvider(context: Context) = null
}
+ inner class PermDeps : PermissionMonitor.Dependencies() {
+ override fun shouldEnforceLocalNetRestrictions(uid: Int) = false
+ }
+
inner class CSContext(base: Context) : BroadcastInterceptingContext(base) {
val pacProxyManager = mock<PacProxyManager>()
val networkPolicyManager = mock<NetworkPolicyManager>()
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
index 6b560de..a53d430 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
@@ -54,6 +54,7 @@
import com.android.modules.utils.build.SdkLevel
import com.android.server.ConnectivityService.Dependencies
import com.android.server.connectivity.ConnectivityResources
+import com.android.server.connectivity.PermissionMonitor
import kotlin.test.fail
import org.mockito.ArgumentMatchers
import org.mockito.ArgumentMatchers.any
@@ -192,13 +193,14 @@
private val TEST_LINGER_DELAY_MS = 400
private val TEST_NASCENT_DELAY_MS = 300
-internal fun makeConnectivityService(context: Context, netd: INetd, deps: Dependencies) =
+internal fun makeConnectivityService(context: Context, netd: INetd, deps: Dependencies,
+ mPermDeps: PermissionMonitor.Dependencies) =
ConnectivityService(
context,
mock<IDnsResolver>(),
mock<IpConnectivityLog>(),
netd,
- deps).also {
+ deps, mPermDeps).also {
it.mLingerDelayMs = TEST_LINGER_DELAY_MS
it.mNascentDelayMs = TEST_NASCENT_DELAY_MS
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
index 581c709..697bf9e 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -163,11 +163,13 @@
import com.android.net.module.util.BpfDump;
import com.android.net.module.util.IBpfMap;
import com.android.net.module.util.LocationPermissionChecker;
+import com.android.net.module.util.SkDestroyListener;
import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.S32;
import com.android.net.module.util.Struct.U8;
import com.android.net.module.util.bpf.CookieTagMapKey;
import com.android.net.module.util.bpf.CookieTagMapValue;
+import com.android.net.module.util.netlink.InetDiagMessage;
import com.android.server.connectivity.ConnectivityResources;
import com.android.server.net.NetworkStatsService.AlertObserver;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
@@ -210,6 +212,7 @@
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.Consumer;
/**
* Tests for {@link NetworkStatsService}.
@@ -605,8 +608,8 @@
}
@Override
- public SkDestroyListener makeSkDestroyListener(
- IBpfMap<CookieTagMapKey, CookieTagMapValue> cookieTagMap, Handler handler) {
+ public SkDestroyListener makeSkDestroyListener(Consumer<InetDiagMessage> consumer,
+ Handler handler) {
return mSkDestroyListener;
}