Merge "Add EthernetTestInterface to list of created interfaces"
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index ac777d7..bd8fe7c 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -90,6 +90,8 @@
compressible: true,
androidManifest: "AndroidManifest.xml",
+
+ compat_configs: ["connectivity-platform-compat-config"],
}
apex_key {
diff --git a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
index 22d2c5d..b865a8e 100644
--- a/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/30/com/android/networkstack/tethering/apishim/api30/BpfCoordinatorShimImpl.java
@@ -30,9 +30,9 @@
import com.android.net.module.util.IBpfMap.ThrowingBiConsumer;
import com.android.net.module.util.bpf.Tether4Key;
import com.android.net.module.util.bpf.Tether4Value;
+import com.android.net.module.util.bpf.TetherStatsValue;
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.networkstack.tethering.TetherStatsValue;
/**
* Bpf coordinator class for API shims.
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 5afb862..0683e5e 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -33,6 +33,8 @@
import com.android.net.module.util.IBpfMap.ThrowingBiConsumer;
import com.android.net.module.util.bpf.Tether4Key;
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.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.BpfUtils;
@@ -42,8 +44,6 @@
import com.android.networkstack.tethering.TetherDownstream6Key;
import com.android.networkstack.tethering.TetherLimitKey;
import com.android.networkstack.tethering.TetherLimitValue;
-import com.android.networkstack.tethering.TetherStatsKey;
-import com.android.networkstack.tethering.TetherStatsValue;
import com.android.networkstack.tethering.TetherUpstream6Key;
import java.io.FileDescriptor;
diff --git a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
index 915e210..69cbab5 100644
--- a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
+++ b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
@@ -25,9 +25,9 @@
import com.android.net.module.util.IBpfMap.ThrowingBiConsumer;
import com.android.net.module.util.bpf.Tether4Key;
import com.android.net.module.util.bpf.Tether4Value;
+import com.android.net.module.util.bpf.TetherStatsValue;
import com.android.networkstack.tethering.BpfCoordinator.Dependencies;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
-import com.android.networkstack.tethering.TetherStatsValue;
/**
* Bpf coordinator class for API shims.
diff --git a/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
index b4e3ba4..836761f 100644
--- a/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
+++ b/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
@@ -36,4 +36,5 @@
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 253eacb..f33f846 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 {
- boolean tetheringSupported;
+ long supportedTypes;
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 6f9b33e..b3f0cf2 100644
--- a/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -183,6 +183,12 @@
*/
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 = {
@@ -520,6 +526,9 @@
}
@Override
+ public void onSupportedTetheringTypes(long supportedBitmap) { }
+
+ @Override
public void onUpstreamChanged(Network network) { }
@Override
@@ -1033,15 +1042,29 @@
/**
* 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 supported The new supported status
+ * @param supportedTypes a set of @TetheringType which is supported.
+ * @hide
*/
- default void onTetheringSupported(boolean supported) {}
+ default void onSupportedTetheringTypes(@NonNull Set<Integer> supportedTypes) {}
/**
* Called when tethering upstream changed.
@@ -1339,7 +1362,8 @@
@Override
public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
executor.execute(() -> {
- callback.onTetheringSupported(parcel.tetheringSupported);
+ callback.onSupportedTetheringTypes(unpackBits(parcel.supportedTypes));
+ callback.onTetheringSupported(parcel.supportedTypes != 0);
callback.onUpstreamChanged(parcel.upstreamNetwork);
sendErrorCallbacks(parcel.states);
sendRegexpsChanged(parcel.config);
@@ -1358,6 +1382,13 @@
});
}
+ @Override
+ public void onSupportedTetheringTypes(long supportedBitmap) {
+ executor.execute(() -> {
+ callback.onSupportedTetheringTypes(unpackBits(supportedBitmap));
+ });
+ }
+
private void sendRegexpsChanged(TetheringConfigurationParcel parcel) {
callback.onTetherableInterfaceRegexpsChanged(new TetheringInterfaceRegexps(
parcel.tetherableBluetoothRegexs,
@@ -1396,6 +1427,23 @@
}
/**
+ * 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/proguard.flags b/Tethering/proguard.flags
index 7b5ae0d..2905e28 100644
--- a/Tethering/proguard.flags
+++ b/Tethering/proguard.flags
@@ -12,6 +12,11 @@
native <methods>;
}
+# Ensure runtime-visible field annotations are kept when using R8 full mode.
+-keepattributes RuntimeVisibleAnnotations,AnnotationDefault
+-keep interface com.android.networkstack.tethering.util.Struct$Field {
+ *;
+}
-keepclassmembers public class * extends com.android.networkstack.tethering.util.Struct {
*;
}
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index f8a1094..ecb6478 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -65,9 +65,12 @@
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.InterfaceParams;
import com.android.net.module.util.NetworkStackConstants;
+import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.U32;
import com.android.net.module.util.bpf.Tether4Key;
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.netlink.ConntrackMessage;
import com.android.net.module.util.netlink.NetlinkConstants;
import com.android.net.module.util.netlink.NetlinkSocket;
@@ -118,6 +121,8 @@
private static final String TETHER_LIMIT_MAP_PATH = makeMapPath("limit");
private static final String TETHER_ERROR_MAP_PATH = makeMapPath("error");
private static final String TETHER_DEV_MAP_PATH = makeMapPath("dev");
+ private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
+ private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
// Using "," as a separator is safe because base64 characters are [0-9a-zA-Z/=+].
private static final String DUMP_BASE64_DELIMITER = ",";
@@ -1072,7 +1077,8 @@
}
}
- private String ipv4RuleToBase64String(Tether4Key key, Tether4Value value) {
+ private <K extends Struct, V extends Struct> String bpfMapEntryToBase64String(
+ final K key, final V value) {
final byte[] keyBytes = key.writeToBytes();
final String keyBase64Str = Base64.encodeToString(keyBytes, Base64.DEFAULT)
.replace("\n", "");
@@ -1083,28 +1089,45 @@
return keyBase64Str + DUMP_BASE64_DELIMITER + valueBase64Str;
}
- private void dumpRawIpv4ForwardingRuleMap(
- BpfMap<Tether4Key, Tether4Value> map, IndentingPrintWriter pw) throws ErrnoException {
+ private <K extends Struct, V extends Struct> void dumpRawMap(BpfMap<K, V> map,
+ IndentingPrintWriter pw) throws ErrnoException {
if (map == null) {
- pw.println("No IPv4 support");
+ pw.println("No BPF support");
return;
}
if (map.isEmpty()) {
- pw.println("No rules");
+ pw.println("No entries");
return;
}
- map.forEach((k, v) -> pw.println(ipv4RuleToBase64String(k, v)));
+ map.forEach((k, v) -> pw.println(bpfMapEntryToBase64String(k, v)));
}
/**
* Dump raw BPF map in base64 encoded strings. For test only.
+ * Only allow to dump one map path once.
+ * Format:
+ * $ dumpsys tethering bpfRawMap --<map name>
*/
- public void dumpRawMap(@NonNull IndentingPrintWriter pw) {
- try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) {
- // TODO: dump downstream map.
- dumpRawIpv4ForwardingRuleMap(upstreamMap, pw);
- } catch (ErrnoException e) {
- pw.println("Error dumping IPv4 map: " + e);
+ public void dumpRawMap(@NonNull IndentingPrintWriter pw, @Nullable String[] args) {
+ // TODO: consider checking the arg order that <map name> is after "bpfRawMap". Probably
+ // it is okay for now because this is used by test only and test is supposed to use
+ // expected argument order.
+ // TODO: dump downstream4 map.
+ if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_STATS)) {
+ try (BpfMap<TetherStatsKey, TetherStatsValue> statsMap = mDeps.getBpfStatsMap()) {
+ dumpRawMap(statsMap, pw);
+ } catch (ErrnoException e) {
+ pw.println("Error dumping stats map: " + e);
+ }
+ return;
+ }
+ if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_UPSTREAM4)) {
+ try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) {
+ dumpRawMap(upstreamMap, pw);
+ } catch (ErrnoException e) {
+ pw.println("Error dumping IPv4 map: " + e);
+ }
+ return;
}
}
diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index 844efde..adc95ab 100644
--- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -34,7 +34,6 @@
import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
import static com.android.networkstack.apishim.ConstantsShim.ACTION_TETHER_UNSUPPORTED_CARRIER_UI;
-import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
import android.app.AlarmManager;
import android.app.PendingIntent;
@@ -48,12 +47,10 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcel;
-import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.provider.Settings;
-import android.telephony.CarrierConfigManager;
import android.util.SparseIntArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -307,13 +304,13 @@
if (SystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)) {
return TETHERING_PROVISIONING_NOT_REQUIRED;
}
- // TODO: Find a way to avoid get carrier config twice.
- if (carrierConfigAffirmsCarrierNotSupport(config)) {
+
+ if (!config.isCarrierSupportTethering) {
// To block tethering, behave as if running provisioning check and failed.
return TETHERING_PROVISIONING_CARRIER_UNSUPPORT;
}
- if (carrierConfigAffirmsEntitlementCheckNotRequired(config)) {
+ if (!config.isCarrierConfigAffirmsEntitlementCheckRequired) {
return TETHERING_PROVISIONING_NOT_REQUIRED;
}
return (config.provisioningApp.length == 2)
@@ -380,57 +377,6 @@
}
/**
- * Get carrier configuration bundle.
- * @param config an object that encapsulates the various tethering configuration elements.
- * */
- public PersistableBundle getCarrierConfig(final TetheringConfiguration config) {
- final CarrierConfigManager configManager = mContext
- .getSystemService(CarrierConfigManager.class);
- if (configManager == null) return null;
-
- final PersistableBundle carrierConfig = configManager.getConfigForSubId(
- config.activeDataSubId);
-
- if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
- return carrierConfig;
- }
-
- return null;
- }
-
- // The logic here is aimed solely at confirming that a CarrierConfig exists
- // and affirms that entitlement checks are not required.
- //
- // TODO: find a better way to express this, or alter the checking process
- // entirely so that this is more intuitive.
- // TODO: Find a way to avoid using getCarrierConfig everytime.
- private boolean carrierConfigAffirmsEntitlementCheckNotRequired(
- final TetheringConfiguration config) {
- // Check carrier config for entitlement checks
- final PersistableBundle carrierConfig = getCarrierConfig(config);
- if (carrierConfig == null) return false;
-
- // A CarrierConfigManager was found and it has a config.
- final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
- CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
- return !isEntitlementCheckRequired;
- }
-
- private boolean carrierConfigAffirmsCarrierNotSupport(final TetheringConfiguration config) {
- if (!SdkLevel.isAtLeastT()) {
- return false;
- }
- // Check carrier config for entitlement checks
- final PersistableBundle carrierConfig = getCarrierConfig(config);
- if (carrierConfig == null) return false;
-
- // A CarrierConfigManager was found and it has a config.
- final boolean mIsCarrierSupport = carrierConfig.getBoolean(
- KEY_CARRIER_SUPPORTS_TETHERING_BOOL, true);
- return !mIsCarrierSupport;
- }
-
- /**
* Run no UI tethering provisioning check.
* @param type tethering type from TetheringManager.TETHERING_{@code *}
* @param subId default data subscription ID.
@@ -479,7 +425,7 @@
private void runTetheringProvisioning(
boolean showProvisioningUi, int downstreamType, final TetheringConfiguration config) {
- if (carrierConfigAffirmsCarrierNotSupport(config)) {
+ if (!config.isCarrierSupportTethering) {
mListener.onTetherProvisioningFailed(downstreamType, "Carrier does not support.");
if (showProvisioningUi) {
showCarrierUnsupportedDialog();
@@ -497,7 +443,7 @@
}
private void showCarrierUnsupportedDialog() {
- // This is only used when carrierConfigAffirmsCarrierNotSupport() is true.
+ // This is only used when TetheringConfiguration.isCarrierSupportTethering is false.
if (!SdkLevel.isAtLeastT()) {
return;
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherStatsKey.java b/Tethering/src/com/android/networkstack/tethering/TetherStatsKey.java
deleted file mode 100644
index 5442480..0000000
--- a/Tethering/src/com/android/networkstack/tethering/TetherStatsKey.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import com.android.net.module.util.Struct;
-import com.android.net.module.util.Struct.Field;
-import com.android.net.module.util.Struct.Type;
-
-/** The key of BpfMap which is used for tethering stats. */
-public class TetherStatsKey extends Struct {
- @Field(order = 0, type = Type.U32)
- public final long ifindex; // upstream interface index
-
- public TetherStatsKey(final long ifindex) {
- this.ifindex = ifindex;
- }
-
- // TODO: remove equals, hashCode and toString once aosp/1536721 is merged.
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
-
- if (!(obj instanceof TetherStatsKey)) return false;
-
- final TetherStatsKey that = (TetherStatsKey) obj;
-
- return ifindex == that.ifindex;
- }
-
- @Override
- public int hashCode() {
- return Long.hashCode(ifindex);
- }
-
- @Override
- public String toString() {
- return String.format("ifindex: %d", ifindex);
- }
-}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetherStatsValue.java b/Tethering/src/com/android/networkstack/tethering/TetherStatsValue.java
deleted file mode 100644
index 844d2e8..0000000
--- a/Tethering/src/com/android/networkstack/tethering/TetherStatsValue.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.networkstack.tethering;
-
-import com.android.net.module.util.Struct;
-import com.android.net.module.util.Struct.Field;
-import com.android.net.module.util.Struct.Type;
-
-/** The key of BpfMap which is used for tethering stats. */
-public class TetherStatsValue extends Struct {
- // Use the signed long variable to store the uint64 stats from stats BPF map.
- // U63 is enough for each data element even at 5Gbps for ~468 years.
- // 2^63 / (5 * 1000 * 1000 * 1000) * 8 / 86400 / 365 = 468.
- @Field(order = 0, type = Type.U63)
- public final long rxPackets;
- @Field(order = 1, type = Type.U63)
- public final long rxBytes;
- @Field(order = 2, type = Type.U63)
- public final long rxErrors;
- @Field(order = 3, type = Type.U63)
- public final long txPackets;
- @Field(order = 4, type = Type.U63)
- public final long txBytes;
- @Field(order = 5, type = Type.U63)
- public final long txErrors;
-
- public TetherStatsValue(final long rxPackets, final long rxBytes, final long rxErrors,
- final long txPackets, final long txBytes, final long txErrors) {
- this.rxPackets = rxPackets;
- this.rxBytes = rxBytes;
- this.rxErrors = rxErrors;
- this.txPackets = txPackets;
- this.txBytes = txBytes;
- this.txErrors = txErrors;
- }
-
- // TODO: remove equals, hashCode and toString once aosp/1536721 is merged.
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
-
- if (!(obj instanceof TetherStatsValue)) return false;
-
- final TetherStatsValue that = (TetherStatsValue) obj;
-
- return rxPackets == that.rxPackets
- && rxBytes == that.rxBytes
- && rxErrors == that.rxErrors
- && txPackets == that.txPackets
- && txBytes == that.txBytes
- && txErrors == that.txErrors;
- }
-
- @Override
- public int hashCode() {
- return Long.hashCode(rxPackets) ^ Long.hashCode(rxBytes) ^ Long.hashCode(rxErrors)
- ^ Long.hashCode(txPackets) ^ Long.hashCode(txBytes) ^ Long.hashCode(txErrors);
- }
-
- @Override
- public String toString() {
- return String.format("rxPackets: %s, rxBytes: %s, rxErrors: %s, txPackets: %s, "
- + "txBytes: %s, txErrors: %s", rxPackets, rxBytes, rxErrors, txPackets,
- txBytes, txErrors);
- }
-}
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 0b607bd..44935fc 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -135,6 +135,7 @@
import com.android.internal.util.StateMachine;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
+import com.android.net.module.util.CollectionUtils;
import com.android.networkstack.apishim.common.BluetoothPanShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
@@ -278,6 +279,11 @@
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");
@@ -476,7 +482,7 @@
// To avoid launching unexpected provisioning checks, ignore re-provisioning
// when no CarrierConfig loaded yet. Assume reevaluateSimCardProvisioning()
// will be triggered again when CarrierConfig is loaded.
- if (mEntitlementMgr.getCarrierConfig(mConfig) != null) {
+ if (TetheringConfiguration.getCarrierConfig(mContext, subId) != null) {
mEntitlementMgr.reevaluateSimCardProvisioning(mConfig);
} else {
mLog.log("IGNORED reevaluate provisioning, no carrier config loaded");
@@ -494,6 +500,8 @@
mUpstreamNetworkMonitor.setUpstreamConfig(mConfig.chooseUpstreamAutomatically,
mConfig.isDunRequired);
reportConfigurationChanged(mConfig.toStableParcelable());
+
+ updateSupportedDownstreams(mConfig);
}
private void maybeDunSettingChanged() {
@@ -1513,26 +1521,6 @@
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;
}
@@ -2322,7 +2310,7 @@
mHandler.post(() -> {
mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
- parcel.tetheringSupported = isTetheringSupported();
+ parcel.supportedTypes = mSupportedTypeBitmap;
parcel.upstreamNetwork = mTetherUpstream;
parcel.config = mConfig.toStableParcelable();
parcel.states =
@@ -2361,6 +2349,22 @@
});
}
+ 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;
@@ -2445,18 +2449,56 @@
}
}
+ 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.
- boolean isTetheringSupported() {
+ private boolean isTetheringAllowed() {
final int defaultVal = mDeps.isTetheringDenied() ? 0 : 1;
final boolean tetherSupported = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.TETHER_SUPPORTED, defaultVal) != 0;
- final boolean tetherEnabledInSettings = tetherSupported
+ return tetherSupported
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
+ }
- return tetherEnabledInSettings && hasAnySupportedDownstream()
- && !mEntitlementMgr.isProvisioningNeededButUnavailable();
+ boolean isTetheringSupported() {
+ return mSupportedTypeBitmap > 0;
}
private void dumpBpf(IndentingPrintWriter pw) {
@@ -2472,13 +2514,12 @@
writer, " ");
// Used for testing instead of human debug.
- // TODO: add options to choose which map to dump.
- if (argsContain(args, "bpfRawMap")) {
- mBpfCoordinator.dumpRawMap(pw);
+ if (CollectionUtils.contains(args, "bpfRawMap")) {
+ mBpfCoordinator.dumpRawMap(pw, args);
return;
}
- if (argsContain(args, "bpf")) {
+ if (CollectionUtils.contains(args, "bpf")) {
dumpBpf(pw);
return;
}
@@ -2544,7 +2585,7 @@
pw.println("Log:");
pw.increaseIndent();
- if (argsContain(args, "--short")) {
+ if (CollectionUtils.contains(args, "--short")) {
pw.println("<log removed for brevity>");
} else {
mLog.dump(fd, pw, args);
@@ -2588,13 +2629,6 @@
if (e != null) throw e;
}
- private static boolean argsContain(String[] args, String target) {
- for (String arg : args) {
- if (target.equals(arg)) return true;
- }
- return false;
- }
-
private void updateConnectedClients(final List<WifiClient> wifiClients) {
if (mConnectedClientsTracker.updateConnectedClients(mTetherMainSM.getAllDownstreams(),
wifiClients)) {
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index f9f3ed9..7c36054 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -24,14 +24,17 @@
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static com.android.net.module.util.DeviceConfigUtils.TETHERING_MODULE_NAME;
+import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.TetheringConfigurationParcel;
import android.net.util.SharedLog;
+import android.os.PersistableBundle;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -142,6 +145,9 @@
public final int provisioningCheckPeriod;
public final String provisioningResponse;
+ public final boolean isCarrierSupportTethering;
+ public final boolean isCarrierConfigAffirmsEntitlementCheckRequired;
+
public final int activeDataSubId;
private final boolean mEnableLegacyDhcpServer;
@@ -207,6 +213,11 @@
provisioningResponse = getResourceString(res,
R.string.config_mobile_hotspot_provision_response);
+ PersistableBundle carrierConfigs = getCarrierConfig(ctx, activeDataSubId);
+ isCarrierSupportTethering = carrierConfigAffirmsCarrierSupport(carrierConfigs);
+ isCarrierConfigAffirmsEntitlementCheckRequired =
+ carrierConfigAffirmsEntitlementCheckRequired(carrierConfigs);
+
mOffloadPollInterval = getResourceInteger(res,
R.integer.config_tether_offload_poll_interval,
DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
@@ -329,6 +340,10 @@
pw.print("provisioningAppNoUi: ");
pw.println(provisioningAppNoUi);
+ pw.println("isCarrierSupportTethering: " + isCarrierSupportTethering);
+ pw.println("isCarrierConfigAffirmsEntitlementCheckRequired: "
+ + isCarrierConfigAffirmsEntitlementCheckRequired);
+
pw.print("enableBpfOffload: ");
pw.println(mEnableBpfOffload);
@@ -361,6 +376,9 @@
toIntArray(preferredUpstreamIfaceTypes)));
sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
+ sj.add(String.format("isCarrierSupportTethering:%s", isCarrierSupportTethering));
+ sj.add(String.format("isCarrierConfigAffirmsEntitlementCheckRequired:%s",
+ isCarrierConfigAffirmsEntitlementCheckRequired));
sj.add(String.format("enableBpfOffload:%s", mEnableBpfOffload));
sj.add(String.format("enableLegacyDhcpServer:%s", mEnableLegacyDhcpServer));
return String.format("TetheringConfiguration{%s}", sj.toString());
@@ -596,6 +614,39 @@
return result;
}
+ private static boolean carrierConfigAffirmsEntitlementCheckRequired(
+ PersistableBundle carrierConfig) {
+ if (carrierConfig == null) {
+ return true;
+ }
+ return carrierConfig.getBoolean(
+ CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, true);
+ }
+
+ private static boolean carrierConfigAffirmsCarrierSupport(PersistableBundle carrierConfig) {
+ if (!SdkLevel.isAtLeastT() || carrierConfig == null) {
+ return true;
+ }
+ return carrierConfig.getBoolean(KEY_CARRIER_SUPPORTS_TETHERING_BOOL, true);
+ }
+
+ /**
+ * Get carrier configuration bundle.
+ */
+ public static PersistableBundle getCarrierConfig(Context context, int activeDataSubId) {
+ final CarrierConfigManager configManager =
+ context.getSystemService(CarrierConfigManager.class);
+ if (configManager == null) {
+ return null;
+ }
+
+ final PersistableBundle carrierConfig = configManager.getConfigForSubId(activeDataSubId);
+ if (CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
+ return carrierConfig;
+ }
+ return null;
+ }
+
/**
* Convert this TetheringConfiguration to a TetheringConfigurationParcel.
*/
diff --git a/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java b/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
index 66d67a1..e6236df 100644
--- a/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringUtils.java
@@ -22,7 +22,7 @@
import androidx.annotation.NonNull;
import com.android.net.module.util.JniUtil;
-import com.android.networkstack.tethering.TetherStatsValue;
+import com.android.net.module.util.bpf.TetherStatsValue;
import java.io.FileDescriptor;
import java.net.Inet6Address;
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index de81a38..e73b7d5 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -74,6 +74,8 @@
import com.android.net.module.util.Struct;
import com.android.net.module.util.bpf.Tether4Key;
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.structs.EthernetHeader;
import com.android.net.module.util.structs.Icmpv6Header;
import com.android.net.module.util.structs.Ipv4Header;
@@ -128,6 +130,13 @@
// Kernel treats a confirmed UDP connection which active after two seconds as stream mode.
// See upstream commit b7b1d02fc43925a4d569ec221715db2dfa1ce4f5.
private static final int UDP_STREAM_TS_MS = 2000;
+ // Per RX UDP packet size: iphdr (20) + udphdr (8) + payload (2) = 30 bytes.
+ private static final int RX_UDP_PACKET_SIZE = 30;
+ private static final int RX_UDP_PACKET_COUNT = 456;
+ // Per TX UDP packet size: ethhdr (14) + iphdr (20) + udphdr (8) + payload (2) = 44 bytes.
+ private static final int TX_UDP_PACKET_SIZE = 44;
+ private static final int TX_UDP_PACKET_COUNT = 123;
+
private static final LinkAddress TEST_IP4_ADDR = new LinkAddress("10.0.0.1/8");
private static final LinkAddress TEST_IP6_ADDR = new LinkAddress("2001:db8:1::101/64");
private static final InetAddress TEST_IP4_DNS = parseNumericAddress("8.8.8.8");
@@ -136,6 +145,8 @@
ByteBuffer.wrap(new byte[] { (byte) 0x55, (byte) 0xaa });
private static final String DUMPSYS_TETHERING_RAWMAP_ARG = "bpfRawMap";
+ private static final String DUMPSYS_RAWMAP_ARG_STATS = "--stats";
+ private static final String DUMPSYS_RAWMAP_ARG_UPSTREAM4 = "--upstream4";
private static final String BASE64_DELIMITER = ",";
private static final String LINE_DELIMITER = "\\n";
@@ -168,12 +179,13 @@
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);
}
@@ -201,7 +213,6 @@
mHandler.post(() -> reader.stop());
mDownstreamReader = null;
}
- mHandlerThread.quitSafely();
mTetheredInterfaceRequester.release();
mEm.setIncludeTestInterfaces(false);
maybeDeleteTestInterface();
@@ -212,6 +223,7 @@
try {
if (mRunTests) cleanUp();
} finally {
+ mHandlerThread.quitSafely();
mUiAutomation.dropShellPermissionIdentity();
}
}
@@ -400,6 +412,23 @@
// 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);
@@ -938,27 +967,69 @@
return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD3);
});
- final HashMap<Tether4Key, Tether4Value> upstreamMap = pollIpv4UpstreamMapFromDump();
+ // [1] Verify IPv4 upstream rule map.
+ final HashMap<Tether4Key, Tether4Value> upstreamMap = pollRawMapFromDump(
+ Tether4Key.class, Tether4Value.class, DUMPSYS_RAWMAP_ARG_UPSTREAM4);
assertNotNull(upstreamMap);
assertEquals(1, upstreamMap.size());
final Map.Entry<Tether4Key, Tether4Value> rule =
upstreamMap.entrySet().iterator().next();
- final Tether4Key key = rule.getKey();
- assertEquals(IPPROTO_UDP, key.l4proto);
- assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), key.src4));
- assertEquals(LOCAL_PORT, key.srcPort);
- assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), key.dst4));
- assertEquals(REMOTE_PORT, key.dstPort);
+ final Tether4Key upstream4Key = rule.getKey();
+ assertEquals(IPPROTO_UDP, upstream4Key.l4proto);
+ assertTrue(Arrays.equals(tethered.ipv4Addr.getAddress(), upstream4Key.src4));
+ assertEquals(LOCAL_PORT, upstream4Key.srcPort);
+ assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(), upstream4Key.dst4));
+ assertEquals(REMOTE_PORT, upstream4Key.dstPort);
- final Tether4Value value = rule.getValue();
+ final Tether4Value upstream4Value = rule.getValue();
assertTrue(Arrays.equals(publicIp4Addr.getAddress(),
- InetAddress.getByAddress(value.src46).getAddress()));
- assertEquals(LOCAL_PORT, value.srcPort);
+ InetAddress.getByAddress(upstream4Value.src46).getAddress()));
+ assertEquals(LOCAL_PORT, upstream4Value.srcPort);
assertTrue(Arrays.equals(REMOTE_IP4_ADDR.getAddress(),
- InetAddress.getByAddress(value.dst46).getAddress()));
- assertEquals(REMOTE_PORT, value.dstPort);
+ InetAddress.getByAddress(upstream4Value.dst46).getAddress()));
+ assertEquals(REMOTE_PORT, upstream4Value.dstPort);
+
+ // [2] Verify stats map.
+ // Transmit packets on both direction for verifying stats. Because we only care the
+ // packet count in stats test, we just reuse the existing packets to increaes
+ // the packet count on both direction.
+
+ // Send packets on original direction.
+ for (int i = 0; i < TX_UDP_PACKET_COUNT; i++) {
+ tester.verifyUpload(remote, originalPacket, p -> {
+ Log.d(TAG, "Packet in upstream: " + dumpHexString(p));
+ return isExpectedUdpPacket(p, false /* hasEther */, PAYLOAD);
+ });
+ }
+
+ // Send packets on reply direction.
+ for (int i = 0; i < RX_UDP_PACKET_COUNT; i++) {
+ remote.verifyDownload(tester, replyPacket, p -> {
+ Log.d(TAG, "Packet in downstream: " + dumpHexString(p));
+ return isExpectedUdpPacket(p, true/* hasEther */, PAYLOAD2);
+ });
+ }
+
+ // Dump stats map to verify.
+ final HashMap<TetherStatsKey, TetherStatsValue> statsMap = pollRawMapFromDump(
+ TetherStatsKey.class, TetherStatsValue.class, DUMPSYS_RAWMAP_ARG_STATS);
+ assertNotNull(statsMap);
+ assertEquals(1, statsMap.size());
+
+ final Map.Entry<TetherStatsKey, TetherStatsValue> stats =
+ statsMap.entrySet().iterator().next();
+
+ // TODO: verify the upstream index in TetherStatsKey.
+
+ final TetherStatsValue statsValue = stats.getValue();
+ assertEquals(RX_UDP_PACKET_COUNT, statsValue.rxPackets);
+ assertEquals(RX_UDP_PACKET_COUNT * RX_UDP_PACKET_SIZE, statsValue.rxBytes);
+ assertEquals(0, statsValue.rxErrors);
+ assertEquals(TX_UDP_PACKET_COUNT, statsValue.txPackets);
+ assertEquals(TX_UDP_PACKET_COUNT * TX_UDP_PACKET_SIZE, statsValue.txBytes);
+ assertEquals(0, statsValue.txErrors);
}
}
@@ -1003,7 +1074,8 @@
}
@Nullable
- private Pair<Tether4Key, Tether4Value> parseTether4KeyValue(@NonNull String dumpStr) {
+ private <K extends Struct, V extends Struct> Pair<K, V> parseMapKeyValue(
+ Class<K> keyClass, Class<V> valueClass, @NonNull String dumpStr) {
Log.w(TAG, "Parsing string: " + dumpStr);
String[] keyValueStrs = dumpStr.split(BASE64_DELIMITER);
@@ -1016,36 +1088,38 @@
Log.d(TAG, "keyBytes: " + dumpHexString(keyBytes));
final ByteBuffer keyByteBuffer = ByteBuffer.wrap(keyBytes);
keyByteBuffer.order(ByteOrder.nativeOrder());
- final Tether4Key tether4Key = Struct.parse(Tether4Key.class, keyByteBuffer);
- Log.w(TAG, "tether4Key: " + tether4Key);
+ final K k = Struct.parse(keyClass, keyByteBuffer);
final byte[] valueBytes = Base64.decode(keyValueStrs[1], Base64.DEFAULT);
Log.d(TAG, "valueBytes: " + dumpHexString(valueBytes));
final ByteBuffer valueByteBuffer = ByteBuffer.wrap(valueBytes);
valueByteBuffer.order(ByteOrder.nativeOrder());
- final Tether4Value tether4Value = Struct.parse(Tether4Value.class, valueByteBuffer);
- Log.w(TAG, "tether4Value: " + tether4Value);
+ final V v = Struct.parse(valueClass, valueByteBuffer);
- return new Pair<>(tether4Key, tether4Value);
+ return new Pair<>(k, v);
}
@NonNull
- private HashMap<Tether4Key, Tether4Value> dumpIpv4UpstreamMap() throws Exception {
- final String rawMapStr = DumpTestUtils.dumpService(Context.TETHERING_SERVICE,
- DUMPSYS_TETHERING_RAWMAP_ARG);
- final HashMap<Tether4Key, Tether4Value> map = new HashMap<>();
+ private <K extends Struct, V extends Struct> HashMap<K, V> dumpAndParseRawMap(
+ Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
+ throws Exception {
+ final String[] args = new String[] {DUMPSYS_TETHERING_RAWMAP_ARG, mapArg};
+ final String rawMapStr = DumpTestUtils.dumpService(Context.TETHERING_SERVICE, args);
+ final HashMap<K, V> map = new HashMap<>();
for (final String line : rawMapStr.split(LINE_DELIMITER)) {
- final Pair<Tether4Key, Tether4Value> rule = parseTether4KeyValue(line.trim());
+ final Pair<K, V> rule = parseMapKeyValue(keyClass, valueClass, line.trim());
map.put(rule.first, rule.second);
}
return map;
}
@Nullable
- private HashMap<Tether4Key, Tether4Value> pollIpv4UpstreamMapFromDump() throws Exception {
+ private <K extends Struct, V extends Struct> HashMap<K, V> pollRawMapFromDump(
+ Class<K> keyClass, Class<V> valueClass, @NonNull String mapArg)
+ throws Exception {
for (int retryCount = 0; retryCount < DUMP_POLLING_MAX_RETRY; retryCount++) {
- final HashMap<Tether4Key, Tether4Value> map = dumpIpv4UpstreamMap();
+ final HashMap<K, V> map = dumpAndParseRawMap(keyClass, valueClass, mapArg);
if (!map.isEmpty()) return map;
Thread.sleep(DUMP_POLLING_INTERVAL_MS);
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 43f1eaa..aac531a 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -103,6 +103,8 @@
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.bpf.Tether4Key;
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.networkstack.tethering.BpfCoordinator;
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
@@ -112,8 +114,6 @@
import com.android.networkstack.tethering.TetherDownstream6Key;
import com.android.networkstack.tethering.TetherLimitKey;
import com.android.networkstack.tethering.TetherLimitValue;
-import com.android.networkstack.tethering.TetherStatsKey;
-import com.android.networkstack.tethering.TetherStatsValue;
import com.android.networkstack.tethering.TetherUpstream6Key;
import com.android.networkstack.tethering.TetheringConfiguration;
import com.android.networkstack.tethering.util.InterfaceSet;
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 4967d27..3630f24 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/BpfCoordinatorTest.java
@@ -102,6 +102,8 @@
import com.android.net.module.util.NetworkStackConstants;
import com.android.net.module.util.bpf.Tether4Key;
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.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/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
index 690ff71..01d7b4b 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/EntitlementManagerTest.java
@@ -304,33 +304,6 @@
}
@Test
- public void toleratesCarrierConfigManagerMissing() {
- setupForRequiredProvisioning();
- mockService(Context.CARRIER_CONFIG_SERVICE, CarrierConfigManager.class, null);
- mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- // Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
- // Therefore provisioning still be required.
- assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
- }
-
- @Test
- public void toleratesCarrierConfigMissing() {
- setupForRequiredProvisioning();
- when(mCarrierConfigManager.getConfig()).thenReturn(null);
- mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
- // We still have a provisioning app configured, so still require provisioning.
- assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
- }
-
- @Test
- public void toleratesCarrierConfigNotLoaded() {
- setupForRequiredProvisioning();
- mCarrierConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, false);
- // We still have a provisioning app configured, so still require provisioning.
- assertTrue(mEnMgr.isTetherProvisioningRequired(mConfig));
- }
-
- @Test
public void provisioningNotRequiredWhenAppNotFound() {
setupForRequiredProvisioning();
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
@@ -706,8 +679,8 @@
@IgnoreUpTo(SC_V2)
public void requestLatestTetheringEntitlementResult_carrierDoesNotSupport_noProvisionCount()
throws Exception {
- setupForRequiredProvisioning();
setupCarrierConfig(false);
+ setupForRequiredProvisioning();
mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
ResultReceiver receiver = new ResultReceiver(null) {
@Override
@@ -735,6 +708,7 @@
mEnMgr.notifyUpstream(false);
mLooper.dispatchAll();
setupCarrierConfig(false);
+ mConfig = new FakeTetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
mEnMgr.reevaluateSimCardProvisioning(mConfig);
// Turn on upstream.
@@ -749,8 +723,8 @@
@IgnoreUpTo(SC_V2)
public void startProvisioningIfNeeded_carrierUnsupport()
throws Exception {
- setupForRequiredProvisioning();
setupCarrierConfig(false);
+ setupForRequiredProvisioning();
mEnMgr.startProvisioningIfNeeded(TETHERING_WIFI, true);
verify(mTetherProvisioningFailedListener, never())
.onTetherProvisioningFailed(TETHERING_WIFI, "Carrier does not support.");
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 7fcf2b2..3190f35 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -22,10 +22,13 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
+import static android.telephony.CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL;
+import static android.telephony.CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_FORCE_USB_FUNCTIONS;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_NCM_FUNCTION;
import static com.android.networkstack.tethering.TetheringConfiguration.TETHER_USB_RNDIS_FUNCTION;
@@ -46,8 +49,10 @@
import android.content.res.Resources;
import android.net.util.SharedLog;
import android.os.Build;
+import android.os.PersistableBundle;
import android.provider.DeviceConfig;
import android.provider.Settings;
+import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import android.test.mock.MockContentResolver;
@@ -56,6 +61,7 @@
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.DeviceConfigUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
@@ -88,6 +94,7 @@
private static final long TEST_PACKAGE_VERSION = 1234L;
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
+ @Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private TelephonyManager mTelephonyManager;
@Mock private Resources mResources;
@Mock private Resources mResourcesForSubId;
@@ -97,6 +104,7 @@
private boolean mHasTelephonyManager;
private MockitoSession mMockingSession;
private MockContentResolver mContentResolver;
+ private final PersistableBundle mCarrierConfig = new PersistableBundle();
private class MockTetheringConfiguration extends TetheringConfiguration {
MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
@@ -474,6 +482,56 @@
PROVISIONING_APP_RESPONSE);
}
+ private <T> void mockService(String serviceName, Class<T> serviceClass, T service) {
+ when(mMockContext.getSystemServiceName(serviceClass)).thenReturn(serviceName);
+ when(mMockContext.getSystemService(serviceName)).thenReturn(service);
+ }
+
+ @Test
+ public void testGetCarrierConfigBySubId_noCarrierConfigManager_configsAreDefault() {
+ // Act like the CarrierConfigManager is present and ready unless told otherwise.
+ mockService(Context.CARRIER_CONFIG_SERVICE,
+ CarrierConfigManager.class, null);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+
+ assertTrue(cfg.isCarrierSupportTethering);
+ assertTrue(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
+ }
+
+ @Test
+ public void testGetCarrierConfigBySubId_carrierConfigMissing_configsAreDefault() {
+ // Act like the CarrierConfigManager is present and ready unless told otherwise.
+ mockService(Context.CARRIER_CONFIG_SERVICE,
+ CarrierConfigManager.class, mCarrierConfigManager);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(null);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+
+ assertTrue(cfg.isCarrierSupportTethering);
+ assertTrue(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
+ }
+
+ @Test
+ public void testGetCarrierConfigBySubId_hasConfigs_carrierUnsupportAndCheckNotRequired() {
+ mockService(Context.CARRIER_CONFIG_SERVICE,
+ CarrierConfigManager.class, mCarrierConfigManager);
+ mCarrierConfig.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+ mCarrierConfig.putBoolean(KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL, false);
+ mCarrierConfig.putBoolean(KEY_CARRIER_SUPPORTS_TETHERING_BOOL, false);
+ when(mCarrierConfigManager.getConfigForSubId(anyInt())).thenReturn(mCarrierConfig);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+
+ if (SdkLevel.isAtLeastT()) {
+ assertFalse(cfg.isCarrierSupportTethering);
+ } else {
+ assertTrue(cfg.isCarrierSupportTethering);
+ }
+ assertFalse(cfg.isCarrierConfigAffirmsEntitlementCheckRequired);
+
+ }
+
@Test
public void testEnableLegacyWifiP2PAddress() throws Exception {
final TetheringConfiguration defaultCfg = new TetheringConfiguration(
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 0388758..2fd7f48 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -142,6 +142,7 @@
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;
@@ -175,6 +176,7 @@
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;
@@ -211,6 +213,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.Set;
import java.util.Vector;
@RunWith(AndroidJUnit4.class)
@@ -1696,6 +1699,7 @@
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.
@@ -1748,6 +1752,10 @@
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);
@@ -1780,11 +1788,17 @@
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());
}
@@ -2836,53 +2850,81 @@
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 */);
- updateConfigAndVerifySupported(true /* supported */);
+ TestTetheringEventCallback callback = new TestTetheringEventCallback();
+ mTethering.registerTetheringEventCallback(callback);
+ mLooper.dispatchAll();
+ updateConfigAndVerifySupported(callback, expectedTypes);
// Could disable tethering supported by settings.
Settings.Global.putInt(mContentResolver, Settings.Global.TETHER_SUPPORTED, 0);
- updateConfigAndVerifySupported(false /* supported */);
+ updateConfigAndVerifySupported(callback, new ArraySet<>());
// Could disable tethering supported by user restriction.
setTetheringSupported(true /* supported */);
+ updateConfigAndVerifySupported(callback, expectedTypes);
when(mUserManager.hasUserRestriction(
UserManager.DISALLOW_CONFIG_TETHERING)).thenReturn(true);
- updateConfigAndVerifySupported(false /* supported */);
+ updateConfigAndVerifySupported(callback, new ArraySet<>());
// 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(true /* supported */);
+ updateConfigAndVerifySupported(callback, expectedTypes);
+ // Wifi tethering is not supported:
+ expectedTypes.remove(TETHERING_WIFI);
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[0]);
- updateConfigAndVerifySupported(true /* supported */);
-
+ 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]);
if (isAtLeastT()) {
- when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
- updateConfigAndVerifySupported(true /* supported */);
+ updateConfigAndVerifySupported(callback, expectedTypes);
+
+ // P2p tethering is not supported:
+ expectedTypes.remove(TETHERING_WIFI_P2P);
when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
.thenReturn(new String[0]);
- updateConfigAndVerifySupported(true /* supported */);
+ updateConfigAndVerifySupported(callback, expectedTypes);
+ // Ncm tethering is not supported:
+ expectedTypes.remove(TETHERING_NCM);
when(mResources.getStringArray(R.array.config_tether_ncm_regexs))
.thenReturn(new String[0]);
- updateConfigAndVerifySupported(true /* supported */);
+ updateConfigAndVerifySupported(callback, expectedTypes);
+ // Ethernet tethering (last supported type) is not supported:
+ expectedTypes.remove(TETHERING_ETHERNET);
mForceEthernetServiceUnavailable = true;
- updateConfigAndVerifySupported(false /* supported */);
+ updateConfigAndVerifySupported(callback, new ArraySet<>());
+
} else {
- when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
- updateConfigAndVerifySupported(false /* supported */);
+ // If wifi, usb and bluetooth are all not supported, all the types are not supported.
+ expectedTypes.clear();
+ updateConfigAndVerifySupported(callback, expectedTypes);
}
}
- private void updateConfigAndVerifySupported(boolean supported) {
+ private void updateConfigAndVerifySupported(final TestTetheringEventCallback callback,
+ final ArraySet<Integer> expectedTypes) {
sendConfigurationChanged();
- assertEquals(supported, mTethering.isTetheringSupported());
+
+ assertEquals(expectedTypes.size() > 0, mTethering.isTetheringSupported());
+ callback.expectSupportedTetheringTypes(expectedTypes);
}
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
diff --git a/bpf_progs/bpf_shared.h b/bpf_progs/bpf_shared.h
index 2ddc7b8..a6e78b6 100644
--- a/bpf_progs/bpf_shared.h
+++ b/bpf_progs/bpf_shared.h
@@ -98,7 +98,7 @@
static const int CONFIGURATION_MAP_SIZE = 2;
static const int UID_OWNER_MAP_SIZE = 2000;
-#define BPF_PATH "/sys/fs/bpf/"
+#define BPF_PATH "/sys/fs/bpf/net_shared/"
#define BPF_EGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_egress_stats"
#define BPF_INGRESS_PROG_PATH BPF_PATH "prog_netd_cgroupskb_ingress_stats"
diff --git a/bpf_progs/dscp_policy.c b/bpf_progs/dscp_policy.c
index 9989e6b..d5df7ef 100644
--- a/bpf_progs/dscp_policy.c
+++ b/bpf_progs/dscp_policy.c
@@ -16,6 +16,7 @@
#include <linux/types.h>
#include <linux/bpf.h>
+#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/if_ether.h>
@@ -27,249 +28,294 @@
#include <string.h>
#include "bpf_helpers.h"
+#include "dscp_policy.h"
-#define MAX_POLICIES 16
-#define MAP_A 1
-#define MAP_B 2
-
-#define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
-
-// TODO: these are already defined in /system/netd/bpf_progs/bpf_net_helpers.h
-// should they be moved to common location?
-static uint64_t (*bpf_get_socket_cookie)(struct __sk_buff* skb) =
- (void*)BPF_FUNC_get_socket_cookie;
-static int (*bpf_skb_store_bytes)(struct __sk_buff* skb, __u32 offset, const void* from, __u32 len,
- __u64 flags) = (void*)BPF_FUNC_skb_store_bytes;
-static int (*bpf_l3_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
- __u64 flags) = (void*)BPF_FUNC_l3_csum_replace;
-
-typedef struct {
- // Add family here to match __sk_buff ?
- struct in_addr srcIp;
- struct in_addr dstIp;
- __be16 srcPort;
- __be16 dstPort;
- uint8_t proto;
- uint8_t dscpVal;
- uint8_t pad[2];
-} Ipv4RuleEntry;
-STRUCT_SIZE(Ipv4RuleEntry, 2 * 4 + 2 * 2 + 2 * 1 + 2); // 16, 4 for in_addr
-
-#define SRC_IP_MASK 1
-#define DST_IP_MASK 2
-#define SRC_PORT_MASK 4
-#define DST_PORT_MASK 8
-#define PROTO_MASK 16
-
-typedef struct {
- struct in6_addr srcIp;
- struct in6_addr dstIp;
- __be16 srcPort;
- __be16 dstPortStart;
- __be16 dstPortEnd;
- uint8_t proto;
- uint8_t dscpVal;
- uint8_t mask;
- uint8_t pad[3];
-} Ipv4Policy;
-STRUCT_SIZE(Ipv4Policy, 2 * 16 + 3 * 2 + 3 * 1 + 3); // 44
-
-typedef struct {
- struct in6_addr srcIp;
- struct in6_addr dstIp;
- __be16 srcPort;
- __be16 dstPortStart;
- __be16 dstPortEnd;
- uint8_t proto;
- uint8_t dscpVal;
- uint8_t mask;
- // should we override this struct to include the param bitmask for linear search?
- // For mapping socket to policies, all the params should match exactly since we can
- // pull any missing from the sock itself.
-} Ipv6RuleEntry;
-STRUCT_SIZE(Ipv6RuleEntry, 2 * 16 + 3 * 2 + 3 * 1 + 3); // 44
-
-// TODO: move to using 1 map. Map v4 address to 0xffff::v4
-DEFINE_BPF_MAP_GRW(ipv4_socket_to_policies_map_A, HASH, uint64_t, Ipv4RuleEntry, MAX_POLICIES,
- AID_SYSTEM)
-DEFINE_BPF_MAP_GRW(ipv4_socket_to_policies_map_B, HASH, uint64_t, Ipv4RuleEntry, MAX_POLICIES,
- AID_SYSTEM)
-DEFINE_BPF_MAP_GRW(ipv6_socket_to_policies_map_A, HASH, uint64_t, Ipv6RuleEntry, MAX_POLICIES,
- AID_SYSTEM)
-DEFINE_BPF_MAP_GRW(ipv6_socket_to_policies_map_B, HASH, uint64_t, Ipv6RuleEntry, MAX_POLICIES,
- AID_SYSTEM)
DEFINE_BPF_MAP_GRW(switch_comp_map, ARRAY, int, uint64_t, 1, AID_SYSTEM)
-DEFINE_BPF_MAP_GRW(ipv4_dscp_policies_map, ARRAY, uint32_t, Ipv4Policy, MAX_POLICIES,
+DEFINE_BPF_MAP_GRW(ipv4_socket_to_policies_map_A, HASH, uint64_t, RuleEntry, MAX_POLICIES,
AID_SYSTEM)
-DEFINE_BPF_MAP_GRW(ipv6_dscp_policies_map, ARRAY, uint32_t, Ipv6RuleEntry, MAX_POLICIES,
+DEFINE_BPF_MAP_GRW(ipv4_socket_to_policies_map_B, HASH, uint64_t, RuleEntry, MAX_POLICIES,
+ AID_SYSTEM)
+DEFINE_BPF_MAP_GRW(ipv6_socket_to_policies_map_A, HASH, uint64_t, RuleEntry, MAX_POLICIES,
+ AID_SYSTEM)
+DEFINE_BPF_MAP_GRW(ipv6_socket_to_policies_map_B, HASH, uint64_t, RuleEntry, MAX_POLICIES,
AID_SYSTEM)
-DEFINE_BPF_PROG_KVER("schedcls/set_dscp", AID_ROOT, AID_SYSTEM,
- schedcls_set_dscp, KVER(5, 4, 0))
-(struct __sk_buff* skb) {
- int one = 0;
- uint64_t* selectedMap = bpf_switch_comp_map_lookup_elem(&one);
+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;
+ const void* data_end = (void*)(long)skb->data_end;
+
+ const int l2_header_size = is_eth ? sizeof(struct ethhdr) : 0;
+ struct ethhdr* eth = is_eth ? data : NULL;
+
+ if (data + l2_header_size > data_end) return;
+
+ int zero = 0;
+ int hdr_size = 0;
+ uint64_t* selectedMap = bpf_switch_comp_map_lookup_elem(&zero);
// use this with HASH map so map lookup only happens once policies have been added?
if (!selectedMap) {
- return TC_ACT_PIPE;
+ return;
}
// used for map lookup
uint64_t cookie = bpf_get_socket_cookie(skb);
+ if (!cookie)
+ return;
- // Do we need separate maps for ipv4/ipv6
- if (skb->protocol == htons(ETH_P_IP)) { //maybe bpf_htons()
- Ipv4RuleEntry* v4Policy;
- if (*selectedMap == MAP_A) {
- v4Policy = bpf_ipv4_socket_to_policies_map_A_lookup_elem(&cookie);
- } else {
- v4Policy = bpf_ipv4_socket_to_policies_map_B_lookup_elem(&cookie);
- }
-
- // How to use bitmask here to compare params efficiently?
- // TODO: add BPF_PROG_TYPE_SK_SKB prog type to Loader?
-
- void* data = (void*)(long)skb->data;
- const void* data_end = (void*)(long)skb->data_end;
- const struct iphdr* const iph = data;
-
+ 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?
+ 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
+ if (ipv4) {
+ const struct iphdr* const iph = is_eth ? (void*)(eth + 1) : data;
// Must have ipv4 header
- if (data + sizeof(*iph) > data_end) return TC_ACT_PIPE;
+ if (data + l2_header_size + sizeof(*iph) > data_end) return;
// IP version must be 4
- if (iph->version != 4) return TC_ACT_PIPE;
+ if (iph->version != 4) return;
// We cannot handle IP options, just standard 20 byte == 5 dword minimal IPv4 header
- if (iph->ihl != 5) return TC_ACT_PIPE;
+ if (iph->ihl != 5) return;
- if (iph->protocol != IPPROTO_UDP) return TC_ACT_PIPE;
+ // V4 mapped address in in6_addr sets 10/11 position to 0xff.
+ srcIp.s6_addr32[2] = htonl(0x0000ffff);
+ dstIp.s6_addr32[2] = htonl(0x0000ffff);
- struct udphdr *udp;
- udp = data + sizeof(struct iphdr); //sizeof(struct ethhdr)
+ // Copy IPv4 address into in6_addr for easy comparison below.
+ srcIp.s6_addr32[3] = iph->saddr;
+ 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;
+ // Must have ipv6 header
+ if (data + l2_header_size + sizeof(*ip6h) > data_end) return;
- if ((void*)(udp + 1) > data_end) return TC_ACT_PIPE;
+ if (ip6h->version != 6) return;
- // Source/destination port in udphdr are stored in be16, need to convert to le16.
- // This can be done via ntohs or htons. Is there a more preferred way?
- // Cached policy was found.
- if (v4Policy && iph->saddr == v4Policy->srcIp.s_addr &&
- iph->daddr == v4Policy->dstIp.s_addr &&
- ntohs(udp->source) == v4Policy->srcPort &&
- ntohs(udp->dest) == v4Policy->dstPort &&
- iph->protocol == v4Policy->proto) {
- // set dscpVal in packet. Least sig 2 bits of TOS
- // reference ipv4_change_dsfield()
+ srcIp = ip6h->saddr;
+ dstIp = ip6h->daddr;
+ protocol = ip6h->nexthdr;
+ priority = ip6h->priority;
+ flow_lbl = ip6h->flow_lbl[0];
+ hdr_size = sizeof(struct ipv6hdr);
+ }
- // TODO: fix checksum...
- int ecn = iph->tos & 3;
- uint8_t newDscpVal = (v4Policy->dscpVal << 2) + ecn;
- int oldDscpVal = iph->tos >> 2;
+ switch (protocol) {
+ case IPPROTO_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;
+ tcp = data + hdr_size;
+ if ((void*)(tcp + 1) > data_end) return;
+ sport = tcp->source;
+ dport = tcp->dest;
+ }
+ break;
+ default:
+ return;
+ }
+
+ RuleEntry* existingRule;
+ if (ipv4) {
+ if (*selectedMap == MAP_A) {
+ existingRule = bpf_ipv4_socket_to_policies_map_A_lookup_elem(&cookie);
+ } else {
+ existingRule = bpf_ipv4_socket_to_policies_map_B_lookup_elem(&cookie);
+ }
+ } else {
+ if (*selectedMap == MAP_A) {
+ existingRule = bpf_ipv6_socket_to_policies_map_A_lookup_elem(&cookie);
+ } else {
+ existingRule = bpf_ipv6_socket_to_policies_map_B_lookup_elem(&cookie);
+ }
+ }
+
+ 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) {
+ 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);
- return TC_ACT_PIPE;
+ } 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);
+ }
+ return;
+ }
+
+ // Linear scan ipv4_dscp_policies_map since no stored params match skb.
+ int bestScore = -1;
+ uint32_t bestMatch = 0;
+
+ for (register uint64_t i = 0; i < MAX_POLICIES; i++) {
+ int score = 0;
+ uint8_t tempMask = 0;
+ // Using a uint64 in for loop prevents infinite loop during BPF load,
+ // but the key is uint32, so convert back.
+ uint32_t key = i;
+
+ DscpPolicy* policy;
+ if (ipv4) {
+ policy = bpf_ipv4_dscp_policies_map_lookup_elem(&key);
+ } else {
+ policy = bpf_ipv6_dscp_policies_map_lookup_elem(&key);
}
- // linear scan ipv4_dscp_policies_map, stored socket params do not match actual
- int bestScore = -1;
- uint32_t bestMatch = 0;
+ // 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;
- for (register uint64_t i = 0; i < MAX_POLICIES; i++) {
- int score = 0;
- uint8_t tempMask = 0;
- // Using a uint62 in for loop prevents infinite loop during BPF load,
- // but the key is uint32, so convert back.
- uint32_t key = i;
- Ipv4Policy* policy = bpf_ipv4_dscp_policies_map_lookup_elem(&key);
+ if ((policy->presentFields & SRC_IP_MASK_FLAG) == SRC_IP_MASK_FLAG &&
+ 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)) {
+ score++;
+ tempMask |= DST_IP_MASK_FLAG;
+ }
+ if ((policy->presentFields & SRC_PORT_MASK_FLAG) == SRC_PORT_MASK_FLAG &&
+ 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)) {
+ score++;
+ tempMask |= DST_PORT_MASK_FLAG;
+ }
+ if ((policy->presentFields & PROTO_MASK_FLAG) == PROTO_MASK_FLAG &&
+ protocol == policy->proto) {
+ score++;
+ tempMask |= PROTO_MASK_FLAG;
+ }
- // if mask is 0 continue, key does not have corresponding policy value
- if (policy && policy->mask != 0) {
- if ((policy->mask & SRC_IP_MASK) == SRC_IP_MASK &&
- iph->saddr == policy->srcIp.s6_addr32[3]) {
- score++;
- tempMask |= SRC_IP_MASK;
- }
- if ((policy->mask & DST_IP_MASK) == DST_IP_MASK &&
- iph->daddr == policy->dstIp.s6_addr32[3]) {
- score++;
- tempMask |= DST_IP_MASK;
- }
- if ((policy->mask & SRC_PORT_MASK) == SRC_PORT_MASK &&
- ntohs(udp->source) == htons(policy->srcPort)) {
- score++;
- tempMask |= SRC_PORT_MASK;
- }
- if ((policy->mask & DST_PORT_MASK) == DST_PORT_MASK &&
- ntohs(udp->dest) >= htons(policy->dstPortStart) &&
- ntohs(udp->dest) <= htons(policy->dstPortEnd)) {
- score++;
- tempMask |= DST_PORT_MASK;
- }
- if ((policy->mask & PROTO_MASK) == PROTO_MASK &&
- iph->protocol == policy->proto) {
- score++;
- tempMask |= PROTO_MASK;
- }
+ if (score > bestScore && tempMask == policy->presentFields) {
+ bestMatch = i;
+ bestScore = score;
+ }
+ }
- if (score > bestScore && tempMask == policy->mask) {
- bestMatch = i;
- bestScore = score;
- }
+ uint8_t new_tos= 0; // Can 0 be used as default forwarding value?
+ uint8_t new_priority = 0;
+ uint8_t new_flow_lbl = 0;
+ if (bestScore > 0) {
+ DscpPolicy* policy;
+ if (ipv4) {
+ policy = bpf_ipv4_dscp_policies_map_lookup_elem(&bestMatch);
+ } else {
+ policy = bpf_ipv6_dscp_policies_map_lookup_elem(&bestMatch);
+ }
+
+ if (policy) {
+ // TODO: if DSCP value is already set ignore?
+ if (ipv4) {
+ int ecn = tos & 3;
+ new_tos = (policy->dscpVal << 2) + ecn;
+ } 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;
}
}
+ } else return;
- uint8_t newDscpVal = 0; // Can 0 be used as default forwarding value?
- uint8_t curDscp = iph->tos & 252;
- if (bestScore > 0) {
- Ipv4Policy* policy = bpf_ipv4_dscp_policies_map_lookup_elem(&bestMatch);
- if (policy) {
- // TODO: if DSCP value is already set ignore?
- // TODO: update checksum, for testing increment counter...
- int ecn = iph->tos & 3;
- newDscpVal = (policy->dscpVal << 2) + ecn;
- }
- }
+ RuleEntry value = {
+ .srcIp = srcIp,
+ .dstIp = dstIp,
+ .ifindex = skb->ifindex,
+ .srcPort = sport,
+ .dstPort = dport,
+ .proto = protocol,
+ .dscpVal = new_tos,
+ };
- Ipv4RuleEntry value = {
- .srcIp.s_addr = iph->saddr,
- .dstIp.s_addr = iph->daddr,
- .srcPort = udp->source,
- .dstPort = udp->dest,
- .proto = iph->protocol,
- .dscpVal = newDscpVal,
- };
-
- if (!cookie)
- return TC_ACT_PIPE;
-
- // Update map
+ //Update map with new policy.
+ if (ipv4) {
if (*selectedMap == MAP_A) {
bpf_ipv4_socket_to_policies_map_A_update_elem(&cookie, &value, BPF_ANY);
} else {
bpf_ipv4_socket_to_policies_map_B_update_elem(&cookie, &value, BPF_ANY);
}
-
- // Need to store bytes after updating map or program will not load.
- if (newDscpVal != curDscp) {
- // 1 is the offset (Version/Header length)
- int oldDscpVal = iph->tos >> 2;
- bpf_l3_csum_replace(skb, 1, oldDscpVal, newDscpVal, sizeof(uint8_t));
- bpf_skb_store_bytes(skb, 1, &newDscpVal, sizeof(uint8_t), 0);
- }
-
- } else if (skb->protocol == htons(ETH_P_IPV6)) { //maybe bpf_htons()
- Ipv6RuleEntry* v6Policy;
+ } else {
if (*selectedMap == MAP_A) {
- v6Policy = bpf_ipv6_socket_to_policies_map_A_lookup_elem(&cookie);
+ bpf_ipv6_socket_to_policies_map_A_update_elem(&cookie, &value, BPF_ANY);
} else {
- v6Policy = bpf_ipv6_socket_to_policies_map_B_lookup_elem(&cookie);
+ bpf_ipv6_socket_to_policies_map_B_update_elem(&cookie, &value, BPF_ANY);
}
+ }
- if (!v6Policy)
- return TC_ACT_PIPE;
+ // 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);
+ } 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);
+ }
+ return;
+}
- // TODO: Add code to process IPv6 packet.
+DEFINE_BPF_PROG_KVER("schedcls/set_dscp_ether", AID_ROOT, AID_SYSTEM,
+ schedcls_set_dscp_ether, KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+
+ if (skb->pkt_type != PACKET_HOST) return TC_ACT_PIPE;
+
+ if (skb->protocol == htons(ETH_P_IP)) {
+ match_policy(skb, true, true);
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ match_policy(skb, false, true);
+ }
+
+ // Always return TC_ACT_PIPE
+ return TC_ACT_PIPE;
+}
+
+DEFINE_BPF_PROG_KVER("schedcls/set_dscp_raw_ip", AID_ROOT, AID_SYSTEM,
+ schedcls_set_dscp_raw_ip, KVER(5, 4, 0))
+(struct __sk_buff* skb) {
+ if (skb->protocol == htons(ETH_P_IP)) {
+ match_policy(skb, true, false);
+ } else if (skb->protocol == htons(ETH_P_IPV6)) {
+ match_policy(skb, false, false);
}
// Always return TC_ACT_PIPE
diff --git a/bpf_progs/dscp_policy.h b/bpf_progs/dscp_policy.h
new file mode 100644
index 0000000..777c4ff
--- /dev/null
+++ b/bpf_progs/dscp_policy.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#define MAX_POLICIES 16
+#define MAP_A 1
+#define MAP_B 2
+
+#define SRC_IP_MASK_FLAG 1
+#define DST_IP_MASK_FLAG 2
+#define SRC_PORT_MASK_FLAG 4
+#define DST_PORT_MASK_FLAG 8
+#define PROTO_MASK_FLAG 16
+
+#define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
+
+#ifndef v6_equal
+#define v6_equal(a, b) (a.s6_addr32[0] == b.s6_addr32[0] && \
+ a.s6_addr32[1] == b.s6_addr32[1] && \
+ a.s6_addr32[2] == b.s6_addr32[2] && \
+ a.s6_addr32[3] == b.s6_addr32[3])
+#endif
+
+// TODO: these are already defined in packages/modules/Connectivity/bpf_progs/bpf_net_helpers.h.
+// smove to common location in future.
+static uint64_t (*bpf_get_socket_cookie)(struct __sk_buff* skb) =
+ (void*)BPF_FUNC_get_socket_cookie;
+static int (*bpf_skb_store_bytes)(struct __sk_buff* skb, __u32 offset, const void* from, __u32 len,
+ __u64 flags) = (void*)BPF_FUNC_skb_store_bytes;
+static int (*bpf_l3_csum_replace)(struct __sk_buff* skb, __u32 offset, __u64 from, __u64 to,
+ __u64 flags) = (void*)BPF_FUNC_l3_csum_replace;
+static long (*bpf_skb_ecn_set_ce)(struct __sk_buff* skb) =
+ (void*)BPF_FUNC_skb_ecn_set_ce;
+
+typedef struct {
+ struct in6_addr srcIp;
+ struct in6_addr dstIp;
+ uint32_t ifindex;
+ __be16 srcPort;
+ __be16 dstPortStart;
+ __be16 dstPortEnd;
+ uint8_t proto;
+ uint8_t dscpVal;
+ uint8_t presentFields;
+ uint8_t pad[3];
+} DscpPolicy;
+STRUCT_SIZE(DscpPolicy, 2 * 16 + 4 + 3 * 2 + 3 * 1 + 3); // 48
+
+typedef struct {
+ struct in6_addr srcIp;
+ struct in6_addr dstIp;
+ __u32 ifindex;
+ __be16 srcPort;
+ __be16 dstPort;
+ __u8 proto;
+ __u8 dscpVal;
+ __u8 pad[2];
+} RuleEntry;
+STRUCT_SIZE(RuleEntry, 2 * 16 + 1 * 4 + 2 * 2 + 2 * 1 + 2); // 44
\ No newline at end of file
diff --git a/framework-t/api/current.txt b/framework-t/api/current.txt
index 1b47481..eb77288 100644
--- a/framework-t/api/current.txt
+++ b/framework-t/api/current.txt
@@ -3,7 +3,7 @@
public final class NetworkStats implements java.lang.AutoCloseable {
method public void close();
- method public boolean getNextBucket(android.app.usage.NetworkStats.Bucket);
+ method public boolean getNextBucket(@Nullable android.app.usage.NetworkStats.Bucket);
method public boolean hasNextBucket();
}
@@ -40,21 +40,21 @@
}
public class NetworkStatsManager {
- method @WorkerThread public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback);
- method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, @Nullable android.os.Handler);
- method public void unregisterUsageCallback(android.app.usage.NetworkStatsManager.UsageCallback);
+ method @WorkerThread public android.app.usage.NetworkStats queryDetails(int, @Nullable String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForUid(int, @Nullable String, long, long, int) throws java.lang.SecurityException;
+ method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTag(int, @Nullable String, long, long, int, int) throws java.lang.SecurityException;
+ method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(int, @Nullable String, long, long, int, int, int) throws java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats querySummary(int, @Nullable String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, @Nullable String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, @Nullable String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
+ method public void registerUsageCallback(int, @Nullable String, long, @NonNull android.app.usage.NetworkStatsManager.UsageCallback);
+ method public void registerUsageCallback(int, @Nullable String, long, @NonNull android.app.usage.NetworkStatsManager.UsageCallback, @Nullable android.os.Handler);
+ method public void unregisterUsageCallback(@NonNull android.app.usage.NetworkStatsManager.UsageCallback);
}
public abstract static class NetworkStatsManager.UsageCallback {
ctor public NetworkStatsManager.UsageCallback();
- method public abstract void onThresholdReached(int, String);
+ method public abstract void onThresholdReached(int, @Nullable String);
}
}
@@ -173,12 +173,12 @@
method public static void incrementOperationCount(int, int);
method public static void setThreadStatsTag(int);
method public static void setThreadStatsUid(int);
- method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
- method public static void tagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException;
- method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
- method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
- method public static void untagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException;
- method public static void untagSocket(java.net.Socket) throws java.net.SocketException;
+ method public static void tagDatagramSocket(@NonNull java.net.DatagramSocket) throws java.net.SocketException;
+ method public static void tagFileDescriptor(@NonNull java.io.FileDescriptor) throws java.io.IOException;
+ method public static void tagSocket(@NonNull java.net.Socket) throws java.net.SocketException;
+ method public static void untagDatagramSocket(@NonNull java.net.DatagramSocket) throws java.net.SocketException;
+ method public static void untagFileDescriptor(@NonNull java.io.FileDescriptor) throws java.io.IOException;
+ method public static void untagSocket(@NonNull java.net.Socket) throws java.net.SocketException;
field public static final int UNSUPPORTED = -1; // 0xffffffff
}
diff --git a/framework-t/api/lint-baseline.txt b/framework-t/api/lint-baseline.txt
index 53e1beb..2996a3e 100644
--- a/framework-t/api/lint-baseline.txt
+++ b/framework-t/api/lint-baseline.txt
@@ -41,86 +41,18 @@
android.net.IpSecTransform.Builder does not declare a `build()` method, but builder classes are expected to
-MissingNullability: android.app.usage.NetworkStats#getNextBucket(android.app.usage.NetworkStats.Bucket) parameter #0:
- Missing nullability on parameter `bucketOut` in method `getNextBucket`
MissingNullability: android.app.usage.NetworkStatsManager#queryDetails(int, String, long, long):
Missing nullability on method `queryDetails` return
-MissingNullability: android.app.usage.NetworkStatsManager#queryDetails(int, String, long, long) parameter #1:
- Missing nullability on parameter `subscriberId` in method `queryDetails`
-MissingNullability: android.app.usage.NetworkStatsManager#queryDetailsForUid(int, String, long, long, int):
- Missing nullability on method `queryDetailsForUid` return
-MissingNullability: android.app.usage.NetworkStatsManager#queryDetailsForUid(int, String, long, long, int) parameter #1:
- Missing nullability on parameter `subscriberId` in method `queryDetailsForUid`
-MissingNullability: android.app.usage.NetworkStatsManager#queryDetailsForUidTag(int, String, long, long, int, int):
- Missing nullability on method `queryDetailsForUidTag` return
-MissingNullability: android.app.usage.NetworkStatsManager#queryDetailsForUidTag(int, String, long, long, int, int) parameter #1:
- Missing nullability on parameter `subscriberId` in method `queryDetailsForUidTag`
-MissingNullability: android.app.usage.NetworkStatsManager#queryDetailsForUidTagState(int, String, long, long, int, int, int):
- Missing nullability on method `queryDetailsForUidTagState` return
-MissingNullability: android.app.usage.NetworkStatsManager#queryDetailsForUidTagState(int, String, long, long, int, int, int) parameter #1:
- Missing nullability on parameter `subscriberId` in method `queryDetailsForUidTagState`
MissingNullability: android.app.usage.NetworkStatsManager#querySummary(int, String, long, long):
Missing nullability on method `querySummary` return
-MissingNullability: android.app.usage.NetworkStatsManager#querySummary(int, String, long, long) parameter #1:
- Missing nullability on parameter `subscriberId` in method `querySummary`
MissingNullability: android.app.usage.NetworkStatsManager#querySummaryForDevice(int, String, long, long):
Missing nullability on method `querySummaryForDevice` return
-MissingNullability: android.app.usage.NetworkStatsManager#querySummaryForDevice(int, String, long, long) parameter #1:
- Missing nullability on parameter `subscriberId` in method `querySummaryForDevice`
MissingNullability: android.app.usage.NetworkStatsManager#querySummaryForUser(int, String, long, long):
Missing nullability on method `querySummaryForUser` return
-MissingNullability: android.app.usage.NetworkStatsManager#querySummaryForUser(int, String, long, long) parameter #1:
- Missing nullability on parameter `subscriberId` in method `querySummaryForUser`
-MissingNullability: android.app.usage.NetworkStatsManager#registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback) parameter #1:
- Missing nullability on parameter `subscriberId` in method `registerUsageCallback`
-MissingNullability: android.app.usage.NetworkStatsManager#registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback) parameter #3:
- Missing nullability on parameter `callback` in method `registerUsageCallback`
-MissingNullability: android.app.usage.NetworkStatsManager#registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, android.os.Handler) parameter #1:
- Missing nullability on parameter `subscriberId` in method `registerUsageCallback`
-MissingNullability: android.app.usage.NetworkStatsManager#registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, android.os.Handler) parameter #3:
- Missing nullability on parameter `callback` in method `registerUsageCallback`
-MissingNullability: android.app.usage.NetworkStatsManager#unregisterUsageCallback(android.app.usage.NetworkStatsManager.UsageCallback) parameter #0:
- Missing nullability on parameter `callback` in method `unregisterUsageCallback`
-MissingNullability: android.app.usage.NetworkStatsManager.UsageCallback#onThresholdReached(int, String) parameter #1:
- Missing nullability on parameter `subscriberId` in method `onThresholdReached`
MissingNullability: android.net.IpSecAlgorithm#writeToParcel(android.os.Parcel, int) parameter #0:
Missing nullability on parameter `out` in method `writeToParcel`
MissingNullability: android.net.IpSecManager.UdpEncapsulationSocket#getFileDescriptor():
Missing nullability on method `getFileDescriptor` return
-MissingNullability: android.net.TrafficStats#tagDatagramSocket(java.net.DatagramSocket) parameter #0:
- Missing nullability on parameter `socket` in method `tagDatagramSocket`
-MissingNullability: android.net.TrafficStats#tagFileDescriptor(java.io.FileDescriptor) parameter #0:
- Missing nullability on parameter `fd` in method `tagFileDescriptor`
-MissingNullability: android.net.TrafficStats#tagSocket(java.net.Socket) parameter #0:
- Missing nullability on parameter `socket` in method `tagSocket`
-MissingNullability: android.net.TrafficStats#untagDatagramSocket(java.net.DatagramSocket) parameter #0:
- Missing nullability on parameter `socket` in method `untagDatagramSocket`
-MissingNullability: android.net.TrafficStats#untagFileDescriptor(java.io.FileDescriptor) parameter #0:
- Missing nullability on parameter `fd` in method `untagFileDescriptor`
-MissingNullability: android.net.TrafficStats#untagSocket(java.net.Socket) parameter #0:
- Missing nullability on parameter `socket` in method `untagSocket`
-MissingNullability: com.android.internal.util.FileRotator#FileRotator(java.io.File, String, long, long) parameter #0:
- Missing nullability on parameter `basePath` in method `FileRotator`
-MissingNullability: com.android.internal.util.FileRotator#FileRotator(java.io.File, String, long, long) parameter #1:
- Missing nullability on parameter `prefix` in method `FileRotator`
-MissingNullability: com.android.internal.util.FileRotator#dumpAll(java.io.OutputStream) parameter #0:
- Missing nullability on parameter `os` in method `dumpAll`
-MissingNullability: com.android.internal.util.FileRotator#readMatching(com.android.internal.util.FileRotator.Reader, long, long) parameter #0:
- Missing nullability on parameter `reader` in method `readMatching`
-MissingNullability: com.android.internal.util.FileRotator#rewriteActive(com.android.internal.util.FileRotator.Rewriter, long) parameter #0:
- Missing nullability on parameter `rewriter` in method `rewriteActive`
-MissingNullability: com.android.internal.util.FileRotator#rewriteAll(com.android.internal.util.FileRotator.Rewriter) parameter #0:
- Missing nullability on parameter `rewriter` in method `rewriteAll`
-MissingNullability: com.android.internal.util.FileRotator.Reader#read(java.io.InputStream) parameter #0:
- Missing nullability on parameter `in` in method `read`
-MissingNullability: com.android.internal.util.FileRotator.Writer#write(java.io.OutputStream) parameter #0:
- Missing nullability on parameter `out` in method `write`
-MissingNullability: com.android.server.NetworkManagementSocketTagger#kernelToTag(String) parameter #0:
- Missing nullability on parameter `string` in method `kernelToTag`
-MissingNullability: com.android.server.NetworkManagementSocketTagger#tag(java.io.FileDescriptor) parameter #0:
- Missing nullability on parameter `fd` in method `tag`
-MissingNullability: com.android.server.NetworkManagementSocketTagger#untag(java.io.FileDescriptor) parameter #0:
- Missing nullability on parameter `fd` in method `untag`
RethrowRemoteException: android.app.usage.NetworkStatsManager#queryDetails(int, String, long, long):
diff --git a/framework-t/api/module-lib-current.txt b/framework-t/api/module-lib-current.txt
index 658c625..c1f7b39 100644
--- a/framework-t/api/module-lib-current.txt
+++ b/framework-t/api/module-lib-current.txt
@@ -4,6 +4,8 @@
public class NetworkStatsManager {
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void forceUpdate();
method public static int getCollapsedRatType(int);
+ method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public android.net.NetworkStats getMobileUidStats();
+ method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public android.net.NetworkStats getWifiUidStats();
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void noteUidForeground(int, boolean);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyNetworkStatus(@NonNull java.util.List<android.net.Network>, @NonNull java.util.List<android.net.NetworkStateSnapshot>, @Nullable String, @NonNull java.util.List<android.net.UnderlyingNetworkInfo>);
method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForDevice(@NonNull android.net.NetworkTemplate, long, long);
@@ -182,6 +184,7 @@
public class TrafficStats {
method public static void attachSocketTagger();
method public static void init(@NonNull android.content.Context);
+ method public static void setThreadStatsTagDownload();
}
public final class UnderlyingNetworkInfo implements android.os.Parcelable {
diff --git a/framework-t/api/system-current.txt b/framework-t/api/system-current.txt
index 0f37b6f..6460fed 100644
--- a/framework-t/api/system-current.txt
+++ b/framework-t/api/system-current.txt
@@ -2,10 +2,8 @@
package android.app.usage {
public class NetworkStatsManager {
- method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public android.net.NetworkStats getMobileUidStats();
- method @NonNull @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public android.net.NetworkStats getWifiUidStats();
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider);
}
}
@@ -113,7 +111,6 @@
public class TrafficStats {
method public static void setThreadStatsTagApp();
method public static void setThreadStatsTagBackup();
- method public static void setThreadStatsTagDownload();
method public static void setThreadStatsTagRestore();
field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f
field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80
diff --git a/framework-t/src/android/app/usage/NetworkStats.java b/framework-t/src/android/app/usage/NetworkStats.java
index 2b6570a..74fe4bd 100644
--- a/framework-t/src/android/app/usage/NetworkStats.java
+++ b/framework-t/src/android/app/usage/NetworkStats.java
@@ -17,6 +17,7 @@
package android.app.usage;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.Context;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
@@ -474,10 +475,11 @@
/**
* Fills the recycled bucket with data of the next bin in the enumeration.
- * @param bucketOut Bucket to be filled with data.
+ * @param bucketOut Bucket to be filled with data. If null, the method does
+ * nothing and returning false.
* @return true if successfully filled the bucket, false otherwise.
*/
- public boolean getNextBucket(Bucket bucketOut) {
+ public boolean getNextBucket(@Nullable Bucket bucketOut) {
if (mSummary != null) {
return getNextSummaryBucket(bucketOut);
} else {
@@ -651,7 +653,7 @@
* @param bucketOut Next item will be set here.
* @return true if a next item could be set.
*/
- private boolean getNextSummaryBucket(Bucket bucketOut) {
+ private boolean getNextSummaryBucket(@Nullable Bucket bucketOut) {
if (bucketOut != null && mEnumerationIndex < mSummary.size()) {
mRecycledSummaryEntry = mSummary.getValues(mEnumerationIndex++, mRecycledSummaryEntry);
fillBucketFromSummaryEntry(bucketOut);
@@ -678,7 +680,7 @@
* @param bucketOut Next item will be set here.
* @return true if a next item could be set.
*/
- private boolean getNextHistoryBucket(Bucket bucketOut) {
+ private boolean getNextHistoryBucket(@Nullable Bucket bucketOut) {
if (bucketOut != null && mHistory != null) {
if (mEnumerationIndex < mHistory.size()) {
mRecycledHistoryEntry = mHistory.getValues(mEnumerationIndex++,
diff --git a/framework-t/src/android/app/usage/NetworkStatsManager.java b/framework-t/src/android/app/usage/NetworkStatsManager.java
index bf518b2..f41475b 100644
--- a/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -290,7 +290,7 @@
* statistics collection.
*/
@WorkerThread
- public Bucket querySummaryForDevice(int networkType, String subscriberId,
+ public Bucket querySummaryForDevice(int networkType, @Nullable String subscriberId,
long startTime, long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
try {
@@ -335,8 +335,8 @@
* statistics collection.
*/
@WorkerThread
- public Bucket querySummaryForUser(int networkType, String subscriberId, long startTime,
- long endTime) throws SecurityException, RemoteException {
+ public Bucket querySummaryForUser(int networkType, @Nullable String subscriberId,
+ long startTime, long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
try {
template = createTemplate(networkType, subscriberId);
@@ -384,7 +384,7 @@
* statistics collection.
*/
@WorkerThread
- public NetworkStats querySummary(int networkType, String subscriberId, long startTime,
+ public NetworkStats querySummary(int networkType, @Nullable String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
try {
@@ -508,15 +508,17 @@
*
* @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
*/
+ @NonNull
@WorkerThread
- public NetworkStats queryDetailsForUid(int networkType, String subscriberId,
+ public NetworkStats queryDetailsForUid(int networkType, @Nullable String subscriberId,
long startTime, long endTime, int uid) throws SecurityException {
return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
}
/** @hide */
- public NetworkStats queryDetailsForUid(NetworkTemplate template,
+ @NonNull
+ public NetworkStats queryDetailsForUid(@NonNull NetworkTemplate template,
long startTime, long endTime, int uid) throws SecurityException {
return queryDetailsForUidTagState(template, startTime, endTime, uid,
NetworkStats.Bucket.TAG_NONE, NetworkStats.Bucket.STATE_ALL);
@@ -524,23 +526,59 @@
/**
* Query network usage statistics details for a given uid and tag.
+ *
+ * This may take a long time, and apps should avoid calling this on their main thread.
+ * Only usable for uids belonging to calling user. Result is not aggregated over time.
+ * This means buckets' start and end timestamps are going to be between 'startTime' and
+ * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag
+ * the same as the 'tag' parameter, and the state the same as the 'state' parameter.
+ * defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
+ * metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
+ * roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
+ * <p>Only includes buckets that atomically occur in the inclusive time range. Doesn't
+ * interpolate across partial buckets. Since bucket length is in the order of hours, this
+ * method cannot be used to measure data usage on a fine grained time scale.
* This may take a long time, and apps should avoid calling this on their main thread.
*
- * @see #queryDetailsForUidTagState(int, String, long, long, int, int, int)
+ * @param networkType As defined in {@link ConnectivityManager}, e.g.
+ * {@link ConnectivityManager#TYPE_MOBILE}, {@link ConnectivityManager#TYPE_WIFI}
+ * etc.
+ * @param subscriberId If applicable, the subscriber id of the network interface.
+ * <p>Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when querying for the mobile network type to receive usage
+ * for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
+ * <p>Starting with API level 31, calling apps can provide a
+ * {@code subscriberId} with wifi network type to receive usage for
+ * wifi networks which is under the given subscription if applicable.
+ * Otherwise, pass {@code null} when querying all wifi networks.
+ * @param startTime Start of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param endTime End of period. Defined in terms of "Unix time", see
+ * {@link java.lang.System#currentTimeMillis}.
+ * @param uid UID of app
+ * @param tag TAG of interest. Use {@link NetworkStats.Bucket#TAG_NONE} for aggregated data
+ * across all the tags.
+ * @return Statistics which is described above.
+ * @throws SecurityException if permissions are insufficient to read network statistics.
*/
+ @NonNull
@WorkerThread
- public NetworkStats queryDetailsForUidTag(int networkType, String subscriberId,
+ public NetworkStats queryDetailsForUidTag(int networkType, @Nullable String subscriberId,
long startTime, long endTime, int uid, int tag) throws SecurityException {
return queryDetailsForUidTagState(networkType, subscriberId, startTime, endTime, uid,
tag, NetworkStats.Bucket.STATE_ALL);
}
/**
- * Query network usage statistics details for a given uid, tag, and state. Only usable for uids
- * belonging to calling user. Result is not aggregated over time. This means buckets' start and
- * end timestamps are going to be between 'startTime' and 'endTime' parameters. The uid is going
- * to be the same as the 'uid' parameter, the tag the same as the 'tag' parameter, and the state
- * the same as the 'state' parameter.
+ * Query network usage statistics details for a given uid, tag, and state.
+ *
+ * Only usable for uids belonging to calling user. Result is not aggregated over time.
+ * This means buckets' start and end timestamps are going to be between 'startTime' and
+ * 'endTime' parameters. The uid is going to be the same as the 'uid' parameter, the tag
+ * the same as the 'tag' parameter, and the state the same as the 'state' parameter.
* defaultNetwork is going to be {@link NetworkStats.Bucket#DEFAULT_NETWORK_ALL},
* metered is going to be {@link NetworkStats.Bucket#METERED_ALL}, and
* roaming is going to be {@link NetworkStats.Bucket#ROAMING_ALL}.
@@ -572,11 +610,12 @@
* across all the tags.
* @param state state of interest. Use {@link NetworkStats.Bucket#STATE_ALL} to aggregate
* traffic from all states.
- * @return Statistics object or null if an error happened during statistics collection.
+ * @return Statistics which is described above.
* @throws SecurityException if permissions are insufficient to read network statistics.
*/
+ @NonNull
@WorkerThread
- public NetworkStats queryDetailsForUidTagState(int networkType, String subscriberId,
+ public NetworkStats queryDetailsForUidTagState(int networkType, @Nullable String subscriberId,
long startTime, long endTime, int uid, int tag, int state) throws SecurityException {
NetworkTemplate template;
template = createTemplate(networkType, subscriberId);
@@ -669,7 +708,7 @@
* statistics collection.
*/
@WorkerThread
- public NetworkStats queryDetails(int networkType, String subscriberId, long startTime,
+ public NetworkStats queryDetails(int networkType, @Nullable String subscriberId, long startTime,
long endTime) throws SecurityException, RemoteException {
NetworkTemplate template;
try {
@@ -698,7 +737,7 @@
*
* @hide
*/
- @SystemApi
+ @SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK})
@@ -724,7 +763,7 @@
*
* @hide
*/
- @SystemApi
+ @SystemApi(client = MODULE_LIBRARIES)
@RequiresPermission(anyOf = {
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
android.Manifest.permission.NETWORK_STACK})
@@ -785,10 +824,28 @@
/**
* Registers to receive notifications about data usage on specified networks.
*
- * @see #registerUsageCallback(int, String, long, UsageCallback, Handler)
+ * <p>The callbacks will continue to be called as long as the process is live or
+ * {@link #unregisterUsageCallback} is called.
+ *
+ * @param networkType Type of network to monitor. Either
+ {@link ConnectivityManager#TYPE_MOBILE} or {@link ConnectivityManager#TYPE_WIFI}.
+ * @param subscriberId If applicable, the subscriber id of the network interface.
+ * <p>Starting with API level 29, the {@code subscriberId} is guarded by
+ * additional restrictions. Calling apps that do not meet the new
+ * requirements to access the {@code subscriberId} can provide a {@code
+ * null} value when registering for the mobile network type to receive
+ * notifications for all mobile networks. For additional details see {@link
+ * TelephonyManager#getSubscriberId()}.
+ * <p>Starting with API level 31, calling apps can provide a
+ * {@code subscriberId} with wifi network type to receive usage for
+ * wifi networks which is under the given subscription if applicable.
+ * Otherwise, pass {@code null} when querying all wifi networks.
+ * @param thresholdBytes Threshold in bytes to be notified on.
+ * @param callback The {@link UsageCallback} that the system will call when data usage
+ * has exceeded the specified threshold.
*/
- public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
- UsageCallback callback) {
+ public void registerUsageCallback(int networkType, @Nullable String subscriberId,
+ long thresholdBytes, @NonNull UsageCallback callback) {
registerUsageCallback(networkType, subscriberId, thresholdBytes, callback,
null /* handler */);
}
@@ -818,8 +875,8 @@
* @param handler to dispatch callback events through, otherwise if {@code null} it uses
* the calling thread.
*/
- public void registerUsageCallback(int networkType, String subscriberId, long thresholdBytes,
- UsageCallback callback, @Nullable Handler handler) {
+ public void registerUsageCallback(int networkType, @Nullable String subscriberId,
+ long thresholdBytes, @NonNull UsageCallback callback, @Nullable Handler handler) {
NetworkTemplate template = createTemplate(networkType, subscriberId);
if (DBG) {
Log.d(TAG, "registerUsageCallback called with: {"
@@ -839,7 +896,7 @@
*
* @param callback The {@link UsageCallback} used when registering.
*/
- public void unregisterUsageCallback(UsageCallback callback) {
+ public void unregisterUsageCallback(@NonNull UsageCallback callback) {
if (callback == null || callback.request == null
|| callback.request.requestId == DataUsageRequest.REQUEST_ID_UNSET) {
throw new IllegalArgumentException("Invalid UsageCallback");
@@ -880,7 +937,7 @@
/**
* Called when data usage has reached the given threshold.
*/
- public abstract void onThresholdReached(int networkType, String subscriberId);
+ public abstract void onThresholdReached(int networkType, @Nullable String subscriberId);
/**
* @hide used for internal bookkeeping
@@ -924,7 +981,7 @@
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_STATS_PROVIDER,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
- @NonNull public void registerNetworkStatsProvider(
+ public void registerNetworkStatsProvider(
@NonNull String tag,
@NonNull NetworkStatsProvider provider) {
try {
@@ -950,7 +1007,7 @@
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_STATS_PROVIDER,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK})
- @NonNull public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) {
+ public void unregisterNetworkStatsProvider(@NonNull NetworkStatsProvider provider) {
try {
provider.getProviderCallbackBinderOrThrow().unregister();
} catch (RemoteException e) {
@@ -958,7 +1015,7 @@
}
}
- private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
+ private static NetworkTemplate createTemplate(int networkType, @Nullable String subscriberId) {
final NetworkTemplate template;
switch (networkType) {
case ConnectivityManager.TYPE_MOBILE:
diff --git a/framework-t/src/android/net/NetworkIdentitySet.java b/framework-t/src/android/net/NetworkIdentitySet.java
index ad3a958..d88408e 100644
--- a/framework-t/src/android/net/NetworkIdentitySet.java
+++ b/framework-t/src/android/net/NetworkIdentitySet.java
@@ -206,6 +206,7 @@
public static int compare(@NonNull NetworkIdentitySet left, @NonNull NetworkIdentitySet right) {
Objects.requireNonNull(left);
Objects.requireNonNull(right);
+ if (left.isEmpty() && right.isEmpty()) return 0;
if (left.isEmpty()) return -1;
if (right.isEmpty()) return 1;
diff --git a/framework-t/src/android/net/NetworkStats.java b/framework-t/src/android/net/NetworkStats.java
index 06f2a62..51ff5ec 100644
--- a/framework-t/src/android/net/NetworkStats.java
+++ b/framework-t/src/android/net/NetworkStats.java
@@ -124,7 +124,6 @@
public @Nullable static final String[] INTERFACES_ALL = null;
/** {@link #tag} value for total data across all tags. */
- // TODO: Rename TAG_NONE to TAG_ALL.
public static final int TAG_NONE = 0;
/** {@link #metered} value to account for all metered states. */
@@ -412,21 +411,24 @@
/**
* @return the metered state.
*/
- @Meteredness public int getMetered() {
+ @Meteredness
+ public int getMetered() {
return metered;
}
/**
* @return the roaming state.
*/
- @Roaming public int getRoaming() {
+ @Roaming
+ public int getRoaming() {
return roaming;
}
/**
* @return the default network state.
*/
- @DefaultNetwork public int getDefaultNetwork() {
+ @DefaultNetwork
+ public int getDefaultNetwork() {
return defaultNetwork;
}
diff --git a/framework-t/src/android/net/NetworkStatsCollection.java b/framework-t/src/android/net/NetworkStatsCollection.java
index e385b33..b59a890 100644
--- a/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/framework-t/src/android/net/NetworkStatsCollection.java
@@ -776,7 +776,7 @@
if (!templateMatches(groupTemplate, key.ident)) continue;
if (key.set >= NetworkStats.SET_DEBUG_START) continue;
- final Key groupKey = new Key(null, key.uid, key.set, key.tag);
+ final Key groupKey = new Key(new NetworkIdentitySet(), key.uid, key.set, key.tag);
NetworkStatsHistory groupHistory = grouped.get(groupKey);
if (groupHistory == null) {
groupHistory = new NetworkStatsHistory(value.getBucketDuration());
diff --git a/framework-t/src/android/net/NetworkTemplate.java b/framework-t/src/android/net/NetworkTemplate.java
index 7b5afd7..b82a126 100644
--- a/framework-t/src/android/net/NetworkTemplate.java
+++ b/framework-t/src/android/net/NetworkTemplate.java
@@ -393,8 +393,9 @@
//constructor passes METERED_YES for these types.
this(matchRule, subscriberId, matchSubscriberIds,
wifiNetworkKey != null ? new String[] { wifiNetworkKey } : new String[0],
- (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD) ? METERED_YES
- : METERED_ALL , ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ (matchRule == MATCH_MOBILE || matchRule == MATCH_MOBILE_WILDCARD
+ || matchRule == MATCH_CARRIER) ? METERED_YES : METERED_ALL,
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
OEM_MANAGED_ALL, NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT);
}
diff --git a/framework-t/src/android/net/TrafficStats.java b/framework-t/src/android/net/TrafficStats.java
index bc836d8..dc4ac55 100644
--- a/framework-t/src/android/net/TrafficStats.java
+++ b/framework-t/src/android/net/TrafficStats.java
@@ -205,7 +205,7 @@
* server context.
* @hide
*/
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SystemApi(client = MODULE_LIBRARIES)
@SuppressLint("VisiblySynchronized")
public static synchronized void init(@NonNull final Context context) {
if (sStatsService != null) {
@@ -376,7 +376,7 @@
*
* @hide
*/
- @SystemApi
+ @SystemApi(client = MODULE_LIBRARIES)
public static void setThreadStatsTagDownload() {
setThreadStatsTag(TAG_SYSTEM_DOWNLOAD);
}
@@ -468,7 +468,7 @@
*
* @see #setThreadStatsTag(int)
*/
- public static void tagSocket(Socket socket) throws SocketException {
+ public static void tagSocket(@NonNull Socket socket) throws SocketException {
SocketTagger.get().tag(socket);
}
@@ -483,7 +483,7 @@
* calling {@code untagSocket()} before sending the socket to another
* process.
*/
- public static void untagSocket(Socket socket) throws SocketException {
+ public static void untagSocket(@NonNull Socket socket) throws SocketException {
SocketTagger.get().untag(socket);
}
@@ -496,14 +496,14 @@
*
* @see #setThreadStatsTag(int)
*/
- public static void tagDatagramSocket(DatagramSocket socket) throws SocketException {
+ public static void tagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException {
SocketTagger.get().tag(socket);
}
/**
* Remove any statistics parameters from the given {@link DatagramSocket}.
*/
- public static void untagDatagramSocket(DatagramSocket socket) throws SocketException {
+ public static void untagDatagramSocket(@NonNull DatagramSocket socket) throws SocketException {
SocketTagger.get().untag(socket);
}
@@ -516,7 +516,7 @@
*
* @see #setThreadStatsTag(int)
*/
- public static void tagFileDescriptor(FileDescriptor fd) throws IOException {
+ public static void tagFileDescriptor(@NonNull FileDescriptor fd) throws IOException {
SocketTagger.get().tag(fd);
}
@@ -524,7 +524,7 @@
* Remove any statistics parameters from the given {@link FileDescriptor}
* socket.
*/
- public static void untagFileDescriptor(FileDescriptor fd) throws IOException {
+ public static void untagFileDescriptor(@NonNull FileDescriptor fd) throws IOException {
SocketTagger.get().untag(fd);
}
diff --git a/framework/Android.bp b/framework/Android.bp
index 3703df8..d7de439 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -92,6 +92,7 @@
"modules-utils-preconditions",
],
libs: [
+ "app-compat-annotations",
"framework-connectivity-t.stubs.module_lib",
"unsupportedappusage",
],
@@ -152,6 +153,11 @@
],
}
+platform_compat_config {
+ name: "connectivity-platform-compat-config",
+ src: ":framework-connectivity",
+}
+
cc_library_shared {
name: "libframework-connectivity-jni",
min_sdk_version: "30",
diff --git a/framework/src/android/net/ITestNetworkManager.aidl b/framework/src/android/net/ITestNetworkManager.aidl
index 847f14e..27d13c1 100644
--- a/framework/src/android/net/ITestNetworkManager.aidl
+++ b/framework/src/android/net/ITestNetworkManager.aidl
@@ -29,7 +29,8 @@
*/
interface ITestNetworkManager
{
- TestNetworkInterface createInterface(boolean isTun, boolean bringUp, in LinkAddress[] addrs);
+ TestNetworkInterface createInterface(boolean isTun, boolean bringUp, in LinkAddress[] addrs,
+ in @nullable String iface);
void setupTestNetwork(in String iface, in LinkProperties lp, in boolean isMetered,
in int[] administratorUids, in IBinder binder);
diff --git a/framework/src/android/net/LinkProperties.java b/framework/src/android/net/LinkProperties.java
index 99f48b4..8782b33 100644
--- a/framework/src/android/net/LinkProperties.java
+++ b/framework/src/android/net/LinkProperties.java
@@ -19,12 +19,16 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.LinkPropertiesUtils;
import java.net.Inet4Address;
@@ -38,6 +42,7 @@
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;
+import java.util.stream.Collectors;
/**
* Describes the properties of a network link.
@@ -52,6 +57,17 @@
*
*/
public final class LinkProperties implements Parcelable {
+ /**
+ * The {@link #getRoutes()} now can contain excluded as well as included routes. Use
+ * {@link RouteInfo#getType()} to determine route type.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) // Switch to S_V2 when it is available.
+ @VisibleForTesting
+ public static final long EXCLUDED_ROUTES = 186082280;
+
// The interface described by the network link.
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private String mIfaceName;
@@ -738,10 +754,25 @@
/**
* Returns all the {@link RouteInfo} set on this link.
*
+ * Only unicast routes are returned for apps targeting Android S or below.
+ *
* @return An unmodifiable {@link List} of {@link RouteInfo} for this link.
*/
public @NonNull List<RouteInfo> getRoutes() {
- return Collections.unmodifiableList(mRoutes);
+ if (CompatChanges.isChangeEnabled(EXCLUDED_ROUTES)) {
+ return Collections.unmodifiableList(mRoutes);
+ } else {
+ return Collections.unmodifiableList(getUnicastRoutes());
+ }
+ }
+
+ /**
+ * 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());
}
/**
@@ -757,11 +788,14 @@
/**
* Returns all the routes on this link and all the links stacked above it.
+ *
+ * Only unicast routes are returned for apps targeting Android S or below.
+ *
* @hide
*/
@SystemApi
public @NonNull List<RouteInfo> getAllRoutes() {
- List<RouteInfo> routes = new ArrayList<>(mRoutes);
+ final List<RouteInfo> routes = new ArrayList<>(getRoutes());
for (LinkProperties stacked: mStackedLinks.values()) {
routes.addAll(stacked.getAllRoutes());
}
diff --git a/framework/src/android/net/TestNetworkManager.java b/framework/src/android/net/TestNetworkManager.java
index 280e497..4e78823 100644
--- a/framework/src/android/net/TestNetworkManager.java
+++ b/framework/src/android/net/TestNetworkManager.java
@@ -45,6 +45,12 @@
*/
public static final String TEST_TAP_PREFIX = "testtap";
+ /**
+ * Prefix for clat interfaces.
+ * @hide
+ */
+ public static final String CLAT_INTERFACE_PREFIX = "v4-";
+
@NonNull private static final String TAG = TestNetworkManager.class.getSimpleName();
@NonNull private final ITestNetworkManager mService;
@@ -160,7 +166,8 @@
public TestNetworkInterface createTunInterface(@NonNull Collection<LinkAddress> linkAddrs) {
try {
final LinkAddress[] arr = new LinkAddress[linkAddrs.size()];
- return mService.createInterface(TUN, BRING_UP, linkAddrs.toArray(arr));
+ return mService.createInterface(TUN, BRING_UP, linkAddrs.toArray(arr),
+ null /* iface */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -178,7 +185,7 @@
@NonNull
public TestNetworkInterface createTapInterface() {
try {
- return mService.createInterface(TAP, BRING_UP, NO_ADDRS);
+ return mService.createInterface(TAP, BRING_UP, NO_ADDRS, null /* iface */);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -197,7 +204,29 @@
@NonNull
public TestNetworkInterface createTapInterface(boolean bringUp) {
try {
- return mService.createInterface(TAP, bringUp, NO_ADDRS);
+ return mService.createInterface(TAP, bringUp, NO_ADDRS, null /* iface */);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Create a tap interface with a given interface name for testing purposes
+ *
+ * @param bringUp whether to bring up the interface before returning it.
+ * @param iface interface name to be assigned, so far only interface name which starts with
+ * "v4-testtap" or "v4-testtun" is allowed to be created. If it's null, then use
+ * the default name(e.g. testtap or testtun).
+ *
+ * @return A ParcelFileDescriptor of the underlying TAP interface. Close this to tear down the
+ * TAP interface.
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
+ @NonNull
+ public TestNetworkInterface createTapInterface(boolean bringUp, @NonNull String iface) {
+ try {
+ return mService.createInterface(TAP, bringUp, NO_ADDRS, iface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index eb22f78..fe27335 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.ConnectivityResources;
import android.net.EthernetManager;
@@ -71,8 +70,6 @@
private final static int NETWORK_SCORE = 70;
private static final String NETWORK_TYPE = "Ethernet";
- private static final String LEGACY_TCP_BUFFER_SIZES =
- "524288,1048576,3145728,524288,1048576,2097152";
private final ConcurrentHashMap<String, NetworkInterfaceState> mTrackingInterfaces =
new ConcurrentHashMap<>();
@@ -99,25 +96,9 @@
return InterfaceParams.getByName(name);
}
- // TODO: remove legacy resource fallback after migrating its overlays.
- private String getPlatformTcpBufferSizes(Context context) {
- final Resources r = context.getResources();
- final int resId = r.getIdentifier("config_ethernet_tcp_buffers", "string",
- context.getPackageName());
- return r.getString(resId);
- }
-
public String getTcpBufferSizesFromResource(Context context) {
- final String tcpBufferSizes;
- final String platformTcpBufferSizes = getPlatformTcpBufferSizes(context);
- if (!LEGACY_TCP_BUFFER_SIZES.equals(platformTcpBufferSizes)) {
- // Platform resource is not the historical default: use the overlay.
- tcpBufferSizes = platformTcpBufferSizes;
- } else {
- final ConnectivityResources resources = new ConnectivityResources(context);
- tcpBufferSizes = resources.get().getString(R.string.config_ethernet_tcp_buffers);
- }
- return tcpBufferSizes;
+ final ConnectivityResources resources = new ConnectivityResources(context);
+ return resources.get().getString(R.string.config_ethernet_tcp_buffers);
}
}
diff --git a/service-t/src/com/android/server/ethernet/EthernetTracker.java b/service-t/src/com/android/server/ethernet/EthernetTracker.java
index 693d91a..e9053dd 100644
--- a/service-t/src/com/android/server/ethernet/EthernetTracker.java
+++ b/service-t/src/com/android/server/ethernet/EthernetTracker.java
@@ -25,7 +25,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Resources;
import android.net.ConnectivityResources;
import android.net.EthernetManager;
import android.net.IEthernetServiceListener;
@@ -86,7 +85,6 @@
private static final boolean DBG = EthernetNetworkFactory.DBG;
private static final String TEST_IFACE_REGEXP = TEST_TAP_PREFIX + "\\d+";
- private static final String LEGACY_IFACE_REGEXP = "eth\\d";
/**
* Interface names we track. This is a product-dependent regular expression, plus,
@@ -134,48 +132,16 @@
}
public static class Dependencies {
- // TODO: remove legacy resource fallback after migrating its overlays.
- private String getPlatformRegexResource(Context context) {
- final Resources r = context.getResources();
- final int resId =
- r.getIdentifier("config_ethernet_iface_regex", "string", context.getPackageName());
- return r.getString(resId);
- }
-
- // TODO: remove legacy resource fallback after migrating its overlays.
- private String[] getPlatformInterfaceConfigs(Context context) {
- final Resources r = context.getResources();
- final int resId = r.getIdentifier("config_ethernet_interfaces", "array",
- context.getPackageName());
- return r.getStringArray(resId);
- }
-
public String getInterfaceRegexFromResource(Context context) {
- final String platformRegex = getPlatformRegexResource(context);
- final String match;
- if (!LEGACY_IFACE_REGEXP.equals(platformRegex)) {
- // Platform resource is not the historical default: use the overlay
- match = platformRegex;
- } else {
- final ConnectivityResources resources = new ConnectivityResources(context);
- match = resources.get().getString(
- com.android.connectivity.resources.R.string.config_ethernet_iface_regex);
- }
- return match;
+ final ConnectivityResources resources = new ConnectivityResources(context);
+ return resources.get().getString(
+ com.android.connectivity.resources.R.string.config_ethernet_iface_regex);
}
public String[] getInterfaceConfigFromResource(Context context) {
- final String[] platformInterfaceConfigs = getPlatformInterfaceConfigs(context);
- final String[] interfaceConfigs;
- if (platformInterfaceConfigs.length != 0) {
- // Platform resource is not the historical default: use the overlay
- interfaceConfigs = platformInterfaceConfigs;
- } else {
- final ConnectivityResources resources = new ConnectivityResources(context);
- interfaceConfigs = resources.get().getStringArray(
- com.android.connectivity.resources.R.array.config_ethernet_interfaces);
- }
- return interfaceConfigs;
+ final ConnectivityResources resources = new ConnectivityResources(context);
+ return resources.get().getStringArray(
+ com.android.connectivity.resources.R.array.config_ethernet_interfaces);
}
}
diff --git a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
index 25c88eb..5011dec 100644
--- a/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
+++ b/service-t/src/com/android/server/net/BpfInterfaceMapUpdater.java
@@ -38,7 +38,7 @@
private static final String TAG = BpfInterfaceMapUpdater.class.getSimpleName();
// This is current path but may be changed soon.
private static final String IFACE_INDEX_NAME_MAP_PATH =
- "/sys/fs/bpf/map_netd_iface_index_name_map";
+ "/sys/fs/bpf/net_shared/map_netd_iface_index_name_map";
private final IBpfMap<U32, InterfaceMapValue> mBpfMap;
private final INetd mNetd;
private final Handler mHandler;
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index e3794e4..82b1fb5 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -218,17 +218,16 @@
private static final String NETSTATS_COMBINE_SUBTYPE_ENABLED =
"netstats_combine_subtype_enabled";
- // This is current path but may be changed soon.
private static final String UID_COUNTERSET_MAP_PATH =
- "/sys/fs/bpf/map_netd_uid_counterset_map";
+ "/sys/fs/bpf/net_shared/map_netd_uid_counterset_map";
private static final String COOKIE_TAG_MAP_PATH =
- "/sys/fs/bpf/map_netd_cookie_tag_map";
+ "/sys/fs/bpf/net_shared/map_netd_cookie_tag_map";
private static final String APP_UID_STATS_MAP_PATH =
- "/sys/fs/bpf/map_netd_app_uid_stats_map";
+ "/sys/fs/bpf/net_shared/map_netd_app_uid_stats_map";
private static final String STATS_MAP_A_PATH =
- "/sys/fs/bpf/map_netd_stats_map_A";
+ "/sys/fs/bpf/net_shared/map_netd_stats_map_A";
private static final String STATS_MAP_B_PATH =
- "/sys/fs/bpf/map_netd_stats_map_B";
+ "/sys/fs/bpf/net_shared/map_netd_stats_map_B";
private final Context mContext;
private final NetworkStatsFactory mStatsFactory;
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index d79bdb8..ae253f5 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -258,6 +258,7 @@
import com.android.net.module.util.netlink.InetDiagMessage;
import com.android.server.connectivity.AutodestructReference;
import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
+import com.android.server.connectivity.ClatCoordinator;
import com.android.server.connectivity.ConnectivityFlags;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
@@ -753,7 +754,7 @@
* The BPF program attached to the tc-police hook to account for to-be-dropped traffic.
*/
private static final String TC_POLICE_BPF_PROG_PATH =
- "/sys/fs/bpf/prog_netd_schedact_ingress_account";
+ "/sys/fs/bpf/net_shared/prog_netd_schedact_ingress_account";
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
@@ -1405,6 +1406,19 @@
}
/**
+ * @see ClatCoordinator
+ */
+ public ClatCoordinator getClatCoordinator(INetd netd) {
+ return new ClatCoordinator(
+ new ClatCoordinator.Dependencies() {
+ @NonNull
+ public INetd getNetd() {
+ return netd;
+ }
+ });
+ }
+
+ /**
* Wraps {@link TcUtils#tcFilterAddDevIngressPolice}
*/
public void enableIngressRateLimit(String iface, long rateInBytesPerSecond) {
@@ -2865,7 +2879,7 @@
private void enforceNetworkFactoryPermission() {
// TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
- if (getCallingUid() == Process.BLUETOOTH_UID) return;
+ if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_FACTORY,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
@@ -2873,7 +2887,7 @@
private void enforceNetworkFactoryOrSettingsPermission() {
// TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
- if (getCallingUid() == Process.BLUETOOTH_UID) return;
+ if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
android.Manifest.permission.NETWORK_FACTORY,
@@ -2882,7 +2896,7 @@
private void enforceNetworkFactoryOrTestNetworksPermission() {
// TODO: Check for the BLUETOOTH_STACK permission once that is in the API surface.
- if (getCallingUid() == Process.BLUETOOTH_UID) return;
+ if (UserHandle.getAppId(getCallingUid()) == Process.BLUETOOTH_UID) return;
enforceAnyPermissionOf(
android.Manifest.permission.MANAGE_TEST_NETWORKS,
android.Manifest.permission.NETWORK_FACTORY,
@@ -2896,7 +2910,7 @@
android.Manifest.permission.NETWORK_SETTINGS, pid, uid)
|| PERMISSION_GRANTED == mContext.checkPermission(
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid)
- || uid == Process.BLUETOOTH_UID;
+ || UserHandle.getAppId(uid) == Process.BLUETOOTH_UID;
}
private boolean checkSettingsPermission() {
@@ -3266,11 +3280,12 @@
return;
}
- pw.print("NetworkProviders for:");
+ pw.println("NetworkProviders for:");
+ pw.increaseIndent();
for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- pw.print(" " + npi.name);
+ pw.println(npi.providerId + ": " + npi.name);
}
- pw.println();
+ pw.decreaseIndent();
pw.println();
final NetworkAgentInfo defaultNai = getDefaultNetwork();
@@ -3319,6 +3334,14 @@
pw.decreaseIndent();
pw.println();
+ pw.println("Network Offers:");
+ pw.increaseIndent();
+ for (final NetworkOfferInfo offerInfo : mNetworkOffers) {
+ pw.println(offerInfo.offer);
+ }
+ pw.decreaseIndent();
+ pw.println();
+
mLegacyTypeTracker.dump(pw);
pw.println();
@@ -3669,7 +3692,7 @@
}
case NetworkAgent.EVENT_REMOVE_ALL_DSCP_POLICIES: {
if (mDscpPolicyTracker != null) {
- mDscpPolicyTracker.removeAllDscpPolicies(nai);
+ mDscpPolicyTracker.removeAllDscpPolicies(nai, true);
}
break;
}
@@ -4410,6 +4433,9 @@
}
private void destroyNativeNetwork(@NonNull NetworkAgentInfo nai) {
+ if (mDscpPolicyTracker != null) {
+ mDscpPolicyTracker.removeAllDscpPolicies(nai, false);
+ }
try {
mNetd.networkDestroy(nai.network.getNetId());
} catch (RemoteException | ServiceSpecificException e) {
diff --git a/service/src/com/android/server/TestNetworkService.java b/service/src/com/android/server/TestNetworkService.java
index ccc2776..e12190c 100644
--- a/service/src/com/android/server/TestNetworkService.java
+++ b/service/src/com/android/server/TestNetworkService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.net.TestNetworkManager.CLAT_INTERFACE_PREFIX;
import static android.net.TestNetworkManager.TEST_TAP_PREFIX;
import static android.net.TestNetworkManager.TEST_TUN_PREFIX;
@@ -98,6 +99,14 @@
}
}
+ // TODO: find a way to allow the caller to pass in non-clat interface names, ensuring that
+ // those names do not conflict with names created by callers that do not pass in an interface
+ // name.
+ private static boolean isValidInterfaceName(@NonNull final String iface) {
+ return iface.startsWith(CLAT_INTERFACE_PREFIX + TEST_TUN_PREFIX)
+ || iface.startsWith(CLAT_INTERFACE_PREFIX + TEST_TAP_PREFIX);
+ }
+
/**
* Create a TUN or TAP interface with the specified parameters.
*
@@ -106,29 +115,35 @@
*/
@Override
public TestNetworkInterface createInterface(boolean isTun, boolean bringUp,
- LinkAddress[] linkAddrs) {
+ LinkAddress[] linkAddrs, @Nullable String iface) {
enforceTestNetworkPermissions(mContext);
Objects.requireNonNull(linkAddrs, "missing linkAddrs");
- String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
- String iface = ifacePrefix + sTestTunIndex.getAndIncrement();
+ String interfaceName = iface;
+ if (iface == null) {
+ String ifacePrefix = isTun ? TEST_TUN_PREFIX : TEST_TAP_PREFIX;
+ interfaceName = ifacePrefix + sTestTunIndex.getAndIncrement();
+ } else if (!isValidInterfaceName(iface)) {
+ throw new IllegalArgumentException("invalid interface name requested: " + iface);
+ }
+
final long token = Binder.clearCallingIdentity();
try {
ParcelFileDescriptor tunIntf =
- ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, iface));
+ ParcelFileDescriptor.adoptFd(jniCreateTunTap(isTun, interfaceName));
for (LinkAddress addr : linkAddrs) {
mNetd.interfaceAddAddress(
- iface,
+ interfaceName,
addr.getAddress().getHostAddress(),
addr.getPrefixLength());
}
if (bringUp) {
- NetdUtils.setInterfaceUp(mNetd, iface);
+ NetdUtils.setInterfaceUp(mNetd, interfaceName);
}
- return new TestNetworkInterface(tunIntf, iface);
+ return new TestNetworkInterface(tunIntf, interfaceName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} finally {
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 8aa5990..cc81522 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -100,11 +100,11 @@
private static final String CLAT_INGRESS6_MAP_PATH = makeMapPath("ingress6");
private static String makeMapPath(String which) {
- return "/sys/fs/bpf/map_clatd_clat_" + which + "_map";
+ return "/sys/fs/bpf/net_shared/map_clatd_clat_" + which + "_map";
}
private static String makeProgPath(boolean ingress, boolean ether) {
- String path = "/sys/fs/bpf/prog_clatd_schedcls_"
+ String path = "/sys/fs/bpf/net_shared/prog_clatd_schedcls_"
+ (ingress ? "ingress6" : "egress4")
+ "_clat_"
+ (ether ? "ether" : "rawip");
@@ -122,8 +122,12 @@
@Nullable
private ClatdTracker mClatdTracker = null;
+ /**
+ * Dependencies of ClatCoordinator which makes ConnectivityService injection
+ * in tests.
+ */
@VisibleForTesting
- abstract static class Dependencies {
+ public abstract static class Dependencies {
/**
* Get netd.
*/
diff --git a/service/src/com/android/server/connectivity/ConnectivityNativeService.java b/service/src/com/android/server/connectivity/ConnectivityNativeService.java
index cde6ea7..c1ba40e 100644
--- a/service/src/com/android/server/connectivity/ConnectivityNativeService.java
+++ b/service/src/com/android/server/connectivity/ConnectivityNativeService.java
@@ -47,10 +47,11 @@
private static final String TAG = ConnectivityNativeService.class.getSimpleName();
private static final String CGROUP_PATH = "/sys/fs/cgroup";
private static final String V4_PROG_PATH =
- "/sys/fs/bpf/prog_block_bind4_block_port";
+ "/sys/fs/bpf/net_shared/prog_block_bind4_block_port";
private static final String V6_PROG_PATH =
- "/sys/fs/bpf/prog_block_bind6_block_port";
- private static final String BLOCKED_PORTS_MAP_PATH = "/sys/fs/bpf/map_block_blocked_ports_map";
+ "/sys/fs/bpf/net_shared/prog_block_bind6_block_port";
+ private static final String BLOCKED_PORTS_MAP_PATH =
+ "/sys/fs/bpf/net_shared/map_block_blocked_ports_map";
private final Context mContext;
diff --git a/service/src/com/android/server/connectivity/DscpPolicyTracker.java b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
index 53b276e..7829d1a 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
@@ -19,6 +19,7 @@
import static android.net.NetworkAgent.DSCP_POLICY_STATUS_DELETED;
import static android.net.NetworkAgent.DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES;
import static android.net.NetworkAgent.DSCP_POLICY_STATUS_POLICY_NOT_FOUND;
+import static android.net.NetworkAgent.DSCP_POLICY_STATUS_REQUEST_DECLINED;
import static android.net.NetworkAgent.DSCP_POLICY_STATUS_SUCCESS;
import static android.system.OsConstants.ETH_P_ALL;
@@ -37,6 +38,7 @@
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.NetworkInterface;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
@@ -50,7 +52,7 @@
private static final String TAG = DscpPolicyTracker.class.getSimpleName();
private static final String PROG_PATH =
- "/sys/fs/bpf/prog_dscp_policy_schedcls_set_dscp";
+ "/sys/fs/bpf/net_shared/prog_dscp_policy_schedcls_set_dscp";
// Name is "map + *.o + map_name + map". Can probably shorten this
private static final String IPV4_POLICY_MAP_PATH = makeMapPath(
"dscp_policy_ipv4_dscp_policies");
@@ -59,37 +61,75 @@
private static final int MAX_POLICIES = 16;
private static String makeMapPath(String which) {
- return "/sys/fs/bpf/map_" + which + "_map";
+ return "/sys/fs/bpf/net_shared/map_" + which + "_map";
}
private Set<String> mAttachedIfaces;
private final BpfMap<Struct.U32, DscpPolicyValue> mBpfDscpIpv4Policies;
private final BpfMap<Struct.U32, DscpPolicyValue> mBpfDscpIpv6Policies;
- private final SparseIntArray mPolicyIdToBpfMapIndex;
+
+ // The actual policy rules used by the BPF code to process packets
+ // are in mBpfDscpIpv4Policies and mBpfDscpIpv4Policies. Both of
+ // these can contain up to MAX_POLICIES rules.
+ //
+ // A given policy always consumes one entry in both the IPv4 and
+ // IPv6 maps even if if's an IPv4-only or IPv6-only policy.
+ //
+ // Each interface index has a SparseIntArray of rules which maps a
+ // policy ID to the index of the corresponding rule in the maps.
+ // mIfaceIndexToPolicyIdBpfMapIndex maps the interface index to
+ // the per-interface SparseIntArray.
+ private final HashMap<Integer, SparseIntArray> mIfaceIndexToPolicyIdBpfMapIndex;
public DscpPolicyTracker() throws ErrnoException {
mAttachedIfaces = new HashSet<String>();
-
- mPolicyIdToBpfMapIndex = new SparseIntArray(MAX_POLICIES);
+ mIfaceIndexToPolicyIdBpfMapIndex = new HashMap<Integer, SparseIntArray>();
mBpfDscpIpv4Policies = new BpfMap<Struct.U32, DscpPolicyValue>(IPV4_POLICY_MAP_PATH,
BpfMap.BPF_F_RDWR, Struct.U32.class, DscpPolicyValue.class);
mBpfDscpIpv6Policies = new BpfMap<Struct.U32, DscpPolicyValue>(IPV6_POLICY_MAP_PATH,
BpfMap.BPF_F_RDWR, Struct.U32.class, DscpPolicyValue.class);
}
+ private boolean isUnusedIndex(int index) {
+ for (SparseIntArray ifacePolicies : mIfaceIndexToPolicyIdBpfMapIndex.values()) {
+ if (ifacePolicies.indexOfValue(index) >= 0) return false;
+ }
+ return true;
+ }
+
private int getFirstFreeIndex() {
+ if (mIfaceIndexToPolicyIdBpfMapIndex.size() == 0) return 0;
for (int i = 0; i < MAX_POLICIES; i++) {
- if (mPolicyIdToBpfMapIndex.indexOfValue(i) < 0) return i;
+ if (isUnusedIndex(i)) {
+ return i;
+ }
}
return MAX_POLICIES;
}
+ private int findIndex(int policyId, int ifIndex) {
+ SparseIntArray ifacePolicies = mIfaceIndexToPolicyIdBpfMapIndex.get(ifIndex);
+ if (ifacePolicies != null) {
+ final int existingIndex = ifacePolicies.get(policyId, -1);
+ if (existingIndex != -1) {
+ return existingIndex;
+ }
+ }
+
+ final int firstIndex = getFirstFreeIndex();
+ if (firstIndex >= MAX_POLICIES) {
+ // New policy is being added, but max policies has already been reached.
+ return -1;
+ }
+ return firstIndex;
+ }
+
private void sendStatus(NetworkAgentInfo nai, int policyId, int status) {
try {
nai.networkAgent.onDscpPolicyStatusUpdated(policyId, status);
} catch (RemoteException e) {
- Log.d(TAG, "Failed update policy status: ", e);
+ Log.e(TAG, "Failed update policy status: ", e);
}
}
@@ -107,37 +147,43 @@
|| policy.getSourceAddress() instanceof Inet6Address));
}
- private int addDscpPolicyInternal(DscpPolicy policy) {
+ private int getIfaceIndex(NetworkAgentInfo nai) {
+ String iface = nai.linkProperties.getInterfaceName();
+ NetworkInterface netIface;
+ try {
+ netIface = NetworkInterface.getByName(iface);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to get iface index for " + iface + ": " + e);
+ netIface = null;
+ }
+ return (netIface != null) ? netIface.getIndex() : 0;
+ }
+
+ private int addDscpPolicyInternal(DscpPolicy policy, int ifIndex) {
// If there is no existing policy with a matching ID, and we are already at
// the maximum number of policies then return INSUFFICIENT_PROCESSING_RESOURCES.
- final int existingIndex = mPolicyIdToBpfMapIndex.get(policy.getPolicyId(), -1);
- if (existingIndex == -1 && mPolicyIdToBpfMapIndex.size() >= MAX_POLICIES) {
- return DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES;
+ SparseIntArray ifacePolicies = mIfaceIndexToPolicyIdBpfMapIndex.get(ifIndex);
+ if (ifacePolicies == null) {
+ ifacePolicies = new SparseIntArray(MAX_POLICIES);
}
// Currently all classifiers are supported, if any are removed return
// DSCP_POLICY_STATUS_REQUESTED_CLASSIFIER_NOT_SUPPORTED,
// and for any other generic error DSCP_POLICY_STATUS_REQUEST_DECLINED
- int addIndex = 0;
- // If a policy with a matching ID exists, replace it, otherwise use the next free
- // index for the policy.
- if (existingIndex != -1) {
- addIndex = mPolicyIdToBpfMapIndex.get(policy.getPolicyId());
- } else {
- addIndex = getFirstFreeIndex();
+ final int addIndex = findIndex(policy.getPolicyId(), ifIndex);
+ if (addIndex == -1) {
+ return DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES;
}
try {
- mPolicyIdToBpfMapIndex.put(policy.getPolicyId(), addIndex);
-
// Add v4 policy to mBpfDscpIpv4Policies if source and destination address
- // are both null or if they are both instances of Inet6Address.
+ // are both null or if they are both instances of Inet4Address.
if (matchesIpv4(policy)) {
mBpfDscpIpv4Policies.insertOrReplaceEntry(
new Struct.U32(addIndex),
new DscpPolicyValue(policy.getSourceAddress(),
- policy.getDestinationAddress(),
+ policy.getDestinationAddress(), ifIndex,
policy.getSourcePort(), policy.getDestinationPortRange(),
(short) policy.getProtocol(), (short) policy.getDscpValue()));
}
@@ -148,10 +194,16 @@
mBpfDscpIpv6Policies.insertOrReplaceEntry(
new Struct.U32(addIndex),
new DscpPolicyValue(policy.getSourceAddress(),
- policy.getDestinationAddress(),
+ policy.getDestinationAddress(), ifIndex,
policy.getSourcePort(), policy.getDestinationPortRange(),
(short) policy.getProtocol(), (short) policy.getDscpValue()));
}
+
+ ifacePolicies.put(policy.getPolicyId(), addIndex);
+ // Only add the policy to the per interface map if the policy was successfully
+ // added to both bpf maps above. It is safe to assume that if insert fails for
+ // one map then it fails for both.
+ mIfaceIndexToPolicyIdBpfMapIndex.put(ifIndex, ifacePolicies);
} catch (ErrnoException e) {
Log.e(TAG, "Failed to insert policy into map: ", e);
return DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES;
@@ -166,6 +218,7 @@
*
* DSCP_POLICY_STATUS_SUCCESS - if policy was added successfully
* DSCP_POLICY_STATUS_INSUFFICIENT_PROCESSING_RESOURCES - if max policies were already set
+ * DSCP_POLICY_STATUS_REQUEST_DECLINED - Interface index was invalid
*/
public void addDscpPolicy(NetworkAgentInfo nai, DscpPolicy policy) {
if (!mAttachedIfaces.contains(nai.linkProperties.getInterfaceName())) {
@@ -177,11 +230,19 @@
}
}
- int status = addDscpPolicyInternal(policy);
+ final int ifIndex = getIfaceIndex(nai);
+ if (ifIndex == 0) {
+ Log.e(TAG, "Iface index is invalid");
+ sendStatus(nai, policy.getPolicyId(), DSCP_POLICY_STATUS_REQUEST_DECLINED);
+ return;
+ }
+
+ int status = addDscpPolicyInternal(policy, ifIndex);
sendStatus(nai, policy.getPolicyId(), status);
}
- private void removePolicyFromMap(NetworkAgentInfo nai, int policyId, int index) {
+ private void removePolicyFromMap(NetworkAgentInfo nai, int policyId, int index,
+ boolean sendCallback) {
int status = DSCP_POLICY_STATUS_POLICY_NOT_FOUND;
try {
mBpfDscpIpv4Policies.replaceEntry(new Struct.U32(index), DscpPolicyValue.NONE);
@@ -191,7 +252,9 @@
Log.e(TAG, "Failed to delete policy from map: ", e);
}
- sendStatus(nai, policyId, status);
+ if (sendCallback) {
+ sendStatus(nai, policyId, status);
+ }
}
/**
@@ -204,36 +267,44 @@
return;
}
- if (mPolicyIdToBpfMapIndex.get(policyId, -1) != -1) {
- removePolicyFromMap(nai, policyId, mPolicyIdToBpfMapIndex.get(policyId));
- mPolicyIdToBpfMapIndex.delete(policyId);
+ SparseIntArray ifacePolicies = mIfaceIndexToPolicyIdBpfMapIndex.get(getIfaceIndex(nai));
+ if (ifacePolicies == null) return;
+
+ final int existingIndex = ifacePolicies.get(policyId, -1);
+ if (existingIndex == -1) {
+ Log.e(TAG, "Policy " + policyId + " does not exist in map.");
+ sendStatus(nai, policyId, DSCP_POLICY_STATUS_POLICY_NOT_FOUND);
+ return;
}
- // TODO: detach should only occur if no more policies are present on the nai's iface.
- if (mPolicyIdToBpfMapIndex.size() == 0) {
+ removePolicyFromMap(nai, policyId, existingIndex, true);
+ ifacePolicies.delete(policyId);
+
+ if (ifacePolicies.size() == 0) {
detachProgram(nai.linkProperties.getInterfaceName());
}
}
/**
- * Remove all DSCP policies and detach program.
+ * Remove all DSCP policies and detach program. Send callback if requested.
*/
- // TODO: Remove all should only remove policies from corresponding nai iface.
- public void removeAllDscpPolicies(NetworkAgentInfo nai) {
+ public void removeAllDscpPolicies(NetworkAgentInfo nai, boolean sendCallback) {
if (!mAttachedIfaces.contains(nai.linkProperties.getInterfaceName())) {
// Nothing to remove since program is not attached. Send update for policy
// id 0. The status update must contain a policy ID, and 0 is an invalid id.
- sendStatus(nai, 0, DSCP_POLICY_STATUS_SUCCESS);
+ if (sendCallback) {
+ sendStatus(nai, 0, DSCP_POLICY_STATUS_SUCCESS);
+ }
return;
}
- for (int i = 0; i < mPolicyIdToBpfMapIndex.size(); i++) {
- removePolicyFromMap(nai, mPolicyIdToBpfMapIndex.keyAt(i),
- mPolicyIdToBpfMapIndex.valueAt(i));
+ SparseIntArray ifacePolicies = mIfaceIndexToPolicyIdBpfMapIndex.get(getIfaceIndex(nai));
+ if (ifacePolicies == null) return;
+ for (int i = 0; i < ifacePolicies.size(); i++) {
+ removePolicyFromMap(nai, ifacePolicies.keyAt(i), ifacePolicies.valueAt(i),
+ sendCallback);
}
- mPolicyIdToBpfMapIndex.clear();
-
- // Can detach program since no policies are active.
+ ifacePolicies.clear();
detachProgram(nai.linkProperties.getInterfaceName());
}
@@ -241,12 +312,12 @@
* Attach BPF program
*/
private boolean attachProgram(@NonNull String iface) {
- // TODO: attach needs to be per iface not program.
-
try {
NetworkInterface netIface = NetworkInterface.getByName(iface);
+ boolean isEth = TcUtils.isEthernet(iface);
+ String path = PROG_PATH + (isEth ? "_ether" : "_raw_ip");
TcUtils.tcFilterAddDevBpf(netIface.getIndex(), false, PRIO_DSCP, (short) ETH_P_ALL,
- PROG_PATH);
+ path);
} catch (IOException e) {
Log.e(TAG, "Unable to attach to TC on " + iface + ": " + e);
return false;
@@ -264,9 +335,9 @@
if (netIface != null) {
TcUtils.tcFilterDelDev(netIface.getIndex(), false, PRIO_DSCP, (short) ETH_P_ALL);
}
+ mAttachedIfaces.remove(iface);
} catch (IOException e) {
Log.e(TAG, "Unable to detach to TC on " + iface + ": " + e);
}
- mAttachedIfaces.remove(iface);
}
}
diff --git a/service/src/com/android/server/connectivity/DscpPolicyValue.java b/service/src/com/android/server/connectivity/DscpPolicyValue.java
index cb40306..6e4e7eb 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyValue.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyValue.java
@@ -31,29 +31,31 @@
public class DscpPolicyValue extends Struct {
private static final String TAG = DscpPolicyValue.class.getSimpleName();
- // TODO: add the interface index.
@Field(order = 0, type = Type.ByteArray, arraysize = 16)
public final byte[] src46;
@Field(order = 1, type = Type.ByteArray, arraysize = 16)
public final byte[] dst46;
- @Field(order = 2, type = Type.UBE16)
- public final int srcPort;
+ @Field(order = 2, type = Type.U32)
+ public final long ifIndex;
@Field(order = 3, type = Type.UBE16)
- public final int dstPortStart;
+ public final int srcPort;
@Field(order = 4, type = Type.UBE16)
+ public final int dstPortStart;
+
+ @Field(order = 5, type = Type.UBE16)
public final int dstPortEnd;
- @Field(order = 5, type = Type.U8)
+ @Field(order = 6, type = Type.U8)
public final short proto;
- @Field(order = 6, type = Type.U8)
+ @Field(order = 7, type = Type.U8)
public final short dscp;
- @Field(order = 7, type = Type.U8, padding = 3)
+ @Field(order = 8, type = Type.U8, padding = 3)
public final short mask;
private static final int SRC_IP_MASK = 0x1;
@@ -69,6 +71,7 @@
return true;
}
+ // TODO: move to frameworks/libs/net and have this and BpfCoordinator import it.
private byte[] toIpv4MappedAddressBytes(InetAddress ia) {
final byte[] addr6 = new byte[16];
if (ia != null) {
@@ -117,13 +120,12 @@
return mask;
}
- // This constructor is necessary for BpfMap#getValue since all values must be
- // in the constructor.
- public DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int srcPort,
- final int dstPortStart, final int dstPortEnd, final short proto,
+ private DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final long ifIndex,
+ final int srcPort, final int dstPortStart, final int dstPortEnd, final short proto,
final short dscp) {
this.src46 = toAddressField(src46);
this.dst46 = toAddressField(dst46);
+ this.ifIndex = ifIndex;
// These params need to be stored as 0 because uints are used in BpfMap.
// If they are -1 BpfMap write will throw errors.
@@ -138,15 +140,15 @@
this.mask = makeMask(this.src46, this.dst46, srcPort, dstPortStart, proto, dscp);
}
- public DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final int srcPort,
- final Range<Integer> dstPort, final short proto,
+ public DscpPolicyValue(final InetAddress src46, final InetAddress dst46, final long ifIndex,
+ final int srcPort, final Range<Integer> dstPort, final short proto,
final short dscp) {
- this(src46, dst46, srcPort, dstPort != null ? dstPort.getLower() : -1,
+ this(src46, dst46, ifIndex, srcPort, dstPort != null ? dstPort.getLower() : -1,
dstPort != null ? dstPort.getUpper() : -1, proto, dscp);
}
public static final DscpPolicyValue NONE = new DscpPolicyValue(
- null /* src46 */, null /* dst46 */, -1 /* srcPort */,
+ null /* src46 */, null /* dst46 */, 0 /* ifIndex */, -1 /* srcPort */,
-1 /* dstPortStart */, -1 /* dstPortEnd */, (short) -1 /* proto */,
(short) 0 /* dscp */);
@@ -170,9 +172,9 @@
try {
return String.format(
- "src46: %s, dst46: %s, srcPort: %d, dstPortStart: %d, dstPortEnd: %d,"
- + " protocol: %d, dscp %s", srcIpString, dstIpString, srcPort, dstPortStart,
- dstPortEnd, proto, dscp);
+ "src46: %s, dst46: %s, ifIndex: %d, srcPort: %d, dstPortStart: %d,"
+ + " dstPortEnd: %d, protocol: %d, dscp %s", srcIpString, dstIpString,
+ ifIndex, srcPort, dstPortStart, dstPortEnd, proto, dscp);
} catch (IllegalArgumentException e) {
return String.format("String format error: " + e);
}
diff --git a/service/src/com/android/server/connectivity/Nat464Xlat.java b/service/src/com/android/server/connectivity/Nat464Xlat.java
index 7b06682..35e02ca 100644
--- a/service/src/com/android/server/connectivity/Nat464Xlat.java
+++ b/service/src/com/android/server/connectivity/Nat464Xlat.java
@@ -36,9 +36,11 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetworkStackConstants;
import com.android.server.ConnectivityService;
+import java.io.IOException;
import java.net.Inet6Address;
import java.util.Objects;
@@ -96,6 +98,7 @@
private String mIface;
private Inet6Address mIPv6Address;
private State mState = State.IDLE;
+ private ClatCoordinator mClatCoordinator;
private boolean mEnableClatOnCellular;
private boolean mPrefixDiscoveryRunning;
@@ -106,6 +109,7 @@
mNetd = netd;
mNetwork = nai;
mEnableClatOnCellular = deps.getCellular464XlatEnabled();
+ mClatCoordinator = deps.getClatCoordinator(mNetd);
}
/**
@@ -179,10 +183,18 @@
private void enterStartingState(String baseIface) {
mNat64PrefixInUse = selectNat64Prefix();
String addrStr = null;
- try {
- addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
- } catch (RemoteException | ServiceSpecificException e) {
- Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+ if (SdkLevel.isAtLeastT()) {
+ try {
+ addrStr = mClatCoordinator.clatStart(baseIface, getNetId(), mNat64PrefixInUse);
+ } catch (IOException e) {
+ Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+ }
+ } else {
+ try {
+ addrStr = mNetd.clatdStart(baseIface, mNat64PrefixInUse.toString());
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "Error starting clatd on " + baseIface + ": " + e);
+ }
}
mIface = CLAT_PREFIX + baseIface;
mBaseIface = baseIface;
@@ -256,10 +268,18 @@
}
Log.i(TAG, "Stopping clatd on " + mBaseIface);
- try {
- mNetd.clatdStop(mBaseIface);
- } catch (RemoteException | ServiceSpecificException e) {
- Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
+ if (SdkLevel.isAtLeastT()) {
+ try {
+ mClatCoordinator.clatStop();
+ } catch (IOException e) {
+ Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
+ }
+ } else {
+ try {
+ mNetd.clatdStop(mBaseIface);
+ } catch (RemoteException | ServiceSpecificException e) {
+ Log.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e);
+ }
}
String iface = mIface;
diff --git a/service/src/com/android/server/connectivity/NetworkOffer.java b/service/src/com/android/server/connectivity/NetworkOffer.java
index 1e975dd..eea382e 100644
--- a/service/src/com/android/server/connectivity/NetworkOffer.java
+++ b/service/src/com/android/server/connectivity/NetworkOffer.java
@@ -22,6 +22,7 @@
import android.net.NetworkRequest;
import android.os.RemoteException;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@@ -143,6 +144,11 @@
@Override
public String toString() {
- return "NetworkOffer [ Score " + score + " Caps " + caps + "]";
+ final ArrayList<Integer> neededRequestIds = new ArrayList<>();
+ for (final NetworkRequest request : mCurrentlyNeeded) {
+ neededRequestIds.add(request.requestId);
+ }
+ return "NetworkOffer [ Provider Id (" + providerId + ") " + score + " Caps "
+ + caps + " Needed by " + neededRequestIds + "]";
}
}
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 62b3add..c02d9cf 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -134,7 +134,9 @@
// Store appIds traffic permissions for each user.
// Keys are users, Values are SparseArrays where each entry maps an appId to the permissions
- // that appId has within that user.
+ // that appId has within that user. The permissions are a bitmask of PERMISSION_INTERNET and
+ // PERMISSION_UPDATE_DEVICE_STATS, or 0 (PERMISSION_NONE) if the app has neither of those
+ // permissions. They can never be PERMISSION_UNINSTALLED.
@GuardedBy("this")
private final Map<UserHandle, SparseIntArray> mUsersTrafficPermissions = new ArrayMap<>();
@@ -545,17 +547,21 @@
// Remove appIds traffic permission that belongs to the user
final SparseIntArray removedUserAppIds = mUsersTrafficPermissions.remove(user);
- // Generate appIds from left users.
+ // Generate appIds from the remaining users.
final SparseIntArray appIds = makeAppIdsTrafficPermForAllUsers();
+
+ if (removedUserAppIds == null) {
+ Log.wtf(TAG, "onUserRemoved: Receive unknown user=" + user);
+ return;
+ }
+
// Clear permission on those appIds belong to this user only, set the permission to
// PERMISSION_UNINSTALLED.
- if (removedUserAppIds != null) {
- for (int i = 0; i < removedUserAppIds.size(); i++) {
- final int appId = removedUserAppIds.keyAt(i);
- // Need to clear permission if the removed appId is not found in the array.
- if (appIds.indexOfKey(appId) < 0) {
- appIds.put(appId, PERMISSION_UNINSTALLED);
- }
+ for (int i = 0; i < removedUserAppIds.size(); i++) {
+ final int appId = removedUserAppIds.keyAt(i);
+ // Need to clear permission if the removed appId is not found in the array.
+ if (appIds.indexOfKey(appId) < 0) {
+ appIds.put(appId, PERMISSION_UNINSTALLED);
}
}
sendAppIdsTrafficPermission(appIds);
@@ -650,7 +656,6 @@
}
private synchronized void updateAppIdTrafficPermission(int uid) {
- final int appId = UserHandle.getAppId(uid);
final int uidTrafficPerm = getTrafficPermissionForUid(uid);
final SparseIntArray userTrafficPerms =
mUsersTrafficPermissions.get(UserHandle.getUserHandleForUid(uid));
@@ -661,6 +666,7 @@
// Do not put PERMISSION_UNINSTALLED into the array. If no package left on the uid
// (PERMISSION_UNINSTALLED), remove the appId from the array. Otherwise, update the latest
// permission to the appId.
+ final int appId = UserHandle.getAppId(uid);
if (uidTrafficPerm == PERMISSION_UNINSTALLED) {
userTrafficPerms.delete(appId);
} else {
@@ -984,10 +990,6 @@
*/
@VisibleForTesting
void sendAppIdsTrafficPermission(SparseIntArray netdPermissionsAppIds) {
- if (mNetd == null) {
- Log.e(TAG, "Failed to get the netd service");
- return;
- }
final ArrayList<Integer> allPermissionAppIds = new ArrayList<>();
final ArrayList<Integer> internetPermissionAppIds = new ArrayList<>();
final ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>();
diff --git a/tests/common/Android.bp b/tests/common/Android.bp
index 7bb7cb5..509e881 100644
--- a/tests/common/Android.bp
+++ b/tests/common/Android.bp
@@ -36,6 +36,7 @@
"modules-utils-build",
"net-tests-utils",
"net-utils-framework-common",
+ "platform-compat-test-rules",
"platform-test-annotations",
],
libs: [
diff --git a/tests/common/AndroidTest_Coverage.xml b/tests/common/AndroidTest_Coverage.xml
index d4898b2..48d26b8 100644
--- a/tests/common/AndroidTest_Coverage.xml
+++ b/tests/common/AndroidTest_Coverage.xml
@@ -14,7 +14,8 @@
-->
<configuration description="Runs coverage tests for Connectivity">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="ConnectivityCoverageTests.apk" />
+ <option name="test-file-name" value="ConnectivityCoverageTests.apk" />
+ <option name="install-arg" value="-t" />
</target_preparer>
<option name="test-tag" value="ConnectivityCoverageTests" />
diff --git a/tests/common/java/android/net/LinkPropertiesTest.java b/tests/common/java/android/net/LinkPropertiesTest.java
index 4d85a57..8fc636a 100644
--- a/tests/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/common/java/android/net/LinkPropertiesTest.java
@@ -20,6 +20,7 @@
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
import static com.android.testutils.ParcelUtils.parcelingRoundTrip;
@@ -30,6 +31,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import android.compat.testing.PlatformCompatChangeRule;
import android.net.LinkProperties.ProvisioningChange;
import android.os.Build;
import android.system.OsConstants;
@@ -45,6 +47,9 @@
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -65,6 +70,9 @@
@Rule
public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule();
+ @Rule
+ public final PlatformCompatChangeRule compatChangeRule = new PlatformCompatChangeRule();
+
private static final InetAddress ADDRV4 = address("75.208.6.1");
private static final InetAddress ADDRV6 = address("2001:0db8:85a3:0000:0000:8a2e:0370:7334");
private static final InetAddress DNS1 = address("75.208.7.1");
@@ -1254,6 +1262,7 @@
}
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @EnableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
public void testRouteAddWithSameKey() throws Exception {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("wlan0");
@@ -1268,4 +1277,36 @@
lp.addRoute(new RouteInfo(v4, address("192.0.2.1"), "wlan0", RTN_THROW, 1460));
assertEquals(2, lp.getRoutes().size());
}
+
+ @Test @IgnoreUpTo(SC_V2)
+ @EnableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
+ public void testExcludedRoutesEnabled() {
+ 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, 0), RTN_THROW));
+ assertEquals(2, lp.getRoutes().size());
+
+ lp.addRoute(new RouteInfo(GATEWAY1));
+ assertEquals(3, lp.getRoutes().size());
+ }
+
+ @Test @IgnoreUpTo(SC_V2)
+ @DisableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
+ public void testExcludedRoutesDisabled() {
+ final LinkProperties lp = new LinkProperties();
+ assertEquals(0, lp.getRoutes().size());
+
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV4, 0), RTN_UNREACHABLE));
+ assertEquals(0, lp.getRoutes().size());
+
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 5), RTN_THROW));
+ assertEquals(0, lp.getRoutes().size());
+
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 2), RTN_UNICAST));
+ assertEquals(1, lp.getRoutes().size());
+ }
}
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index 9ae5fab..c30e1d3 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -82,12 +82,11 @@
import android.util.ArraySet;
import android.util.Range;
-import androidx.test.runner.AndroidJUnit4;
-
import com.android.testutils.CompatUtil;
import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Rule;
import org.junit.Test;
@@ -99,8 +98,12 @@
import java.util.List;
import java.util.Set;
-@RunWith(AndroidJUnit4.class)
@SmallTest
+@RunWith(DevSdkIgnoreRunner.class)
+// NetworkCapabilities is only updatable on S+, and this test covers behavior which implementation
+// is self-contained within NetworkCapabilities.java, so it does not need to be run on, or
+// compatible with, earlier releases.
+@IgnoreUpTo(Build.VERSION_CODES.R)
@ConnectivityModuleTest
public class NetworkCapabilitiesTest {
private static final String TEST_SSID = "TEST_SSID";
@@ -489,7 +492,7 @@
assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
public void testOemPrivate() {
NetworkCapabilities nc = new NetworkCapabilities();
// By default OEM_PRIVATE is neither in the required or forbidden lists and the network is
@@ -516,7 +519,7 @@
assertFalse(nr.satisfiedByNetworkCapabilities(new NetworkCapabilities()));
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
public void testForbiddenCapabilities() {
NetworkCapabilities network = new NetworkCapabilities();
@@ -630,7 +633,7 @@
return new Range<Integer>(from, to);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @Test
public void testSetAdministratorUids() {
NetworkCapabilities nc =
new NetworkCapabilities().setAdministratorUids(new int[] {2, 1, 3});
@@ -638,7 +641,7 @@
assertArrayEquals(new int[] {1, 2, 3}, nc.getAdministratorUids());
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @Test
public void testSetAdministratorUidsWithDuplicates() {
try {
new NetworkCapabilities().setAdministratorUids(new int[] {1, 1});
@@ -750,7 +753,7 @@
() -> nc2.addTransportType(TRANSPORT_WIFI));
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R) // New behavior in updatable NetworkCapabilities (S+)
+ @Test
public void testSetNetworkSpecifierOnTestMultiTransportNc() {
final NetworkSpecifier specifier = CompatUtil.makeEthernetNetworkSpecifier("eth0");
NetworkCapabilities nc = new NetworkCapabilities.Builder()
@@ -859,7 +862,7 @@
assertEquals(TRANSPORT_TEST, transportTypes[3]);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @Test
public void testTelephonyNetworkSpecifier() {
final TelephonyNetworkSpecifier specifier = new TelephonyNetworkSpecifier(1);
final NetworkCapabilities nc1 = new NetworkCapabilities.Builder()
@@ -970,7 +973,7 @@
assertEquals(specifier, nc.getNetworkSpecifier());
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @Test
public void testAdministratorUidsAndOwnerUid() {
// Test default owner uid.
// If the owner uid is not set, the default value should be Process.INVALID_UID.
@@ -1014,7 +1017,7 @@
return nc;
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
public void testSubIds() throws Exception {
final NetworkCapabilities ncWithoutId = capsWithSubIds();
final NetworkCapabilities ncWithId = capsWithSubIds(TEST_SUBID1);
@@ -1036,7 +1039,7 @@
assertTrue(requestWithoutId.canBeSatisfiedBy(ncWithId));
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
public void testEqualsSubIds() throws Exception {
assertEquals(capsWithSubIds(), capsWithSubIds());
assertNotEquals(capsWithSubIds(), capsWithSubIds(TEST_SUBID1));
@@ -1185,7 +1188,7 @@
}
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ @Test
public void testBuilder() {
final int ownerUid = 1001;
final int signalStrength = -80;
@@ -1255,7 +1258,7 @@
}
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
public void testBuilderWithoutDefaultCap() {
final NetworkCapabilities nc =
NetworkCapabilities.Builder.withoutDefaultCapabilities().build();
@@ -1266,12 +1269,12 @@
assertEquals(0, nc.getCapabilities().length);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
public void testRestrictCapabilitiesForTestNetworkByNotOwnerWithNonRestrictedNc() {
testRestrictCapabilitiesForTestNetworkWithNonRestrictedNc(false /* isOwner */);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
public void testRestrictCapabilitiesForTestNetworkByOwnerWithNonRestrictedNc() {
testRestrictCapabilitiesForTestNetworkWithNonRestrictedNc(true /* isOwner */);
}
@@ -1316,12 +1319,12 @@
assertEquals(expectedNcBuilder.build(), nonRestrictedNc);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
public void testRestrictCapabilitiesForTestNetworkByNotOwnerWithRestrictedNc() {
testRestrictCapabilitiesForTestNetworkWithRestrictedNc(false /* isOwner */);
}
- @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ @Test
public void testRestrictCapabilitiesForTestNetworkByOwnerWithRestrictedNc() {
testRestrictCapabilitiesForTestNetworkWithRestrictedNc(true /* isOwner */);
}
diff --git a/tests/cts/hostside/TEST_MAPPING b/tests/cts/hostside/TEST_MAPPING
index fcec483..ab6de82 100644
--- a/tests/cts/hostside/TEST_MAPPING
+++ b/tests/cts/hostside/TEST_MAPPING
@@ -4,9 +4,6 @@
"name": "CtsHostsideNetworkTests",
"options": [
{
- "include-filter": "com.android.cts.net.HostsideRestrictBackgroundNetworkTests"
- },
- {
"exclude-annotation": "androidx.test.filters.FlakyTest"
},
{
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
index 28437c2..e7b2815 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
@@ -28,5 +28,5 @@
void sendNotification(int notificationId, String notificationType);
void registerNetworkCallback(in NetworkRequest request, in INetworkCallback cb);
void unregisterNetworkCallback();
- void scheduleJob(in JobInfo jobInfo);
+ int scheduleJob(in JobInfo jobInfo);
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index f460180..96ce65f 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -16,6 +16,7 @@
package com.android.cts.net.hostside;
+import static android.app.job.JobScheduler.RESULT_SUCCESS;
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.os.BatteryManager.BATTERY_PLUGGED_AC;
import static android.os.BatteryManager.BATTERY_PLUGGED_USB;
@@ -151,7 +152,7 @@
protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 20_000; // 20 sec
- private static final long BROADCAST_TIMEOUT_MS = 15_000;
+ private static final long BROADCAST_TIMEOUT_MS = 5_000;
protected Context mContext;
protected Instrumentation mInstrumentation;
@@ -855,7 +856,8 @@
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setTransientExtras(extras)
.build();
- mServiceClient.scheduleJob(jobInfo);
+ assertEquals("Error scheduling " + jobInfo,
+ RESULT_SUCCESS, mServiceClient.scheduleJob(jobInfo));
forceRunJob(TEST_APP2_PKG, TEST_JOB_ID);
if (latch.await(JOB_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
final int resultCode = result.get(0).first;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
index 8b70f9b..0610774 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -107,7 +107,7 @@
mService.unregisterNetworkCallback();
}
- public void scheduleJob(JobInfo jobInfo) throws RemoteException {
- mService.scheduleJob(jobInfo);
+ public int scheduleJob(JobInfo jobInfo) throws RemoteException {
+ return mService.scheduleJob(jobInfo);
}
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
index b6218d2..56be3e3 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -25,7 +25,7 @@
import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_METERED;
import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
import static org.junit.Assert.assertEquals;
@@ -390,7 +390,7 @@
}
public static String executeShellCommand(String command) {
- final String result = runShellCommand(command).trim();
+ final String result = runShellCommandOrThrow(command).trim();
Log.d(TAG, "Output of '" + command + "': '" + result + "'");
return result;
}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
index f2a7b3f..3ed5391 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -165,10 +165,10 @@
}
@Override
- public void scheduleJob(JobInfo jobInfo) {
+ public int scheduleJob(JobInfo jobInfo) {
final JobScheduler jobScheduler = getApplicationContext()
.getSystemService(JobScheduler.class);
- jobScheduler.schedule(jobInfo);
+ return jobScheduler.schedule(jobInfo);
}
};
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 9055861..a129108 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -151,6 +151,7 @@
import android.os.Looper;
import android.os.MessageQueue;
import android.os.Process;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -661,10 +662,12 @@
// CaptivePortalApiUrl & CaptivePortalData will be preserved if the given uid holds the
// NETWORK_SETTINGS permission.
- assertEquals(capportUrl,
+ assertNotNull(lp.getCaptivePortalApiUrl());
+ assertNotNull(lp.getCaptivePortalData());
+ assertEquals(lp.getCaptivePortalApiUrl(),
mCm.getRedactedLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg)
.getCaptivePortalApiUrl());
- assertEquals(capportData,
+ assertEquals(lp.getCaptivePortalData(),
mCm.getRedactedLinkPropertiesForPackage(lp, privilegedUid, privilegedPkg)
.getCaptivePortalData());
});
@@ -887,9 +890,21 @@
//
// Note that this test this will still fail in instant mode if a device supports Ethernet
// via other hardware means. We are not currently aware of any such device.
- return (mContext.getSystemService(Context.ETHERNET_SERVICE) != null) ||
- mPackageManager.hasSystemFeature(FEATURE_ETHERNET) ||
- mPackageManager.hasSystemFeature(FEATURE_USB_HOST);
+ return hasEthernetService()
+ || mPackageManager.hasSystemFeature(FEATURE_ETHERNET)
+ || mPackageManager.hasSystemFeature(FEATURE_USB_HOST);
+ }
+
+ private boolean hasEthernetService() {
+ // On Q creating EthernetManager from a thread that does not have a looper (like the test
+ // thread) crashes because it tried to use Looper.myLooper() through the default Handler
+ // constructor to run onAvailabilityChanged callbacks. Use ServiceManager to check whether
+ // the service exists instead.
+ // TODO: remove once Q is no longer supported in MTS, as ServiceManager is hidden API
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
+ return ServiceManager.getService(Context.ETHERNET_SERVICE) != null;
+ }
+ return mContext.getSystemService(Context.ETHERNET_SERVICE) != null;
}
private boolean shouldBeSupported(int networkType) {
diff --git a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
index e8add6b..1e42fe6 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -48,9 +48,11 @@
import android.platform.test.annotations.AppModeFull
import android.system.Os
import android.system.OsConstants.AF_INET
+import android.system.OsConstants.AF_INET6
import android.system.OsConstants.IPPROTO_UDP
import android.system.OsConstants.SOCK_DGRAM
import android.system.OsConstants.SOCK_NONBLOCK
+import android.util.Log
import android.util.Range
import androidx.test.InstrumentationRegistry
import androidx.test.runner.AndroidJUnit4
@@ -71,6 +73,8 @@
import org.junit.Test
import org.junit.runner.RunWith
import java.net.Inet4Address
+import java.net.Inet6Address
+import java.net.InetAddress
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.regex.Pattern
@@ -81,6 +85,9 @@
private const val MAX_PACKET_LENGTH = 1500
+private const val IP4_PREFIX_LEN = 32
+private const val IP6_PREFIX_LEN = 128
+
private val instrumentation: Instrumentation
get() = InstrumentationRegistry.getInstrumentation()
@@ -97,6 +104,9 @@
private val LOCAL_IPV4_ADDRESS = InetAddresses.parseNumericAddress("192.0.2.1")
private val TEST_TARGET_IPV4_ADDR =
InetAddresses.parseNumericAddress("8.8.8.8") as Inet4Address
+ private val LOCAL_IPV6_ADDRESS = InetAddresses.parseNumericAddress("2001:db8::1")
+ private val TEST_TARGET_IPV6_ADDR =
+ InetAddresses.parseNumericAddress("2001:4860:4860::8888") as Inet6Address
private val realContext = InstrumentationRegistry.getContext()
private val cm = realContext.getSystemService(ConnectivityManager::class.java)
@@ -132,7 +142,9 @@
runAsShell(MANAGE_TEST_NETWORKS) {
val tnm = realContext.getSystemService(TestNetworkManager::class.java)
- iface = tnm.createTunInterface(Array(1) { LinkAddress(LOCAL_IPV4_ADDRESS, 32) })
+ iface = tnm.createTunInterface(arrayOf(
+ LinkAddress(LOCAL_IPV4_ADDRESS, IP4_PREFIX_LEN),
+ LinkAddress(LOCAL_IPV6_ADDRESS, IP6_PREFIX_LEN)))
assertNotNull(iface)
}
@@ -154,6 +166,8 @@
// reader.stop() cleans up tun fd
reader.handler.post { reader.stop() }
+ if (iface.fileDescriptor.fileDescriptor != null)
+ Os.close(iface.fileDescriptor.fileDescriptor)
handlerThread.quitSafely()
}
@@ -196,9 +210,11 @@
}
}
val lp = LinkProperties().apply {
- addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, 32))
+ addLinkAddress(LinkAddress(LOCAL_IPV4_ADDRESS, IP4_PREFIX_LEN))
+ addLinkAddress(LinkAddress(LOCAL_IPV6_ADDRESS, IP6_PREFIX_LEN))
addRoute(RouteInfo(IpPrefix("0.0.0.0/0"), null, null))
- setInterfaceName(iface.getInterfaceName())
+ addRoute(RouteInfo(InetAddress.getByName("fe80::1234")))
+ setInterfaceName(specifier)
}
val config = NetworkAgentConfig.Builder().build()
val agent = TestableNetworkAgent(context, handlerThread.looper, nc, lp, config)
@@ -218,47 +234,114 @@
eachByte -> "%02x".format(eachByte)
}
- fun checkDscpValue(
+ fun sendPacket(
agent: TestableNetworkAgent,
- callback: TestableNetworkCallback,
- dscpValue: Int = 0,
- dstPort: Int = 0
+ sendV6: Boolean,
+ dstPort: Int = 0,
) {
val testString = "test string"
val testPacket = ByteBuffer.wrap(testString.toByteArray(Charsets.UTF_8))
var packetFound = false
- val socket = Os.socket(AF_INET, SOCK_DGRAM or SOCK_NONBLOCK, IPPROTO_UDP)
+ val socket = Os.socket(if (sendV6) AF_INET6 else AF_INET, SOCK_DGRAM or SOCK_NONBLOCK,
+ IPPROTO_UDP)
agent.network.bindSocket(socket)
val originalPacket = testPacket.readAsArray()
- Os.sendto(socket, originalPacket, 0 /* bytesOffset */, originalPacket.size,
- 0 /* flags */, TEST_TARGET_IPV4_ADDR, dstPort)
-
+ Os.sendto(socket, originalPacket, 0 /* bytesOffset */, originalPacket.size, 0 /* flags */,
+ if(sendV6) TEST_TARGET_IPV6_ADDR else TEST_TARGET_IPV4_ADDR, dstPort)
Os.close(socket)
+ }
+
+ fun parseV4PacketDscp(buffer : ByteBuffer) : Int {
+ val ip_ver = buffer.get()
+ val tos = buffer.get()
+ val length = buffer.getShort()
+ val id = buffer.getShort()
+ val offset = buffer.getShort()
+ val ttl = buffer.get()
+ val ipType = buffer.get()
+ val checksum = buffer.getShort()
+ return tos.toInt().shr(2)
+ }
+
+ fun parseV6PacketDscp(buffer : ByteBuffer) : Int {
+ val ip_ver = buffer.get()
+ val tc = buffer.get()
+ val fl = buffer.getShort()
+ val length = buffer.getShort()
+ val proto = buffer.get()
+ val hop = buffer.get()
+ // DSCP is bottom 4 bits of ip_ver and top 2 of tc.
+ val ip_ver_bottom = ip_ver.toInt().and(0xf)
+ val tc_dscp = tc.toInt().shr(6)
+ return ip_ver_bottom.toInt().shl(2) + tc_dscp
+ }
+
+ fun parsePacketIp(
+ buffer : ByteBuffer,
+ sendV6 : Boolean,
+ ) : Boolean {
+ val ipAddr = if (sendV6) ByteArray(16) else ByteArray(4)
+ buffer.get(ipAddr)
+ val srcIp = if (sendV6) Inet6Address.getByAddress(ipAddr)
+ else Inet4Address.getByAddress(ipAddr)
+ buffer.get(ipAddr)
+ val dstIp = if (sendV6) Inet6Address.getByAddress(ipAddr)
+ else Inet4Address.getByAddress(ipAddr)
+
+ Log.e(TAG, "IP Src:" + srcIp + " dst: " + dstIp)
+
+ if ((sendV6 && srcIp == LOCAL_IPV6_ADDRESS && dstIp == TEST_TARGET_IPV6_ADDR) ||
+ (!sendV6 && srcIp == LOCAL_IPV4_ADDRESS && dstIp == TEST_TARGET_IPV4_ADDR)) {
+ Log.e(TAG, "IP return true");
+ return true
+ }
+ Log.e(TAG, "IP return false");
+ return false
+ }
+
+ fun parsePacketPort(
+ buffer : ByteBuffer,
+ srcPort : Int,
+ dstPort : Int
+ ) : Boolean {
+ if (srcPort == 0 && dstPort == 0) return true
+
+ val packetSrcPort = buffer.getShort().toInt()
+ val packetDstPort = buffer.getShort().toInt()
+
+ Log.e(TAG, "Port Src:" + packetSrcPort + " dst: " + packetDstPort)
+
+ if ((srcPort == 0 || (srcPort != 0 && srcPort == packetSrcPort)) &&
+ (dstPort == 0 || (dstPort != 0 && dstPort == packetDstPort))) {
+ Log.e(TAG, "Port return true");
+ return true
+ }
+ Log.e(TAG, "Port return false");
+ return false
+ }
+
+ fun validatePacket(
+ agent : TestableNetworkAgent,
+ sendV6 : Boolean = false,
+ dscpValue : Int = 0,
+ dstPort : Int = 0,
+ ) {
+ var packetFound = false;
+ sendPacket(agent, sendV6, dstPort)
+ // TODO: grab source port from socket in sendPacket
+
+ Log.e(TAG, "find DSCP value:" + dscpValue)
generateSequence { reader.poll(PACKET_TIMEOUT_MS) }.forEach { packet ->
val buffer = ByteBuffer.wrap(packet, 0, packet.size).order(ByteOrder.BIG_ENDIAN)
- val ip_ver = buffer.get()
- val tos = buffer.get()
- val length = buffer.getShort()
- val id = buffer.getShort()
- val offset = buffer.getShort()
- val ttl = buffer.get()
- val ipType = buffer.get()
- val checksum = buffer.getShort()
+ val dscp = if (sendV6) parseV6PacketDscp(buffer) else parseV4PacketDscp(buffer)
+ Log.e(TAG, "DSCP value:" + dscp)
- val ipAddr = ByteArray(4)
- buffer.get(ipAddr)
- val srcIp = Inet4Address.getByAddress(ipAddr)
- buffer.get(ipAddr)
- val dstIp = Inet4Address.getByAddress(ipAddr)
- val packetSrcPort = buffer.getShort().toInt()
- val packetDstPort = buffer.getShort().toInt()
-
- // TODO: Add source port comparison.
- if (srcIp == LOCAL_IPV4_ADDRESS && dstIp == TEST_TARGET_IPV4_ADDR &&
- packetDstPort == dstPort) {
- assertEquals(dscpValue, (tos.toInt().shr(2)))
+ // TODO: Add source port comparison. Use 0 for now.
+ if (parsePacketIp(buffer, sendV6) && parsePacketPort(buffer, 0, dstPort)) {
+ Log.e(TAG, "DSCP value found")
+ assertEquals(dscpValue, dscp)
packetFound = true
}
}
@@ -275,12 +358,12 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(policyId, it.policyId)
assertEquals(DSCP_POLICY_STATUS_DELETED, it.status)
- checkDscpValue(agent, callback, dstPort = portNumber)
}
}
@Test
- fun testDscpPolicyAddPolicies(): Unit = createConnectedNetworkAgent().let { (agent, callback) ->
+ fun testDscpPolicyAddPolicies(): Unit = createConnectedNetworkAgent().let {
+ (agent, callback) ->
val policy = DscpPolicy.Builder(1, 1)
.setDestinationPortRange(Range(4444, 4444)).build()
agent.sendAddDscpPolicy(policy)
@@ -288,8 +371,7 @@
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
}
-
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 4444)
+ validatePacket(agent, dscpValue = 1, dstPort = 4444)
agent.sendRemoveDscpPolicy(1)
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
@@ -298,15 +380,54 @@
}
val policy2 = DscpPolicy.Builder(1, 4)
- .setDestinationPortRange(Range(5555, 5555)).setSourceAddress(LOCAL_IPV4_ADDRESS)
- .setDestinationAddress(TEST_TARGET_IPV4_ADDR).setProtocol(IPPROTO_UDP).build()
+ .setDestinationPortRange(Range(5555, 5555))
+ .setDestinationAddress(TEST_TARGET_IPV4_ADDR)
+ .setSourceAddress(LOCAL_IPV4_ADDRESS)
+ .setProtocol(IPPROTO_UDP).build()
agent.sendAddDscpPolicy(policy2)
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
}
- checkDscpValue(agent, callback, dscpValue = 4, dstPort = 5555)
+ validatePacket(agent, dscpValue = 4, dstPort = 5555)
+
+ agent.sendRemoveDscpPolicy(1)
+ agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
+ assertEquals(1, it.policyId)
+ assertEquals(DSCP_POLICY_STATUS_DELETED, it.status)
+ }
+ }
+
+ @Test
+ fun testDscpPolicyAddV6Policies(): Unit = createConnectedNetworkAgent().let {
+ (agent, callback) ->
+ val policy = DscpPolicy.Builder(1, 1)
+ .setDestinationPortRange(Range(4444, 4444)).build()
+ agent.sendAddDscpPolicy(policy)
+ agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
+ assertEquals(1, it.policyId)
+ assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
+ }
+ validatePacket(agent, true, dscpValue = 1, dstPort = 4444)
+
+ agent.sendRemoveDscpPolicy(1)
+ agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
+ assertEquals(1, it.policyId)
+ assertEquals(DSCP_POLICY_STATUS_DELETED, it.status)
+ }
+
+ val policy2 = DscpPolicy.Builder(1, 4)
+ .setDestinationPortRange(Range(5555, 5555))
+ .setDestinationAddress(TEST_TARGET_IPV6_ADDR)
+ .setSourceAddress(LOCAL_IPV6_ADDRESS)
+ .setProtocol(IPPROTO_UDP).build()
+ agent.sendAddDscpPolicy(policy2)
+ agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
+ assertEquals(1, it.policyId)
+ assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
+ }
+ validatePacket(agent, true, dscpValue = 4, dstPort = 5555)
agent.sendRemoveDscpPolicy(1)
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
@@ -324,7 +445,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 1111)
+ validatePacket(agent, dscpValue = 1, dstPort = 1111)
}
val policy2 = DscpPolicy.Builder(2, 1).setDestinationPortRange(Range(2222, 2222)).build()
@@ -332,7 +453,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 2222)
+ validatePacket(agent, dscpValue = 1, dstPort = 2222)
}
val policy3 = DscpPolicy.Builder(3, 1).setDestinationPortRange(Range(3333, 3333)).build()
@@ -340,13 +461,16 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 3333)
+ validatePacket(agent, dscpValue = 1, dstPort = 3333)
}
/* Remove Policies and check CE is no longer set */
doRemovePolicyTest(agent, callback, 1)
+ validatePacket(agent, dscpValue = 0, dstPort = 1111)
doRemovePolicyTest(agent, callback, 2)
+ validatePacket(agent, dscpValue = 0, dstPort = 2222)
doRemovePolicyTest(agent, callback, 3)
+ validatePacket(agent, dscpValue = 0, dstPort = 3333)
}
@Test
@@ -357,7 +481,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 1111)
+ validatePacket(agent, dscpValue = 1, dstPort = 1111)
}
doRemovePolicyTest(agent, callback, 1)
@@ -366,7 +490,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 2222)
+ validatePacket(agent, dscpValue = 1, dstPort = 2222)
}
doRemovePolicyTest(agent, callback, 2)
@@ -375,7 +499,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 3333)
+ validatePacket(agent, dscpValue = 1, dstPort = 3333)
}
doRemovePolicyTest(agent, callback, 3)
}
@@ -389,7 +513,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 1111)
+ validatePacket(agent, dscpValue = 1, dstPort = 1111)
}
val policy2 = DscpPolicy.Builder(2, 1).setDestinationPortRange(Range(2222, 2222)).build()
@@ -397,7 +521,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 2222)
+ validatePacket(agent, dscpValue = 1, dstPort = 2222)
}
val policy3 = DscpPolicy.Builder(3, 1).setDestinationPortRange(Range(3333, 3333)).build()
@@ -405,7 +529,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 3333)
+ validatePacket(agent, dscpValue = 1, dstPort = 3333)
}
/* Remove Policies and check CE is no longer set */
@@ -423,14 +547,15 @@
}
@Test
- fun testRemoveAllDscpPolicies(): Unit = createConnectedNetworkAgent().let { (agent, callback) ->
+ fun testRemoveAllDscpPolicies(): Unit = createConnectedNetworkAgent().let {
+ (agent, callback) ->
val policy = DscpPolicy.Builder(1, 1)
.setDestinationPortRange(Range(1111, 1111)).build()
agent.sendAddDscpPolicy(policy)
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 1111)
+ validatePacket(agent, dscpValue = 1, dstPort = 1111)
}
val policy2 = DscpPolicy.Builder(2, 1)
@@ -439,7 +564,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 2222)
+ validatePacket(agent, dscpValue = 1, dstPort = 2222)
}
val policy3 = DscpPolicy.Builder(3, 1)
@@ -448,24 +573,24 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 3333)
+ validatePacket(agent, dscpValue = 1, dstPort = 3333)
}
agent.sendRemoveAllDscpPolicies()
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_DELETED, it.status)
- checkDscpValue(agent, callback, dstPort = 1111)
+ validatePacket(agent, false, dstPort = 1111)
}
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_DELETED, it.status)
- checkDscpValue(agent, callback, dstPort = 2222)
+ validatePacket(agent, false, dstPort = 2222)
}
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_DELETED, it.status)
- checkDscpValue(agent, callback, dstPort = 3333)
+ validatePacket(agent, false, dstPort = 3333)
}
}
@@ -477,12 +602,9 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 4444)
+ validatePacket(agent, dscpValue = 1, dstPort = 4444)
}
- // TODO: send packet on socket and confirm that changing the DSCP policy
- // updates the mark to the new value.
-
val policy2 = DscpPolicy.Builder(1, 1).setDestinationPortRange(Range(5555, 5555)).build()
agent.sendAddDscpPolicy(policy2)
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
@@ -490,8 +612,8 @@
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
// Sending packet with old policy should fail
- checkDscpValue(agent, callback, dstPort = 4444)
- checkDscpValue(agent, callback, dscpValue = 1, dstPort = 5555)
+ validatePacket(agent, dscpValue = 0, dstPort = 4444)
+ validatePacket(agent, dscpValue = 1, dstPort = 5555)
}
agent.sendRemoveDscpPolicy(1)
diff --git a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
index 9590f88..7286bf6 100644
--- a/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
+++ b/tests/cts/net/src/android/net/cts/Ikev2VpnTest.java
@@ -20,8 +20,6 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.cts.util.CtsNetUtils.TestNetworkCallback;
-import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
-import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS;
import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
@@ -51,6 +49,7 @@
import android.net.TestNetworkInterface;
import android.net.VpnManager;
import android.net.cts.util.CtsNetUtils;
+import android.net.cts.util.IkeSessionTestUtils;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.os.Build;
import android.os.Process;
@@ -87,6 +86,7 @@
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -196,6 +196,7 @@
private final X509Certificate mServerRootCa;
private final CertificateAndKey mUserCertKey;
+ private final List<TestableNetworkCallback> mCallbacksToUnregister = new ArrayList<>();
public Ikev2VpnTest() throws Exception {
// Build certificates
@@ -205,6 +206,9 @@
@After
public void tearDown() {
+ for (TestableNetworkCallback callback : mCallbacksToUnregister) {
+ sCM.unregisterNetworkCallback(callback);
+ }
setAppop(AppOpsManager.OP_ACTIVATE_VPN, false);
setAppop(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, false);
}
@@ -247,6 +251,28 @@
return builder.build();
}
+ private Ikev2VpnProfile buildIkev2VpnProfileIkeTunConnParams(
+ final boolean isRestrictedToTestNetworks, final boolean requiresValidation,
+ final boolean testIpv6) throws Exception {
+ final IkeTunnelConnectionParams params =
+ new IkeTunnelConnectionParams(testIpv6
+ ? IkeSessionTestUtils.IKE_PARAMS_V6 : IkeSessionTestUtils.IKE_PARAMS_V4,
+ IkeSessionTestUtils.CHILD_PARAMS);
+
+ final Ikev2VpnProfileBuilderShim builderShim =
+ Ikev2VpnProfileBuilderShimImpl.newInstance(null, null, params)
+ .setRequiresInternetValidation(requiresValidation)
+ .setProxy(TEST_PROXY_INFO)
+ .setMaxMtu(TEST_MTU)
+ .setMetered(false);
+
+ final Ikev2VpnProfile.Builder builder = (Ikev2VpnProfile.Builder) builderShim.getBuilder();
+ if (isRestrictedToTestNetworks) {
+ builder.restrictToTestNetworks();
+ }
+ return builder.build();
+ }
+
private Ikev2VpnProfile buildIkev2VpnProfilePsk(@NonNull String remote,
boolean isRestrictedToTestNetworks, boolean requiresValidation) throws Exception {
final Ikev2VpnProfileBuilderShim builder =
@@ -320,8 +346,8 @@
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
assumeTrue(TestUtils.shouldTestTApis());
- final IkeTunnelConnectionParams expectedParams =
- new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+ final IkeTunnelConnectionParams expectedParams = new IkeTunnelConnectionParams(
+ IkeSessionTestUtils.IKE_PARAMS_V6, IkeSessionTestUtils.CHILD_PARAMS);
final Ikev2VpnProfileBuilderShim ikeProfileBuilder =
Ikev2VpnProfileBuilderShimImpl.newInstance(null, null, expectedParams);
// Verify the other Ike options could not be set with IkeTunnelConnectionParams.
@@ -467,7 +493,8 @@
}
private void checkStartStopVpnProfileBuildsNetworks(@NonNull IkeTunUtils tunUtils,
- boolean testIpv6, boolean requiresValidation, boolean testSessionKey)
+ boolean testIpv6, boolean requiresValidation, boolean testSessionKey,
+ boolean testIkeTunConnParams)
throws Exception {
String serverAddr = testIpv6 ? TEST_SERVER_ADDR_V6 : TEST_SERVER_ADDR_V4;
String initResp = testIpv6 ? SUCCESSFUL_IKE_INIT_RESP_V6 : SUCCESSFUL_IKE_INIT_RESP_V4;
@@ -477,14 +504,17 @@
// Requires MANAGE_TEST_NETWORKS to provision a test-mode profile.
mCtsNetUtils.setAppopPrivileged(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN, true);
- final Ikev2VpnProfile profile = buildIkev2VpnProfilePsk(serverAddr,
- true /* isRestrictedToTestNetworks */, requiresValidation);
+ final Ikev2VpnProfile profile = testIkeTunConnParams
+ ? buildIkev2VpnProfileIkeTunConnParams(true /* isRestrictedToTestNetworks */,
+ requiresValidation, testIpv6)
+ : buildIkev2VpnProfilePsk(serverAddr, true /* isRestrictedToTestNetworks */,
+ requiresValidation);
assertNull(sVpnMgr.provisionVpnProfile(profile));
final TestableNetworkCallback cb = new TestableNetworkCallback(TIMEOUT_MS);
final NetworkRequest nr = new NetworkRequest.Builder()
.clearCapabilities().addTransportType(TRANSPORT_VPN).build();
- sCM.registerNetworkCallback(nr, cb);
+ registerNetworkCallback(nr, cb);
if (testSessionKey) {
// testSessionKey will never be true if running on <T
@@ -520,7 +550,8 @@
assertFalse(profileState.isLockdownEnabled());
}
- cb.expectCapabilitiesThat(vpnNetwork, TIMEOUT_MS, caps -> caps.hasTransport(TRANSPORT_VPN)
+ cb.expectCapabilitiesThat(vpnNetwork, TIMEOUT_MS,
+ caps -> caps.hasTransport(TRANSPORT_VPN)
&& caps.hasCapability(NET_CAPABILITY_INTERNET)
&& !caps.hasCapability(NET_CAPABILITY_VALIDATED)
&& Process.myUid() == caps.getOwnerUid());
@@ -533,8 +564,10 @@
// but unexpectedly sends this callback, expecting LOST below will fail because the next
// callback will be the validated capabilities instead.
// In S and below, |requiresValidation| is ignored, so this callback is always sent
- // regardless of its value.
- if (!requiresValidation || !TestUtils.shouldTestTApis()) {
+ // regardless of its value. However, there is a race in Vpn(see b/228574221) that VPN may
+ // misuse VPN network itself as the underlying network. The fix is not available without
+ // SDK > T platform. Thus, verify this only on T+ platform.
+ if (!requiresValidation && TestUtils.shouldTestTApis()) {
cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, TIMEOUT_MS,
entry -> ((CallbackEntry.CapabilitiesChanged) entry).getCaps()
.hasCapability(NET_CAPABILITY_VALIDATED));
@@ -547,10 +580,16 @@
lost -> vpnNetwork.equals(lost.getNetwork()));
}
+ private void registerNetworkCallback(NetworkRequest request, TestableNetworkCallback callback) {
+ sCM.registerNetworkCallback(request, callback);
+ mCallbacksToUnregister.add(callback);
+ }
+
private class VerifyStartStopVpnProfileTest implements TestNetworkRunnable.Test {
private final boolean mTestIpv6Only;
private final boolean mRequiresValidation;
private final boolean mTestSessionKey;
+ private final boolean mTestIkeTunConnParams;
/**
* Constructs the test
@@ -560,10 +599,11 @@
* @param testSessionKey if true, start VPN by calling startProvisionedVpnProfileSession()
*/
VerifyStartStopVpnProfileTest(boolean testIpv6Only, boolean requiresValidation,
- boolean testSessionKey) {
+ boolean testSessionKey, boolean testIkeTunConnParams) {
mTestIpv6Only = testIpv6Only;
mRequiresValidation = requiresValidation;
mTestSessionKey = testSessionKey;
+ mTestIkeTunConnParams = testIkeTunConnParams;
}
@Override
@@ -571,8 +611,8 @@
throws Exception {
final IkeTunUtils tunUtils = new IkeTunUtils(testIface.getFileDescriptor());
- checkStartStopVpnProfileBuildsNetworks(
- tunUtils, mTestIpv6Only, mRequiresValidation, mTestSessionKey);
+ checkStartStopVpnProfileBuildsNetworks(tunUtils, mTestIpv6Only, mRequiresValidation,
+ mTestSessionKey, mTestIkeTunConnParams);
}
@Override
@@ -590,53 +630,83 @@
}
}
- @Test
- public void testStartStopVpnProfileV4() throws Exception {
+ private void doTestStartStopVpnProfile(boolean testIpv6Only, boolean requiresValidation,
+ boolean testSessionKey, boolean testIkeTunConnParams) throws Exception {
assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
-
// Requires shell permission to update appops.
runWithShellPermissionIdentity(
new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
- false /* testIpv6Only */, false /* requiresValidation */,
- false /* testSessionKey */)));
+ testIpv6Only, requiresValidation, testSessionKey , testIkeTunConnParams)));
+ }
- runWithShellPermissionIdentity(
- new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
- false /* testIpv6Only */, true /* requiresValidation */,
- false /* testSessionKey */)));
+ @Test
+ public void testStartStopVpnProfileV4() throws Exception {
+ doTestStartStopVpnProfile(false /* testIpv6Only */, false /* requiresValidation */,
+ false /* testSessionKey */, false /* testIkeTunConnParams */);
+ }
+
+ @Test @IgnoreUpTo(SC_V2)
+ public void testStartStopVpnProfileV4WithValidation() throws Exception {
+ assumeTrue(TestUtils.shouldTestTApis());
+ doTestStartStopVpnProfile(false /* testIpv6Only */, true /* requiresValidation */,
+ false /* testSessionKey */, false /* testIkeTunConnParams */);
}
@Test
public void testStartStopVpnProfileV6() throws Exception {
- assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
+ doTestStartStopVpnProfile(true /* testIpv6Only */, false /* requiresValidation */,
+ false /* testSessionKey */, false /* testIkeTunConnParams */);
+ }
- // Requires shell permission to update appops.
- runWithShellPermissionIdentity(
- new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
- true /* testIpv6Only */, false /* requiresValidation */,
- false /* testSessionKey */)));
- runWithShellPermissionIdentity(
- new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
- true /* testIpv6Only */, true /* requiresValidation */,
- false /* testSessionKey */)));
+ @Test @IgnoreUpTo(SC_V2)
+ public void testStartStopVpnProfileV6WithValidation() throws Exception {
+ assumeTrue(TestUtils.shouldTestTApis());
+ doTestStartStopVpnProfile(true /* testIpv6Only */, true /* requiresValidation */,
+ false /* testSessionKey */, false /* testIkeTunConnParams */);
+ }
+
+ @Test @IgnoreUpTo(SC_V2)
+ public void testStartStopVpnProfileIkeTunConnParamsV4() throws Exception {
+ assumeTrue(TestUtils.shouldTestTApis());
+ doTestStartStopVpnProfile(false /* testIpv6Only */, false /* requiresValidation */,
+ false /* testSessionKey */, true /* testIkeTunConnParams */);
+ }
+
+ @Test @IgnoreUpTo(SC_V2)
+ public void testStartStopVpnProfileIkeTunConnParamsV4WithValidation() throws Exception {
+ assumeTrue(TestUtils.shouldTestTApis());
+ doTestStartStopVpnProfile(false /* testIpv6Only */, true /* requiresValidation */,
+ false /* testSessionKey */, true /* testIkeTunConnParams */);
+ }
+
+ @Test @IgnoreUpTo(SC_V2)
+ public void testStartStopVpnProfileIkeTunConnParamsV6() throws Exception {
+ assumeTrue(TestUtils.shouldTestTApis());
+ doTestStartStopVpnProfile(true /* testIpv6Only */, false /* requiresValidation */,
+ false /* testSessionKey */, true /* testIkeTunConnParams */);
+ }
+
+ @Test @IgnoreUpTo(SC_V2)
+ public void testStartStopVpnProfileIkeTunConnParamsV6WithValidation() throws Exception {
+ assumeTrue(TestUtils.shouldTestTApis());
+ doTestStartStopVpnProfile(true /* testIpv6Only */, true /* requiresValidation */,
+ false /* testSessionKey */, true /* testIkeTunConnParams */);
}
@IgnoreUpTo(SC_V2)
@Test
- public void testStartProvisionedVpnProfileSession() throws Exception {
- assumeTrue(mCtsNetUtils.hasIpsecTunnelsFeature());
+ public void testStartProvisionedVpnV4ProfileSession() throws Exception {
assumeTrue(TestUtils.shouldTestTApis());
+ doTestStartStopVpnProfile(false /* testIpv6Only */, false /* requiresValidation */,
+ true /* testSessionKey */, false /* testIkeTunConnParams */);
+ }
- // Requires shell permission to update appops.
- runWithShellPermissionIdentity(
- new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
- false /* testIpv6Only */, false /* requiresValidation */,
- true /* testSessionKey */)));
-
- runWithShellPermissionIdentity(
- new TestNetworkRunnable(new VerifyStartStopVpnProfileTest(
- true /* testIpv6Only */, false /* requiresValidation */,
- true /* testSessionKey */)));
+ @IgnoreUpTo(SC_V2)
+ @Test
+ public void testStartProvisionedVpnV6ProfileSession() throws Exception {
+ assumeTrue(TestUtils.shouldTestTApis());
+ doTestStartStopVpnProfile(true /* testIpv6Only */, false /* requiresValidation */,
+ true /* testSessionKey */, false /* testIkeTunConnParams */);
}
private static class CertificateAndKey {
diff --git a/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java b/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java
index b4ebcdb..244bfc5 100644
--- a/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/IkeSessionTestUtils.java
@@ -16,44 +16,73 @@
package android.net.cts.util;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_4096_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128;
-import static android.net.ipsec.ike.SaProposal.KEY_LEN_UNUSED;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
+import android.net.InetAddresses;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
import android.net.ipsec.ike.IkeSaProposal;
import android.net.ipsec.ike.IkeSessionParams;
-import android.net.ipsec.ike.SaProposal;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+
/** Shared testing parameters and util methods for testing IKE */
public class IkeSessionTestUtils {
- private static final String TEST_CLIENT_ADDR = "test.client.com";
- private static final String TEST_SERVER_ADDR = "test.server.com";
- private static final String TEST_SERVER = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+ private static final String TEST_SERVER_ADDR_V4 = "192.0.2.2";
+ private static final String TEST_SERVER_ADDR_V6 = "2001:db8::2";
+ private static final String TEST_IDENTITY = "client.cts.android.com";
+ private static final byte[] TEST_PSK = "ikeAndroidPsk".getBytes();
+ public static final IkeSessionParams IKE_PARAMS_V4 = getTestIkeSessionParams(false);
+ public static final IkeSessionParams IKE_PARAMS_V6 = getTestIkeSessionParams(true);
- public static final IkeSaProposal SA_PROPOSAL = new IkeSaProposal.Builder()
- .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_3DES, KEY_LEN_UNUSED)
- .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96)
- .addPseudorandomFunction(SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC)
- .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
- .build();
- public static final ChildSaProposal CHILD_PROPOSAL = new ChildSaProposal.Builder()
- .addEncryptionAlgorithm(SaProposal.ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128)
- .addIntegrityAlgorithm(SaProposal.INTEGRITY_ALGORITHM_NONE)
- .addDhGroup(SaProposal.DH_GROUP_1024_BIT_MODP)
- .build();
+ public static final TunnelModeChildSessionParams CHILD_PARAMS = getChildSessionParams();
- public static final IkeSessionParams IKE_PARAMS =
- new IkeSessionParams.Builder()
- .setServerHostname(TEST_SERVER)
- .addSaProposal(SA_PROPOSAL)
- .setLocalIdentification(new IkeFqdnIdentification(TEST_CLIENT_ADDR))
- .setRemoteIdentification(new IkeFqdnIdentification(TEST_SERVER_ADDR))
- .setAuthPsk("psk".getBytes())
- .build();
- public static final TunnelModeChildSessionParams CHILD_PARAMS =
- new TunnelModeChildSessionParams.Builder()
- .addSaProposal(CHILD_PROPOSAL)
- .build();
+ private static TunnelModeChildSessionParams getChildSessionParams() {
+ final TunnelModeChildSessionParams.Builder childOptionsBuilder =
+ new TunnelModeChildSessionParams.Builder()
+ .addSaProposal(getChildSaProposals());
+
+ return childOptionsBuilder.build();
+ }
+
+ private static IkeSessionParams getTestIkeSessionParams(boolean testIpv6) {
+ final String testServer = testIpv6 ? TEST_SERVER_ADDR_V6 : TEST_SERVER_ADDR_V4;
+ final InetAddress addr = InetAddresses.parseNumericAddress(testServer);
+ final IkeSessionParams.Builder ikeOptionsBuilder =
+ new IkeSessionParams.Builder()
+ .setServerHostname(testServer)
+ .setLocalIdentification(new IkeFqdnIdentification(TEST_IDENTITY))
+ .setRemoteIdentification(testIpv6
+ ? new IkeIpv6AddrIdentification((Inet6Address) addr)
+ : new IkeIpv4AddrIdentification((Inet4Address) addr))
+ .setAuthPsk(TEST_PSK)
+ .addSaProposal(getIkeSaProposals());
+
+ return ikeOptionsBuilder.build();
+ }
+
+ private static IkeSaProposal getIkeSaProposals() {
+ return new IkeSaProposal.Builder()
+ .addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
+ .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+ .addDhGroup(DH_GROUP_4096_BIT_MODP)
+ .addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC).build();
+ }
+
+ private static ChildSaProposal getChildSaProposals() {
+ return new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128)
+ .build();
+ }
}
diff --git a/tests/mts/bpf_existence_test.cpp b/tests/mts/bpf_existence_test.cpp
index 2bba282..25694d7 100644
--- a/tests/mts/bpf_existence_test.cpp
+++ b/tests/mts/bpf_existence_test.cpp
@@ -42,6 +42,7 @@
#define PLATFORM "/sys/fs/bpf/"
#define TETHERING "/sys/fs/bpf/tethering/"
+#define SHARED "/sys/fs/bpf/net_shared/"
class BpfExistenceTest : public ::testing::Test {
};
@@ -84,6 +85,42 @@
};
static const set<string> INTRODUCED_T = {
+ SHARED "map_block_blocked_ports_map",
+ SHARED "map_clatd_clat_egress4_map",
+ SHARED "map_clatd_clat_ingress6_map",
+ SHARED "map_dscp_policy_ipv4_dscp_policies_map",
+ SHARED "map_dscp_policy_ipv4_socket_to_policies_map_A",
+ SHARED "map_dscp_policy_ipv4_socket_to_policies_map_B",
+ SHARED "map_dscp_policy_ipv6_dscp_policies_map",
+ SHARED "map_dscp_policy_ipv6_socket_to_policies_map_A",
+ SHARED "map_dscp_policy_ipv6_socket_to_policies_map_B",
+ SHARED "map_dscp_policy_switch_comp_map",
+ SHARED "map_netd_app_uid_stats_map",
+ SHARED "map_netd_configuration_map",
+ SHARED "map_netd_cookie_tag_map",
+ SHARED "map_netd_iface_index_name_map",
+ SHARED "map_netd_iface_stats_map",
+ SHARED "map_netd_stats_map_A",
+ SHARED "map_netd_stats_map_B",
+ SHARED "map_netd_uid_counterset_map",
+ SHARED "map_netd_uid_owner_map",
+ SHARED "map_netd_uid_permission_map",
+ SHARED "prog_block_bind4_block_port",
+ SHARED "prog_block_bind6_block_port",
+ SHARED "prog_clatd_schedcls_egress4_clat_ether",
+ SHARED "prog_clatd_schedcls_egress4_clat_rawip",
+ SHARED "prog_clatd_schedcls_ingress6_clat_ether",
+ SHARED "prog_clatd_schedcls_ingress6_clat_rawip",
+ SHARED "prog_dscp_policy_schedcls_set_dscp_ether",
+ SHARED "prog_dscp_policy_schedcls_set_dscp_raw_ip",
+ SHARED "prog_netd_cgroupskb_egress_stats",
+ SHARED "prog_netd_cgroupskb_ingress_stats",
+ SHARED "prog_netd_cgroupsock_inet_create",
+ SHARED "prog_netd_schedact_ingress_account",
+ SHARED "prog_netd_skfilter_allowlist_xtbpf",
+ SHARED "prog_netd_skfilter_denylist_xtbpf",
+ SHARED "prog_netd_skfilter_egress_xtbpf",
+ SHARED "prog_netd_skfilter_ingress_xtbpf",
};
static const set<string> REMOVED_T = {
diff --git a/tests/native/connectivity_native_test.cpp b/tests/native/connectivity_native_test.cpp
index 8b089ab..3db5265 100644
--- a/tests/native/connectivity_native_test.cpp
+++ b/tests/native/connectivity_native_test.cpp
@@ -14,14 +14,15 @@
* limitations under the License.
*/
+#include <aidl/android/net/connectivity/aidl/ConnectivityNative.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <android-modules-utils/sdk_level.h>
#include <cutils/misc.h> // FIRST_APPLICATION_UID
#include <gtest/gtest.h>
#include <netinet/in.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <aidl/android/net/connectivity/aidl/ConnectivityNative.h>
+#include "bpf/BpfUtils.h"
using aidl::android::net::connectivity::aidl::IConnectivityNative;
@@ -40,6 +41,10 @@
if (!android::modules::sdklevel::IsAtLeastT()) GTEST_SKIP() <<
"Should be at least T device.";
+ // Skip test case if not on 5.4 kernel which is required by bpf prog.
+ if (!android::bpf::isAtLeastKernelVersion(5, 4, 0)) GTEST_SKIP() <<
+ "Kernel should be at least 5.4.";
+
ASSERT_NE(nullptr, mService.get());
// If there are already ports being blocked on device unblockAllPortsForBind() store
diff --git a/tests/unit/Android.bp b/tests/unit/Android.bp
index 545f7b9..5b926de 100644
--- a/tests/unit/Android.bp
+++ b/tests/unit/Android.bp
@@ -63,6 +63,7 @@
"java/android/net/IpSecManagerTest.java",
"java/android/net/IpSecTransformTest.java",
"java/android/net/KeepalivePacketDataUtilTest.java",
+ "java/android/net/NetworkIdentitySetTest.kt",
"java/android/net/NetworkIdentityTest.kt",
"java/android/net/NetworkStats*.java",
"java/android/net/NetworkTemplateTest.kt",
diff --git a/tests/unit/AndroidManifest.xml b/tests/unit/AndroidManifest.xml
index 887f171..54e1cd0 100644
--- a/tests/unit/AndroidManifest.xml
+++ b/tests/unit/AndroidManifest.xml
@@ -50,7 +50,7 @@
<uses-permission android:name="android.permission.NETWORK_STATS_PROVIDER" />
<uses-permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE" />
- <application>
+ <application android:testOnly="true">
<uses-library android:name="android.test.runner" />
<uses-library android:name="android.net.ipsec.ike" />
<activity
diff --git a/tests/unit/AndroidTest.xml b/tests/unit/AndroidTest.xml
index 939ae49..2d32e55 100644
--- a/tests/unit/AndroidTest.xml
+++ b/tests/unit/AndroidTest.xml
@@ -15,7 +15,8 @@
-->
<configuration description="Runs Frameworks Networking Tests.">
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="test-file-name" value="FrameworksNetTests.apk" />
+ <option name="test-file-name" value="FrameworksNetTests.apk" />
+ <option name="install-arg" value="-t" />
</target_preparer>
<option name="test-suite-tag" value="apct" />
diff --git a/tests/unit/java/android/net/Ikev2VpnProfileTest.java b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
index 8222ca1..5cb014f 100644
--- a/tests/unit/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/unit/java/android/net/Ikev2VpnProfileTest.java
@@ -17,7 +17,7 @@
package android.net;
import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
-import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V6;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
@@ -448,7 +448,7 @@
@Test
public void testConversionIsLosslessWithIkeTunConnParams() throws Exception {
final IkeTunnelConnectionParams tunnelParams =
- new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+ new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
// Config authentication related fields is not required while building with
// IkeTunnelConnectionParams.
final Ikev2VpnProfile ikeProfile = new Ikev2VpnProfile.Builder(tunnelParams).build();
@@ -464,9 +464,9 @@
// Verify building with IkeTunnelConnectionParams
final IkeTunnelConnectionParams tunnelParams =
- new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+ new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
final IkeTunnelConnectionParams tunnelParams2 =
- new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS);
+ new IkeTunnelConnectionParams(IKE_PARAMS_V6, CHILD_PARAMS);
assertEquals(new Ikev2VpnProfile.Builder(tunnelParams).build(),
new Ikev2VpnProfile.Builder(tunnelParams2).build());
}
diff --git a/tests/unit/java/android/net/NetworkIdentitySetTest.kt b/tests/unit/java/android/net/NetworkIdentitySetTest.kt
new file mode 100644
index 0000000..d61ebf9
--- /dev/null
+++ b/tests/unit/java/android/net/NetworkIdentitySetTest.kt
@@ -0,0 +1,66 @@
+/*
+ * 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 android.net
+
+import android.content.Context
+import android.net.ConnectivityManager.TYPE_MOBILE
+import android.os.Build
+import android.telephony.TelephonyManager
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import kotlin.test.assertEquals
+
+private const val TEST_IMSI1 = "testimsi1"
+
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2)
+@RunWith(DevSdkIgnoreRunner::class)
+class NetworkIdentitySetTest {
+ private val mockContext = mock(Context::class.java)
+
+ private fun buildMobileNetworkStateSnapshot(
+ caps: NetworkCapabilities,
+ subscriberId: String
+ ): NetworkStateSnapshot {
+ return NetworkStateSnapshot(mock(Network::class.java), caps,
+ LinkProperties(), subscriberId, TYPE_MOBILE)
+ }
+
+ @Test
+ fun testCompare() {
+ val ident1 = NetworkIdentity.buildNetworkIdentity(mockContext,
+ buildMobileNetworkStateSnapshot(NetworkCapabilities(), TEST_IMSI1),
+ false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+ val ident2 = NetworkIdentity.buildNetworkIdentity(mockContext,
+ buildMobileNetworkStateSnapshot(NetworkCapabilities(), TEST_IMSI1),
+ true /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS)
+
+ // Verify that the results of comparing two empty sets are equal
+ assertEquals(0, NetworkIdentitySet.compare(NetworkIdentitySet(), NetworkIdentitySet()))
+
+ val identSet1 = NetworkIdentitySet()
+ val identSet2 = NetworkIdentitySet()
+ identSet1.add(ident1)
+ identSet2.add(ident2)
+ assertEquals(-1, NetworkIdentitySet.compare(NetworkIdentitySet(), identSet1))
+ assertEquals(1, NetworkIdentitySet.compare(identSet1, NetworkIdentitySet()))
+ assertEquals(0, NetworkIdentitySet.compare(identSet1, identSet1))
+ assertEquals(-1, NetworkIdentitySet.compare(identSet1, identSet2))
+ }
+}
diff --git a/tests/unit/java/com/android/internal/net/VpnProfileTest.java b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
index 360390d..0a6d2f2 100644
--- a/tests/unit/java/com/android/internal/net/VpnProfileTest.java
+++ b/tests/unit/java/com/android/internal/net/VpnProfileTest.java
@@ -17,7 +17,7 @@
package com.android.internal.net;
import static android.net.cts.util.IkeSessionTestUtils.CHILD_PARAMS;
-import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS;
+import static android.net.cts.util.IkeSessionTestUtils.IKE_PARAMS_V4;
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
import static com.android.testutils.ParcelUtils.assertParcelSane;
@@ -128,7 +128,7 @@
private VpnProfile getSampleIkev2ProfileWithIkeTunConnParams(String key) {
final VpnProfile p = new VpnProfile(key, true /* isRestrictedToTestNetworks */,
false /* excludesLocalRoutes */, true /* requiresPlatformValidation */,
- new IkeTunnelConnectionParams(IKE_PARAMS, CHILD_PARAMS));
+ new IkeTunnelConnectionParams(IKE_PARAMS_V4, CHILD_PARAMS));
p.name = "foo";
p.server = "bar";
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 4c76803..37df4eb 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -159,8 +159,8 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.junit.Assume.assumeTrue;
import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
@@ -342,6 +342,7 @@
import com.android.server.ConnectivityService.NetworkRequestInfo;
import com.android.server.ConnectivityServiceTest.ConnectivityServiceDependencies.ReportedInterfaces;
import com.android.server.connectivity.CarrierPrivilegeAuthenticator;
+import com.android.server.connectivity.ClatCoordinator;
import com.android.server.connectivity.ConnectivityFlags;
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.Nat464Xlat;
@@ -540,6 +541,7 @@
@Mock VpnProfileStore mVpnProfileStore;
@Mock SystemConfigManager mSystemConfigManager;
@Mock Resources mResources;
+ @Mock ClatCoordinator mClatCoordinator;
@Mock PacProxyManager mPacProxyManager;
@Mock BpfNetMaps mBpfNetMaps;
@Mock CarrierPrivilegeAuthenticator mCarrierPrivilegeAuthenticator;
@@ -2004,6 +2006,11 @@
return mBpfNetMaps;
}
+ @Override
+ public ClatCoordinator getClatCoordinator(INetd netd) {
+ return mClatCoordinator;
+ }
+
final ArrayTrackRecord<Pair<String, Long>> mRateLimitHistory = new ArrayTrackRecord<>();
final Map<String, Long> mActiveRateLimit = new HashMap<>();
@@ -9569,6 +9576,59 @@
return event;
}
+ private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
+ if (inOrder != null) {
+ return inOrder.verify(t);
+ } else {
+ return verify(t);
+ }
+ }
+
+ private <T> T verifyNeverWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
+ if (inOrder != null) {
+ return inOrder.verify(t, never());
+ } else {
+ return verify(t, never());
+ }
+ }
+
+ private void verifyClatdStart(@Nullable InOrder inOrder, @NonNull String iface, int netId,
+ @NonNull String nat64Prefix) throws Exception {
+ if (SdkLevel.isAtLeastT()) {
+ verifyWithOrder(inOrder, mClatCoordinator)
+ .clatStart(eq(iface), eq(netId), eq(new IpPrefix(nat64Prefix)));
+ } else {
+ verifyWithOrder(inOrder, mMockNetd).clatdStart(eq(iface), eq(nat64Prefix));
+ }
+ }
+
+ private void verifyNeverClatdStart(@Nullable InOrder inOrder, @NonNull String iface)
+ throws Exception {
+ if (SdkLevel.isAtLeastT()) {
+ verifyNeverWithOrder(inOrder, mClatCoordinator).clatStart(eq(iface), anyInt(), any());
+ } else {
+ verifyNeverWithOrder(inOrder, mMockNetd).clatdStart(eq(iface), anyString());
+ }
+ }
+
+ private void verifyClatdStop(@Nullable InOrder inOrder, @NonNull String iface)
+ throws Exception {
+ if (SdkLevel.isAtLeastT()) {
+ verifyWithOrder(inOrder, mClatCoordinator).clatStop();
+ } else {
+ verifyWithOrder(inOrder, mMockNetd).clatdStop(eq(iface));
+ }
+ }
+
+ private void verifyNeverClatdStop(@Nullable InOrder inOrder, @NonNull String iface)
+ throws Exception {
+ if (SdkLevel.isAtLeastT()) {
+ verifyNeverWithOrder(inOrder, mClatCoordinator).clatStop();
+ } else {
+ verifyNeverWithOrder(inOrder, mMockNetd).clatdStop(eq(iface));
+ }
+ }
+
@Test
public void testStackedLinkProperties() throws Exception {
final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24");
@@ -9601,6 +9661,7 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
reset(mMockDnsResolver);
reset(mMockNetd);
+ reset(mClatCoordinator);
// Connect with ipv6 link properties. Expect prefix discovery to be started.
mCellNetworkAgent.connect(true);
@@ -9639,8 +9700,10 @@
&& ri.iface != null && ri.iface.startsWith("v4-")));
verifyNoMoreInteractions(mMockNetd);
+ verifyNoMoreInteractions(mClatCoordinator);
verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
+ reset(mClatCoordinator);
reset(mMockDnsResolver);
doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
.interfaceGetCfg(CLAT_MOBILE_IFNAME);
@@ -9661,7 +9724,7 @@
CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent).getLp();
assertEquals(0, lpBeforeClat.getStackedLinks().size());
assertEquals(kNat64Prefix, lpBeforeClat.getNat64Prefix());
- verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
+ verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, cellNetId, kNat64Prefix.toString());
// Clat iface comes up. Expect stacked link to be added.
clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
@@ -9693,6 +9756,7 @@
new int[] { TRANSPORT_CELLULAR })));
}
reset(mMockNetd);
+ reset(mClatCoordinator);
doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
.interfaceGetCfg(CLAT_MOBILE_IFNAME);
// Change the NAT64 prefix without first removing it.
@@ -9701,11 +9765,12 @@
cellNetId, PREFIX_OPERATION_ADDED, kOtherNat64PrefixString, 96));
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 0);
- verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+ verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
assertRoutesRemoved(cellNetId, stackedDefault);
verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
- verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kOtherNat64Prefix.toString());
+ verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, cellNetId,
+ kOtherNat64Prefix.toString());
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getNat64Prefix().equals(kOtherNat64Prefix));
clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
@@ -9714,6 +9779,7 @@
assertRoutesAdded(cellNetId, stackedDefault);
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, CLAT_MOBILE_IFNAME);
reset(mMockNetd);
+ reset(mClatCoordinator);
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
// linkproperties are cleaned up.
@@ -9722,7 +9788,7 @@
mCellNetworkAgent.sendLinkProperties(cellLp);
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
assertRoutesAdded(cellNetId, ipv4Subnet);
- verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+ verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
// As soon as stop is called, the linkproperties lose the stacked interface.
@@ -9739,8 +9805,10 @@
networkCallback.assertNoCallback();
verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
verifyNoMoreInteractions(mMockNetd);
+ verifyNoMoreInteractions(mClatCoordinator);
verifyNoMoreInteractions(mMockDnsResolver);
reset(mMockNetd);
+ reset(mClatCoordinator);
reset(mMockDnsResolver);
doReturn(getClatInterfaceConfigParcel(myIpv4)).when(mMockNetd)
.interfaceGetCfg(CLAT_MOBILE_IFNAME);
@@ -9762,7 +9830,7 @@
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(makeNat64PrefixEvent(
cellNetId, PREFIX_OPERATION_ADDED, kNat64PrefixString, 96));
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
- verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
+ verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, cellNetId, kNat64Prefix.toString());
// Clat iface comes up. Expect stacked link to be added.
clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true);
@@ -9779,7 +9847,7 @@
assertRoutesRemoved(cellNetId, ipv4Subnet, stackedDefault);
// Stop has no effect because clat is already stopped.
- verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
+ verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
(lp) -> lp.getStackedLinks().size() == 0);
verify(mMockNetd, times(1)).networkRemoveInterface(cellNetId, CLAT_MOBILE_IFNAME);
@@ -9792,7 +9860,9 @@
eq(Integer.toString(TRANSPORT_CELLULAR)));
verify(mMockNetd).networkDestroy(cellNetId);
verifyNoMoreInteractions(mMockNetd);
+ verifyNoMoreInteractions(mClatCoordinator);
reset(mMockNetd);
+ reset(mClatCoordinator);
// Test disconnecting a network that is running 464xlat.
@@ -9809,7 +9879,7 @@
assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default);
// Clatd is started and clat iface comes up. Expect stacked link to be added.
- verify(mMockNetd).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString());
+ verifyClatdStart(null /* inOrder */, MOBILE_IFNAME, cellNetId, kNat64Prefix.toString());
clat = getNat464Xlat(mCellNetworkAgent);
clat.interfaceLinkStateChanged(CLAT_MOBILE_IFNAME, true /* up */);
networkCallback.expectLinkPropertiesThat(mCellNetworkAgent,
@@ -9819,16 +9889,18 @@
// assertRoutesAdded sees all calls since last mMockNetd reset, so expect IPv6 routes again.
assertRoutesAdded(cellNetId, ipv6Subnet, ipv6Default, stackedDefault);
reset(mMockNetd);
+ reset(mClatCoordinator);
// Disconnect the network. clat is stopped and the network is destroyed.
mCellNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
networkCallback.assertNoCallback();
- verify(mMockNetd).clatdStop(MOBILE_IFNAME);
+ verifyClatdStop(null /* inOrder */, MOBILE_IFNAME);
verify(mMockNetd).idletimerRemoveInterface(eq(MOBILE_IFNAME), anyInt(),
eq(Integer.toString(TRANSPORT_CELLULAR)));
verify(mMockNetd).networkDestroy(cellNetId);
verifyNoMoreInteractions(mMockNetd);
+ verifyNoMoreInteractions(mClatCoordinator);
mCm.unregisterNetworkCallback(networkCallback);
}
@@ -9859,7 +9931,7 @@
baseLp.addDnsServer(InetAddress.getByName("2001:4860:4860::6464"));
reset(mMockNetd, mMockDnsResolver);
- InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver);
+ InOrder inOrder = inOrder(mMockNetd, mMockDnsResolver, mClatCoordinator);
// If a network already has a NAT64 prefix on connect, clatd is started immediately and
// prefix discovery is never started.
@@ -9870,7 +9942,7 @@
final Network network = mWiFiNetworkAgent.getNetwork();
int netId = network.getNetId();
callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
- inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+ verifyClatdStart(inOrder, iface, netId, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
callback.assertNoCallback();
@@ -9880,7 +9952,7 @@
lp.setNat64Prefix(null);
mWiFiNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
- inOrder.verify(mMockNetd).clatdStop(iface);
+ verifyClatdStop(inOrder, iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
@@ -9889,7 +9961,7 @@
lp.setNat64Prefix(pref64FromRa);
mWiFiNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa);
- inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+ verifyClatdStart(inOrder, iface, netId, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
@@ -9898,22 +9970,22 @@
lp.setNat64Prefix(null);
mWiFiNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
- inOrder.verify(mMockNetd).clatdStop(iface);
+ verifyClatdStop(inOrder, iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96));
expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
- inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+ verifyClatdStart(inOrder, iface, netId, pref64FromDns.toString());
// If an RA advertises the same prefix that was discovered by DNS, nothing happens: prefix
// discovery is not stopped, and there are no callbacks.
lp.setNat64Prefix(pref64FromDns);
mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
- inOrder.verify(mMockNetd, never()).clatdStop(iface);
- inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ verifyNeverClatdStop(inOrder, iface);
+ verifyNeverClatdStart(inOrder, iface);
inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
@@ -9922,8 +9994,8 @@
lp.setNat64Prefix(null);
mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
- inOrder.verify(mMockNetd, never()).clatdStop(iface);
- inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ verifyNeverClatdStop(inOrder, iface);
+ verifyNeverClatdStart(inOrder, iface);
inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
@@ -9932,14 +10004,14 @@
lp.setNat64Prefix(pref64FromRa);
mWiFiNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromRa);
- inOrder.verify(mMockNetd).clatdStop(iface);
+ verifyClatdStop(inOrder, iface);
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
// Stopping prefix discovery results in a prefix removed notification.
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
makeNat64PrefixEvent(netId, PREFIX_OPERATION_REMOVED, pref64FromDnsStr, 96));
- inOrder.verify(mMockNetd).clatdStart(iface, pref64FromRa.toString());
+ verifyClatdStart(inOrder, iface, netId, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver).setPrefix64(netId, pref64FromRa.toString());
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
@@ -9947,9 +10019,9 @@
lp.setNat64Prefix(newPref64FromRa);
mWiFiNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mWiFiNetworkAgent, newPref64FromRa);
- inOrder.verify(mMockNetd).clatdStop(iface);
+ verifyClatdStop(inOrder, iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
- inOrder.verify(mMockNetd).clatdStart(iface, newPref64FromRa.toString());
+ verifyClatdStart(inOrder, iface, netId, newPref64FromRa.toString());
inOrder.verify(mMockDnsResolver).setPrefix64(netId, newPref64FromRa.toString());
inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
@@ -9959,8 +10031,8 @@
mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
assertEquals(newPref64FromRa, mCm.getLinkProperties(network).getNat64Prefix());
- inOrder.verify(mMockNetd, never()).clatdStop(iface);
- inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ verifyNeverClatdStop(inOrder, iface);
+ verifyNeverClatdStart(inOrder, iface);
inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
@@ -9972,20 +10044,20 @@
lp.setNat64Prefix(null);
mWiFiNetworkAgent.sendLinkProperties(lp);
expectNat64PrefixChange(callback, mWiFiNetworkAgent, null);
- inOrder.verify(mMockNetd).clatdStop(iface);
+ verifyClatdStop(inOrder, iface);
inOrder.verify(mMockDnsResolver).setPrefix64(netId, "");
inOrder.verify(mMockDnsResolver).startPrefix64Discovery(netId);
mService.mResolverUnsolEventCallback.onNat64PrefixEvent(
makeNat64PrefixEvent(netId, PREFIX_OPERATION_ADDED, pref64FromDnsStr, 96));
expectNat64PrefixChange(callback, mWiFiNetworkAgent, pref64FromDns);
- inOrder.verify(mMockNetd).clatdStart(iface, pref64FromDns.toString());
+ verifyClatdStart(inOrder, iface, netId, pref64FromDns.toString());
inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), any());
lp.setNat64Prefix(pref64FromDns);
mWiFiNetworkAgent.sendLinkProperties(lp);
callback.assertNoCallback();
- inOrder.verify(mMockNetd, never()).clatdStop(iface);
- inOrder.verify(mMockNetd, never()).clatdStart(eq(iface), anyString());
+ verifyNeverClatdStop(inOrder, iface);
+ verifyNeverClatdStart(inOrder, iface);
inOrder.verify(mMockDnsResolver, never()).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).startPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
@@ -9998,7 +10070,7 @@
callback.expectCallback(CallbackEntry.LOST, mWiFiNetworkAgent);
b.expectBroadcast();
- inOrder.verify(mMockNetd).clatdStop(iface);
+ verifyClatdStop(inOrder, iface);
inOrder.verify(mMockDnsResolver).stopPrefix64Discovery(netId);
inOrder.verify(mMockDnsResolver, never()).setPrefix64(eq(netId), anyString());
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index c3d64cb..f84d10f 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -109,9 +109,9 @@
new FileDescriptor());
private static final String EGRESS_PROG_PATH =
- "/sys/fs/bpf/prog_clatd_schedcls_egress4_clat_rawip";
+ "/sys/fs/bpf/net_shared/prog_clatd_schedcls_egress4_clat_rawip";
private static final String INGRESS_PROG_PATH =
- "/sys/fs/bpf/prog_clatd_schedcls_ingress6_clat_ether";
+ "/sys/fs/bpf/net_shared/prog_clatd_schedcls_ingress6_clat_ether";
private static final ClatEgress4Key EGRESS_KEY = new ClatEgress4Key(STACKED_IFINDEX,
INET4_LOCAL4);
private static final ClatEgress4Value EGRESS_VALUE = new ClatEgress4Value(BASE_IFINDEX,
diff --git a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
index aa4c4e3..06e0d6d 100644
--- a/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
+++ b/tests/unit/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -21,6 +21,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.inOrder;
@@ -44,8 +46,11 @@
import android.os.Handler;
import android.os.test.TestLooper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
+import com.android.modules.utils.build.SdkLevel;
import com.android.server.ConnectivityService;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -75,13 +80,20 @@
@Mock IDnsResolver mDnsResolver;
@Mock INetd mNetd;
@Mock NetworkAgentInfo mNai;
+ @Mock ClatCoordinator mClatCoordinator;
TestLooper mLooper;
Handler mHandler;
NetworkAgentConfig mAgentConfig = new NetworkAgentConfig();
Nat464Xlat makeNat464Xlat(boolean isCellular464XlatEnabled) {
- return new Nat464Xlat(mNai, mNetd, mDnsResolver, new ConnectivityService.Dependencies()) {
+ final ConnectivityService.Dependencies deps = new ConnectivityService.Dependencies() {
+ @Override public ClatCoordinator getClatCoordinator(INetd netd) {
+ return mClatCoordinator;
+ }
+ };
+
+ return new Nat464Xlat(mNai, mNetd, mDnsResolver, deps) {
@Override protected int getNetId() {
return NETID;
}
@@ -208,6 +220,39 @@
}
}
+ private <T> T verifyWithOrder(@Nullable InOrder inOrder, @NonNull T t) {
+ if (inOrder != null) {
+ return inOrder.verify(t);
+ } else {
+ return verify(t);
+ }
+ }
+
+ private void verifyClatdStart(@Nullable InOrder inOrder) throws Exception {
+ if (SdkLevel.isAtLeastT()) {
+ verifyWithOrder(inOrder, mClatCoordinator)
+ .clatStart(eq(BASE_IFACE), eq(NETID), eq(new IpPrefix(NAT64_PREFIX)));
+ } else {
+ verifyWithOrder(inOrder, mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+ }
+ }
+
+ private void verifyNeverClatdStart() throws Exception {
+ if (SdkLevel.isAtLeastT()) {
+ verify(mClatCoordinator, never()).clatStart(anyString(), anyInt(), any());
+ } else {
+ verify(mNetd, never()).clatdStart(anyString(), anyString());
+ }
+ }
+
+ private void verifyClatdStop(@Nullable InOrder inOrder) throws Exception {
+ if (SdkLevel.isAtLeastT()) {
+ verifyWithOrder(inOrder, mClatCoordinator).clatStop();
+ } else {
+ verifyWithOrder(inOrder, mNetd).clatdStop(eq(BASE_IFACE));
+ }
+ }
+
private void checkNormalStartAndStop(boolean dueToDisconnect) throws Exception {
Nat464Xlat nat = makeNat464Xlat(true);
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
@@ -219,7 +264,7 @@
// Start clat.
nat.start();
- verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+ verifyClatdStart(null /* inOrder */);
// Stacked interface up notification arrives.
nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -235,7 +280,7 @@
makeClatUnnecessary(dueToDisconnect);
nat.stop();
- verify(mNetd).clatdStop(eq(BASE_IFACE));
+ verifyClatdStop(null /* inOrder */);
verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
assertTrue(c.getValue().getStackedLinks().isEmpty());
assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
@@ -262,7 +307,7 @@
private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception {
Nat464Xlat nat = makeNat464Xlat(true);
ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
- InOrder inOrder = inOrder(mNetd, mConnectivity);
+ InOrder inOrder = inOrder(mNetd, mConnectivity, mClatCoordinator);
mNai.linkProperties.addLinkAddress(V6ADDR);
@@ -270,7 +315,7 @@
nat.start();
- inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+ verifyClatdStart(inOrder);
// Stacked interface up notification arrives.
nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -284,7 +329,7 @@
// ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
nat.stop();
- inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
+ verifyClatdStop(inOrder);
inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
assertTrue(c.getValue().getStackedLinks().isEmpty());
@@ -306,7 +351,7 @@
nat.start();
- inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+ verifyClatdStart(inOrder);
if (!interfaceRemovedFirst) {
// Stacked interface removed notification arrives and is ignored.
@@ -328,7 +373,7 @@
// ConnectivityService stops clat again.
nat.stop();
- inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE));
+ verifyClatdStop(inOrder);
inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
assertTrue(c.getValue().getStackedLinks().isEmpty());
@@ -357,7 +402,7 @@
nat.start();
- verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+ verifyClatdStart(null /* inOrder */);
// Stacked interface up notification arrives.
nat.interfaceLinkStateChanged(STACKED_IFACE, true);
@@ -373,7 +418,7 @@
nat.interfaceRemoved(STACKED_IFACE);
mLooper.dispatchNext();
- verify(mNetd).clatdStop(eq(BASE_IFACE));
+ verifyClatdStop(null /* inOrder */);
verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertTrue(c.getValue().getStackedLinks().isEmpty());
@@ -395,13 +440,13 @@
nat.start();
- verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+ verifyClatdStart(null /* inOrder */);
// ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
makeClatUnnecessary(dueToDisconnect);
nat.stop();
- verify(mNetd).clatdStop(eq(BASE_IFACE));
+ verifyClatdStop(null /* inOrder */);
verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertIdle(nat);
@@ -437,13 +482,13 @@
nat.start();
- verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX));
+ verifyClatdStart(null /* inOrder */);
// ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
makeClatUnnecessary(dueToDisconnect);
nat.stop();
- verify(mNetd).clatdStop(eq(BASE_IFACE));
+ verifyClatdStop(null /* inOrder */);
verify(mDnsResolver).stopPrefix64Discovery(eq(NETID));
assertIdle(nat);
@@ -518,7 +563,7 @@
mNai.linkProperties.setNat64Prefix(nat64Prefix);
nat.setNat64PrefixFromRa(nat64Prefix);
nat.update();
- verify(mNetd, never()).clatdStart(anyString(), anyString());
+ verifyNeverClatdStart();
assertIdle(nat);
} else {
// Prefix discovery is started.
@@ -529,7 +574,7 @@
mNai.linkProperties.setNat64Prefix(nat64Prefix);
nat.setNat64PrefixFromRa(nat64Prefix);
nat.update();
- verify(mNetd).clatdStart(BASE_IFACE, NAT64_PREFIX);
+ verifyClatdStart(null /* inOrder */);
assertStarting(nat);
}
}