Merge "ethernet: fix receiving NetworkOffer callbacks on stale object"
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index bfba5cd..26eef96 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -31,7 +31,6 @@
"apishim/**/*.java",
"src/**/*.java",
":framework-connectivity-shared-srcs",
- ":tethering-module-utils-srcs",
":services-tethering-shared-srcs",
":statslog-tethering-java-gen",
],
@@ -47,6 +46,7 @@
"net-utils-framework-common",
"net-utils-device-common",
"net-utils-device-common-bpf",
+ "net-utils-device-common-ip",
"net-utils-device-common-netlink",
"netd-client",
"tetheringstatsprotos",
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index b3cae7c..ee10639 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -18,6 +18,20 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+prebuilt_etc {
+ name: "TetheringInProcessFlag",
+ src: "in-process",
+ filename_from_src: true,
+ sub_dir: "flag",
+}
+
+prebuilt_etc {
+ name: "TetheringOutOfProcessFlag",
+ src: "out-of-process",
+ filename_from_src: true,
+ sub_dir: "flag",
+}
+
// Defaults to enable/disable java targets which uses development APIs. "enabled" may have a
// different value depending on the branch.
java_defaults {
@@ -74,7 +88,9 @@
"dscp_policy.o",
"netd.o",
"offload.o",
+ "offload@btf.o",
"test.o",
+ "test@btf.o",
],
apps: [
"ServiceConnectivityResources",
@@ -82,6 +98,7 @@
prebuilts: [
"current_sdkinfo",
"privapp_allowlist_com.android.tethering",
+ "TetheringOutOfProcessFlag",
],
manifest: "manifest.json",
key: "com.android.tethering.key",
@@ -187,4 +204,9 @@
"ServiceConnectivityResources",
"InProcessTethering",
],
+ prebuilts: [
+ "current_sdkinfo",
+ "privapp_allowlist_com.android.tethering",
+ "TetheringInProcessFlag",
+ ],
}
diff --git a/Tethering/apex/in-process b/Tethering/apex/in-process
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tethering/apex/in-process
diff --git a/Tethering/apex/out-of-process b/Tethering/apex/out-of-process
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/Tethering/apex/out-of-process
diff --git a/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
index 836761f..b4e3ba4 100644
--- a/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
@@ -36,5 +36,4 @@
void onTetherStatesChanged(in TetherStatesParcel states);
void onTetherClientsChanged(in List<TetheredClient> clients);
void onOffloadStatusChanged(int status);
- void onSupportedTetheringTypes(long supportedBitmap);
}
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl b/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
index f33f846..253eacb 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
@@ -26,7 +26,7 @@
* @hide
*/
parcelable TetheringCallbackStartedParcel {
- long supportedTypes;
+ boolean tetheringSupported;
Network upstreamNetwork;
TetheringConfigurationParcel config;
TetherStatesParcel states;
diff --git a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index b3f0cf2..6f9b33e 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -183,12 +183,6 @@
*/
public static final int TETHERING_WIGIG = 6;
- /**
- * The int value of last tethering type.
- * @hide
- */
- public static final int MAX_TETHERING_TYPE = TETHERING_WIGIG;
-
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(value = {
@@ -526,9 +520,6 @@
}
@Override
- public void onSupportedTetheringTypes(long supportedBitmap) { }
-
- @Override
public void onUpstreamChanged(Network network) { }
@Override
@@ -1042,29 +1033,15 @@
/**
* Called when tethering supported status changed.
*
- * <p>This callback will be called immediately after the callback is
- * registered, and never be called if there is changes afterward.
- *
- * <p>Tethering may be disabled via system properties, device configuration, or device
- * policy restrictions.
- *
- * @param supported whether any tethering type is supported.
- */
- default void onTetheringSupported(boolean supported) {}
-
- /**
- * Called when tethering supported status changed.
- *
* <p>This will be called immediately after the callback is registered, and may be called
* multiple times later upon changes.
*
* <p>Tethering may be disabled via system properties, device configuration, or device
* policy restrictions.
*
- * @param supportedTypes a set of @TetheringType which is supported.
- * @hide
+ * @param supported The new supported status
*/
- default void onSupportedTetheringTypes(@NonNull Set<Integer> supportedTypes) {}
+ default void onTetheringSupported(boolean supported) {}
/**
* Called when tethering upstream changed.
@@ -1362,8 +1339,7 @@
@Override
public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
executor.execute(() -> {
- callback.onSupportedTetheringTypes(unpackBits(parcel.supportedTypes));
- callback.onTetheringSupported(parcel.supportedTypes != 0);
+ callback.onTetheringSupported(parcel.tetheringSupported);
callback.onUpstreamChanged(parcel.upstreamNetwork);
sendErrorCallbacks(parcel.states);
sendRegexpsChanged(parcel.config);
@@ -1382,13 +1358,6 @@
});
}
- @Override
- public void onSupportedTetheringTypes(long supportedBitmap) {
- executor.execute(() -> {
- callback.onSupportedTetheringTypes(unpackBits(supportedBitmap));
- });
- }
-
private void sendRegexpsChanged(TetheringConfigurationParcel parcel) {
callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps(
parcel.tetherableBluetoothRegexs,
@@ -1427,23 +1396,6 @@
}
/**
- * Unpack bitmap to a set of bit position intergers.
- * @hide
- */
- public static ArraySet<Integer> unpackBits(long val) {
- final ArraySet<Integer> result = new ArraySet<>(Long.bitCount(val));
- int bitPos = 0;
- while (val != 0) {
- if ((val & 1) == 1) result.add(bitPos);
-
- val = val >>> 1;
- bitPos++;
- }
-
- return result;
- }
-
- /**
* Remove tethering event callback previously registered with
* {@link #registerTetheringEventCallback}.
*
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index da7ca56..438b592 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -44,7 +44,6 @@
import android.net.dhcp.DhcpServingParamsParcelExt;
import android.net.dhcp.IDhcpEventCallbacks;
import android.net.dhcp.IDhcpServer;
-import android.net.ip.IpNeighborMonitor.NeighborEvent;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.os.Handler;
import android.os.Looper;
@@ -64,6 +63,9 @@
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.ip.InterfaceController;
+import com.android.net.module.util.ip.IpNeighborMonitor;
+import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEvent;
import com.android.networkstack.tethering.BpfCoordinator;
import com.android.networkstack.tethering.BpfCoordinator.ClientInfo;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index 133ae01..49442a6 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -23,11 +23,11 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStats.UID_TETHERING;
-import static android.net.ip.ConntrackMonitor.ConntrackEvent;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6;
+import static com.android.net.module.util.ip.ConntrackMonitor.ConntrackEvent;
import static com.android.networkstack.tethering.BpfUtils.DOWNSTREAM;
import static com.android.networkstack.tethering.BpfUtils.UPSTREAM;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
@@ -40,8 +40,6 @@
import android.net.NetworkStats;
import android.net.NetworkStats.Entry;
import android.net.TetherOffloadRuleParcel;
-import android.net.ip.ConntrackMonitor;
-import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
import android.net.ip.IpServer;
import android.net.netstats.provider.NetworkStatsProvider;
import android.os.Handler;
@@ -71,6 +69,8 @@
import com.android.net.module.util.bpf.Tether4Value;
import com.android.net.module.util.bpf.TetherStatsKey;
import com.android.net.module.util.bpf.TetherStatsValue;
+import com.android.net.module.util.ip.ConntrackMonitor;
+import com.android.net.module.util.ip.ConntrackMonitor.ConntrackEventConsumer;
import com.android.net.module.util.netlink.ConntrackMessage;
import com.android.net.module.util.netlink.NetlinkConstants;
import com.android.net.module.util.netlink.NetlinkSocket;
diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 741af5c..784ebd5 100644
--- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -296,7 +296,7 @@
* 4th priority : Checks whether provisioning is required from RRO configuration.
*
* @param config
- * @return integer {@see #TETHERING_PROVISIONING_NOT_REQUIRED,
+ * @return integer See {@link #TETHERING_PROVISIONING_NOT_REQUIRED,
* #TETHERING_PROVISIONING_REQUIRED,
* #TETHERING_PROVISIONING_CARRIER_UNSUPPORT}
*/
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 47b1bd7..89ed620 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -98,7 +98,6 @@
import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringRequestParcel;
import android.net.ip.IpServer;
-import android.net.shared.NetdUtils;
import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.WifiP2pGroup;
@@ -135,6 +134,7 @@
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.CollectionUtils;
+import com.android.net.module.util.NetdUtils;
import com.android.net.module.util.SharedLog;
import com.android.networkstack.apishim.common.BluetoothPanShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
@@ -281,11 +281,6 @@
private BluetoothPan mBluetoothPan;
private PanServiceListener mBluetoothPanListener;
private ArrayList<Pair<Boolean, IIntResultListener>> mPendingPanRequests;
- // AIDL doesn't support Set<Integer>. Maintain a int bitmap here. When the bitmap is passed to
- // TetheringManager, TetheringManager would convert it to a set of Integer types.
- // mSupportedTypeBitmap should always be updated inside tethering internal thread but it may be
- // read from binder thread which called TetheringService directly.
- private volatile long mSupportedTypeBitmap;
public Tethering(TetheringDependencies deps) {
mLog.mark("Tethering.constructed");
@@ -517,8 +512,6 @@
mUpstreamNetworkMonitor.setUpstreamConfig(mConfig.chooseUpstreamAutomatically,
mConfig.isDunRequired);
reportConfigurationChanged(mConfig.toStableParcelable());
-
- updateSupportedDownstreams(mConfig);
}
private void maybeDunSettingChanged() {
@@ -1557,6 +1550,26 @@
return mConfig;
}
+ boolean hasAnySupportedDownstream() {
+ if ((mConfig.tetherableUsbRegexs.length != 0)
+ || (mConfig.tetherableWifiRegexs.length != 0)
+ || (mConfig.tetherableBluetoothRegexs.length != 0)) {
+ return true;
+ }
+
+ // Before T, isTetheringSupported would return true if wifi, usb and bluetooth tethering are
+ // disabled (whole tethering settings would be hidden). This means tethering would also not
+ // support wifi p2p, ethernet tethering and mirrorlink. This is wrong but probably there are
+ // some devices in the field rely on this to disable tethering entirely.
+ if (!SdkLevel.isAtLeastT()) return false;
+
+ return (mConfig.tetherableWifiP2pRegexs.length != 0)
+ || (mConfig.tetherableNcmRegexs.length != 0)
+ || isEthernetSupported();
+ }
+
+ // TODO: using EtherentManager new API to check whether ethernet is supported when the API is
+ // ready to use.
private boolean isEthernetSupported() {
return mContext.getSystemService(Context.ETHERNET_SERVICE) != null;
}
@@ -2346,7 +2359,7 @@
mHandler.post(() -> {
mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
- parcel.supportedTypes = mSupportedTypeBitmap;
+ parcel.tetheringSupported = isTetheringSupported();
parcel.upstreamNetwork = mTetherUpstream;
parcel.config = mConfig.toStableParcelable();
parcel.states =
@@ -2385,22 +2398,6 @@
});
}
- private void reportTetheringSupportedChange(final long supportedBitmap) {
- final int length = mTetheringEventCallbacks.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mTetheringEventCallbacks.getBroadcastItem(i).onSupportedTetheringTypes(
- supportedBitmap);
- } catch (RemoteException e) {
- // Not really very much to do here.
- }
- }
- } finally {
- mTetheringEventCallbacks.finishBroadcast();
- }
- }
-
private void reportUpstreamChanged(UpstreamNetworkState ns) {
final int length = mTetheringEventCallbacks.beginBroadcast();
final Network network = (ns != null) ? ns.network : null;
@@ -2485,56 +2482,18 @@
}
}
- private void updateSupportedDownstreams(final TetheringConfiguration config) {
- final long preSupportedBitmap = mSupportedTypeBitmap;
-
- if (!isTetheringAllowed() || mEntitlementMgr.isProvisioningNeededButUnavailable()) {
- mSupportedTypeBitmap = 0;
- } else {
- mSupportedTypeBitmap = makeSupportedDownstreams(config);
- }
-
- if (preSupportedBitmap != mSupportedTypeBitmap) {
- reportTetheringSupportedChange(mSupportedTypeBitmap);
- }
- }
-
- private long makeSupportedDownstreams(final TetheringConfiguration config) {
- long types = 0;
- if (config.tetherableUsbRegexs.length != 0) types |= (1 << TETHERING_USB);
-
- if (config.tetherableWifiRegexs.length != 0) types |= (1 << TETHERING_WIFI);
-
- if (config.tetherableBluetoothRegexs.length != 0) types |= (1 << TETHERING_BLUETOOTH);
-
- // Before T, isTetheringSupported would return true if wifi, usb and bluetooth tethering are
- // disabled (whole tethering settings would be hidden). This means tethering would also not
- // support wifi p2p, ethernet tethering and mirrorlink. This is wrong but probably there are
- // some devices in the field rely on this to disable tethering entirely.
- if (!SdkLevel.isAtLeastT() && types == 0) return types;
-
- if (config.tetherableNcmRegexs.length != 0) types |= (1 << TETHERING_NCM);
-
- if (config.tetherableWifiP2pRegexs.length != 0) types |= (1 << TETHERING_WIFI_P2P);
-
- if (isEthernetSupported()) types |= (1 << TETHERING_ETHERNET);
-
- return types;
- }
-
// if ro.tether.denied = true we default to no tethering
// gservices could set the secure setting to 1 though to enable it on a build where it
// had previously been turned off.
- private boolean isTetheringAllowed() {
+ boolean isTetheringSupported() {
final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1;
final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
- return tetherSupported
+ final boolean tetherEnabledInSettings = tetherSupported
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
- }
- boolean isTetheringSupported() {
- return mSupportedTypeBitmap > 0;
+ return tetherEnabledInSettings && hasAnySupportedDownstream()
+ && !mEntitlementMgr.isProvisioningNeededButUnavailable();
}
private void dumpBpf(IndentingPrintWriter pw) {
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index ef4f052..86dca1c 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -192,13 +192,12 @@
mUiAutomation.adoptShellPermissionIdentity(
MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE,
CONNECTIVITY_USE_RESTRICTED_NETWORKS, DUMP);
+ mRunTests = mTm.isTetheringSupported() && mEm != null;
+ assumeTrue(mRunTests);
+
mHandlerThread = new HandlerThread(getClass().getSimpleName());
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
-
- mRunTests = isEthernetTetheringSupported();
- assumeTrue(mRunTests);
-
mTetheredInterfaceRequester = new TetheredInterfaceRequester(mHandler, mEm);
}
@@ -226,6 +225,7 @@
mHandler.post(() -> reader.stop());
mDownstreamReader = null;
}
+ mHandlerThread.quitSafely();
mTetheredInterfaceRequester.release();
mEm.setIncludeTestInterfaces(false);
maybeDeleteTestInterface();
@@ -236,7 +236,6 @@
try {
if (mRunTests) cleanUp();
} finally {
- mHandlerThread.quitSafely();
mUiAutomation.dropShellPermissionIdentity();
}
}
@@ -411,23 +410,6 @@
// client, which is not possible in this test.
}
- private boolean isEthernetTetheringSupported() throws Exception {
- final CompletableFuture<Boolean> future = new CompletableFuture<>();
- final TetheringEventCallback callback = new TetheringEventCallback() {
- @Override
- public void onSupportedTetheringTypes(Set<Integer> supportedTypes) {
- future.complete(supportedTypes.contains(TETHERING_ETHERNET));
- }
- };
-
- try {
- mTm.registerTetheringEventCallback(mHandler::post, callback);
- return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
- } finally {
- mTm.unregisterTetheringEventCallback(callback);
- }
- }
-
private static final class MyTetheringEventCallback implements TetheringEventCallback {
private final TetheringManager mTm;
private final CountDownLatch mTetheringStartedLatch = new CountDownLatch(1);
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index ef143de..f242227 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -83,8 +83,6 @@
import android.net.dhcp.IDhcpEventCallbacks;
import android.net.dhcp.IDhcpServer;
import android.net.dhcp.IDhcpServerCallbacks;
-import android.net.ip.IpNeighborMonitor.NeighborEvent;
-import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.os.Build;
import android.os.Handler;
@@ -105,6 +103,10 @@
import com.android.net.module.util.bpf.Tether4Value;
import com.android.net.module.util.bpf.TetherStatsKey;
import com.android.net.module.util.bpf.TetherStatsValue;
+import com.android.net.module.util.ip.ConntrackMonitor;
+import com.android.net.module.util.ip.IpNeighborMonitor;
+import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEvent;
+import com.android.net.module.util.ip.IpNeighborMonitor.NeighborEventConsumer;
import com.android.networkstack.tethering.BpfCoordinator;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
index 20a222d..fa1d881 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -23,7 +23,6 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStats.UID_TETHERING;
-import static android.net.ip.ConntrackMonitor.ConntrackEvent;
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6;
@@ -33,6 +32,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker;
+import static com.android.net.module.util.ip.ConntrackMonitor.ConntrackEvent;
import static com.android.net.module.util.netlink.ConntrackMessage.DYING_MASK;
import static com.android.net.module.util.netlink.ConntrackMessage.ESTABLISHED_MASK;
import static com.android.net.module.util.netlink.ConntrackMessage.Tuple;
@@ -82,8 +82,6 @@
import android.net.NetworkStats;
import android.net.TetherOffloadRuleParcel;
import android.net.TetherStatsParcel;
-import android.net.ip.ConntrackMonitor;
-import android.net.ip.ConntrackMonitor.ConntrackEventConsumer;
import android.net.ip.IpServer;
import android.os.Build;
import android.os.Handler;
@@ -104,6 +102,8 @@
import com.android.net.module.util.bpf.Tether4Value;
import com.android.net.module.util.bpf.TetherStatsKey;
import com.android.net.module.util.bpf.TetherStatsValue;
+import com.android.net.module.util.ip.ConntrackMonitor;
+import com.android.net.module.util.ip.ConntrackMonitor.ConntrackEventConsumer;
import com.android.net.module.util.netlink.ConntrackMessage;
import com.android.net.module.util.netlink.NetlinkConstants;
import com.android.net.module.util.netlink.NetlinkSocket;
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index cc80174..b402bc3 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -144,7 +144,6 @@
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
import android.net.TetheringInterface;
-import android.net.TetheringManager;
import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpLeaseParcelable;
import android.net.dhcp.DhcpServerCallbacks;
@@ -152,7 +151,6 @@
import android.net.dhcp.IDhcpEventCallbacks;
import android.net.dhcp.IDhcpServer;
import android.net.ip.DadProxy;
-import android.net.ip.IpNeighborMonitor;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.NetworkConstants;
@@ -177,7 +175,6 @@
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
-import android.util.ArraySet;
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
@@ -189,6 +186,7 @@
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.SharedLog;
+import com.android.net.module.util.ip.IpNeighborMonitor;
import com.android.networkstack.apishim.common.BluetoothPanShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
@@ -219,7 +217,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
-import java.util.Set;
import java.util.Vector;
@RunWith(AndroidJUnit4.class)
@@ -1732,7 +1729,6 @@
private final ArrayList<TetherStatesParcel> mTetherStates = new ArrayList<>();
private final ArrayList<Integer> mOffloadStatus = new ArrayList<>();
private final ArrayList<List<TetheredClient>> mTetheredClients = new ArrayList<>();
- private final ArrayList<Long> mSupportedBitmaps = new ArrayList<>();
// This function will remove the recorded callbacks, so it must be called once for
// each callback. If this is called after multiple callback, the order matters.
@@ -1785,10 +1781,6 @@
assertTrue(leases.containsAll(result));
}
- public void expectSupportedTetheringTypes(Set<Integer> expectedTypes) {
- assertEquals(expectedTypes, TetheringManager.unpackBits(mSupportedBitmaps.remove(0)));
- }
-
@Override
public void onUpstreamChanged(Network network) {
mActualUpstreams.add(network);
@@ -1821,17 +1813,11 @@
mTetherStates.add(parcel.states);
mOffloadStatus.add(parcel.offloadStatus);
mTetheredClients.add(parcel.tetheredClients);
- mSupportedBitmaps.add(parcel.supportedTypes);
}
@Override
public void onCallbackStopped(int errorCode) { }
- @Override
- public void onSupportedTetheringTypes(long supportedBitmap) {
- mSupportedBitmaps.add(supportedBitmap);
- }
-
public void assertNoUpstreamChangeCallback() {
assertTrue(mActualUpstreams.isEmpty());
}
@@ -2959,81 +2945,53 @@
runStopUSBTethering();
}
- public static ArraySet<Integer> getAllSupportedTetheringTypes() {
- return new ArraySet<>(new Integer[] { TETHERING_USB, TETHERING_NCM, TETHERING_WIFI,
- TETHERING_WIFI_P2P, TETHERING_BLUETOOTH, TETHERING_ETHERNET });
- }
-
@Test
public void testTetheringSupported() throws Exception {
- final ArraySet<Integer> expectedTypes = getAllSupportedTetheringTypes();
- // Check tethering is supported after initialization.
setTetheringSupported(true /* supported */);
- TestTetheringEventCallback callback = new TestTetheringEventCallback();
- mTethering.registerTetheringEventCallback(callback);
- mLooper.dispatchAll();
- updateConfigAndVerifySupported(callback, expectedTypes);
+ updateConfigAndVerifySupported(true /* supported */);
// Could disable tethering supported by settings.
Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, 0);
- updateConfigAndVerifySupported(callback, new ArraySet<>());
+ updateConfigAndVerifySupported(false /* supported */);
// Could disable tethering supported by user restriction.
setTetheringSupported(true /* supported */);
- updateConfigAndVerifySupported(callback, expectedTypes);
when(mUserManager.hasUserRestriction(
UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(true);
- updateConfigAndVerifySupported(callback, new ArraySet<>());
+ updateConfigAndVerifySupported(false /* supported */);
// Tethering is supported if it has any supported downstream.
setTetheringSupported(true /* supported */);
- updateConfigAndVerifySupported(callback, expectedTypes);
- // Usb tethering is not supported:
- expectedTypes.remove(TETHERING_USB);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[0]);
- updateConfigAndVerifySupported(callback, expectedTypes);
- // Wifi tethering is not supported:
- expectedTypes.remove(TETHERING_WIFI);
+ updateConfigAndVerifySupported(true /* supported */);
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[0]);
- updateConfigAndVerifySupported(callback, expectedTypes);
- // Bluetooth tethering is not supported:
- expectedTypes.remove(TETHERING_BLUETOOTH);
- when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
+ updateConfigAndVerifySupported(true /* supported */);
+
if (isAtLeastT()) {
- updateConfigAndVerifySupported(callback, expectedTypes);
-
- // P2p tethering is not supported:
- expectedTypes.remove(TETHERING_WIFI_P2P);
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+ .thenReturn(new String[0]);
+ updateConfigAndVerifySupported(true /* supported */);
when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
.thenReturn(new String[0]);
- updateConfigAndVerifySupported(callback, expectedTypes);
- // Ncm tethering is not supported:
- expectedTypes.remove(TETHERING_NCM);
+ updateConfigAndVerifySupported(true /* supported */);
when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
.thenReturn(new String[0]);
- updateConfigAndVerifySupported(callback, expectedTypes);
- // Ethernet tethering (last supported type) is not supported:
- expectedTypes.remove(TETHERING_ETHERNET);
+ updateConfigAndVerifySupported(true /* supported */);
mForceEthernetServiceUnavailable = true;
- updateConfigAndVerifySupported(callback, new ArraySet<>());
-
+ updateConfigAndVerifySupported(false /* supported */);
} else {
- // If wifi, usb and bluetooth are all not supported, all the types are not supported.
- expectedTypes.clear();
- updateConfigAndVerifySupported(callback, expectedTypes);
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
+ .thenReturn(new String[0]);
+ updateConfigAndVerifySupported(false /* supported */);
}
}
- private void updateConfigAndVerifySupported(final TestTetheringEventCallback callback,
- final ArraySet<Integer> expectedTypes) {
+ private void updateConfigAndVerifySupported(boolean supported) {
sendConfigurationChanged();
-
- assertEquals(expectedTypes.size() > 0, mTethering.isTetheringSupported());
- callback.expectSupportedTetheringTypes(expectedTypes);
+ assertEquals(supported, mTethering.isTetheringSupported());
}
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
diff --git a/bpf_progs/Android.bp b/bpf_progs/Android.bp
index 78fca29..d9eb547 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -92,6 +92,17 @@
}
bpf {
+ name: "offload@btf.o",
+ srcs: ["offload@btf.c"],
+ btf: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-DBTF",
+ ],
+}
+
+bpf {
name: "test.o",
srcs: ["test.c"],
cflags: [
@@ -101,6 +112,17 @@
}
bpf {
+ name: "test@btf.o",
+ srcs: ["test@btf.c"],
+ btf: true,
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-DBTF",
+ ],
+}
+
+bpf {
name: "clatd.o",
srcs: ["clatd.c"],
btf: true,
diff --git a/bpf_progs/dscp_policy.c b/bpf_progs/dscp_policy.c
index 38e1050..92ea0e2 100644
--- a/bpf_progs/dscp_policy.c
+++ b/bpf_progs/dscp_policy.c
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-#include <linux/types.h>
#include <linux/bpf.h>
+#include <linux/if_ether.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
-#include <linux/if_ether.h>
#include <linux/pkt_cls.h>
#include <linux/tcp.h>
-#include <stdint.h>
+#include <linux/types.h>
#include <netinet/in.h>
#include <netinet/udp.h>
+#include <stdint.h>
#include <string.h>
// The resulting .o needs to load on the Android T beta 3 bpfloader
@@ -33,21 +33,25 @@
#include "bpf_helpers.h"
#include "dscp_policy.h"
+#define ECN_MASK 3
+#define IP4_OFFSET(field, header) (header + offsetof(struct iphdr, field))
+#define UPDATE_TOS(dscp, tos) (dscp << 2) | (tos & ECN_MASK)
+#define UPDATE_PRIORITY(dscp) ((dscp >> 2) + 0x60)
+#define UPDATE_FLOW_LABEL(dscp, flow_lbl) ((dscp & 0xf) << 6) + (flow_lbl >> 6)
+
DEFINE_BPF_MAP_GRW(switch_comp_map, ARRAY, int, uint64_t, 1, AID_SYSTEM)
DEFINE_BPF_MAP_GRW(ipv4_socket_to_policies_map_A, HASH, uint64_t, RuleEntry, MAX_POLICIES,
- AID_SYSTEM)
+ AID_SYSTEM)
DEFINE_BPF_MAP_GRW(ipv4_socket_to_policies_map_B, HASH, uint64_t, RuleEntry, MAX_POLICIES,
- AID_SYSTEM)
+ AID_SYSTEM)
DEFINE_BPF_MAP_GRW(ipv6_socket_to_policies_map_A, HASH, uint64_t, RuleEntry, MAX_POLICIES,
- AID_SYSTEM)
+ AID_SYSTEM)
DEFINE_BPF_MAP_GRW(ipv6_socket_to_policies_map_B, HASH, uint64_t, RuleEntry, MAX_POLICIES,
- AID_SYSTEM)
+ AID_SYSTEM)
-DEFINE_BPF_MAP_GRW(ipv4_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES,
- AID_SYSTEM)
-DEFINE_BPF_MAP_GRW(ipv6_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES,
- AID_SYSTEM)
+DEFINE_BPF_MAP_GRW(ipv4_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES, AID_SYSTEM)
+DEFINE_BPF_MAP_GRW(ipv6_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES, AID_SYSTEM)
static inline __always_inline void match_policy(struct __sk_buff* skb, bool ipv4, bool is_eth) {
void* data = (void*)(long)skb->data;
@@ -69,21 +73,21 @@
// used for map lookup
uint64_t cookie = bpf_get_socket_cookie(skb);
- if (!cookie)
- return;
+ if (!cookie) return;
uint16_t sport = 0;
uint16_t dport = 0;
- uint8_t protocol = 0; // TODO: Use are reserved value? Or int (-1) and cast to uint below?
+ uint8_t protocol = 0; // TODO: Use are reserved value? Or int (-1) and cast to uint below?
struct in6_addr srcIp = {};
struct in6_addr dstIp = {};
- uint8_t tos = 0; // Only used for IPv4
- uint8_t priority = 0; // Only used for IPv6
- uint8_t flow_lbl = 0; // Only used for IPv6
+ uint8_t tos = 0; // Only used for IPv4
+ uint8_t priority = 0; // Only used for IPv6
+ uint8_t flow_lbl = 0; // Only used for IPv6
if (ipv4) {
const struct iphdr* const iph = is_eth ? (void*)(eth + 1) : data;
+ hdr_size = l2_header_size + sizeof(struct iphdr);
// Must have ipv4 header
- if (data + l2_header_size + sizeof(*iph) > data_end) return;
+ if (data + hdr_size > data_end) return;
// IP version must be 4
if (iph->version != 4) return;
@@ -100,11 +104,11 @@
dstIp.s6_addr32[3] = iph->daddr;
protocol = iph->protocol;
tos = iph->tos;
- hdr_size = sizeof(struct iphdr);
} else {
struct ipv6hdr* ip6h = is_eth ? (void*)(eth + 1) : data;
+ hdr_size = l2_header_size + sizeof(struct ipv6hdr);
// Must have ipv6 header
- if (data + l2_header_size + sizeof(*ip6h) > data_end) return;
+ if (data + hdr_size > data_end) return;
if (ip6h->version != 6) return;
@@ -113,29 +117,24 @@
protocol = ip6h->nexthdr;
priority = ip6h->priority;
flow_lbl = ip6h->flow_lbl[0];
- hdr_size = sizeof(struct ipv6hdr);
}
switch (protocol) {
case IPPROTO_UDP:
- case IPPROTO_UDPLITE:
- {
- struct udphdr *udp;
+ case IPPROTO_UDPLITE: {
+ struct udphdr* udp;
udp = data + hdr_size;
if ((void*)(udp + 1) > data_end) return;
sport = udp->source;
dport = udp->dest;
- }
- break;
- case IPPROTO_TCP:
- {
- struct tcphdr *tcp;
+ } break;
+ case IPPROTO_TCP: {
+ struct tcphdr* tcp;
tcp = data + hdr_size;
if ((void*)(tcp + 1) > data_end) return;
sport = tcp->source;
dport = tcp->dest;
- }
- break;
+ } break;
default:
return;
}
@@ -156,22 +155,19 @@
}
if (existingRule && v6_equal(srcIp, existingRule->srcIp) &&
- v6_equal(dstIp, existingRule->dstIp) &&
- skb->ifindex == existingRule->ifindex &&
- ntohs(sport) == htons(existingRule->srcPort) &&
- ntohs(dport) == htons(existingRule->dstPort) &&
- protocol == existingRule->proto) {
+ v6_equal(dstIp, existingRule->dstIp) && skb->ifindex == existingRule->ifindex &&
+ ntohs(sport) == htons(existingRule->srcPort) &&
+ ntohs(dport) == htons(existingRule->dstPort) && protocol == existingRule->proto) {
if (ipv4) {
- int ecn = tos & 3;
- uint8_t newDscpVal = (existingRule->dscpVal << 2) + ecn;
- int oldDscpVal = tos >> 2;
- bpf_l3_csum_replace(skb, 1, oldDscpVal, newDscpVal, sizeof(uint8_t));
- bpf_skb_store_bytes(skb, 1, &newDscpVal, sizeof(uint8_t), 0);
+ uint8_t newTos = UPDATE_TOS(existingRule->dscpVal, tos);
+ bpf_l3_csum_replace(skb, IP4_OFFSET(check, l2_header_size), htons(tos), htons(newTos),
+ sizeof(uint16_t));
+ bpf_skb_store_bytes(skb, IP4_OFFSET(tos, l2_header_size), &newTos, sizeof(newTos), 0);
} else {
- uint8_t new_priority = (existingRule->dscpVal >> 2) + 0x60;
- uint8_t new_flow_label = ((existingRule->dscpVal & 0xf) << 6) + (priority >> 6);
- bpf_skb_store_bytes(skb, 0, &new_priority, sizeof(uint8_t), 0);
- bpf_skb_store_bytes(skb, 1, &new_flow_label, sizeof(uint8_t), 0);
+ uint8_t new_priority = UPDATE_PRIORITY(existingRule->dscpVal);
+ uint8_t new_flow_label = UPDATE_FLOW_LABEL(existingRule->dscpVal, flow_lbl);
+ bpf_skb_store_bytes(skb, 0 + l2_header_size, &new_priority, sizeof(uint8_t), 0);
+ bpf_skb_store_bytes(skb, 1 + l2_header_size, &new_flow_label, sizeof(uint8_t), 0);
}
return;
}
@@ -196,32 +192,31 @@
// If the policy lookup failed, presentFields is 0, or iface index does not match
// index on skb buff, then we can continue to next policy.
- if (!policy || policy->presentFields == 0 || policy->ifindex != skb->ifindex)
- continue;
+ if (!policy || policy->presentFields == 0 || policy->ifindex != skb->ifindex) continue;
if ((policy->presentFields & SRC_IP_MASK_FLAG) == SRC_IP_MASK_FLAG &&
- v6_equal(srcIp, policy->srcIp)) {
+ v6_equal(srcIp, policy->srcIp)) {
score++;
tempMask |= SRC_IP_MASK_FLAG;
}
if ((policy->presentFields & DST_IP_MASK_FLAG) == DST_IP_MASK_FLAG &&
- v6_equal(dstIp, policy->dstIp)) {
+ v6_equal(dstIp, policy->dstIp)) {
score++;
tempMask |= DST_IP_MASK_FLAG;
}
if ((policy->presentFields & SRC_PORT_MASK_FLAG) == SRC_PORT_MASK_FLAG &&
- ntohs(sport) == htons(policy->srcPort)) {
+ ntohs(sport) == htons(policy->srcPort)) {
score++;
tempMask |= SRC_PORT_MASK_FLAG;
}
if ((policy->presentFields & DST_PORT_MASK_FLAG) == DST_PORT_MASK_FLAG &&
- ntohs(dport) >= htons(policy->dstPortStart) &&
- ntohs(dport) <= htons(policy->dstPortEnd)) {
+ ntohs(dport) >= htons(policy->dstPortStart) &&
+ ntohs(dport) <= htons(policy->dstPortEnd)) {
score++;
tempMask |= DST_PORT_MASK_FLAG;
}
if ((policy->presentFields & PROTO_MASK_FLAG) == PROTO_MASK_FLAG &&
- protocol == policy->proto) {
+ protocol == policy->proto) {
score++;
tempMask |= PROTO_MASK_FLAG;
}
@@ -232,7 +227,8 @@
}
}
- uint8_t new_tos= 0; // Can 0 be used as default forwarding value?
+ uint8_t new_tos = 0; // Can 0 be used as default forwarding value?
+ uint8_t new_dscp = 0;
uint8_t new_priority = 0;
uint8_t new_flow_lbl = 0;
if (bestScore > 0) {
@@ -244,20 +240,16 @@
}
if (policy) {
- // TODO: if DSCP value is already set ignore?
+ new_dscp = policy->dscpVal;
if (ipv4) {
- int ecn = tos & 3;
- new_tos = (policy->dscpVal << 2) + ecn;
+ new_tos = UPDATE_TOS(new_dscp, tos);
} else {
- new_priority = (policy->dscpVal >> 2) + 0x60;
- new_flow_lbl = ((policy->dscpVal & 0xf) << 6) + (flow_lbl >> 6);
-
- // Set IPv6 curDscp value to stored value and recalulate priority
- // and flow label during next use.
- new_tos = policy->dscpVal;
+ new_priority = UPDATE_PRIORITY(new_dscp);
+ new_flow_lbl = UPDATE_FLOW_LABEL(new_dscp, flow_lbl);
}
}
- } else return;
+ } else
+ return;
RuleEntry value = {
.srcIp = srcIp,
@@ -266,10 +258,10 @@
.srcPort = sport,
.dstPort = dport,
.proto = protocol,
- .dscpVal = new_tos,
+ .dscpVal = new_dscp,
};
- //Update map with new policy.
+ // Update map with new policy.
if (ipv4) {
if (*selectedMap == MAP_A) {
bpf_ipv4_socket_to_policies_map_A_update_elem(&cookie, &value, BPF_ANY);
@@ -286,12 +278,11 @@
// Need to store bytes after updating map or program will not load.
if (ipv4 && new_tos != (tos & 252)) {
- int oldDscpVal = tos >> 2;
- bpf_l3_csum_replace(skb, 1, oldDscpVal, new_tos, sizeof(uint8_t));
- bpf_skb_store_bytes(skb, 1, &new_tos, sizeof(uint8_t), 0);
+ bpf_l3_csum_replace(skb, IP4_OFFSET(check, l2_header_size), htons(tos), htons(new_tos), 2);
+ bpf_skb_store_bytes(skb, IP4_OFFSET(tos, l2_header_size), &new_tos, sizeof(new_tos), 0);
} else if (!ipv4 && (new_priority != priority || new_flow_lbl != flow_lbl)) {
- bpf_skb_store_bytes(skb, 0, &new_priority, sizeof(uint8_t), 0);
- bpf_skb_store_bytes(skb, 1, &new_flow_lbl, sizeof(uint8_t), 0);
+ bpf_skb_store_bytes(skb, l2_header_size, &new_priority, sizeof(new_priority), 0);
+ bpf_skb_store_bytes(skb, l2_header_size + 1, &new_flow_lbl, sizeof(new_flow_lbl), 0);
}
return;
}
@@ -299,7 +290,6 @@
DEFINE_BPF_PROG_KVER("schedcls/set_dscp_ether", AID_ROOT, AID_SYSTEM,
schedcls_set_dscp_ether, KVER(5, 15, 0))
(struct __sk_buff* skb) {
-
if (skb->pkt_type != PACKET_HOST) return TC_ACT_PIPE;
if (skb->protocol == htons(ETH_P_IP)) {
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 2ec0792..c71e881 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -24,8 +24,16 @@
#define __kernel_udphdr udphdr
#include <linux/udp.h>
+#ifdef BTF
+// BTF is incompatible with bpfloaders < v0.10, hence for S (v0.2) we must
+// ship a different file than for later versions, but we need bpfloader v0.25+
+// for obj@ver.o support
+#define BPFLOADER_MIN_VER BPFLOADER_OBJ_AT_VER_VERSION
+#else /* BTF */
// The resulting .o needs to load on the Android S bpfloader
#define BPFLOADER_MIN_VER BPFLOADER_S_VERSION
+#define BPFLOADER_MAX_VER BPFLOADER_OBJ_AT_VER_VERSION
+#endif /* BTF */
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
diff --git a/bpf_progs/offload@btf.c b/bpf_progs/offload@btf.c
new file mode 120000
index 0000000..4092e0d
--- /dev/null
+++ b/bpf_progs/offload@btf.c
@@ -0,0 +1 @@
+offload.c
\ No newline at end of file
diff --git a/bpf_progs/test.c b/bpf_progs/test.c
index f2fcc8c..e22fe2a 100644
--- a/bpf_progs/test.c
+++ b/bpf_progs/test.c
@@ -18,8 +18,16 @@
#include <linux/in.h>
#include <linux/ip.h>
+#ifdef BTF
+// BTF is incompatible with bpfloaders < v0.10, hence for S (v0.2) we must
+// ship a different file than for later versions, but we need bpfloader v0.25+
+// for obj@ver.o support
+#define BPFLOADER_MIN_VER BPFLOADER_OBJ_AT_VER_VERSION
+#else /* BTF */
// The resulting .o needs to load on the Android S bpfloader
#define BPFLOADER_MIN_VER BPFLOADER_S_VERSION
+#define BPFLOADER_MAX_VER BPFLOADER_OBJ_AT_VER_VERSION
+#endif /* BTF */
#include "bpf_helpers.h"
#include "bpf_net_helpers.h"
diff --git a/bpf_progs/test@btf.c b/bpf_progs/test@btf.c
new file mode 120000
index 0000000..aeebb26
--- /dev/null
+++ b/bpf_progs/test@btf.c
@@ -0,0 +1 @@
+test.c
\ No newline at end of file
diff --git a/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java b/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
index d37a53d..66d99a1 100644
--- a/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
+++ b/framework-t/src/android/net/netstats/provider/NetworkStatsProvider.java
@@ -118,7 +118,7 @@
*
* @param token the token under which these stats were gathered. Providers can call this method
* with the current token as often as they want, until the token changes.
- * {@see NetworkStatsProvider#onRequestStatsUpdate()}
+ * See {@link NetworkStatsProvider#onRequestStatsUpdate(int)}
* @param ifaceStats the {@link NetworkStats} per interface to be reported.
* The provider should not include any traffic that is already counted by
* kernel interface counters.
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index fad63e5..3fcc11b 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -126,7 +126,7 @@
* http://www.iana.org/form/ports-service. Existing services can be found at
* http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xml
*
- * {@see NsdServiceInfo}
+ * @see NsdServiceInfo
*/
@SystemService(Context.NSD_SERVICE)
public final class NsdManager {
diff --git a/framework-t/src/android/net/nsd/NsdServiceInfo.java b/framework-t/src/android/net/nsd/NsdServiceInfo.java
index 200c808..6438a60 100644
--- a/framework-t/src/android/net/nsd/NsdServiceInfo.java
+++ b/framework-t/src/android/net/nsd/NsdServiceInfo.java
@@ -34,7 +34,7 @@
/**
* A class representing service information for network service discovery
- * {@see NsdManager}
+ * @see NsdManager
*/
public final class NsdServiceInfo implements Parcelable {
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index eeedfd1..6ccd77e 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -556,7 +556,7 @@
*
* @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
* {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
- * appropriate network. {@see NetworkCapabilities} for supported transports.
+ * appropriate network. See {@link NetworkCapabilities} for supported transports.
*/
@Deprecated
public static final int TYPE_MOBILE = 0;
@@ -566,7 +566,7 @@
*
* @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
* {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
- * appropriate network. {@see NetworkCapabilities} for supported transports.
+ * appropriate network. See {@link NetworkCapabilities} for supported transports.
*/
@Deprecated
public static final int TYPE_WIFI = 1;
@@ -617,7 +617,7 @@
*
* @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
* {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
- * appropriate network. {@see NetworkCapabilities} for supported transports.
+ * appropriate network. See {@link NetworkCapabilities} for supported transports.
*/
@Deprecated
public static final int TYPE_MOBILE_HIPRI = 5;
@@ -627,7 +627,7 @@
*
* @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
* {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
- * appropriate network. {@see NetworkCapabilities} for supported transports.
+ * appropriate network. See {@link NetworkCapabilities} for supported transports.
*/
@Deprecated
public static final int TYPE_WIMAX = 6;
@@ -637,7 +637,7 @@
*
* @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
* {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
- * appropriate network. {@see NetworkCapabilities} for supported transports.
+ * appropriate network. See {@link NetworkCapabilities} for supported transports.
*/
@Deprecated
public static final int TYPE_BLUETOOTH = 7;
@@ -654,7 +654,7 @@
*
* @deprecated Applications should instead use {@link NetworkCapabilities#hasTransport} or
* {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request an
- * appropriate network. {@see NetworkCapabilities} for supported transports.
+ * appropriate network. See {@link NetworkCapabilities} for supported transports.
*/
@Deprecated
public static final int TYPE_ETHERNET = 9;
@@ -1204,7 +1204,7 @@
/**
* Preference for {@link ProfileNetworkPreference#setPreference(int)}.
- * {@see #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
+ * See {@link #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
* Specify that the traffic for this user should by follow the default rules.
* @hide
*/
@@ -1213,7 +1213,7 @@
/**
* Preference for {@link ProfileNetworkPreference#setPreference(int)}.
- * {@see #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
+ * See {@link #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
* Specify that the traffic for this user should by default go on a network with
* {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE}, and on the system default network
* if no such network is available.
@@ -1224,7 +1224,7 @@
/**
* Preference for {@link ProfileNetworkPreference#setPreference(int)}.
- * {@see #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
+ * See {@link #setProfileNetworkPreferences(UserHandle, List, Executor, Runnable)}
* Specify that the traffic for this user should by default go on a network with
* {@link NetworkCapabilities#NET_CAPABILITY_ENTERPRISE} and if no such network is available
* should not go on the system default network
@@ -3383,8 +3383,8 @@
* proxy is likely to break networking on multiple networks. This method is only meant
* for device policy clients looking to do general internal filtering or similar use cases.
*
- * {@see #getGlobalProxy}
- * {@see LinkProperties#getHttpProxy}
+ * @see #getGlobalProxy
+ * @see LinkProperties#getHttpProxy
*
* @param p A {@link ProxyInfo} object defining the new global HTTP proxy. Calling this
* method with a {@code null} value will clear the global HTTP proxy.
@@ -4277,7 +4277,7 @@
* network, unless it becomes the best again at some later time. All callbacks are invoked
* in order on the same thread, which by default is a thread created by the framework running
* in the app.
- * {@see #requestNetwork(NetworkRequest, NetworkCallback, Handler)} to change where the
+ * See {@link #requestNetwork(NetworkRequest, NetworkCallback, Handler)} to change where the
* callbacks are invoked.
*
* <p>This{@link NetworkRequest} will live until released via
diff --git a/framework/src/android/net/DnsResolver.java b/framework/src/android/net/DnsResolver.java
index 164160f..5e637f9 100644
--- a/framework/src/android/net/DnsResolver.java
+++ b/framework/src/android/net/DnsResolver.java
@@ -137,7 +137,7 @@
* @param answer <T> answer to the query.
* @param rcode The response code in the DNS response.
*
- * {@see android.net.DnsResolver#query query()}
+ * @see android.net.DnsResolver#query query()
*/
void onAnswer(@NonNull T answer, int rcode);
/**
diff --git a/framework/src/android/net/LinkProperties.java b/framework/src/android/net/LinkProperties.java
index a8f707e..b7ee846 100644
--- a/framework/src/android/net/LinkProperties.java
+++ b/framework/src/android/net/LinkProperties.java
@@ -29,6 +29,8 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils;
import java.net.Inet4Address;
@@ -42,7 +44,6 @@
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
-import java.util.stream.Collectors;
/**
* Describes the properties of a network link.
@@ -759,9 +760,15 @@
* @return An unmodifiable {@link List} of {@link RouteInfo} for this link.
*/
public @NonNull List<RouteInfo> getRoutes() {
- if (CompatChanges.isChangeEnabled(EXCLUDED_ROUTES)) {
+ // Before T, there's no throw routes because VpnService is not updatable, so no need to
+ // filter them out.
+ if (CompatChanges.isChangeEnabled(EXCLUDED_ROUTES) || !SdkLevel.isAtLeastT()) {
return Collections.unmodifiableList(mRoutes);
} else {
+ // Apps that added a throw route themselves (not obtaining LinkProperties from the
+ // system) will not see it in getRoutes on T+ if they do not have the compat change
+ // enabled (target SDK < T); but this is expected to be rare and typically only affect
+ // tests creating LinkProperties themselves (like CTS v12, which is only running on S).
return Collections.unmodifiableList(getUnicastRoutes());
}
}
@@ -770,9 +777,7 @@
* Returns all the {@link RouteInfo} of type {@link RouteInfo#RTN_UNICAST} set on this link.
*/
private @NonNull List<RouteInfo> getUnicastRoutes() {
- return mRoutes.stream()
- .filter(route -> route.getType() == RouteInfo.RTN_UNICAST)
- .collect(Collectors.toList());
+ return CollectionUtils.filter(mRoutes, route -> route.getType() == RouteInfo.RTN_UNICAST);
}
/**
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index 5659a35..1486619 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -84,7 +84,7 @@
* the correct packets. Devices typically have a small number of slots
* per radio technology, and the specific number of slots for each
* technology is specified in configuration files.
- * {@see SocketKeepalive} for details.
+ * See {@link SocketKeepalive} for details.
*
* @hide
*/
diff --git a/framework/src/android/net/NetworkAgentConfig.java b/framework/src/android/net/NetworkAgentConfig.java
index 0d2b620..b6f3314 100644
--- a/framework/src/android/net/NetworkAgentConfig.java
+++ b/framework/src/android/net/NetworkAgentConfig.java
@@ -252,7 +252,7 @@
/**
* Whether network validation should be performed for this VPN network.
- * {@see #isVpnValidationRequired}
+ * @see #isVpnValidationRequired
* @hide
*/
private boolean mVpnRequiresValidation = false;
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index dbb05a9..ea8a3df 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -192,7 +192,7 @@
/**
* Bitfield representing the network's enterprise capability identifier. If any are specified
* they will be satisfied by any Network that matches all of them.
- * {@see addEnterpriseId} for details on how masks are added
+ * See {@link #addEnterpriseId(int)} for details on how masks are added
*/
private int mEnterpriseId;
@@ -1460,7 +1460,7 @@
* Sets the upstream bandwidth for this network in Kbps. This always only refers to
* the estimated first hop transport bandwidth.
* <p>
- * {@see Builder#setLinkUpstreamBandwidthKbps}
+ * @see Builder#setLinkUpstreamBandwidthKbps
*
* @param upKbps the estimated first hop upstream (device to network) bandwidth.
* @hide
@@ -1484,7 +1484,7 @@
* Sets the downstream bandwidth for this network in Kbps. This always only refers to
* the estimated first hop transport bandwidth.
* <p>
- * {@see Builder#setLinkUpstreamBandwidthKbps}
+ * @see Builder#setLinkUpstreamBandwidthKbps
*
* @param downKbps the estimated first hop downstream (network to device) bandwidth.
* @hide
@@ -2534,7 +2534,7 @@
/**
* Set the uid and package name of the app causing this network to exist.
*
- * {@see #setRequestorUid} and {@link #setRequestorPackageName}
+ * See {@link #setRequestorUid} and {@link #setRequestorPackageName}
*
* @param uid UID of the app.
* @param packageName package name of the app.
@@ -2719,7 +2719,7 @@
/**
* Removes the given transport type.
*
- * {@see #addTransportType}.
+ * @see #addTransportType
*
* @param transportType the transport type to be added or removed.
* @return this builder
diff --git a/framework/src/android/net/ProfileNetworkPreference.java b/framework/src/android/net/ProfileNetworkPreference.java
index fdcab02..8b98721 100644
--- a/framework/src/android/net/ProfileNetworkPreference.java
+++ b/framework/src/android/net/ProfileNetworkPreference.java
@@ -79,7 +79,7 @@
* if included is not empty, then only included UIDs are applied.
* if excluded is not empty, then it is all uids in the user profile except these UIDs.
* @return Array of uids included for the profile preference.
- * {@see #getExcludedUids()}
+ * @see #getExcludedUids()
*/
public @NonNull int[] getIncludedUids() {
return mIncludedUids.clone();
@@ -93,7 +93,7 @@
* <ul>If included is not empty, then only included UIDs are applied.</ul>
* <ul>If excluded is not empty, then it is all uids in the user profile except these UIDs.</ul>
* @return Array of uids not included for the profile preference.
- * {@see #getIncludedUids()}
+ * @see #getIncludedUids()
*/
public @NonNull int[] getExcludedUids() {
return mExcludedUids.clone();
@@ -177,7 +177,7 @@
/**
* This is a array of uids for which profile perefence is set.
* Empty would mean that this preference applies to all uids in the profile.
- * {@see #setExcludedUids(int[])}
+ * @see #setExcludedUids(int[])
* Included UIDs and Excluded UIDs can't both be non-empty.
* if both are empty, it means this request applies to all uids in the user profile.
* if included is not empty, then only included UIDs are applied.
@@ -195,7 +195,7 @@
/**
* This is a array of uids that are excluded for the profile perefence.
- * {@see #setIncludedUids(int[])}
+ * @see #setIncludedUids(int[])
* Included UIDs and Excluded UIDs can't both be non-empty.
* if both are empty, it means this request applies to all uids in the user profile.
* if included is not empty, then only included UIDs are applied.
diff --git a/framework/src/android/net/QosCallbackException.java b/framework/src/android/net/QosCallbackException.java
index b80cff4..72430d2 100644
--- a/framework/src/android/net/QosCallbackException.java
+++ b/framework/src/android/net/QosCallbackException.java
@@ -57,6 +57,9 @@
private static final String TAG = "QosCallbackException";
// Types of exceptions supported //
+ // The constants are used for the sendQosCallbackError system API, so they must not be changed
+ // as there may be callers relying on their historical values to call that API.
+ // TODO: mark the constants as @SystemApi, since they are necessary to call a system API.
/** {@hide} */
public static final int EX_TYPE_FILTER_NONE = 0;
@@ -67,13 +70,13 @@
public static final int EX_TYPE_FILTER_SOCKET_NOT_BOUND = 2;
/** {@hide} */
- public static final int EX_TYPE_FILTER_SOCKET_NOT_CONNECTED = 3;
+ public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 3;
/** {@hide} */
- public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 4;
+ public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 4;
/** {@hide} */
- public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 5;
+ public static final int EX_TYPE_FILTER_SOCKET_NOT_CONNECTED = 5;
/** {@hide} */
public static final int EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED = 6;
diff --git a/framework/src/android/net/TestNetworkManager.java b/framework/src/android/net/TestNetworkManager.java
index 9cae9e6..788834a 100644
--- a/framework/src/android/net/TestNetworkManager.java
+++ b/framework/src/android/net/TestNetworkManager.java
@@ -195,6 +195,24 @@
/**
* 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, 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
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 7115720..8818460 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.LinkProperties;
@@ -51,6 +50,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import com.android.net.module.util.PermissionUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -861,12 +861,7 @@
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump " + TAG
- + " due to missing android.permission.DUMP permission");
- return;
- }
+ if (!PermissionUtils.checkDumpPermission(mContext, TAG, pw)) return;
for (ClientInfo client : mClients.values()) {
pw.println("Client Info");
diff --git a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
index f058f94..dae3d2a 100644
--- a/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
+++ b/service-t/src/com/android/server/ethernet/EthernetServiceImpl.java
@@ -32,7 +32,6 @@
import android.net.IpConfiguration;
import android.net.NetworkCapabilities;
import android.net.NetworkSpecifier;
-import android.os.Binder;
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
@@ -188,13 +187,7 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump EthernetService from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
+ if (!PermissionUtils.checkDumpPermission(mContext, TAG, pw)) return;
pw.println("Current Ethernet state: ");
pw.increaseIndent();
diff --git a/service-t/src/com/android/server/net/NetworkStatsFactory.java b/service-t/src/com/android/server/net/NetworkStatsFactory.java
index 3b93f1a..b628251 100644
--- a/service-t/src/com/android/server/net/NetworkStatsFactory.java
+++ b/service-t/src/com/android/server/net/NetworkStatsFactory.java
@@ -164,16 +164,17 @@
}
public NetworkStatsFactory(@NonNull Context ctx) {
- this(ctx, new File("/proc/"), true);
+ this(ctx, new File("/proc/"), true, new BpfNetMaps());
}
@VisibleForTesting
- public NetworkStatsFactory(@NonNull Context ctx, File procRoot, boolean useBpfStats) {
+ public NetworkStatsFactory(@NonNull Context ctx, File procRoot, boolean useBpfStats,
+ BpfNetMaps bpfNetMaps) {
mStatsXtIfaceAll = new File(procRoot, "net/xt_qtaguid/iface_stat_all");
mStatsXtIfaceFmt = new File(procRoot, "net/xt_qtaguid/iface_stat_fmt");
mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
mUseBpfStats = useBpfStats;
- mBpfNetMaps = new BpfNetMaps();
+ mBpfNetMaps = bpfNetMaps;
synchronized (mPersistentDataLock) {
mPersistSnapshot = new NetworkStats(SystemClock.elapsedRealtime(), -1);
mTunAnd464xlatAdjustedStats = new NetworkStats(SystemClock.elapsedRealtime(), -1);
diff --git a/service-t/src/com/android/server/net/NetworkStatsRecorder.java b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
index 7c801d7..3da1585 100644
--- a/service-t/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
@@ -541,7 +541,8 @@
/**
* Recover from {@link FileRotator} failure by dumping state to
- * {@link DropBoxManager} and deleting contents.
+ * {@link DropBoxManager} and deleting contents if this recorder
+ * sets {@code mWipeOnError} to true, otherwise keep the contents.
*/
void recoverAndDeleteData() {
if (DUMP_BEFORE_DELETE) {
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 424dcd9..fef6afb 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -24,7 +24,6 @@
import static android.content.Intent.ACTION_UID_REMOVED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_UID;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
@@ -304,7 +303,7 @@
/**
* When enabled, all mobile data is reported under {@link NetworkTemplate#NETWORK_TYPE_ALL}.
* When disabled, mobile data is broken down by a granular ratType representative of the
- * actual ratType. {@see android.app.usage.NetworkStatsManager#getCollapsedRatType}.
+ * actual ratType. See {@link android.app.usage.NetworkStatsManager#getCollapsedRatType}.
* Enabling this decreases the level of detail but saves performance, disk space and
* amount of data logged.
*/
@@ -2816,24 +2815,6 @@
return stats;
}
- // TODO: It is copied from ConnectivityService, consider refactor these check permission
- // functions to a proper util.
- private boolean checkAnyPermissionOf(String... permissions) {
- for (String permission : permissions) {
- if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
- return true;
- }
- }
- return false;
- }
-
- private void enforceAnyPermissionOf(String... permissions) {
- if (!checkAnyPermissionOf(permissions)) {
- throw new SecurityException("Requires one of the following permissions: "
- + String.join(", ", permissions) + ".");
- }
- }
-
/**
* Registers a custom provider of {@link android.net.NetworkStats} to combine the network
* statistics that cannot be seen by the kernel to system. To unregister, invoke the
@@ -2848,7 +2829,7 @@
*/
public @NonNull INetworkStatsProviderCallback registerNetworkStatsProvider(
@NonNull String tag, @NonNull INetworkStatsProvider provider) {
- enforceAnyPermissionOf(NETWORK_STATS_PROVIDER,
+ PermissionUtils.enforceAnyPermissionOf(mContext, NETWORK_STATS_PROVIDER,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
Objects.requireNonNull(provider, "provider is null");
Objects.requireNonNull(tag, "tag is null");
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 6e107c2..d7c5a06 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -24,7 +24,11 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
+import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
+import static android.net.ConnectivityManager.FIREWALL_RULE_DENY;
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 android.net.INetd;
@@ -33,8 +37,8 @@
import android.system.ErrnoException;
import android.system.Os;
import android.util.Log;
-import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BpfMap;
@@ -49,10 +53,17 @@
* {@hide}
*/
public class BpfNetMaps {
+ private static final boolean PRE_T = !SdkLevel.isAtLeastT();
+ static {
+ if (!PRE_T) {
+ System.loadLibrary("service-connectivity");
+ }
+ }
+
private static final String TAG = "BpfNetMaps";
private final INetd mNetd;
+ private final Dependencies mDeps;
// Use legacy netd for releases before T.
- private static final boolean PRE_T = !SdkLevel.isAtLeastT();
private static boolean sInitialized = false;
// Lock for sConfigurationMap entry for UID_RULES_CONFIGURATION_KEY.
@@ -62,47 +73,70 @@
private static final String CONFIGURATION_MAP_PATH =
"/sys/fs/bpf/netd_shared/map_netd_configuration_map";
+ private static final String UID_OWNER_MAP_PATH =
+ "/sys/fs/bpf/netd_shared/map_netd_uid_owner_map";
private static final U32 UID_RULES_CONFIGURATION_KEY = new U32(0);
private static BpfMap<U32, U32> sConfigurationMap = null;
+ // BpfMap for UID_OWNER_MAP_PATH. This map is not accessed by others.
+ private static BpfMap<U32, UidOwnerValue> sUidOwnerMap = null;
// LINT.IfChange(match_type)
- private static final long NO_MATCH = 0;
- private static final long HAPPY_BOX_MATCH = (1 << 0);
- private static final long PENALTY_BOX_MATCH = (1 << 1);
- private static final long DOZABLE_MATCH = (1 << 2);
- private static final long STANDBY_MATCH = (1 << 3);
- private static final long POWERSAVE_MATCH = (1 << 4);
- private static final long RESTRICTED_MATCH = (1 << 5);
- private static final long LOW_POWER_STANDBY_MATCH = (1 << 6);
- private static final long IIF_MATCH = (1 << 7);
- private static final long LOCKDOWN_VPN_MATCH = (1 << 8);
- private static final long OEM_DENY_1_MATCH = (1 << 9);
- private static final long OEM_DENY_2_MATCH = (1 << 10);
- private static final long OEM_DENY_3_MATCH = (1 << 11);
+ @VisibleForTesting public static final long NO_MATCH = 0;
+ @VisibleForTesting public static final long HAPPY_BOX_MATCH = (1 << 0);
+ @VisibleForTesting public static final long PENALTY_BOX_MATCH = (1 << 1);
+ @VisibleForTesting public static final long DOZABLE_MATCH = (1 << 2);
+ @VisibleForTesting public static final long STANDBY_MATCH = (1 << 3);
+ @VisibleForTesting public static final long POWERSAVE_MATCH = (1 << 4);
+ @VisibleForTesting public static final long RESTRICTED_MATCH = (1 << 5);
+ @VisibleForTesting public static final long LOW_POWER_STANDBY_MATCH = (1 << 6);
+ @VisibleForTesting public static final long IIF_MATCH = (1 << 7);
+ @VisibleForTesting public static final long LOCKDOWN_VPN_MATCH = (1 << 8);
+ @VisibleForTesting public static final long OEM_DENY_1_MATCH = (1 << 9);
+ @VisibleForTesting public static final long OEM_DENY_2_MATCH = (1 << 10);
+ @VisibleForTesting public static final long OEM_DENY_3_MATCH = (1 << 11);
// LINT.ThenChange(packages/modules/Connectivity/bpf_progs/bpf_shared.h)
- // TODO: Use Java BpfMap instead of JNI code (TrafficController) for map update.
- // Currently, BpfNetMaps uses TrafficController for map update and TrafficController
- // (changeUidOwnerRule and toggleUidOwnerMap) also does conversion from "firewall chain" to
- // "match". Migrating map update from JNI to Java BpfMap will solve this duplication.
- private static final SparseLongArray FIREWALL_CHAIN_TO_MATCH = new SparseLongArray();
- static {
- FIREWALL_CHAIN_TO_MATCH.put(FIREWALL_CHAIN_DOZABLE, DOZABLE_MATCH);
- FIREWALL_CHAIN_TO_MATCH.put(FIREWALL_CHAIN_STANDBY, STANDBY_MATCH);
- FIREWALL_CHAIN_TO_MATCH.put(FIREWALL_CHAIN_POWERSAVE, POWERSAVE_MATCH);
- FIREWALL_CHAIN_TO_MATCH.put(FIREWALL_CHAIN_RESTRICTED, RESTRICTED_MATCH);
- FIREWALL_CHAIN_TO_MATCH.put(FIREWALL_CHAIN_LOW_POWER_STANDBY, LOW_POWER_STANDBY_MATCH);
- FIREWALL_CHAIN_TO_MATCH.put(FIREWALL_CHAIN_OEM_DENY_1, OEM_DENY_1_MATCH);
- FIREWALL_CHAIN_TO_MATCH.put(FIREWALL_CHAIN_OEM_DENY_2, OEM_DENY_2_MATCH);
- FIREWALL_CHAIN_TO_MATCH.put(FIREWALL_CHAIN_OEM_DENY_3, OEM_DENY_3_MATCH);
+ /**
+ * Set configurationMap for test.
+ */
+ @VisibleForTesting
+ public static void setConfigurationMapForTest(BpfMap<U32, U32> configurationMap) {
+ sConfigurationMap = configurationMap;
}
/**
- * Only tests or BpfNetMaps#ensureInitialized can call this function.
+ * Set uidOwnerMap for test.
*/
@VisibleForTesting
- public static void initialize(final Dependencies deps) {
- sConfigurationMap = deps.getConfigurationMap();
+ public static void setUidOwnerMapForTest(BpfMap<U32, UidOwnerValue> uidOwnerMap) {
+ sUidOwnerMap = uidOwnerMap;
+ }
+
+ private static BpfMap<U32, U32> getConfigurationMap() {
+ try {
+ return new BpfMap<>(
+ CONFIGURATION_MAP_PATH, BpfMap.BPF_F_RDWR, U32.class, U32.class);
+ } catch (ErrnoException e) {
+ throw new IllegalStateException("Cannot open netd configuration map", e);
+ }
+ }
+
+ private static BpfMap<U32, UidOwnerValue> getUidOwnerMap() {
+ try {
+ return new BpfMap<>(
+ UID_OWNER_MAP_PATH, BpfMap.BPF_F_RDWR, U32.class, UidOwnerValue.class);
+ } catch (ErrnoException e) {
+ throw new IllegalStateException("Cannot open uid owner map", e);
+ }
+ }
+
+ private static void setBpfMaps() {
+ if (sConfigurationMap == null) {
+ sConfigurationMap = getConfigurationMap();
+ }
+ if (sUidOwnerMap == null) {
+ sUidOwnerMap = getUidOwnerMap();
+ }
}
/**
@@ -111,11 +145,8 @@
*/
private static synchronized void ensureInitialized() {
if (sInitialized) return;
- if (!PRE_T) {
- System.loadLibrary("service-connectivity");
- native_init();
- initialize(new Dependencies());
- }
+ setBpfMaps();
+ native_init();
sInitialized = true;
}
@@ -125,16 +156,10 @@
@VisibleForTesting
public static class Dependencies {
/**
- * Get configuration BPF map.
+ * Get interface index.
*/
- public BpfMap<U32, U32> getConfigurationMap() {
- try {
- return new BpfMap<>(
- CONFIGURATION_MAP_PATH, BpfMap.BPF_F_RDWR, U32.class, U32.class);
- } catch (ErrnoException e) {
- Log.e(TAG, "Cannot open netd configuration map: " + e);
- return null;
- }
+ public int getIfIndex(final String ifName) {
+ return Os.if_nametoindex(ifName);
}
}
@@ -146,8 +171,16 @@
}
public BpfNetMaps(final INetd netd) {
- ensureInitialized();
+ this(netd, new Dependencies());
+ }
+
+ @VisibleForTesting
+ public BpfNetMaps(final INetd netd, final Dependencies deps) {
+ if (!PRE_T) {
+ ensureInitialized();
+ }
mNetd = netd;
+ mDeps = deps;
}
/**
@@ -155,11 +188,50 @@
*/
@VisibleForTesting
public long getMatchByFirewallChain(final int chain) {
- final long match = FIREWALL_CHAIN_TO_MATCH.get(chain, NO_MATCH);
- if (match == NO_MATCH) {
- throw new ServiceSpecificException(EINVAL, "Invalid firewall chain: " + chain);
+ switch (chain) {
+ case FIREWALL_CHAIN_DOZABLE:
+ return DOZABLE_MATCH;
+ case FIREWALL_CHAIN_STANDBY:
+ return STANDBY_MATCH;
+ case FIREWALL_CHAIN_POWERSAVE:
+ return POWERSAVE_MATCH;
+ case FIREWALL_CHAIN_RESTRICTED:
+ return RESTRICTED_MATCH;
+ case FIREWALL_CHAIN_LOW_POWER_STANDBY:
+ return LOW_POWER_STANDBY_MATCH;
+ case FIREWALL_CHAIN_OEM_DENY_1:
+ return OEM_DENY_1_MATCH;
+ case FIREWALL_CHAIN_OEM_DENY_2:
+ return OEM_DENY_2_MATCH;
+ case FIREWALL_CHAIN_OEM_DENY_3:
+ return OEM_DENY_3_MATCH;
+ default:
+ throw new ServiceSpecificException(EINVAL, "Invalid firewall chain: " + chain);
}
- return match;
+ }
+
+ /**
+ * Get if the chain is allow list or not.
+ *
+ * ALLOWLIST means the firewall denies all by default, uids must be explicitly allowed
+ * DENYLIST means the firewall allows all by default, uids must be explicitly denyed
+ */
+ @VisibleForTesting
+ public boolean isFirewallAllowList(final int chain) {
+ switch (chain) {
+ case FIREWALL_CHAIN_DOZABLE:
+ case FIREWALL_CHAIN_POWERSAVE:
+ case FIREWALL_CHAIN_RESTRICTED:
+ case FIREWALL_CHAIN_LOW_POWER_STANDBY:
+ return true;
+ case FIREWALL_CHAIN_STANDBY:
+ case FIREWALL_CHAIN_OEM_DENY_1:
+ case FIREWALL_CHAIN_OEM_DENY_2:
+ case FIREWALL_CHAIN_OEM_DENY_3:
+ return false;
+ default:
+ throw new ServiceSpecificException(EINVAL, "Invalid firewall chain: " + chain);
+ }
}
private void maybeThrow(final int err, final String msg) {
@@ -174,6 +246,67 @@
}
}
+ private void removeRule(final int uid, final long match, final String caller) {
+ try {
+ synchronized (sUidOwnerMap) {
+ final UidOwnerValue oldMatch = sUidOwnerMap.getValue(new U32(uid));
+
+ if (oldMatch == null) {
+ throw new ServiceSpecificException(ENOENT,
+ "sUidOwnerMap does not have entry for uid: " + uid);
+ }
+
+ final UidOwnerValue newMatch = new UidOwnerValue(
+ (match == IIF_MATCH) ? 0 : oldMatch.iif,
+ oldMatch.rule & ~match
+ );
+
+ if (newMatch.rule == 0) {
+ sUidOwnerMap.deleteEntry(new U32(uid));
+ } else {
+ sUidOwnerMap.updateEntry(new U32(uid), newMatch);
+ }
+ }
+ } catch (ErrnoException e) {
+ throw new ServiceSpecificException(e.errno,
+ caller + " failed to remove rule: " + Os.strerror(e.errno));
+ }
+ }
+
+ private void addRule(final int uid, final long match, final long iif, final String caller) {
+ if (match != IIF_MATCH && iif != 0) {
+ throw new ServiceSpecificException(EINVAL,
+ "Non-interface match must have zero interface index");
+ }
+
+ try {
+ synchronized (sUidOwnerMap) {
+ final UidOwnerValue oldMatch = sUidOwnerMap.getValue(new U32(uid));
+
+ final UidOwnerValue newMatch;
+ if (oldMatch != null) {
+ newMatch = new UidOwnerValue(
+ (match == IIF_MATCH) ? iif : oldMatch.iif,
+ oldMatch.rule | match
+ );
+ } else {
+ newMatch = new UidOwnerValue(
+ iif,
+ match
+ );
+ }
+ sUidOwnerMap.updateEntry(new U32(uid), newMatch);
+ }
+ } catch (ErrnoException e) {
+ throw new ServiceSpecificException(e.errno,
+ caller + " failed to add rule: " + Os.strerror(e.errno));
+ }
+ }
+
+ private void addRule(final int uid, final long match, final String caller) {
+ addRule(uid, match, 0 /* iif */, caller);
+ }
+
/**
* Add naughty app bandwidth rule for specific app
*
@@ -182,8 +315,8 @@
* cause of the failure.
*/
public void addNaughtyApp(final int uid) {
- final int err = native_addNaughtyApp(uid);
- maybeThrow(err, "Unable to add naughty app");
+ throwIfPreT("addNaughtyApp is not available on pre-T devices");
+ addRule(uid, PENALTY_BOX_MATCH, "addNaughtyApp");
}
/**
@@ -194,8 +327,8 @@
* cause of the failure.
*/
public void removeNaughtyApp(final int uid) {
- final int err = native_removeNaughtyApp(uid);
- maybeThrow(err, "Unable to remove naughty app");
+ throwIfPreT("removeNaughtyApp is not available on pre-T devices");
+ removeRule(uid, PENALTY_BOX_MATCH, "removeNaughtyApp");
}
/**
@@ -206,8 +339,8 @@
* cause of the failure.
*/
public void addNiceApp(final int uid) {
- final int err = native_addNiceApp(uid);
- maybeThrow(err, "Unable to add nice app");
+ throwIfPreT("addNiceApp is not available on pre-T devices");
+ addRule(uid, HAPPY_BOX_MATCH, "addNiceApp");
}
/**
@@ -218,8 +351,8 @@
* cause of the failure.
*/
public void removeNiceApp(final int uid) {
- final int err = native_removeNiceApp(uid);
- maybeThrow(err, "Unable to remove nice app");
+ throwIfPreT("removeNiceApp is not available on pre-T devices");
+ removeRule(uid, HAPPY_BOX_MATCH, "removeNiceApp");
}
/**
@@ -284,11 +417,13 @@
*/
public int replaceUidChain(final String chainName, final boolean isAllowlist,
final int[] uids) {
- final int err = native_replaceUidChain(chainName, isAllowlist, uids);
- if (err != 0) {
- Log.e(TAG, "replaceUidChain failed: " + Os.strerror(-err));
+ synchronized (sUidOwnerMap) {
+ final int err = native_replaceUidChain(chainName, isAllowlist, uids);
+ if (err != 0) {
+ Log.e(TAG, "replaceUidChain failed: " + Os.strerror(-err));
+ }
+ return -err;
}
- return -err;
}
/**
@@ -301,8 +436,18 @@
* cause of the failure.
*/
public void setUidRule(final int childChain, final int uid, final int firewallRule) {
- final int err = native_setUidRule(childChain, uid, firewallRule);
- maybeThrow(err, "Unable to set uid rule");
+ throwIfPreT("setUidRule is not available on pre-T devices");
+
+ final long match = getMatchByFirewallChain(childChain);
+ final boolean isAllowList = isFirewallAllowList(childChain);
+ final boolean add = (firewallRule == FIREWALL_RULE_ALLOW && isAllowList)
+ || (firewallRule == FIREWALL_RULE_DENY && !isAllowList);
+
+ if (add) {
+ addRule(uid, match, "setUidRule");
+ } else {
+ removeRule(uid, match, "setUidRule");
+ }
}
/**
@@ -327,8 +472,25 @@
mNetd.firewallAddUidInterfaceRules(ifName, uids);
return;
}
- final int err = native_addUidInterfaceRules(ifName, uids);
- maybeThrow(err, "Unable to add uid interface rules");
+ // Null ifName is a wildcard to allow apps to receive packets on all interfaces and ifIndex
+ // is set to 0.
+ final int ifIndex;
+ if (ifName == null) {
+ ifIndex = 0;
+ } else {
+ ifIndex = mDeps.getIfIndex(ifName);
+ if (ifIndex == 0) {
+ throw new ServiceSpecificException(ENODEV,
+ "Failed to get index of interface " + ifName);
+ }
+ }
+ for (final int uid: uids) {
+ try {
+ addRule(uid, IIF_MATCH, ifIndex, "addUidInterfaceRules");
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "addRule failed uid=" + uid + " ifName=" + ifName + ", " + e);
+ }
+ }
}
/**
@@ -347,8 +509,13 @@
mNetd.firewallRemoveUidInterfaceRules(uids);
return;
}
- final int err = native_removeUidInterfaceRules(uids);
- maybeThrow(err, "Unable to remove uid interface rules");
+ for (final int uid: uids) {
+ try {
+ removeRule(uid, IIF_MATCH, "removeUidInterfaceRules");
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "removeRule failed uid=" + uid + ", " + e);
+ }
+ }
}
/**
@@ -360,8 +527,12 @@
* cause of the failure.
*/
public void updateUidLockdownRule(final int uid, final boolean add) {
- final int err = native_updateUidLockdownRule(uid, add);
- maybeThrow(err, "Unable to update lockdown rule");
+ throwIfPreT("updateUidLockdownRule is not available on pre-T devices");
+ if (add) {
+ addRule(uid, LOCKDOWN_VPN_MATCH, "updateUidLockdownRule");
+ } else {
+ removeRule(uid, LOCKDOWN_VPN_MATCH, "updateUidLockdownRule");
+ }
}
/**
@@ -411,14 +582,23 @@
}
private static native void native_init();
+ @GuardedBy("sUidOwnerMap")
private native int native_addNaughtyApp(int uid);
+ @GuardedBy("sUidOwnerMap")
private native int native_removeNaughtyApp(int uid);
+ @GuardedBy("sUidOwnerMap")
private native int native_addNiceApp(int uid);
+ @GuardedBy("sUidOwnerMap")
private native int native_removeNiceApp(int uid);
+ @GuardedBy("sUidOwnerMap")
private native int native_replaceUidChain(String name, boolean isAllowlist, int[] uids);
+ @GuardedBy("sUidOwnerMap")
private native int native_setUidRule(int childChain, int uid, int firewallRule);
+ @GuardedBy("sUidOwnerMap")
private native int native_addUidInterfaceRules(String ifName, int[] uids);
+ @GuardedBy("sUidOwnerMap")
private native int native_removeUidInterfaceRules(int[] uids);
+ @GuardedBy("sUidOwnerMap")
private native int native_updateUidLockdownRule(int uid, boolean add);
private native int native_swapActiveStatsMap();
private native void native_setPermissionForUids(int permissions, int[] uids);
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 37fc391..7050b42 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -98,6 +98,9 @@
import static android.system.OsConstants.IPPROTO_UDP;
import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
+import static com.android.net.module.util.PermissionUtils.enforceAnyPermissionOf;
+import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermission;
+import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPermissionOr;
import static java.util.Map.Entry;
@@ -450,7 +453,7 @@
* direct device-originated data traffic of the specific UIDs to the correct
* default network for each app.
* Order ints passed to netd must be in the 0~999 range. Larger values code for
- * a lower priority, {@see NativeUidRangeConfig}
+ * a lower priority, see {@link NativeUidRangeConfig}.
*
* Requests that don't code for a per-app preference use PREFERENCE_ORDER_INVALID.
* The default request uses PREFERENCE_ORDER_DEFAULT.
@@ -1956,7 +1959,7 @@
@Override
public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) {
- PermissionUtils.enforceNetworkStackPermission(mContext);
+ enforceNetworkStackPermission(mContext);
return getActiveNetworkForUidInternal(uid, ignoreBlocked);
}
@@ -1979,7 +1982,7 @@
@Override
public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
- PermissionUtils.enforceNetworkStackPermission(mContext);
+ enforceNetworkStackPermission(mContext);
final NetworkAgentInfo nai = getNetworkAgentInfoForUid(uid);
if (nai == null) return null;
return getFilteredNetworkInfo(nai, uid, ignoreBlocked);
@@ -2518,7 +2521,7 @@
@Override
public NetworkState[] getAllNetworkState() {
// This contains IMSI details, so make sure the caller is privileged.
- PermissionUtils.enforceNetworkStackPermission(mContext);
+ enforceNetworkStackPermission(mContext);
final ArrayList<NetworkState> result = new ArrayList<>();
for (NetworkStateSnapshot snapshot : getAllNetworkStateSnapshots()) {
@@ -2783,15 +2786,6 @@
setUidBlockedReasons(uid, blockedReasons);
}
- private boolean checkAnyPermissionOf(String... permissions) {
- for (String permission : permissions) {
- if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
- return true;
- }
- }
- return false;
- }
-
private boolean checkAnyPermissionOf(int pid, int uid, String... permissions) {
for (String permission : permissions) {
if (mContext.checkPermission(permission, pid, uid) == PERMISSION_GRANTED) {
@@ -2801,13 +2795,6 @@
return false;
}
- private void enforceAnyPermissionOf(String... permissions) {
- if (!checkAnyPermissionOf(permissions)) {
- throw new SecurityException("Requires one of the following permissions: "
- + String.join(", ", permissions) + ".");
- }
- }
-
private void enforceInternetPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERNET,
@@ -2867,7 +2854,7 @@
}
private void enforceSettingsPermission() {
- enforceAnyPermissionOf(
+ enforceAnyPermissionOf(mContext,
android.Manifest.permission.NETWORK_SETTINGS,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
@@ -2875,7 +2862,7 @@
private void enforceNetworkFactoryPermission() {
// TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
- enforceAnyPermissionOf(
+ enforceAnyPermissionOf(mContext,
android.Manifest.permission.NETWORK_FACTORY,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
@@ -2883,7 +2870,7 @@
private void enforceNetworkFactoryOrSettingsPermission() {
// TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
- enforceAnyPermissionOf(
+ enforceAnyPermissionOf(mContext,
android.Manifest.permission.NETWORK_SETTINGS,
android.Manifest.permission.NETWORK_FACTORY,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
@@ -2892,7 +2879,7 @@
private void enforceNetworkFactoryOrTestNetworksPermission() {
// TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
- enforceAnyPermissionOf(
+ enforceAnyPermissionOf(mContext,
android.Manifest.permission.MANAGE_TEST_NETWORKS,
android.Manifest.permission.NETWORK_FACTORY,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
@@ -2909,7 +2896,7 @@
}
private boolean checkSettingsPermission() {
- return checkAnyPermissionOf(
+ return PermissionUtils.checkAnyPermissionOf(mContext,
android.Manifest.permission.NETWORK_SETTINGS,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
@@ -2922,27 +2909,21 @@
}
private void enforceNetworkStackOrSettingsPermission() {
- enforceAnyPermissionOf(
- android.Manifest.permission.NETWORK_SETTINGS,
- android.Manifest.permission.NETWORK_STACK,
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ enforceNetworkStackPermissionOr(mContext,
+ android.Manifest.permission.NETWORK_SETTINGS);
}
private void enforceNetworkStackSettingsOrSetup() {
- enforceAnyPermissionOf(
+ enforceNetworkStackPermissionOr(mContext,
android.Manifest.permission.NETWORK_SETTINGS,
- android.Manifest.permission.NETWORK_SETUP_WIZARD,
- android.Manifest.permission.NETWORK_STACK,
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ android.Manifest.permission.NETWORK_SETUP_WIZARD);
}
private void enforceAirplaneModePermission() {
- enforceAnyPermissionOf(
+ enforceNetworkStackPermissionOr(mContext,
android.Manifest.permission.NETWORK_AIRPLANE_MODE,
android.Manifest.permission.NETWORK_SETTINGS,
- android.Manifest.permission.NETWORK_SETUP_WIZARD,
- android.Manifest.permission.NETWORK_STACK,
- NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+ android.Manifest.permission.NETWORK_SETUP_WIZARD);
}
private void enforceOemNetworkPreferencesPermission() {
@@ -2958,7 +2939,7 @@
}
private boolean checkNetworkStackPermission() {
- return checkAnyPermissionOf(
+ return PermissionUtils.checkAnyPermissionOf(mContext,
android.Manifest.permission.NETWORK_STACK,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
@@ -5746,7 +5727,7 @@
@Override
public void setGlobalProxy(@Nullable final ProxyInfo proxyProperties) {
- PermissionUtils.enforceNetworkStackPermission(mContext);
+ enforceNetworkStackPermission(mContext);
mProxyTracker.setGlobalProxy(proxyProperties);
}
@@ -7294,7 +7275,7 @@
Objects.requireNonNull(initialScore, "initialScore must not be null");
Objects.requireNonNull(networkAgentConfig, "networkAgentConfig must not be null");
if (networkCapabilities.hasTransport(TRANSPORT_TEST)) {
- enforceAnyPermissionOf(Manifest.permission.MANAGE_TEST_NETWORKS);
+ enforceAnyPermissionOf(mContext, Manifest.permission.MANAGE_TEST_NETWORKS);
} else {
enforceNetworkFactoryPermission();
}
@@ -10307,7 +10288,8 @@
Objects.requireNonNull(network, "network must not be null");
Objects.requireNonNull(extras, "extras must not be null");
- enforceAnyPermissionOf(android.Manifest.permission.MANAGE_TEST_NETWORKS,
+ enforceAnyPermissionOf(mContext,
+ android.Manifest.permission.MANAGE_TEST_NETWORKS,
android.Manifest.permission.NETWORK_STACK);
final NetworkCapabilities nc = getNetworkCapabilitiesInternal(network);
if (!nc.hasTransport(TRANSPORT_TEST)) {
@@ -10715,7 +10697,7 @@
preferences.add(pref);
}
- PermissionUtils.enforceNetworkStackPermission(mContext);
+ enforceNetworkStackPermission(mContext);
if (DBG) {
log("setProfileNetworkPreferences " + profile + " to " + preferences);
}
diff --git a/service/src/com/android/server/UidOwnerValue.java b/service/src/com/android/server/UidOwnerValue.java
new file mode 100644
index 0000000..f89e354
--- /dev/null
+++ b/service/src/com/android/server/UidOwnerValue.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import com.android.net.module.util.Struct;
+
+/** Value type for per uid traffic control configuration map */
+public class UidOwnerValue extends Struct {
+ // Allowed interface index. Only applicable if IIF_MATCH is set in the rule bitmask below.
+ @Field(order = 0, type = Type.U32)
+ public final long iif;
+
+ // A bitmask of match type.
+ @Field(order = 1, type = Type.U32)
+ public final long rule;
+
+ public UidOwnerValue(final long iif, final long rule) {
+ this.iif = iif;
+ this.rule = rule;
+ }
+}
diff --git a/service/src/com/android/server/connectivity/FullScore.java b/service/src/com/android/server/connectivity/FullScore.java
index b13ba93..b156045 100644
--- a/service/src/com/android/server/connectivity/FullScore.java
+++ b/service/src/com/android/server/connectivity/FullScore.java
@@ -76,16 +76,16 @@
public static final int POLICY_IS_VPN = 62;
// This network has been selected by the user manually from settings or a 3rd party app
- // at least once. {@see NetworkAgentConfig#explicitlySelected}.
+ // at least once. @see NetworkAgentConfig#explicitlySelected.
/** @hide */
public static final int POLICY_EVER_USER_SELECTED = 61;
// The user has indicated in UI that this network should be used even if it doesn't
- // validate. {@see NetworkAgentConfig#acceptUnvalidated}.
+ // validate. @see NetworkAgentConfig#acceptUnvalidated.
/** @hide */
public static final int POLICY_ACCEPT_UNVALIDATED = 60;
- // This network is unmetered. {@see NetworkCapabilities.NET_CAPABILITY_NOT_METERED}.
+ // This network is unmetered. @see NetworkCapabilities.NET_CAPABILITY_NOT_METERED.
/** @hide */
public static final int POLICY_IS_UNMETERED = 59;
diff --git a/tests/common/java/android/net/LinkPropertiesTest.java b/tests/common/java/android/net/LinkPropertiesTest.java
index 9ed2bb3..9506fc9 100644
--- a/tests/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/common/java/android/net/LinkPropertiesTest.java
@@ -1312,7 +1312,26 @@
assertEquals(3, lp.getRoutes().size());
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R) @IgnoreAfter(Build.VERSION_CODES.S_V2)
+ @CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
+ @DisableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
+ public void testExcludedRoutesDisabled_S() {
+ final LinkProperties lp = new LinkProperties();
+ assertEquals(0, lp.getRoutes().size());
+
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV4, 0), RTN_UNREACHABLE));
+ assertEquals(1, lp.getRoutes().size());
+
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 5), RTN_THROW));
+ // RTN_THROW routes are visible on S when added by the caller (but they are not added by
+ // the system). This is uncommon usage but was tested by CTSv12.
+ assertEquals(2, lp.getRoutes().size());
+
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 2), RTN_UNICAST));
+ assertEquals(3, lp.getRoutes().size());
+ }
+
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
@CtsNetTestCasesMaxTargetSdk31(reason = "Compat change cannot be overridden when targeting T+")
@DisableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
public void testExcludedRoutesDisabled() {
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 80338aa..efc24d3 100644
--- a/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -47,6 +47,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.connectivity.resources.R
+import com.android.server.BpfNetMaps
import com.android.server.ConnectivityService
import com.android.server.NetworkAgentWrapper
import com.android.server.TestNetIdManager
@@ -208,6 +209,7 @@
doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any())
doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties
doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
+ doReturn(mock(BpfNetMaps::class.java)).`when`(deps).getBpfNetMaps(any())
doAnswer { inv ->
object : MultinetworkPolicyTracker(inv.getArgument(0), inv.getArgument(1),
inv.getArgument(2)) {
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
index db39e6f..3376c15 100644
--- a/tests/mts/bpf_existence_test.cpp
+++ b/tests/mts/bpf_existence_test.cpp
@@ -31,15 +31,11 @@
using std::set;
using std::string;
+using android::bpf::isAtLeastKernelVersion;
using android::modules::sdklevel::IsAtLeastR;
using android::modules::sdklevel::IsAtLeastS;
using android::modules::sdklevel::IsAtLeastT;
-// Mainline development branches lack the constant for the current development OS.
-#ifndef __ANDROID_API_T__
-#define __ANDROID_API_T__ 33
-#endif
-
#define PLATFORM "/sys/fs/bpf/"
#define TETHERING "/sys/fs/bpf/tethering/"
#define PRIVATE "/sys/fs/bpf/net_private/"
@@ -49,7 +45,8 @@
class BpfExistenceTest : public ::testing::Test {
};
-static const set<string> INTRODUCED_R = {
+// Part of Android R platform, but mainlined in S
+static const set<string> PLATFORM_ONLY_IN_R = {
PLATFORM "map_offload_tether_ingress_map",
PLATFORM "map_offload_tether_limit_map",
PLATFORM "map_offload_tether_stats_map",
@@ -57,7 +54,8 @@
PLATFORM "prog_offload_schedcls_ingress_tether_rawip",
};
-static const set<string> INTRODUCED_S = {
+// Provided by *current* mainline module for S+ devices
+static const set<string> MAINLINE_FOR_S_PLUS = {
TETHERING "map_offload_tether_dev_map",
TETHERING "map_offload_tether_downstream4_map",
TETHERING "map_offload_tether_downstream64_map",
@@ -78,15 +76,8 @@
TETHERING "prog_offload_schedcls_tether_upstream6_rawip",
};
-static const set<string> REMOVED_S = {
- PLATFORM "map_offload_tether_ingress_map",
- PLATFORM "map_offload_tether_limit_map",
- PLATFORM "map_offload_tether_stats_map",
- PLATFORM "prog_offload_schedcls_ingress_tether_ether",
- PLATFORM "prog_offload_schedcls_ingress_tether_rawip",
-};
-
-static const set<string> INTRODUCED_T = {
+// Provided by *current* mainline module for T+ devices
+static const set<string> MAINLINE_FOR_T_PLUS = {
SHARED "map_block_blocked_ports_map",
SHARED "map_clatd_clat_egress4_map",
SHARED "map_clatd_clat_ingress6_map",
@@ -121,58 +112,46 @@
NETD "prog_netd_skfilter_ingress_xtbpf",
};
-static const set<string> INTRODUCED_T_5_4 = {
+// Provided by *current* mainline module for T+ devices with 5.4+ kernels
+static const set<string> MAINLINE_FOR_T_5_4_PLUS = {
SHARED "prog_block_bind4_block_port",
SHARED "prog_block_bind6_block_port",
- SHARED "prog_dscp_policy_schedcls_set_dscp_ether",
- SHARED "prog_dscp_policy_schedcls_set_dscp_raw_ip",
};
-static const set<string> REMOVED_T = {
+// Provided by *current* mainline module for T+ devices with 5.15+ kernels
+static const set<string> MAINLINE_FOR_T_5_15_PLUS = {
+ SHARED "prog_dscp_policy_schedcls_set_dscp_ether",
+ SHARED "prog_dscp_policy_schedcls_set_dscp_raw_ip",
};
void addAll(set<string>* a, const set<string>& b) {
a->insert(b.begin(), b.end());
}
-void removeAll(set<string>* a, const set<string>& b) {
- for (const auto& toRemove : b) {
- a->erase(toRemove);
- }
-}
+#define DO_EXPECT(B, V) do { \
+ if (B) addAll(expected, (V)); else addAll(unexpected, (V)); \
+} while (0)
void getFileLists(set<string>* expected, set<string>* unexpected) {
unexpected->clear();
expected->clear();
- addAll(unexpected, INTRODUCED_R);
- addAll(unexpected, INTRODUCED_S);
- addAll(unexpected, INTRODUCED_T);
+ // We do not actually check the platform P/Q (netd) and Q (clatd) things
+ // and only verify the mainline module relevant R+ offload maps & progs.
+ //
+ // The goal of this test is to verify compatibility with the tethering mainline module,
+ // and not to test the platform itself, which may have been modified by vendor or oems,
+ // so we should only test for the removal of stuff that was mainline'd,
+ // and for the presence of mainline stuff.
+ DO_EXPECT(IsAtLeastR() && !IsAtLeastS(), PLATFORM_ONLY_IN_R);
- if (IsAtLeastR()) {
- addAll(expected, INTRODUCED_R);
- removeAll(unexpected, INTRODUCED_R);
- // Nothing removed in R.
- }
-
- if (IsAtLeastS()) {
- addAll(expected, INTRODUCED_S);
- removeAll(expected, REMOVED_S);
-
- addAll(unexpected, REMOVED_S);
- removeAll(unexpected, INTRODUCED_S);
- }
+ DO_EXPECT(IsAtLeastS(), MAINLINE_FOR_S_PLUS);
// Nothing added or removed in SCv2.
- if (IsAtLeastT()) {
- addAll(expected, INTRODUCED_T);
- if (android::bpf::isAtLeastKernelVersion(5, 4, 0)) addAll(expected, INTRODUCED_T_5_4);
- removeAll(expected, REMOVED_T);
-
- addAll(unexpected, REMOVED_T);
- removeAll(unexpected, INTRODUCED_T);
- }
+ DO_EXPECT(IsAtLeastT(), MAINLINE_FOR_T_PLUS);
+ DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 4, 0), MAINLINE_FOR_T_5_4_PLUS);
+ DO_EXPECT(IsAtLeastT() && isAtLeastKernelVersion(5, 15, 0), MAINLINE_FOR_T_5_15_PLUS);
}
void checkFiles() {
diff --git a/tests/unit/java/com/android/server/BpfNetMapsTest.java b/tests/unit/java/com/android/server/BpfNetMapsTest.java
index e9fad9b..0718952 100644
--- a/tests/unit/java/com/android/server/BpfNetMapsTest.java
+++ b/tests/unit/java/com/android/server/BpfNetMapsTest.java
@@ -24,13 +24,26 @@
import static android.net.ConnectivityManager.FIREWALL_CHAIN_POWERSAVE;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_RESTRICTED;
import static android.net.ConnectivityManager.FIREWALL_CHAIN_STANDBY;
+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 com.android.server.BpfNetMaps.DOZABLE_MATCH;
+import static com.android.server.BpfNetMaps.HAPPY_BOX_MATCH;
+import static com.android.server.BpfNetMaps.IIF_MATCH;
+import static com.android.server.BpfNetMaps.LOCKDOWN_VPN_MATCH;
+import static com.android.server.BpfNetMaps.NO_MATCH;
+import static com.android.server.BpfNetMaps.PENALTY_BOX_MATCH;
+import static com.android.server.BpfNetMaps.POWERSAVE_MATCH;
+import static com.android.server.BpfNetMaps.RESTRICTED_MATCH;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import android.net.INetd;
@@ -68,7 +81,10 @@
private static final int TEST_UID = 10086;
private static final int[] TEST_UIDS = {10002, 10003};
- private static final String IFNAME = "wlan0";
+ private static final String TEST_IF_NAME = "wlan0";
+ private static final int TEST_IF_INDEX = 7;
+ private static final int NO_IIF = 0;
+ private static final int NULL_IIF = 0;
private static final String CHAINNAME = "fw_dozable";
private static final U32 UID_RULES_CONFIGURATION_KEY = new U32(0);
private static final List<Integer> FIREWALL_CHAINS = List.of(
@@ -85,43 +101,41 @@
private BpfNetMaps mBpfNetMaps;
@Mock INetd mNetd;
- private static final TestBpfMap<U32, U32> sConfigurationMap =
- new TestBpfMap<>(U32.class, U32.class);
+ @Mock BpfNetMaps.Dependencies mDeps;
+ private final BpfMap<U32, U32> mConfigurationMap = new TestBpfMap<>(U32.class, U32.class);
+ private final BpfMap<U32, UidOwnerValue> mUidOwnerMap =
+ new TestBpfMap<>(U32.class, UidOwnerValue.class);
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mBpfNetMaps = new BpfNetMaps(mNetd);
- BpfNetMaps.initialize(makeDependencies());
- sConfigurationMap.clear();
- }
-
- private static BpfNetMaps.Dependencies makeDependencies() {
- return new BpfNetMaps.Dependencies() {
- @Override
- public BpfMap<U32, U32> getConfigurationMap() {
- return sConfigurationMap;
- }
- };
+ doReturn(TEST_IF_INDEX).when(mDeps).getIfIndex(TEST_IF_NAME);
+ BpfNetMaps.setConfigurationMapForTest(mConfigurationMap);
+ BpfNetMaps.setUidOwnerMapForTest(mUidOwnerMap);
+ mBpfNetMaps = new BpfNetMaps(mNetd, mDeps);
}
@Test
public void testBpfNetMapsBeforeT() throws Exception {
assumeFalse(SdkLevel.isAtLeastT());
- mBpfNetMaps.addUidInterfaceRules(IFNAME, TEST_UIDS);
- verify(mNetd).firewallAddUidInterfaceRules(IFNAME, TEST_UIDS);
+ mBpfNetMaps.addUidInterfaceRules(TEST_IF_NAME, TEST_UIDS);
+ verify(mNetd).firewallAddUidInterfaceRules(TEST_IF_NAME, TEST_UIDS);
mBpfNetMaps.removeUidInterfaceRules(TEST_UIDS);
verify(mNetd).firewallRemoveUidInterfaceRules(TEST_UIDS);
mBpfNetMaps.setNetPermForUids(PERMISSION_INTERNET, TEST_UIDS);
verify(mNetd).trafficSetNetPermForUids(PERMISSION_INTERNET, TEST_UIDS);
}
- private void doTestIsChainEnabled(final List<Integer> enableChains) throws Exception {
+ private long getMatch(final List<Integer> chains) {
long match = 0;
- for (final int chain: enableChains) {
+ for (final int chain: chains) {
match |= mBpfNetMaps.getMatchByFirewallChain(chain);
}
- sConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(match));
+ return match;
+ }
+
+ private void doTestIsChainEnabled(final List<Integer> enableChains) throws Exception {
+ mConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(getMatch(enableChains)));
for (final int chain: FIREWALL_CHAINS) {
final String testCase = "EnabledChains: " + enableChains + " CheckedChain: " + chain;
@@ -187,17 +201,17 @@
expectedMatch |= mBpfNetMaps.getMatchByFirewallChain(chain);
}
- assertEquals(0, sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val);
+ assertEquals(0, mConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val);
for (final int chain: testChains) {
mBpfNetMaps.setChildChain(chain, true /* enable */);
}
- assertEquals(expectedMatch, sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val);
+ assertEquals(expectedMatch, mConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val);
for (final int chain: testChains) {
mBpfNetMaps.setChildChain(chain, false /* enable */);
}
- assertEquals(0, sConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val);
+ assertEquals(0, mConfigurationMap.getValue(UID_RULES_CONFIGURATION_KEY).val);
}
private void doTestSetChildChain(final int testChain) throws Exception {
@@ -207,7 +221,7 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testSetChildChain() throws Exception {
- sConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(0));
+ mConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(0));
doTestSetChildChain(FIREWALL_CHAIN_DOZABLE);
doTestSetChildChain(FIREWALL_CHAIN_STANDBY);
doTestSetChildChain(FIREWALL_CHAIN_POWERSAVE);
@@ -221,7 +235,7 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testSetChildChainMultipleChain() throws Exception {
- sConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(0));
+ mConfigurationMap.updateEntry(UID_RULES_CONFIGURATION_KEY, new U32(0));
doTestSetChildChain(List.of(
FIREWALL_CHAIN_DOZABLE,
FIREWALL_CHAIN_STANDBY));
@@ -249,4 +263,390 @@
assertThrows(UnsupportedOperationException.class,
() -> mBpfNetMaps.setChildChain(FIREWALL_CHAIN_DOZABLE, true /* enable */));
}
+
+ private void checkUidOwnerValue(final long uid, final long expectedIif,
+ final long expectedMatch) throws Exception {
+ final UidOwnerValue config = mUidOwnerMap.getValue(new U32(uid));
+ if (expectedMatch == 0) {
+ assertNull(config);
+ } else {
+ assertEquals(expectedIif, config.iif);
+ assertEquals(expectedMatch, config.rule);
+ }
+ }
+
+ private void doTestRemoveNaughtyApp(final long iif, final long match) throws Exception {
+ mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+
+ mBpfNetMaps.removeNaughtyApp(TEST_UID);
+
+ checkUidOwnerValue(TEST_UID, iif, match & ~PENALTY_BOX_MATCH);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testRemoveNaughtyApp() throws Exception {
+ doTestRemoveNaughtyApp(NO_IIF, PENALTY_BOX_MATCH);
+
+ // PENALTY_BOX_MATCH with other matches
+ doTestRemoveNaughtyApp(NO_IIF, PENALTY_BOX_MATCH | DOZABLE_MATCH | POWERSAVE_MATCH);
+
+ // PENALTY_BOX_MATCH with IIF_MATCH
+ doTestRemoveNaughtyApp(TEST_IF_INDEX, PENALTY_BOX_MATCH | IIF_MATCH);
+
+ // PENALTY_BOX_MATCH is not enabled
+ doTestRemoveNaughtyApp(NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testRemoveNaughtyAppMissingUid() {
+ // UidOwnerMap does not have entry for TEST_UID
+ assertThrows(ServiceSpecificException.class,
+ () -> mBpfNetMaps.removeNaughtyApp(TEST_UID));
+ }
+
+ @Test
+ @IgnoreAfter(Build.VERSION_CODES.S_V2)
+ public void testRemoveNaughtyAppBeforeT() {
+ assertThrows(UnsupportedOperationException.class,
+ () -> mBpfNetMaps.removeNaughtyApp(TEST_UID));
+ }
+
+ private void doTestAddNaughtyApp(final long iif, final long match) throws Exception {
+ if (match != NO_MATCH) {
+ mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+ }
+
+ mBpfNetMaps.addNaughtyApp(TEST_UID);
+
+ checkUidOwnerValue(TEST_UID, iif, match | PENALTY_BOX_MATCH);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testAddNaughtyApp() throws Exception {
+ doTestAddNaughtyApp(NO_IIF, NO_MATCH);
+
+ // Other matches are enabled
+ doTestAddNaughtyApp(NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH);
+
+ // IIF_MATCH is enabled
+ doTestAddNaughtyApp(TEST_IF_INDEX, IIF_MATCH);
+
+ // PENALTY_BOX_MATCH is already enabled
+ doTestAddNaughtyApp(NO_IIF, PENALTY_BOX_MATCH | DOZABLE_MATCH);
+ }
+
+ @Test
+ @IgnoreAfter(Build.VERSION_CODES.S_V2)
+ public void testAddNaughtyAppBeforeT() {
+ assertThrows(UnsupportedOperationException.class,
+ () -> mBpfNetMaps.addNaughtyApp(TEST_UID));
+ }
+
+ private void doTestRemoveNiceApp(final long iif, final long match) throws Exception {
+ mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+
+ mBpfNetMaps.removeNiceApp(TEST_UID);
+
+ checkUidOwnerValue(TEST_UID, iif, match & ~HAPPY_BOX_MATCH);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testRemoveNiceApp() throws Exception {
+ doTestRemoveNiceApp(NO_IIF, HAPPY_BOX_MATCH);
+
+ // HAPPY_BOX_MATCH with other matches
+ doTestRemoveNiceApp(NO_IIF, HAPPY_BOX_MATCH | DOZABLE_MATCH | POWERSAVE_MATCH);
+
+ // HAPPY_BOX_MATCH with IIF_MATCH
+ doTestRemoveNiceApp(TEST_IF_INDEX, HAPPY_BOX_MATCH | IIF_MATCH);
+
+ // HAPPY_BOX_MATCH is not enabled
+ doTestRemoveNiceApp(NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testRemoveNiceAppMissingUid() {
+ // UidOwnerMap does not have entry for TEST_UID
+ assertThrows(ServiceSpecificException.class,
+ () -> mBpfNetMaps.removeNiceApp(TEST_UID));
+ }
+
+ @Test
+ @IgnoreAfter(Build.VERSION_CODES.S_V2)
+ public void testRemoveNiceAppBeforeT() {
+ assertThrows(UnsupportedOperationException.class,
+ () -> mBpfNetMaps.removeNiceApp(TEST_UID));
+ }
+
+ private void doTestAddNiceApp(final long iif, final long match) throws Exception {
+ if (match != NO_MATCH) {
+ mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+ }
+
+ mBpfNetMaps.addNiceApp(TEST_UID);
+
+ checkUidOwnerValue(TEST_UID, iif, match | HAPPY_BOX_MATCH);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testAddNiceApp() throws Exception {
+ doTestAddNiceApp(NO_IIF, NO_MATCH);
+
+ // Other matches are enabled
+ doTestAddNiceApp(NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH);
+
+ // IIF_MATCH is enabled
+ doTestAddNiceApp(TEST_IF_INDEX, IIF_MATCH);
+
+ // HAPPY_BOX_MATCH is already enabled
+ doTestAddNiceApp(NO_IIF, HAPPY_BOX_MATCH | DOZABLE_MATCH);
+ }
+
+ @Test
+ @IgnoreAfter(Build.VERSION_CODES.S_V2)
+ public void testAddNiceAppBeforeT() {
+ assertThrows(UnsupportedOperationException.class,
+ () -> mBpfNetMaps.addNiceApp(TEST_UID));
+ }
+
+ private void doTestUpdateUidLockdownRule(final long iif, final long match, final boolean add)
+ throws Exception {
+ if (match != NO_MATCH) {
+ mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(iif, match));
+ }
+
+ mBpfNetMaps.updateUidLockdownRule(TEST_UID, add);
+
+ final long expectedMatch = add ? match | LOCKDOWN_VPN_MATCH : match & ~LOCKDOWN_VPN_MATCH;
+ checkUidOwnerValue(TEST_UID, iif, expectedMatch);
+ }
+
+ private static final boolean ADD = true;
+ private static final boolean REMOVE = false;
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testUpdateUidLockdownRuleAddLockdown() throws Exception {
+ doTestUpdateUidLockdownRule(NO_IIF, NO_MATCH, ADD);
+
+ // Other matches are enabled
+ doTestUpdateUidLockdownRule(
+ NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH, ADD);
+
+ // IIF_MATCH is enabled
+ doTestUpdateUidLockdownRule(TEST_IF_INDEX, DOZABLE_MATCH, ADD);
+
+ // LOCKDOWN_VPN_MATCH is already enabled
+ doTestUpdateUidLockdownRule(NO_IIF, LOCKDOWN_VPN_MATCH | DOZABLE_MATCH, ADD);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testUpdateUidLockdownRuleRemoveLockdown() throws Exception {
+ doTestUpdateUidLockdownRule(NO_IIF, LOCKDOWN_VPN_MATCH, REMOVE);
+
+ // LOCKDOWN_VPN_MATCH with other matches
+ doTestUpdateUidLockdownRule(
+ NO_IIF, LOCKDOWN_VPN_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH, REMOVE);
+
+ // LOCKDOWN_VPN_MATCH with IIF_MATCH
+ doTestUpdateUidLockdownRule(TEST_IF_INDEX, LOCKDOWN_VPN_MATCH | IIF_MATCH, REMOVE);
+
+ // LOCKDOWN_VPN_MATCH is not enabled
+ doTestUpdateUidLockdownRule(NO_IIF, POWERSAVE_MATCH | RESTRICTED_MATCH, REMOVE);
+ }
+
+ @Test
+ @IgnoreAfter(Build.VERSION_CODES.S_V2)
+ public void testUpdateUidLockdownRuleBeforeT() {
+ assertThrows(UnsupportedOperationException.class,
+ () -> mBpfNetMaps.updateUidLockdownRule(TEST_UID, true /* add */));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testAddUidInterfaceRules() throws Exception {
+ final int uid0 = TEST_UIDS[0];
+ final int uid1 = TEST_UIDS[1];
+
+ mBpfNetMaps.addUidInterfaceRules(TEST_IF_NAME, TEST_UIDS);
+
+ checkUidOwnerValue(uid0, TEST_IF_INDEX, IIF_MATCH);
+ checkUidOwnerValue(uid1, TEST_IF_INDEX, IIF_MATCH);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testAddUidInterfaceRulesWithOtherMatch() throws Exception {
+ final int uid0 = TEST_UIDS[0];
+ final int uid1 = TEST_UIDS[1];
+ final long match0 = DOZABLE_MATCH;
+ final long match1 = DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH;
+ mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(NO_IIF, match0));
+ mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(NO_IIF, match1));
+
+ mBpfNetMaps.addUidInterfaceRules(TEST_IF_NAME, TEST_UIDS);
+
+ checkUidOwnerValue(uid0, TEST_IF_INDEX, match0 | IIF_MATCH);
+ checkUidOwnerValue(uid1, TEST_IF_INDEX, match1 | IIF_MATCH);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testAddUidInterfaceRulesWithExistingIifMatch() throws Exception {
+ final int uid0 = TEST_UIDS[0];
+ final int uid1 = TEST_UIDS[1];
+ final long match0 = IIF_MATCH;
+ final long match1 = IIF_MATCH | DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH;
+ mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(TEST_IF_INDEX + 1, match0));
+ mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(NULL_IIF, match1));
+
+ mBpfNetMaps.addUidInterfaceRules(TEST_IF_NAME, TEST_UIDS);
+
+ checkUidOwnerValue(uid0, TEST_IF_INDEX, match0);
+ checkUidOwnerValue(uid1, TEST_IF_INDEX, match1);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testAddUidInterfaceRulesGetIfIndexFail() {
+ doReturn(0).when(mDeps).getIfIndex(TEST_IF_NAME);
+ assertThrows(ServiceSpecificException.class,
+ () -> mBpfNetMaps.addUidInterfaceRules(TEST_IF_NAME, TEST_UIDS));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testAddUidInterfaceRulesWithNullInterface() throws Exception {
+ final int uid0 = TEST_UIDS[0];
+ final int uid1 = TEST_UIDS[1];
+ final long match0 = IIF_MATCH;
+ final long match1 = IIF_MATCH | DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH;
+ mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(TEST_IF_INDEX, match0));
+ mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(NULL_IIF, match1));
+
+ mBpfNetMaps.addUidInterfaceRules(null /* ifName */, TEST_UIDS);
+
+ checkUidOwnerValue(uid0, NULL_IIF, match0);
+ checkUidOwnerValue(uid1, NULL_IIF, match1);
+ }
+
+ private void doTestRemoveUidInterfaceRules(final long iif0, final long match0,
+ final long iif1, final long match1) throws Exception {
+ final int uid0 = TEST_UIDS[0];
+ final int uid1 = TEST_UIDS[1];
+ mUidOwnerMap.updateEntry(new U32(uid0), new UidOwnerValue(iif0, match0));
+ mUidOwnerMap.updateEntry(new U32(uid1), new UidOwnerValue(iif1, match1));
+
+ mBpfNetMaps.removeUidInterfaceRules(TEST_UIDS);
+
+ checkUidOwnerValue(uid0, NO_IIF, match0 & ~IIF_MATCH);
+ checkUidOwnerValue(uid1, NO_IIF, match1 & ~IIF_MATCH);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testRemoveUidInterfaceRules() throws Exception {
+ doTestRemoveUidInterfaceRules(TEST_IF_INDEX, IIF_MATCH, NULL_IIF, IIF_MATCH);
+
+ // IIF_MATCH and other matches are enabled
+ doTestRemoveUidInterfaceRules(TEST_IF_INDEX, IIF_MATCH | DOZABLE_MATCH,
+ NULL_IIF, IIF_MATCH | DOZABLE_MATCH | RESTRICTED_MATCH);
+
+ // IIF_MATCH is not enabled
+ doTestRemoveUidInterfaceRules(NO_IIF, DOZABLE_MATCH,
+ NO_IIF, DOZABLE_MATCH | POWERSAVE_MATCH | RESTRICTED_MATCH);
+ }
+
+ private void doTestSetUidRule(final List<Integer> testChains) throws Exception {
+ mUidOwnerMap.updateEntry(new U32(TEST_UID), new UidOwnerValue(TEST_IF_INDEX, IIF_MATCH));
+
+ for (final int chain: testChains) {
+ final int ruleToAddMatch = mBpfNetMaps.isFirewallAllowList(chain)
+ ? FIREWALL_RULE_ALLOW : FIREWALL_RULE_DENY;
+ mBpfNetMaps.setUidRule(chain, TEST_UID, ruleToAddMatch);
+ }
+
+ checkUidOwnerValue(TEST_UID, TEST_IF_INDEX, IIF_MATCH | getMatch(testChains));
+
+ for (final int chain: testChains) {
+ final int ruleToRemoveMatch = mBpfNetMaps.isFirewallAllowList(chain)
+ ? FIREWALL_RULE_DENY : FIREWALL_RULE_ALLOW;
+ mBpfNetMaps.setUidRule(chain, TEST_UID, ruleToRemoveMatch);
+ }
+
+ checkUidOwnerValue(TEST_UID, TEST_IF_INDEX, IIF_MATCH);
+ }
+
+ private void doTestSetUidRule(final int testChain) throws Exception {
+ doTestSetUidRule(List.of(testChain));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testSetUidRule() throws Exception {
+ doTestSetUidRule(FIREWALL_CHAIN_DOZABLE);
+ doTestSetUidRule(FIREWALL_CHAIN_STANDBY);
+ doTestSetUidRule(FIREWALL_CHAIN_POWERSAVE);
+ doTestSetUidRule(FIREWALL_CHAIN_RESTRICTED);
+ doTestSetUidRule(FIREWALL_CHAIN_LOW_POWER_STANDBY);
+ doTestSetUidRule(FIREWALL_CHAIN_OEM_DENY_1);
+ doTestSetUidRule(FIREWALL_CHAIN_OEM_DENY_2);
+ doTestSetUidRule(FIREWALL_CHAIN_OEM_DENY_3);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testSetUidRuleMultipleChain() throws Exception {
+ doTestSetUidRule(List.of(
+ FIREWALL_CHAIN_DOZABLE,
+ FIREWALL_CHAIN_STANDBY));
+ doTestSetUidRule(List.of(
+ FIREWALL_CHAIN_DOZABLE,
+ FIREWALL_CHAIN_STANDBY,
+ FIREWALL_CHAIN_POWERSAVE,
+ FIREWALL_CHAIN_RESTRICTED));
+ doTestSetUidRule(FIREWALL_CHAINS);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testSetUidRuleRemoveRuleFromUidWithNoRule() {
+ final Class<ServiceSpecificException> expected = ServiceSpecificException.class;
+ assertThrows(expected,
+ () -> mBpfNetMaps.setUidRule(FIREWALL_CHAIN_DOZABLE, TEST_UID, FIREWALL_RULE_DENY));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testSetUidRuleInvalidChain() {
+ final Class<ServiceSpecificException> expected = ServiceSpecificException.class;
+ assertThrows(expected,
+ () -> mBpfNetMaps.setUidRule(-1 /* childChain */, TEST_UID, FIREWALL_RULE_ALLOW));
+ assertThrows(expected,
+ () -> mBpfNetMaps.setUidRule(1000 /* childChain */, TEST_UID, FIREWALL_RULE_ALLOW));
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testSetUidRuleInvalidRule() {
+ final Class<ServiceSpecificException> expected = ServiceSpecificException.class;
+ assertThrows(expected, () ->
+ mBpfNetMaps.setUidRule(FIREWALL_CHAIN_DOZABLE, TEST_UID, -1 /* firewallRule */));
+ assertThrows(expected, () ->
+ mBpfNetMaps.setUidRule(FIREWALL_CHAIN_DOZABLE, TEST_UID, 1000 /* firewallRule */));
+ }
+
+ @Test
+ @IgnoreAfter(Build.VERSION_CODES.S_V2)
+ public void testSetUidRuleBeforeT() {
+ assertThrows(UnsupportedOperationException.class, () ->
+ mBpfNetMaps.setUidRule(FIREWALL_CHAIN_DOZABLE, TEST_UID, FIREWALL_RULE_ALLOW));
+ }
}
diff --git a/tests/unit/java/com/android/server/NsdServiceTest.java b/tests/unit/java/com/android/server/NsdServiceTest.java
index 9365bee..58d002a 100644
--- a/tests/unit/java/com/android/server/NsdServiceTest.java
+++ b/tests/unit/java/com/android/server/NsdServiceTest.java
@@ -86,10 +86,15 @@
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
public class NsdServiceTest {
-
static final int PROTOCOL = NsdManager.PROTOCOL_DNS_SD;
private static final long CLEANUP_DELAY_MS = 500;
private static final long TIMEOUT_MS = 500;
+ private static final String SERVICE_NAME = "a_name";
+ private static final String SERVICE_TYPE = "a_type";
+ private static final String SERVICE_FULL_NAME = SERVICE_NAME + "." + SERVICE_TYPE;
+ private static final String DOMAIN_NAME = "mytestdevice.local";
+ private static final int PORT = 2201;
+ private static final int IFACE_IDX_ANY = 0;
// Records INsdManagerCallback created when NsdService#connect is called.
// Only accessed on the test thread, since NsdService#connect is called by the NsdManager
@@ -103,6 +108,7 @@
@Mock MDnsManager mMockMDnsM;
HandlerThread mThread;
TestHandler mHandler;
+ NsdService mService;
private static class LinkToDeathRecorder extends Binder {
IBinder.DeathRecipient mDr;
@@ -134,6 +140,8 @@
doReturn(true).when(mMockMDnsM).discover(anyInt(), anyString(), anyInt());
doReturn(true).when(mMockMDnsM).resolve(
anyInt(), anyString(), anyString(), anyString(), anyInt());
+
+ mService = makeService();
}
@After
@@ -147,18 +155,14 @@
@Test
@DisableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testPreSClients() throws Exception {
- NsdService service = makeService();
-
// Pre S client connected, the daemon should be started.
- connectClient(service);
- waitForIdle();
+ connectClient(mService);
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
verify(mMockMDnsM, times(1)).registerEventListener(any());
verify(mMockMDnsM, times(1)).startDaemon();
- connectClient(service);
- waitForIdle();
+ connectClient(mService);
final INsdManagerCallback cb2 = getCallback();
final IBinder.DeathRecipient deathRecipient2 = verifyLinkToDeath(cb2);
// Daemon has been started, it should not try to start it again.
@@ -178,19 +182,15 @@
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testNoDaemonStartedWhenClientsConnect() throws Exception {
- final NsdService service = makeService();
-
// Creating an NsdManager will not cause daemon startup.
- connectClient(service);
- waitForIdle();
+ connectClient(mService);
verify(mMockMDnsM, never()).registerEventListener(any());
verify(mMockMDnsM, never()).startDaemon();
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient1 = verifyLinkToDeath(cb1);
// Creating another NsdManager will not cause daemon startup either.
- connectClient(service);
- waitForIdle();
+ connectClient(mService);
verify(mMockMDnsM, never()).registerEventListener(any());
verify(mMockMDnsM, never()).startDaemon();
final INsdManagerCallback cb2 = getCallback();
@@ -216,70 +216,66 @@
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testClientRequestsAreGCedAtDisconnection() throws Exception {
- NsdService service = makeService();
-
- NsdManager client = connectClient(service);
- waitForIdle();
+ final NsdManager client = connectClient(mService);
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
verify(mMockMDnsM, never()).registerEventListener(any());
verify(mMockMDnsM, never()).startDaemon();
- NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
- request.setPort(2201);
+ final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+ request.setPort(PORT);
// Client registration request
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
client.registerService(request, PROTOCOL, listener1);
waitForIdle();
- verify(mMockMDnsM, times(1)).registerEventListener(any());
- verify(mMockMDnsM, times(1)).startDaemon();
- verify(mMockMDnsM, times(1)).registerService(
- eq(2), eq("a_name"), eq("a_type"), eq(2201), any(), eq(0));
+ verify(mMockMDnsM).registerEventListener(any());
+ verify(mMockMDnsM).startDaemon();
+ verify(mMockMDnsM).registerService(
+ eq(2), eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY));
// Client discovery request
NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
- client.discoverServices("a_type", PROTOCOL, listener2);
+ client.discoverServices(SERVICE_TYPE, PROTOCOL, listener2);
waitForIdle();
- verify(mMockMDnsM, times(1)).discover(eq(3), eq("a_type"), eq(0));
+ verify(mMockMDnsM).discover(3 /* id */, SERVICE_TYPE, IFACE_IDX_ANY);
// Client resolve request
NsdManager.ResolveListener listener3 = mock(NsdManager.ResolveListener.class);
client.resolveService(request, listener3);
waitForIdle();
- verify(mMockMDnsM, times(1)).resolve(
- eq(4), eq("a_name"), eq("a_type"), eq("local."), eq(0));
+ verify(mMockMDnsM).resolve(
+ 4 /* id */, SERVICE_NAME, SERVICE_TYPE, "local." /* domain */, IFACE_IDX_ANY);
// Client disconnects, stop the daemon after CLEANUP_DELAY_MS.
deathRecipient.binderDied();
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
// checks that request are cleaned
- verify(mMockMDnsM, times(1)).stopOperation(eq(2));
- verify(mMockMDnsM, times(1)).stopOperation(eq(3));
- verify(mMockMDnsM, times(1)).stopOperation(eq(4));
+ verify(mMockMDnsM).stopOperation(2 /* id */);
+ verify(mMockMDnsM).stopOperation(3 /* id */);
+ verify(mMockMDnsM).stopOperation(4 /* id */);
}
@Test
@EnableCompatChanges(NsdManager.RUN_NATIVE_NSD_ONLY_IF_LEGACY_APPS)
public void testCleanupDelayNoRequestActive() throws Exception {
- NsdService service = makeService();
- NsdManager client = connectClient(service);
+ final NsdManager client = connectClient(mService);
- NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
- request.setPort(2201);
+ final NsdServiceInfo request = new NsdServiceInfo(SERVICE_NAME, SERVICE_TYPE);
+ request.setPort(PORT);
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
client.registerService(request, PROTOCOL, listener1);
waitForIdle();
- verify(mMockMDnsM, times(1)).registerEventListener(any());
- verify(mMockMDnsM, times(1)).startDaemon();
+ verify(mMockMDnsM).registerEventListener(any());
+ verify(mMockMDnsM).startDaemon();
final INsdManagerCallback cb1 = getCallback();
final IBinder.DeathRecipient deathRecipient = verifyLinkToDeath(cb1);
- verify(mMockMDnsM, times(1)).registerService(
- eq(2), eq("a_name"), eq("a_type"), eq(2201), any(), eq(0));
+ verify(mMockMDnsM).registerService(
+ eq(2), eq(SERVICE_NAME), eq(SERVICE_TYPE), eq(PORT), any(), eq(IFACE_IDX_ANY));
client.unregisterService(listener1);
waitForIdle();
- verify(mMockMDnsM, times(1)).stopOperation(eq(2));
+ verify(mMockMDnsM).stopOperation(2 /* id */);
verifyDelayMaybeStopDaemon(CLEANUP_DELAY_MS);
reset(mMockMDnsM);
@@ -291,33 +287,28 @@
@Test
public void testDiscoverOnTetheringDownstream() throws Exception {
- NsdService service = makeService();
- NsdManager client = connectClient(service);
-
- final String serviceType = "a_type";
- final String serviceName = "a_name";
- final String domainName = "mytestdevice.local";
+ final NsdManager client = connectClient(mService);
final int interfaceIdx = 123;
final NsdManager.DiscoveryListener discListener = mock(NsdManager.DiscoveryListener.class);
- client.discoverServices(serviceType, NsdManager.PROTOCOL_DNS_SD, discListener);
+ client.discoverServices(SERVICE_TYPE, PROTOCOL, discListener);
waitForIdle();
final ArgumentCaptor<IMDnsEventListener> listenerCaptor =
ArgumentCaptor.forClass(IMDnsEventListener.class);
verify(mMockMDnsM).registerEventListener(listenerCaptor.capture());
final ArgumentCaptor<Integer> discIdCaptor = ArgumentCaptor.forClass(Integer.class);
- verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(serviceType),
+ verify(mMockMDnsM).discover(discIdCaptor.capture(), eq(SERVICE_TYPE),
eq(0) /* interfaceIdx */);
// NsdManager uses a separate HandlerThread to dispatch callbacks (on ServiceHandler), so
// this needs to use a timeout
- verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(serviceType);
+ verify(discListener, timeout(TIMEOUT_MS)).onDiscoveryStarted(SERVICE_TYPE);
final DiscoveryInfo discoveryInfo = new DiscoveryInfo(
discIdCaptor.getValue(),
IMDnsEventListener.SERVICE_FOUND,
- serviceName,
- serviceType,
- domainName,
+ SERVICE_NAME,
+ SERVICE_TYPE,
+ DOMAIN_NAME,
interfaceIdx,
INetd.LOCAL_NET_ID); // LOCAL_NET_ID (99) used on tethering downstreams
final IMDnsEventListener eventListener = listenerCaptor.getValue();
@@ -328,8 +319,8 @@
ArgumentCaptor.forClass(NsdServiceInfo.class);
verify(discListener, timeout(TIMEOUT_MS)).onServiceFound(discoveredInfoCaptor.capture());
final NsdServiceInfo foundInfo = discoveredInfoCaptor.getValue();
- assertEquals(serviceName, foundInfo.getServiceName());
- assertEquals(serviceType, foundInfo.getServiceType());
+ assertEquals(SERVICE_NAME, foundInfo.getServiceName());
+ assertEquals(SERVICE_TYPE, foundInfo.getServiceType());
assertNull(foundInfo.getHost());
assertNull(foundInfo.getNetwork());
assertEquals(interfaceIdx, foundInfo.getInterfaceIndex());
@@ -340,19 +331,18 @@
waitForIdle();
final ArgumentCaptor<Integer> resolvIdCaptor = ArgumentCaptor.forClass(Integer.class);
- verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(serviceName), eq(serviceType),
+ verify(mMockMDnsM).resolve(resolvIdCaptor.capture(), eq(SERVICE_NAME), eq(SERVICE_TYPE),
eq("local.") /* domain */, eq(interfaceIdx));
final int servicePort = 10123;
- final String serviceFullName = serviceName + "." + serviceType;
final ResolutionInfo resolutionInfo = new ResolutionInfo(
resolvIdCaptor.getValue(),
IMDnsEventListener.SERVICE_RESOLVED,
null /* serviceName */,
null /* serviceType */,
null /* domain */,
- serviceFullName,
- domainName,
+ SERVICE_FULL_NAME,
+ DOMAIN_NAME,
servicePort,
new byte[0] /* txtRecord */,
interfaceIdx);
@@ -362,14 +352,14 @@
waitForIdle();
final ArgumentCaptor<Integer> getAddrIdCaptor = ArgumentCaptor.forClass(Integer.class);
- verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(domainName),
+ verify(mMockMDnsM).getServiceAddress(getAddrIdCaptor.capture(), eq(DOMAIN_NAME),
eq(interfaceIdx));
final String serviceAddress = "192.0.2.123";
final GetAddressInfo addressInfo = new GetAddressInfo(
getAddrIdCaptor.getValue(),
IMDnsEventListener.SERVICE_GET_ADDR_SUCCESS,
- serviceFullName,
+ SERVICE_FULL_NAME,
serviceAddress,
interfaceIdx,
INetd.LOCAL_NET_ID);
@@ -380,8 +370,8 @@
ArgumentCaptor.forClass(NsdServiceInfo.class);
verify(resolveListener, timeout(TIMEOUT_MS)).onServiceResolved(resInfoCaptor.capture());
final NsdServiceInfo resolvedService = resInfoCaptor.getValue();
- assertEquals(serviceName, resolvedService.getServiceName());
- assertEquals("." + serviceType, resolvedService.getServiceType());
+ assertEquals(SERVICE_NAME, resolvedService.getServiceName());
+ assertEquals("." + SERVICE_TYPE, resolvedService.getServiceType());
assertEquals(InetAddresses.parseNumericAddress(serviceAddress), resolvedService.getHost());
assertEquals(servicePort, resolvedService.getPort());
assertNull(resolvedService.getNetwork());
@@ -415,7 +405,10 @@
}
NsdManager connectClient(NsdService service) {
- return new NsdManager(mContext, service);
+ final NsdManager nsdManager = new NsdManager(mContext, service);
+ // Wait for client registration done.
+ waitForIdle();
+ return nsdManager;
}
void verifyDelayMaybeStopDaemon(long cleanupDelayMs) throws Exception {
diff --git a/tests/unit/java/com/android/server/VpnManagerServiceTest.java b/tests/unit/java/com/android/server/VpnManagerServiceTest.java
index ece13b3..c814cc5 100644
--- a/tests/unit/java/com/android/server/VpnManagerServiceTest.java
+++ b/tests/unit/java/com/android/server/VpnManagerServiceTest.java
@@ -24,7 +24,6 @@
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
@@ -32,15 +31,18 @@
import static org.mockito.Mockito.verify;
import android.annotation.UserIdInt;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.INetd;
+import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.INetworkManagementService;
import android.os.Looper;
+import android.os.UserHandle;
import android.os.UserManager;
import androidx.test.filters.SmallTest;
@@ -48,14 +50,15 @@
import com.android.server.connectivity.Vpn;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.HandlerUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
@RunWith(DevSdkIgnoreRunner.class)
@IgnoreUpTo(R) // VpnManagerService is not available before R
@@ -64,18 +67,23 @@
@Rule
public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
- @Spy Context mContext;
+ private static final int TIMEOUT_MS = 2_000;
+
+ @Mock Context mContext;
+ @Mock Context mSystemContext;
+ @Mock Context mUserAllContext;
private HandlerThread mHandlerThread;
- @Mock private Handler mHandler;
@Mock private Vpn mVpn;
@Mock private INetworkManagementService mNms;
@Mock private ConnectivityManager mCm;
@Mock private UserManager mUserManager;
@Mock private INetd mNetd;
@Mock private PackageManager mPackageManager;
+
private VpnManagerServiceDependencies mDeps;
private VpnManagerService mService;
-
+ private BroadcastReceiver mUserPresentReceiver;
+ private BroadcastReceiver mIntentReceiver;
private final String mNotMyVpnPkg = "com.not.my.vpn";
class VpnManagerServiceDependencies extends VpnManagerService.Dependencies {
@@ -107,46 +115,54 @@
mHandlerThread = new HandlerThread("TestVpnManagerService");
mDeps = new VpnManagerServiceDependencies();
- doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+ doReturn(mUserAllContext).when(mContext).createContextAsUser(UserHandle.ALL, 0);
+ doReturn(mSystemContext).when(mContext).createContextAsUser(UserHandle.SYSTEM, 0);
doReturn(mPackageManager).when(mContext).getPackageManager();
setMockedPackages(mPackageManager, sPackages);
mockService(mContext, ConnectivityManager.class, Context.CONNECTIVITY_SERVICE, mCm);
mockService(mContext, UserManager.class, Context.USER_SERVICE, mUserManager);
-
- doReturn(new Intent()).when(mContext).registerReceiver(
- any() /* receiver */,
- any() /* intentFilter */,
- any() /* broadcastPermission */,
- eq(mHandler) /* scheduler */);
doReturn(SYSTEM_USER).when(mUserManager).getUserInfo(eq(SYSTEM_USER_ID));
+
mService = new VpnManagerService(mContext, mDeps);
+ mService.systemReady();
+
+ final ArgumentCaptor<BroadcastReceiver> intentReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ final ArgumentCaptor<BroadcastReceiver> userPresentReceiverCaptor =
+ ArgumentCaptor.forClass(BroadcastReceiver.class);
+ verify(mSystemContext).registerReceiver(
+ userPresentReceiverCaptor.capture(), any(), any(), any());
+ verify(mUserAllContext, times(2)).registerReceiver(
+ intentReceiverCaptor.capture(), any(), any(), any());
+ mUserPresentReceiver = userPresentReceiverCaptor.getValue();
+ mIntentReceiver = intentReceiverCaptor.getValue();
+
+ // Add user to create vpn in mVpn
+ onUserStarted(SYSTEM_USER_ID);
+ assertNotNull(mService.mVpns.get(SYSTEM_USER_ID));
}
@Test
public void testUpdateAppExclusionList() {
- // Add user to create vpn in mVpn
- mService.onUserStarted(SYSTEM_USER_ID);
- assertNotNull(mService.mVpns.get(SYSTEM_USER_ID));
-
// Start vpn
mService.startVpnProfile(TEST_VPN_PKG);
verify(mVpn).startVpnProfile(eq(TEST_VPN_PKG));
// Remove package due to package replaced.
- mService.onPackageRemoved(PKGS[0], PKG_UIDS[0], true /* isReplacing */);
+ onPackageRemoved(PKGS[0], PKG_UIDS[0], true /* isReplacing */);
verify(mVpn, never()).refreshPlatformVpnAppExclusionList();
// Add package due to package replaced.
- mService.onPackageAdded(PKGS[0], PKG_UIDS[0], true /* isReplacing */);
+ onPackageAdded(PKGS[0], PKG_UIDS[0], true /* isReplacing */);
verify(mVpn, never()).refreshPlatformVpnAppExclusionList();
// Remove package
- mService.onPackageRemoved(PKGS[0], PKG_UIDS[0], false /* isReplacing */);
+ onPackageRemoved(PKGS[0], PKG_UIDS[0], false /* isReplacing */);
verify(mVpn).refreshPlatformVpnAppExclusionList();
// Add the package back
- mService.onPackageAdded(PKGS[0], PKG_UIDS[0], false /* isReplacing */);
+ onPackageAdded(PKGS[0], PKG_UIDS[0], false /* isReplacing */);
verify(mVpn, times(2)).refreshPlatformVpnAppExclusionList();
}
@@ -160,4 +176,69 @@
public void testStopVpnProfileFromDiffPackage() {
assertThrows(SecurityException.class, () -> mService.stopVpnProfile(mNotMyVpnPkg));
}
+
+ @Test
+ public void testGetProvisionedVpnProfileStateFromDiffPackage() {
+ assertThrows(SecurityException.class, () ->
+ mService.getProvisionedVpnProfileState(mNotMyVpnPkg));
+ }
+
+ @Test
+ public void testGetProvisionedVpnProfileState() {
+ mService.getProvisionedVpnProfileState(TEST_VPN_PKG);
+ verify(mVpn).getProvisionedVpnProfileState(TEST_VPN_PKG);
+ }
+
+ private Intent buildIntent(String action, String packageName, int userId, int uid,
+ boolean isReplacing) {
+ final Intent intent = new Intent(action);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ intent.putExtra(Intent.EXTRA_REPLACING, isReplacing);
+ if (packageName != null) {
+ intent.setData(Uri.fromParts("package" /* scheme */, packageName, null /* fragment */));
+ }
+
+ return intent;
+ }
+
+ private void sendIntent(Intent intent) {
+ final Handler h = mHandlerThread.getThreadHandler();
+
+ // Send in handler thread.
+ h.post(() -> mIntentReceiver.onReceive(mContext, intent));
+ HandlerUtils.waitForIdle(mHandlerThread, TIMEOUT_MS);
+ }
+
+ private void onUserStarted(int userId) {
+ sendIntent(buildIntent(Intent.ACTION_USER_STARTED,
+ null /* packageName */, userId, -1 /* uid */, false /* isReplacing */));
+ }
+
+ private void onPackageAdded(String packageName, int userId, int uid, boolean isReplacing) {
+ sendIntent(buildIntent(Intent.ACTION_PACKAGE_ADDED, packageName, userId, uid, isReplacing));
+ }
+
+ private void onPackageAdded(String packageName, int uid, boolean isReplacing) {
+ onPackageAdded(packageName, UserHandle.USER_SYSTEM, uid, isReplacing);
+ }
+
+ private void onPackageRemoved(String packageName, int userId, int uid, boolean isReplacing) {
+ sendIntent(buildIntent(Intent.ACTION_PACKAGE_REMOVED, packageName, userId, uid,
+ isReplacing));
+ }
+
+ private void onPackageRemoved(String packageName, int uid, boolean isReplacing) {
+ onPackageRemoved(packageName, UserHandle.USER_SYSTEM, uid, isReplacing);
+ }
+
+ @Test
+ public void testReceiveIntentFromNonHandlerThread() {
+ assertThrows(IllegalStateException.class, () ->
+ mIntentReceiver.onReceive(mContext, buildIntent(Intent.ACTION_PACKAGE_REMOVED,
+ PKGS[0], UserHandle.USER_SYSTEM, PKG_UIDS[0], true /* isReplacing */)));
+
+ assertThrows(IllegalStateException.class, () ->
+ mUserPresentReceiver.onReceive(mContext, new Intent(Intent.ACTION_USER_PRESENT)));
+ }
}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 0891ee3..5c1992d 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -232,7 +232,7 @@
private static final int TEST_TUNNEL_RESOURCE_ID = 0x2345;
private static final long TEST_TIMEOUT_MS = 500L;
private static final String PRIMARY_USER_APP_EXCLUDE_KEY =
- "VPN_APP_EXCLUDED_27_com.testvpn.vpn";
+ "VPNAPPEXCLUDED_27_com.testvpn.vpn";
static final String PKGS_BYTES = getPackageByteString(List.of(PKGS));
private static final Range<Integer> PRIMARY_USER_RANGE = uidRangeForUser(PRIMARY_USER.id);
@@ -1360,6 +1360,31 @@
}
@Test
+ public void testReconnectVpnManagerVpnWithAlwaysOnEnabled() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+
+ // Enable VPN always-on for TEST_VPN_PKG.
+ assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, false /* lockdown */,
+ null /* lockdownAllowlist */));
+
+ // Reset to verify next startVpnProfile.
+ reset(mAppOps);
+
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+
+ // Reconnect the vpn with different package will cause exception.
+ assertThrows(SecurityException.class, () -> vpn.startVpnProfile(PKGS[0]));
+
+ // Reconnect the vpn again with the vpn always on package w/o exception.
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+ }
+
+ @Test
public void testSetPackageAuthorizationVpnService() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index 5400a00..f6fb45c 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -44,6 +44,7 @@
import androidx.test.filters.SmallTest;
import com.android.frameworks.tests.net.R;
+import com.android.server.BpfNetMaps;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -74,6 +75,7 @@
private File mTestProc;
private NetworkStatsFactory mFactory;
@Mock private Context mContext;
+ @Mock private BpfNetMaps mBpfNetMaps;
@Before
public void setUp() throws Exception {
@@ -84,7 +86,7 @@
// applications. So in order to have a test support native library, the native code
// related to networkStatsFactory is compiled to a minimal native library and loaded here.
System.loadLibrary("networkstatsfactorytestjni");
- mFactory = new NetworkStatsFactory(mContext, mTestProc, false);
+ mFactory = new NetworkStatsFactory(mContext, mTestProc, false, mBpfNetMaps);
mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]);
}