Merge "Register service on the specified network"
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index bd8fe7c..76c5d5c 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -70,9 +70,9 @@
canned_fs_config: "canned_fs_config",
bpfs: [
"block.o",
- "clatd.o_mainline",
+ "clatd.o",
"dscp_policy.o",
- "netd.o_mainline",
+ "netd.o",
"offload.o",
"test.o",
],
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/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 4504829..44935fc 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -482,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");
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/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index e73b7d5..3699f7a 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -59,6 +59,7 @@
import android.os.HandlerThread;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.VintfRuntimeInfo;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
@@ -84,6 +85,7 @@
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.DeviceInfoUtils;
import com.android.testutils.DumpTestUtils;
import com.android.testutils.HandlerUtils;
import com.android.testutils.TapPacketReader;
@@ -1058,19 +1060,33 @@
}
@Test
- @IgnoreAfter(Build.VERSION_CODES.Q)
- public void testTetherUdpV4WithoutBpf() throws Exception {
+ @IgnoreAfter(Build.VERSION_CODES.R)
+ public void testTetherUdpV4UpToR() throws Exception {
initializeTethering();
runUdp4Test(new TetheringTester(mDownstreamReader), new RemoteResponder(mUpstreamReader),
false /* usingBpf */);
}
+ private static boolean isUdpOffloadSupportedByKernel() {
+ final String kVersionString = VintfRuntimeInfo.getKernelVersion();
+ // Kernel version which is older than 4.14 doesn't support UDP offload absolutely. Kernel
+ // version which is between 4.14 and 5.8 support UDP offload probably. Simply apply kernel
+ // 4.14 to be threshold first and monitor on what devices tests fail for improving the
+ // offload support checking.
+ return DeviceInfoUtils.compareMajorMinorVersion(kVersionString, "4.14") >= 0;
+ }
+
@Test
@IgnoreUpTo(Build.VERSION_CODES.R)
- public void testTetherUdpV4WithBpf() throws Exception {
+ public void testTetherUdpV4AfterR() throws Exception {
initializeTethering();
+ boolean usingBpf = isUdpOffloadSupportedByKernel();
+ if (!usingBpf) {
+ Log.i(TAG, "testTetherUdpV4AfterR will skip BPF offload test for kernel "
+ + VintfRuntimeInfo.getKernelVersion());
+ }
runUdp4Test(new TetheringTester(mDownstreamReader), new RemoteResponder(mUpstreamReader),
- true /* usingBpf */);
+ usingBpf);
}
@Nullable
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/bpf_progs/Android.bp b/bpf_progs/Android.bp
index 1fe0e9a..6c78244 100644
--- a/bpf_progs/Android.bp
+++ b/bpf_progs/Android.bp
@@ -61,6 +61,7 @@
bpf {
name: "block.o",
srcs: ["block.c"],
+ btf: true,
cflags: [
"-Wall",
"-Werror",
@@ -71,6 +72,7 @@
bpf {
name: "dscp_policy.o",
srcs: ["dscp_policy.c"],
+ btf: true,
cflags: [
"-Wall",
"-Werror",
@@ -97,8 +99,9 @@
}
bpf {
- name: "clatd.o_mainline",
+ name: "clatd.o",
srcs: ["clatd.c"],
+ btf: true,
cflags: [
"-Wall",
"-Werror",
@@ -110,8 +113,9 @@
}
bpf {
- name: "netd.o_mainline",
+ name: "netd.o",
srcs: ["netd.c"],
+ btf: true,
cflags: [
"-Wall",
"-Werror",
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/bpf_tethering.h b/bpf_progs/bpf_tethering.h
index b0ec8f6..f9ef6ef 100644
--- a/bpf_progs/bpf_tethering.h
+++ b/bpf_progs/bpf_tethering.h
@@ -73,10 +73,6 @@
#define STRUCT_SIZE(name, size) _Static_assert(sizeof(name) == (size), "Incorrect struct size.")
-#define BPF_PATH_TETHER BPF_PATH "tethering/"
-
-#define TETHER_STATS_MAP_PATH BPF_PATH_TETHER "map_offload_tether_stats_map"
-
typedef uint32_t TetherStatsKey; // upstream ifindex
typedef struct {
@@ -89,19 +85,9 @@
} TetherStatsValue;
STRUCT_SIZE(TetherStatsValue, 6 * 8); // 48
-#define TETHER_LIMIT_MAP_PATH BPF_PATH_TETHER "map_offload_tether_limit_map"
-
typedef uint32_t TetherLimitKey; // upstream ifindex
typedef uint64_t TetherLimitValue; // in bytes
-#define TETHER_DOWNSTREAM6_TC_PROG_RAWIP_NAME "prog_offload_schedcls_tether_downstream6_rawip"
-#define TETHER_DOWNSTREAM6_TC_PROG_ETHER_NAME "prog_offload_schedcls_tether_downstream6_ether"
-
-#define TETHER_DOWNSTREAM6_TC_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM6_TC_PROG_RAWIP_NAME
-#define TETHER_DOWNSTREAM6_TC_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM6_TC_PROG_ETHER_NAME
-
-#define TETHER_DOWNSTREAM6_MAP_PATH BPF_PATH_TETHER "map_offload_tether_downstream6_map"
-
// For now tethering offload only needs to support downstreams that use 6-byte MAC addresses,
// because all downstream types that are currently supported (WiFi, USB, Bluetooth and
// Ethernet) have 6-byte MAC addresses.
@@ -121,8 +107,6 @@
} Tether6Value;
STRUCT_SIZE(Tether6Value, 4 + 14 + 2); // 20
-#define TETHER_DOWNSTREAM64_MAP_PATH BPF_PATH_TETHER "map_offload_tether_downstream64_map"
-
typedef struct {
uint32_t iif; // The input interface index
uint8_t dstMac[ETH_ALEN]; // destination ethernet mac address (zeroed iff rawip ingress)
@@ -146,14 +130,6 @@
} TetherDownstream64Value;
STRUCT_SIZE(TetherDownstream64Value, 4 + 14 + 2 + 4 + 4 + 2 + 2 + 8); // 40
-#define TETHER_UPSTREAM6_TC_PROG_RAWIP_NAME "prog_offload_schedcls_tether_upstream6_rawip"
-#define TETHER_UPSTREAM6_TC_PROG_ETHER_NAME "prog_offload_schedcls_tether_upstream6_ether"
-
-#define TETHER_UPSTREAM6_TC_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_UPSTREAM6_TC_PROG_RAWIP_NAME
-#define TETHER_UPSTREAM6_TC_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_UPSTREAM6_TC_PROG_ETHER_NAME
-
-#define TETHER_UPSTREAM6_MAP_PATH BPF_PATH_TETHER "map_offload_tether_upstream6_map"
-
typedef struct {
uint32_t iif; // The input interface index
uint8_t dstMac[ETH_ALEN]; // destination ethernet mac address (zeroed iff rawip ingress)
@@ -162,23 +138,6 @@
} TetherUpstream6Key;
STRUCT_SIZE(TetherUpstream6Key, 12);
-#define TETHER_DOWNSTREAM4_TC_PROG_RAWIP_NAME "prog_offload_schedcls_tether_downstream4_rawip"
-#define TETHER_DOWNSTREAM4_TC_PROG_ETHER_NAME "prog_offload_schedcls_tether_downstream4_ether"
-
-#define TETHER_DOWNSTREAM4_TC_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM4_TC_PROG_RAWIP_NAME
-#define TETHER_DOWNSTREAM4_TC_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM4_TC_PROG_ETHER_NAME
-
-#define TETHER_DOWNSTREAM4_MAP_PATH BPF_PATH_TETHER "map_offload_tether_downstream4_map"
-
-
-#define TETHER_UPSTREAM4_TC_PROG_RAWIP_NAME "prog_offload_schedcls_tether_upstream4_rawip"
-#define TETHER_UPSTREAM4_TC_PROG_ETHER_NAME "prog_offload_schedcls_tether_upstream4_ether"
-
-#define TETHER_UPSTREAM4_TC_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_UPSTREAM4_TC_PROG_RAWIP_NAME
-#define TETHER_UPSTREAM4_TC_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_UPSTREAM4_TC_PROG_ETHER_NAME
-
-#define TETHER_UPSTREAM4_MAP_PATH BPF_PATH_TETHER "map_offload_tether_upstream4_map"
-
typedef struct {
uint32_t iif; // The input interface index
uint8_t dstMac[ETH_ALEN]; // destination ethernet mac address (zeroed iff rawip ingress)
@@ -202,16 +161,4 @@
} Tether4Value;
STRUCT_SIZE(Tether4Value, 4 + 14 + 2 + 16 + 16 + 2 + 2 + 8); // 64
-#define TETHER_DOWNSTREAM_XDP_PROG_RAWIP_NAME "prog_offload_xdp_tether_downstream_rawip"
-#define TETHER_DOWNSTREAM_XDP_PROG_ETHER_NAME "prog_offload_xdp_tether_downstream_ether"
-
-#define TETHER_DOWNSTREAM_XDP_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM_XDP_PROG_RAWIP_NAME
-#define TETHER_DOWNSTREAM_XDP_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_DOWNSTREAM_XDP_PROG_ETHER_NAME
-
-#define TETHER_UPSTREAM_XDP_PROG_RAWIP_NAME "prog_offload_xdp_tether_upstream_rawip"
-#define TETHER_UPSTREAM_XDP_PROG_ETHER_NAME "prog_offload_xdp_tether_upstream_ether"
-
-#define TETHER_UPSTREAM_XDP_PROG_RAWIP_PATH BPF_PATH_TETHER TETHER_UPSTREAM_XDP_PROG_RAWIP_NAME
-#define TETHER_UPSTREAM_XDP_PROG_ETHER_PATH BPF_PATH_TETHER TETHER_UPSTREAM_XDP_PROG_ETHER_NAME
-
#undef STRUCT_SIZE
diff --git a/bpf_progs/offload.c b/bpf_progs/offload.c
index 92a774c..896bc09 100644
--- a/bpf_progs/offload.c
+++ b/bpf_progs/offload.c
@@ -355,88 +355,10 @@
DEFINE_BPF_MAP_GRW(tether_upstream4_map, HASH, Tether4Key, Tether4Value, 1024, AID_NETWORK_STACK)
-static inline __always_inline int do_forward4(struct __sk_buff* skb, const bool is_ethernet,
- const bool downstream, const bool updatetime) {
- // Require ethernet dst mac address to be our unicast address.
- if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_PIPE;
-
- // Must be meta-ethernet IPv4 frame
- if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_PIPE;
-
- const int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
-
- // Since the program never writes via DPA (direct packet access) auto-pull/unclone logic does
- // not trigger and thus we need to manually make sure we can read packet headers via DPA.
- // Note: this is a blind best effort pull, which may fail or pull less - this doesn't matter.
- // It has to be done early cause it will invalidate any skb->data/data_end derived pointers.
- try_make_writable(skb, l2_header_size + IP4_HLEN + TCP_HLEN);
-
- void* data = (void*)(long)skb->data;
- const void* data_end = (void*)(long)skb->data_end;
- struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet
- struct iphdr* ip = is_ethernet ? (void*)(eth + 1) : data;
-
- // Must have (ethernet and) ipv4 header
- if (data + l2_header_size + sizeof(*ip) > data_end) return TC_ACT_PIPE;
-
- // Ethertype - if present - must be IPv4
- if (is_ethernet && (eth->h_proto != htons(ETH_P_IP))) return TC_ACT_PIPE;
-
- // IP version must be 4
- if (ip->version != 4) TC_PUNT(INVALID_IP_VERSION);
-
- // We cannot handle IP options, just standard 20 byte == 5 dword minimal IPv4 header
- if (ip->ihl != 5) TC_PUNT(HAS_IP_OPTIONS);
-
- // Calculate the IPv4 one's complement checksum of the IPv4 header.
- __wsum sum4 = 0;
- for (int i = 0; i < sizeof(*ip) / sizeof(__u16); ++i) {
- sum4 += ((__u16*)ip)[i];
- }
- // Note that sum4 is guaranteed to be non-zero by virtue of ip4->version == 4
- sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse u32 into range 1 .. 0x1FFFE
- sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse any potential carry into u16
- // for a correct checksum we should get *a* zero, but sum4 must be positive, ie 0xFFFF
- if (sum4 != 0xFFFF) TC_PUNT(CHECKSUM);
-
- // Minimum IPv4 total length is the size of the header
- if (ntohs(ip->tot_len) < sizeof(*ip)) TC_PUNT(TRUNCATED_IPV4);
-
- // We are incapable of dealing with IPv4 fragments
- if (ip->frag_off & ~htons(IP_DF)) TC_PUNT(IS_IP_FRAG);
-
- // Cannot decrement during forward if already zero or would be zero,
- // Let the kernel's stack handle these cases and generate appropriate ICMP errors.
- if (ip->ttl <= 1) TC_PUNT(LOW_TTL);
-
- // If we cannot update the 'last_used' field due to lack of bpf_ktime_get_boot_ns() helper,
- // then it is not safe to offload UDP due to the small conntrack timeouts, as such,
- // in such a situation we can only support TCP. This also has the added nice benefit of
- // using a separate error counter, and thus making it obvious which version of the program
- // is loaded.
- if (!updatetime && ip->protocol != IPPROTO_TCP) TC_PUNT(NON_TCP);
-
- // We do not support offloading anything besides IPv4 TCP and UDP, due to need for NAT,
- // but no need to check this if !updatetime due to check immediately above.
- if (updatetime && (ip->protocol != IPPROTO_TCP) && (ip->protocol != IPPROTO_UDP))
- TC_PUNT(NON_TCP_UDP);
-
- // We want to make sure that the compiler will, in the !updatetime case, entirely optimize
- // out all the non-tcp logic. Also note that at this point is_udp === !is_tcp.
- const bool is_tcp = !updatetime || (ip->protocol == IPPROTO_TCP);
-
- // This is a bit of a hack to make things easier on the bpf verifier.
- // (In particular I believe the Linux 4.14 kernel's verifier can get confused later on about
- // what offsets into the packet are valid and can spuriously reject the program, this is
- // because it fails to realize that is_tcp && !is_tcp is impossible)
- //
- // For both TCP & UDP we'll need to read and modify the src/dst ports, which so happen to
- // always be in the first 4 bytes of the L4 header. Additionally for UDP we'll need access
- // to the checksum field which is in bytes 7 and 8. While for TCP we'll need to read the
- // TCP flags (at offset 13) and access to the checksum field (2 bytes at offset 16).
- // As such we *always* need access to at least 8 bytes.
- if (data + l2_header_size + sizeof(*ip) + 8 > data_end) TC_PUNT(SHORT_L4_HEADER);
-
+static inline __always_inline int do_forward4_bottom(struct __sk_buff* skb,
+ const int l2_header_size, void* data, const void* data_end,
+ struct ethhdr* eth, struct iphdr* ip, const bool is_ethernet,
+ const bool downstream, const bool updatetime, const bool is_tcp) {
struct tcphdr* tcph = is_tcp ? (void*)(ip + 1) : NULL;
struct udphdr* udph = is_tcp ? NULL : (void*)(ip + 1);
@@ -625,6 +547,102 @@
return bpf_redirect(v->oif, 0 /* this is effectively BPF_F_EGRESS */);
}
+static inline __always_inline int do_forward4(struct __sk_buff* skb, const bool is_ethernet,
+ const bool downstream, const bool updatetime) {
+ // Require ethernet dst mac address to be our unicast address.
+ if (is_ethernet && (skb->pkt_type != PACKET_HOST)) return TC_ACT_PIPE;
+
+ // Must be meta-ethernet IPv4 frame
+ if (skb->protocol != htons(ETH_P_IP)) return TC_ACT_PIPE;
+
+ const int l2_header_size = is_ethernet ? sizeof(struct ethhdr) : 0;
+
+ // Since the program never writes via DPA (direct packet access) auto-pull/unclone logic does
+ // not trigger and thus we need to manually make sure we can read packet headers via DPA.
+ // Note: this is a blind best effort pull, which may fail or pull less - this doesn't matter.
+ // It has to be done early cause it will invalidate any skb->data/data_end derived pointers.
+ try_make_writable(skb, l2_header_size + IP4_HLEN + TCP_HLEN);
+
+ void* data = (void*)(long)skb->data;
+ const void* data_end = (void*)(long)skb->data_end;
+ struct ethhdr* eth = is_ethernet ? data : NULL; // used iff is_ethernet
+ struct iphdr* ip = is_ethernet ? (void*)(eth + 1) : data;
+
+ // Must have (ethernet and) ipv4 header
+ if (data + l2_header_size + sizeof(*ip) > data_end) return TC_ACT_PIPE;
+
+ // Ethertype - if present - must be IPv4
+ if (is_ethernet && (eth->h_proto != htons(ETH_P_IP))) return TC_ACT_PIPE;
+
+ // IP version must be 4
+ if (ip->version != 4) TC_PUNT(INVALID_IP_VERSION);
+
+ // We cannot handle IP options, just standard 20 byte == 5 dword minimal IPv4 header
+ if (ip->ihl != 5) TC_PUNT(HAS_IP_OPTIONS);
+
+ // Calculate the IPv4 one's complement checksum of the IPv4 header.
+ __wsum sum4 = 0;
+ for (int i = 0; i < sizeof(*ip) / sizeof(__u16); ++i) {
+ sum4 += ((__u16*)ip)[i];
+ }
+ // Note that sum4 is guaranteed to be non-zero by virtue of ip4->version == 4
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse u32 into range 1 .. 0x1FFFE
+ sum4 = (sum4 & 0xFFFF) + (sum4 >> 16); // collapse any potential carry into u16
+ // for a correct checksum we should get *a* zero, but sum4 must be positive, ie 0xFFFF
+ if (sum4 != 0xFFFF) TC_PUNT(CHECKSUM);
+
+ // Minimum IPv4 total length is the size of the header
+ if (ntohs(ip->tot_len) < sizeof(*ip)) TC_PUNT(TRUNCATED_IPV4);
+
+ // We are incapable of dealing with IPv4 fragments
+ if (ip->frag_off & ~htons(IP_DF)) TC_PUNT(IS_IP_FRAG);
+
+ // Cannot decrement during forward if already zero or would be zero,
+ // Let the kernel's stack handle these cases and generate appropriate ICMP errors.
+ if (ip->ttl <= 1) TC_PUNT(LOW_TTL);
+
+ // If we cannot update the 'last_used' field due to lack of bpf_ktime_get_boot_ns() helper,
+ // then it is not safe to offload UDP due to the small conntrack timeouts, as such,
+ // in such a situation we can only support TCP. This also has the added nice benefit of
+ // using a separate error counter, and thus making it obvious which version of the program
+ // is loaded.
+ if (!updatetime && ip->protocol != IPPROTO_TCP) TC_PUNT(NON_TCP);
+
+ // We do not support offloading anything besides IPv4 TCP and UDP, due to need for NAT,
+ // but no need to check this if !updatetime due to check immediately above.
+ if (updatetime && (ip->protocol != IPPROTO_TCP) && (ip->protocol != IPPROTO_UDP))
+ TC_PUNT(NON_TCP_UDP);
+
+ // We want to make sure that the compiler will, in the !updatetime case, entirely optimize
+ // out all the non-tcp logic. Also note that at this point is_udp === !is_tcp.
+ const bool is_tcp = !updatetime || (ip->protocol == IPPROTO_TCP);
+
+ // This is a bit of a hack to make things easier on the bpf verifier.
+ // (In particular I believe the Linux 4.14 kernel's verifier can get confused later on about
+ // what offsets into the packet are valid and can spuriously reject the program, this is
+ // because it fails to realize that is_tcp && !is_tcp is impossible)
+ //
+ // For both TCP & UDP we'll need to read and modify the src/dst ports, which so happen to
+ // always be in the first 4 bytes of the L4 header. Additionally for UDP we'll need access
+ // to the checksum field which is in bytes 7 and 8. While for TCP we'll need to read the
+ // TCP flags (at offset 13) and access to the checksum field (2 bytes at offset 16).
+ // As such we *always* need access to at least 8 bytes.
+ if (data + l2_header_size + sizeof(*ip) + 8 > data_end) TC_PUNT(SHORT_L4_HEADER);
+
+ // We're forcing the compiler to emit two copies of the following code, optimized
+ // separately for is_tcp being true or false. This simplifies the resulting bpf
+ // byte code sufficiently that the 4.14 bpf verifier is able to keep track of things.
+ // Without this (updatetime == true) case would fail to bpf verify on 4.14 even
+ // if the underlying requisite kernel support (bpf_ktime_get_boot_ns) was backported.
+ if (is_tcp) {
+ return do_forward4_bottom(skb, l2_header_size, data, data_end, eth, ip,
+ is_ethernet, downstream, updatetime, /* is_tcp */ true);
+ } else {
+ return do_forward4_bottom(skb, l2_header_size, data, data_end, eth, ip,
+ is_ethernet, downstream, updatetime, /* is_tcp */ false);
+ }
+}
+
// Full featured (required) implementations for 5.8+ kernels (these are S+ by definition)
DEFINE_BPF_PROG_KVER("schedcls/tether_downstream4_rawip$5_8", AID_ROOT, AID_NETWORK_STACK,
diff --git a/framework-t/src/android/net/EthernetManager.java b/framework-t/src/android/net/EthernetManager.java
index 2b76dd9..886d194 100644
--- a/framework-t/src/android/net/EthernetManager.java
+++ b/framework-t/src/android/net/EthernetManager.java
@@ -32,13 +32,13 @@
import android.os.Build;
import android.os.OutcomeReceiver;
import android.os.RemoteException;
+import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
import com.android.modules.utils.BackgroundThread;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -56,37 +56,12 @@
private final IEthernetManager mService;
@GuardedBy("mListenerLock")
- private final ArrayList<ListenerInfo<InterfaceStateListener>> mIfaceListeners =
- new ArrayList<>();
+ private final ArrayMap<InterfaceStateListener, IEthernetServiceListener>
+ mIfaceServiceListeners = new ArrayMap<>();
@GuardedBy("mListenerLock")
- private final ArrayList<ListenerInfo<IntConsumer>> mEthernetStateListeners =
- new ArrayList<>();
+ private final ArrayMap<IntConsumer, IEthernetServiceListener> mStateServiceListeners =
+ new ArrayMap<>();
final Object mListenerLock = new Object();
- private final IEthernetServiceListener.Stub mServiceListener =
- new IEthernetServiceListener.Stub() {
- @Override
- public void onEthernetStateChanged(int state) {
- synchronized (mListenerLock) {
- for (ListenerInfo<IntConsumer> li : mEthernetStateListeners) {
- li.executor.execute(() -> {
- li.listener.accept(state);
- });
- }
- }
- }
-
- @Override
- public void onInterfaceStateChanged(String iface, int state, int role,
- IpConfiguration configuration) {
- synchronized (mListenerLock) {
- for (ListenerInfo<InterfaceStateListener> li : mIfaceListeners) {
- li.executor.execute(() ->
- li.listener.onInterfaceStateChanged(iface, state, role,
- configuration));
- }
- }
- }
- };
/**
* Indicates that Ethernet is disabled.
@@ -104,18 +79,6 @@
@SystemApi(client = MODULE_LIBRARIES)
public static final int ETHERNET_STATE_ENABLED = 1;
- private static class ListenerInfo<T> {
- @NonNull
- public final Executor executor;
- @NonNull
- public final T listener;
-
- private ListenerInfo(@NonNull Executor executor, @NonNull T listener) {
- this.executor = executor;
- this.listener = listener;
- }
- }
-
/**
* The interface is absent.
* @hide
@@ -323,18 +286,28 @@
if (listener == null || executor == null) {
throw new NullPointerException("listener and executor must not be null");
}
+
+ final IEthernetServiceListener.Stub serviceListener = new IEthernetServiceListener.Stub() {
+ @Override
+ public void onEthernetStateChanged(int state) {}
+
+ @Override
+ public void onInterfaceStateChanged(String iface, int state, int role,
+ IpConfiguration configuration) {
+ executor.execute(() ->
+ listener.onInterfaceStateChanged(iface, state, role, configuration));
+ }
+ };
synchronized (mListenerLock) {
- maybeAddServiceListener();
- mIfaceListeners.add(new ListenerInfo<InterfaceStateListener>(executor, listener));
+ addServiceListener(serviceListener);
+ mIfaceServiceListeners.put(listener, serviceListener);
}
}
@GuardedBy("mListenerLock")
- private void maybeAddServiceListener() {
- if (!mIfaceListeners.isEmpty() || !mEthernetStateListeners.isEmpty()) return;
-
+ private void addServiceListener(@NonNull final IEthernetServiceListener listener) {
try {
- mService.addListener(mServiceListener);
+ mService.addListener(listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -364,17 +337,16 @@
public void removeInterfaceStateListener(@NonNull InterfaceStateListener listener) {
Objects.requireNonNull(listener);
synchronized (mListenerLock) {
- mIfaceListeners.removeIf(l -> l.listener == listener);
- maybeRemoveServiceListener();
+ maybeRemoveServiceListener(mIfaceServiceListeners.remove(listener));
}
}
@GuardedBy("mListenerLock")
- private void maybeRemoveServiceListener() {
- if (!mIfaceListeners.isEmpty() || !mEthernetStateListeners.isEmpty()) return;
+ private void maybeRemoveServiceListener(@Nullable final IEthernetServiceListener listener) {
+ if (listener == null) return;
try {
- mService.removeListener(mServiceListener);
+ mService.removeListener(listener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -687,9 +659,19 @@
@NonNull IntConsumer listener) {
Objects.requireNonNull(executor);
Objects.requireNonNull(listener);
+ final IEthernetServiceListener.Stub serviceListener = new IEthernetServiceListener.Stub() {
+ @Override
+ public void onEthernetStateChanged(int state) {
+ executor.execute(() -> listener.accept(state));
+ }
+
+ @Override
+ public void onInterfaceStateChanged(String iface, int state, int role,
+ IpConfiguration configuration) {}
+ };
synchronized (mListenerLock) {
- maybeAddServiceListener();
- mEthernetStateListeners.add(new ListenerInfo<IntConsumer>(executor, listener));
+ addServiceListener(serviceListener);
+ mStateServiceListeners.put(listener, serviceListener);
}
}
@@ -705,8 +687,7 @@
public void removeEthernetStateListener(@NonNull IntConsumer listener) {
Objects.requireNonNull(listener);
synchronized (mListenerLock) {
- mEthernetStateListeners.removeIf(l -> l.listener == listener);
- maybeRemoveServiceListener();
+ maybeRemoveServiceListener(mStateServiceListeners.remove(listener));
}
}
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index a174fe3..d16a6f5 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -2589,9 +2589,24 @@
* {@hide}
*/
public ConnectivityManager(Context context, IConnectivityManager service) {
+ this(context, service, true /* newStatic */);
+ }
+
+ private ConnectivityManager(Context context, IConnectivityManager service, boolean newStatic) {
mContext = Objects.requireNonNull(context, "missing context");
mService = Objects.requireNonNull(service, "missing IConnectivityManager");
- sInstance = this;
+ // sInstance is accessed without a lock, so it may actually be reassigned several times with
+ // different ConnectivityManager, but that's still OK considering its usage.
+ if (sInstance == null && newStatic) {
+ final Context appContext = mContext.getApplicationContext();
+ // Don't create static ConnectivityManager instance again to prevent infinite loop.
+ // If the application context is null, we're either in the system process or
+ // it's the application context very early in app initialization. In both these
+ // cases, the passed-in Context will not be freed, so it's safe to pass it to the
+ // service. http://b/27532714 .
+ sInstance = new ConnectivityManager(appContext != null ? appContext : context, service,
+ false /* newStatic */);
+ }
}
/** {@hide} */
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 8782b33..a8f707e 100644
--- a/framework/src/android/net/LinkProperties.java
+++ b/framework/src/android/net/LinkProperties.java
@@ -64,7 +64,7 @@
* @hide
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) // Switch to S_V2 when it is available.
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
@VisibleForTesting
public static final long EXCLUDED_ROUTES = 186082280;
@@ -1366,6 +1366,21 @@
}
/**
+ * Returns true if this link has a throw route.
+ *
+ * @return {@code true} if there is an exclude route, {@code false} otherwise.
+ * @hide
+ */
+ public boolean hasExcludeRoute() {
+ for (RouteInfo r : mRoutes) {
+ if (r.getType() == RouteInfo.RTN_THROW) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Compares this {@code LinkProperties} interface name against the target
*
* @param target LinkProperties to compare.
diff --git a/framework/src/android/net/NetworkAgent.java b/framework/src/android/net/NetworkAgent.java
index 29add1c..2c50c73 100644
--- a/framework/src/android/net/NetworkAgent.java
+++ b/framework/src/android/net/NetworkAgent.java
@@ -1076,11 +1076,12 @@
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public final void sendNetworkInfo(NetworkInfo networkInfo) {
- queueOrSendNetworkInfo(new NetworkInfo(networkInfo));
+ queueOrSendNetworkInfo(networkInfo);
}
private void queueOrSendNetworkInfo(NetworkInfo networkInfo) {
- queueOrSendMessage(reg -> reg.sendNetworkInfo(networkInfo));
+ final NetworkInfo ni = new NetworkInfo(networkInfo);
+ queueOrSendMessage(reg -> reg.sendNetworkInfo(ni));
}
/**
diff --git a/framework/src/android/net/ProfileNetworkPreference.java b/framework/src/android/net/ProfileNetworkPreference.java
index fb271e3..fdcab02 100644
--- a/framework/src/android/net/ProfileNetworkPreference.java
+++ b/framework/src/android/net/ProfileNetworkPreference.java
@@ -120,8 +120,8 @@
public String toString() {
return "ProfileNetworkPreference{"
+ "mPreference=" + getPreference()
- + "mIncludedUids=" + mIncludedUids.toString()
- + "mExcludedUids=" + mExcludedUids.toString()
+ + "mIncludedUids=" + Arrays.toString(mIncludedUids)
+ + "mExcludedUids=" + Arrays.toString(mExcludedUids)
+ "mPreferenceEnterpriseId=" + mPreferenceEnterpriseId
+ '}';
}
diff --git a/framework/src/android/net/QosCallbackException.java b/framework/src/android/net/QosCallbackException.java
index ed6eb15..b80cff4 100644
--- a/framework/src/android/net/QosCallbackException.java
+++ b/framework/src/android/net/QosCallbackException.java
@@ -46,8 +46,10 @@
EX_TYPE_FILTER_NONE,
EX_TYPE_FILTER_NETWORK_RELEASED,
EX_TYPE_FILTER_SOCKET_NOT_BOUND,
+ EX_TYPE_FILTER_SOCKET_NOT_CONNECTED,
EX_TYPE_FILTER_NOT_SUPPORTED,
EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED,
+ EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ExceptionType {}
@@ -65,10 +67,16 @@
public static final int EX_TYPE_FILTER_SOCKET_NOT_BOUND = 2;
/** {@hide} */
- public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 3;
+ public static final int EX_TYPE_FILTER_SOCKET_NOT_CONNECTED = 3;
/** {@hide} */
- public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 4;
+ public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 4;
+
+ /** {@hide} */
+ public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 5;
+
+ /** {@hide} */
+ public static final int EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED = 6;
/**
* Creates exception based off of a type and message. Not all types of exceptions accept a
@@ -83,12 +91,17 @@
return new QosCallbackException(new NetworkReleasedException());
case EX_TYPE_FILTER_SOCKET_NOT_BOUND:
return new QosCallbackException(new SocketNotBoundException());
+ case EX_TYPE_FILTER_SOCKET_NOT_CONNECTED:
+ return new QosCallbackException(new SocketNotConnectedException());
case EX_TYPE_FILTER_NOT_SUPPORTED:
return new QosCallbackException(new UnsupportedOperationException(
"This device does not support the specified filter"));
case EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED:
return new QosCallbackException(
new SocketLocalAddressChangedException());
+ case EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED:
+ return new QosCallbackException(
+ new SocketRemoteAddressChangedException());
default:
Log.wtf(TAG, "create: No case setup for exception type: '" + type + "'");
return new QosCallbackException(
diff --git a/framework/src/android/net/QosFilter.java b/framework/src/android/net/QosFilter.java
index 5c1c3cc..b432644 100644
--- a/framework/src/android/net/QosFilter.java
+++ b/framework/src/android/net/QosFilter.java
@@ -90,5 +90,15 @@
*/
public abstract boolean matchesRemoteAddress(@NonNull InetAddress address,
int startPort, int endPort);
+
+ /**
+ * Determines whether or not the parameter will be matched with this filter.
+ *
+ * @param protocol the protocol such as TCP or UDP included in IP packet filter set of a QoS
+ * flow assigned on {@link Network}.
+ * @return whether the parameters match the socket type of the filter
+ * @hide
+ */
+ public abstract boolean matchesProtocol(int protocol);
}
diff --git a/framework/src/android/net/QosFilterParcelable.java b/framework/src/android/net/QosFilterParcelable.java
index da3b2cf..6e71fa3 100644
--- a/framework/src/android/net/QosFilterParcelable.java
+++ b/framework/src/android/net/QosFilterParcelable.java
@@ -104,7 +104,7 @@
if (mQosFilter instanceof QosSocketFilter) {
dest.writeInt(QOS_SOCKET_FILTER);
final QosSocketFilter qosSocketFilter = (QosSocketFilter) mQosFilter;
- qosSocketFilter.getQosSocketInfo().writeToParcel(dest, 0);
+ qosSocketFilter.getQosSocketInfo().writeToParcelWithoutFd(dest, 0);
return;
}
dest.writeInt(NO_FILTER_PRESENT);
diff --git a/framework/src/android/net/QosSocketFilter.java b/framework/src/android/net/QosSocketFilter.java
index 69da7f4..5ceeb67 100644
--- a/framework/src/android/net/QosSocketFilter.java
+++ b/framework/src/android/net/QosSocketFilter.java
@@ -18,6 +18,13 @@
import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE;
import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_CONNECTED;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -74,19 +81,34 @@
* 2. In the scenario that the socket is now bound to a different local address, which can
* happen in the case of UDP, then
* {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED} is returned.
+ * 3. In the scenario that the UDP socket changed remote address, then
+ * {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED} is returned.
+ *
* @return validation error code
*/
@Override
public int validate() {
- final InetSocketAddress sa = getAddressFromFileDescriptor();
- if (sa == null) {
- return QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
+ final InetSocketAddress sa = getLocalAddressFromFileDescriptor();
+
+ if (sa == null || (sa.getAddress().isAnyLocalAddress() && sa.getPort() == 0)) {
+ return EX_TYPE_FILTER_SOCKET_NOT_BOUND;
}
if (!sa.equals(mQosSocketInfo.getLocalSocketAddress())) {
return EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
}
+ if (mQosSocketInfo.getRemoteSocketAddress() != null) {
+ final InetSocketAddress da = getRemoteAddressFromFileDescriptor();
+ if (da == null) {
+ return EX_TYPE_FILTER_SOCKET_NOT_CONNECTED;
+ }
+
+ if (!da.equals(mQosSocketInfo.getRemoteSocketAddress())) {
+ return EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED;
+ }
+ }
+
return EX_TYPE_FILTER_NONE;
}
@@ -98,17 +120,14 @@
* @return the local address
*/
@Nullable
- private InetSocketAddress getAddressFromFileDescriptor() {
+ private InetSocketAddress getLocalAddressFromFileDescriptor() {
final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor();
- if (parcelFileDescriptor == null) return null;
-
final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor();
- if (fd == null) return null;
final SocketAddress address;
try {
address = Os.getsockname(fd);
- } catch (final ErrnoException e) {
+ } catch (ErrnoException e) {
Log.e(TAG, "getAddressFromFileDescriptor: getLocalAddress exception", e);
return null;
}
@@ -119,6 +138,31 @@
}
/**
+ * The remote address of the socket's connected.
+ *
+ * <p>Note: If the socket is no longer connected, null is returned.
+ *
+ * @return the remote address
+ */
+ @Nullable
+ private InetSocketAddress getRemoteAddressFromFileDescriptor() {
+ final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor();
+ final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor();
+
+ final SocketAddress address;
+ try {
+ address = Os.getpeername(fd);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "getAddressFromFileDescriptor: getRemoteAddress exception", e);
+ return null;
+ }
+ if (address instanceof InetSocketAddress) {
+ return (InetSocketAddress) address;
+ }
+ return null;
+ }
+
+ /**
* The network used with this filter.
*
* @return the registered {@link Network}
@@ -156,6 +200,18 @@
}
/**
+ * @inheritDoc
+ */
+ @Override
+ public boolean matchesProtocol(final int protocol) {
+ if ((mQosSocketInfo.getSocketType() == SOCK_STREAM && protocol == IPPROTO_TCP)
+ || (mQosSocketInfo.getSocketType() == SOCK_DGRAM && protocol == IPPROTO_UDP)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)}
* and {@link QosSocketFilter#matchesRemoteAddress(InetAddress, int, int)} with the
* filterSocketAddress coming from {@link QosSocketInfo#getLocalSocketAddress()}.
@@ -174,6 +230,7 @@
final int startPort, final int endPort) {
return startPort <= filterSocketAddress.getPort()
&& endPort >= filterSocketAddress.getPort()
- && filterSocketAddress.getAddress().equals(address);
+ && (address.isAnyLocalAddress()
+ || filterSocketAddress.getAddress().equals(address));
}
}
diff --git a/framework/src/android/net/QosSocketInfo.java b/framework/src/android/net/QosSocketInfo.java
index 39c2f33..49ac22b 100644
--- a/framework/src/android/net/QosSocketInfo.java
+++ b/framework/src/android/net/QosSocketInfo.java
@@ -165,25 +165,28 @@
/* Parcelable methods */
private QosSocketInfo(final Parcel in) {
mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in));
- mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+ final boolean withFd = in.readBoolean();
+ if (withFd) {
+ mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+ } else {
+ mParcelFileDescriptor = null;
+ }
- final int localAddressLength = in.readInt();
- mLocalSocketAddress = readSocketAddress(in, localAddressLength);
-
- final int remoteAddressLength = in.readInt();
- mRemoteSocketAddress = remoteAddressLength == 0 ? null
- : readSocketAddress(in, remoteAddressLength);
+ mLocalSocketAddress = readSocketAddress(in);
+ mRemoteSocketAddress = readSocketAddress(in);
mSocketType = in.readInt();
}
- private @NonNull InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) {
- final byte[] address = new byte[addressLength];
- in.readByteArray(address);
+ private InetSocketAddress readSocketAddress(final Parcel in) {
+ final byte[] addrBytes = in.createByteArray();
+ if (addrBytes == null) {
+ return null;
+ }
final int port = in.readInt();
try {
- return new InetSocketAddress(InetAddress.getByAddress(address), port);
+ return new InetSocketAddress(InetAddress.getByAddress(addrBytes), port);
} catch (final UnknownHostException e) {
/* This can never happen. UnknownHostException will never be thrown
since the address provided is numeric and non-null. */
@@ -198,20 +201,35 @@
@Override
public void writeToParcel(@NonNull final Parcel dest, final int flags) {
- mNetwork.writeToParcel(dest, 0);
- mParcelFileDescriptor.writeToParcel(dest, 0);
+ writeToParcelInternal(dest, flags, /*includeFd=*/ true);
+ }
- final byte[] localAddress = mLocalSocketAddress.getAddress().getAddress();
- dest.writeInt(localAddress.length);
- dest.writeByteArray(localAddress);
+ /**
+ * Used when sending QosSocketInfo to telephony, which does not need access to the socket FD.
+ * @hide
+ */
+ public void writeToParcelWithoutFd(@NonNull final Parcel dest, final int flags) {
+ writeToParcelInternal(dest, flags, /*includeFd=*/ false);
+ }
+
+ private void writeToParcelInternal(
+ @NonNull final Parcel dest, final int flags, boolean includeFd) {
+ mNetwork.writeToParcel(dest, 0);
+
+ if (includeFd) {
+ dest.writeBoolean(true);
+ mParcelFileDescriptor.writeToParcel(dest, 0);
+ } else {
+ dest.writeBoolean(false);
+ }
+
+ dest.writeByteArray(mLocalSocketAddress.getAddress().getAddress());
dest.writeInt(mLocalSocketAddress.getPort());
if (mRemoteSocketAddress == null) {
- dest.writeInt(0);
+ dest.writeByteArray(null);
} else {
- final byte[] remoteAddress = mRemoteSocketAddress.getAddress().getAddress();
- dest.writeInt(remoteAddress.length);
- dest.writeByteArray(remoteAddress);
+ dest.writeByteArray(mRemoteSocketAddress.getAddress().getAddress());
dest.writeInt(mRemoteSocketAddress.getPort());
}
dest.writeInt(mSocketType);
diff --git a/framework/src/android/net/SocketNotConnectedException.java b/framework/src/android/net/SocketNotConnectedException.java
new file mode 100644
index 0000000..fa2a615
--- /dev/null
+++ b/framework/src/android/net/SocketNotConnectedException.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Thrown when a previously bound socket becomes unbound.
+ *
+ * @hide
+ */
+public class SocketNotConnectedException extends Exception {
+ /** @hide */
+ public SocketNotConnectedException() {
+ super("The socket is not connected");
+ }
+}
diff --git a/framework/src/android/net/SocketRemoteAddressChangedException.java b/framework/src/android/net/SocketRemoteAddressChangedException.java
new file mode 100644
index 0000000..ecaeebc
--- /dev/null
+++ b/framework/src/android/net/SocketRemoteAddressChangedException.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Thrown when the local address of the socket has changed.
+ *
+ * @hide
+ */
+public class SocketRemoteAddressChangedException extends Exception {
+ /** @hide */
+ public SocketRemoteAddressChangedException() {
+ super("The remote address of the socket changed");
+ }
+}
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..09782fd 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);
}
}
@@ -213,7 +194,7 @@
}
final NetworkInterfaceState iface = new NetworkInterfaceState(
- ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, this, mDeps);
+ ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, getProvider(), mDeps);
mTrackingInterfaces.put(ifaceName, iface);
updateCapabilityFilter();
}
@@ -380,7 +361,7 @@
private final String mHwAddress;
private final Handler mHandler;
private final Context mContext;
- private final NetworkFactory mNetworkFactory;
+ private final NetworkProvider mNetworkProvider;
private final Dependencies mDeps;
private static String sTcpBufferSizes = null; // Lazy initialized.
@@ -490,14 +471,14 @@
NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context,
@NonNull IpConfiguration ipConfig, @NonNull NetworkCapabilities capabilities,
- NetworkFactory networkFactory, Dependencies deps) {
+ NetworkProvider networkProvider, Dependencies deps) {
name = ifaceName;
mIpConfig = Objects.requireNonNull(ipConfig);
mCapabilities = Objects.requireNonNull(capabilities);
mLegacyType = getLegacyType(mCapabilities);
mHandler = handler;
mContext = context;
- mNetworkFactory = networkFactory;
+ mNetworkProvider = networkProvider;
mDeps = deps;
mHwAddress = hwAddress;
}
@@ -594,7 +575,7 @@
.setLegacyExtraInfo(mHwAddress)
.build();
mNetworkAgent = mDeps.makeEthernetNetworkAgent(mContext, mHandler.getLooper(),
- mCapabilities, mLinkProperties, config, mNetworkFactory.getProvider(),
+ mCapabilities, mLinkProperties, config, mNetworkProvider,
new EthernetNetworkAgent.Callbacks() {
@Override
public void onNetworkUnwanted() {
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/NetworkStatsObservers.java b/service-t/src/com/android/server/net/NetworkStatsObservers.java
index fdfc893..c51a886 100644
--- a/service-t/src/com/android/server/net/NetworkStatsObservers.java
+++ b/service-t/src/com/android/server/net/NetworkStatsObservers.java
@@ -18,6 +18,7 @@
import static android.app.usage.NetworkStatsManager.MIN_THRESHOLD_BYTES;
+import android.annotation.NonNull;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -38,6 +39,7 @@
import android.os.Process;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.SparseArray;
@@ -52,12 +54,15 @@
*/
class NetworkStatsObservers {
private static final String TAG = "NetworkStatsObservers";
+ private static final boolean LOG = true;
private static final boolean LOGV = false;
private static final int MSG_REGISTER = 1;
private static final int MSG_UNREGISTER = 2;
private static final int MSG_UPDATE_STATS = 3;
+ private static final int DUMP_USAGE_REQUESTS_COUNT = 200;
+
// All access to this map must be done from the handler thread.
// indexed by DataUsageRequest#requestId
private final SparseArray<RequestInfo> mDataUsageRequests = new SparseArray<>();
@@ -77,13 +82,15 @@
*
* @return the normalized request wrapped within {@link RequestInfo}.
*/
- public DataUsageRequest register(Context context, DataUsageRequest inputRequest,
- IUsageCallback callback, int callingUid, @NetworkStatsAccess.Level int accessLevel) {
+ public DataUsageRequest register(@NonNull Context context,
+ @NonNull DataUsageRequest inputRequest, @NonNull IUsageCallback callback,
+ int callingPid, int callingUid, @NonNull String callingPackage,
+ @NetworkStatsAccess.Level int accessLevel) {
DataUsageRequest request = buildRequest(context, inputRequest, callingUid);
- RequestInfo requestInfo = buildRequestInfo(request, callback, callingUid,
- accessLevel);
+ RequestInfo requestInfo = buildRequestInfo(request, callback, callingPid, callingUid,
+ callingPackage, accessLevel);
- if (LOGV) Log.v(TAG, "Registering observer for " + request);
+ if (LOG) Log.d(TAG, "Registering observer for " + requestInfo);
getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo));
return request;
}
@@ -172,7 +179,7 @@
RequestInfo requestInfo;
requestInfo = mDataUsageRequests.get(request.requestId);
if (requestInfo == null) {
- if (LOGV) Log.v(TAG, "Trying to unregister unknown request " + request);
+ if (LOG) Log.d(TAG, "Trying to unregister unknown request " + request);
return;
}
if (Process.SYSTEM_UID != callingUid && requestInfo.mCallingUid != callingUid) {
@@ -180,7 +187,7 @@
return;
}
- if (LOGV) Log.v(TAG, "Unregistering " + request);
+ if (LOG) Log.d(TAG, "Unregistering " + requestInfo);
mDataUsageRequests.remove(request.requestId);
requestInfo.unlinkDeathRecipient();
requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
@@ -214,18 +221,19 @@
}
private RequestInfo buildRequestInfo(DataUsageRequest request, IUsageCallback callback,
- int callingUid, @NetworkStatsAccess.Level int accessLevel) {
+ int callingPid, int callingUid, @NonNull String callingPackage,
+ @NetworkStatsAccess.Level int accessLevel) {
if (accessLevel <= NetworkStatsAccess.Level.USER) {
- return new UserUsageRequestInfo(this, request, callback, callingUid,
- accessLevel);
+ return new UserUsageRequestInfo(this, request, callback, callingPid,
+ callingUid, callingPackage, accessLevel);
} else {
// Safety check in case a new access level is added and we forgot to update this
if (accessLevel < NetworkStatsAccess.Level.DEVICESUMMARY) {
throw new IllegalArgumentException(
"accessLevel " + accessLevel + " is less than DEVICESUMMARY.");
}
- return new NetworkUsageRequestInfo(this, request, callback, callingUid,
- accessLevel);
+ return new NetworkUsageRequestInfo(this, request, callback, callingPid,
+ callingUid, callingPackage, accessLevel);
}
}
@@ -237,18 +245,22 @@
private final NetworkStatsObservers mStatsObserver;
protected final DataUsageRequest mRequest;
private final IUsageCallback mCallback;
+ protected final int mCallingPid;
protected final int mCallingUid;
+ protected final String mCallingPackage;
protected final @NetworkStatsAccess.Level int mAccessLevel;
protected NetworkStatsRecorder mRecorder;
protected NetworkStatsCollection mCollection;
RequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
- IUsageCallback callback, int callingUid,
- @NetworkStatsAccess.Level int accessLevel) {
+ IUsageCallback callback, int callingPid, int callingUid,
+ @NonNull String callingPackage, @NetworkStatsAccess.Level int accessLevel) {
mStatsObserver = statsObserver;
mRequest = request;
mCallback = callback;
+ mCallingPid = callingPid;
mCallingUid = callingUid;
+ mCallingPackage = callingPackage;
mAccessLevel = accessLevel;
try {
@@ -269,7 +281,8 @@
@Override
public String toString() {
- return "RequestInfo from uid:" + mCallingUid
+ return "RequestInfo from pid/uid:" + mCallingPid + "/" + mCallingUid
+ + "(" + mCallingPackage + ")"
+ " for " + mRequest + " accessLevel:" + mAccessLevel;
}
@@ -338,9 +351,10 @@
private static class NetworkUsageRequestInfo extends RequestInfo {
NetworkUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
- IUsageCallback callback, int callingUid,
- @NetworkStatsAccess.Level int accessLevel) {
- super(statsObserver, request, callback, callingUid, accessLevel);
+ IUsageCallback callback, int callingPid, int callingUid,
+ @NonNull String callingPackage, @NetworkStatsAccess.Level int accessLevel) {
+ super(statsObserver, request, callback, callingPid, callingUid, callingPackage,
+ accessLevel);
}
@Override
@@ -380,9 +394,10 @@
private static class UserUsageRequestInfo extends RequestInfo {
UserUsageRequestInfo(NetworkStatsObservers statsObserver, DataUsageRequest request,
- IUsageCallback callback, int callingUid,
- @NetworkStatsAccess.Level int accessLevel) {
- super(statsObserver, request, callback, callingUid, accessLevel);
+ IUsageCallback callback, int callingPid, int callingUid,
+ @NonNull String callingPackage, @NetworkStatsAccess.Level int accessLevel) {
+ super(statsObserver, request, callback, callingPid, callingUid,
+ callingPackage, accessLevel);
}
@Override
@@ -448,4 +463,10 @@
mCurrentTime = currentTime;
}
}
+
+ public void dump(IndentingPrintWriter pw) {
+ for (int i = 0; i < Math.min(mDataUsageRequests.size(), DUMP_USAGE_REQUESTS_COUNT); i++) {
+ pw.println(mDataUsageRequests.valueAt(i));
+ }
+ }
}
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index e3794e4..7c167ed 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -159,8 +159,11 @@
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
@@ -218,17 +221,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;
@@ -375,9 +377,9 @@
private long mLastStatsSessionPoll;
- /** Map from UID to number of opened sessions */
- @GuardedBy("mOpenSessionCallsPerUid")
- private final SparseIntArray mOpenSessionCallsPerUid = new SparseIntArray();
+ /** Map from key {@code OpenSessionKey} to count of opened sessions */
+ @GuardedBy("mOpenSessionCallsPerCaller")
+ private final HashMap<OpenSessionKey, Integer> mOpenSessionCallsPerCaller = new HashMap<>();
private final static int DUMP_STATS_SESSION_COUNT = 20;
@@ -408,6 +410,48 @@
Clock.systemUTC());
}
+ /**
+ * This class is a key that used in {@code mOpenSessionCallsPerCaller} to identify the count of
+ * the caller.
+ */
+ private static class OpenSessionKey {
+ public final int mPid;
+ public final int mUid;
+ public final String mPackage;
+
+ OpenSessionKey(int pid, int uid, @NonNull String packageName) {
+ mPid = pid;
+ mUid = uid;
+ mPackage = packageName;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ sb.append("pid=").append(mPid).append(",");
+ sb.append("uid=").append(mUid).append(",");
+ sb.append("package=").append(mPackage);
+ sb.append("}");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(@NonNull Object o) {
+ if (this == o) return true;
+ if (o.getClass() != getClass()) return false;
+
+ final OpenSessionKey key = (OpenSessionKey) o;
+ return mPid == key.mPid && mUid == key.mUid
+ && TextUtils.equals(mPackage, key.mPackage);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mPid, mUid, mPackage);
+ }
+ }
+
private final class NetworkStatsHandler extends Handler {
NetworkStatsHandler(@NonNull Looper looper) {
super(looper);
@@ -795,24 +839,29 @@
return openSessionInternal(flags, callingPackage);
}
- private boolean isRateLimitedForPoll(int callingUid) {
- if (callingUid == android.os.Process.SYSTEM_UID) {
- return false;
- }
-
+ private boolean isRateLimitedForPoll(@NonNull OpenSessionKey key) {
final long lastCallTime;
final long now = SystemClock.elapsedRealtime();
- synchronized (mOpenSessionCallsPerUid) {
- int calls = mOpenSessionCallsPerUid.get(callingUid, 0);
- mOpenSessionCallsPerUid.put(callingUid, calls + 1);
+
+ synchronized (mOpenSessionCallsPerCaller) {
+ Integer calls = mOpenSessionCallsPerCaller.get(key);
+ if (calls == null) {
+ mOpenSessionCallsPerCaller.put((key), 1);
+ } else {
+ mOpenSessionCallsPerCaller.put(key, Integer.sum(calls, 1));
+ }
lastCallTime = mLastStatsSessionPoll;
mLastStatsSessionPoll = now;
}
+ if (key.mUid == android.os.Process.SYSTEM_UID) {
+ return false;
+ }
+
return now - lastCallTime < POLL_RATE_LIMIT_MS;
}
- private int restrictFlagsForCaller(int flags) {
+ private int restrictFlagsForCaller(int flags, @NonNull String callingPackage) {
// All non-privileged callers are not allowed to turn off POLL_ON_OPEN.
final boolean isPrivileged = PermissionUtils.checkAnyPermissionOf(mContext,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
@@ -821,15 +870,17 @@
flags |= NetworkStatsManager.FLAG_POLL_ON_OPEN;
}
// Non-system uids are rate limited for POLL_ON_OPEN.
+ final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
- flags = isRateLimitedForPoll(callingUid)
+ final OpenSessionKey key = new OpenSessionKey(callingPid, callingUid, callingPackage);
+ flags = isRateLimitedForPoll(key)
? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
: flags;
return flags;
}
private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
- final int restrictedFlags = restrictFlagsForCaller(flags);
+ final int restrictedFlags = restrictFlagsForCaller(flags, callingPackage);
if ((restrictedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
| NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
final long ident = Binder.clearCallingIdentity();
@@ -1280,13 +1331,14 @@
Objects.requireNonNull(request.template, "NetworkTemplate is null");
Objects.requireNonNull(callback, "callback is null");
- int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
@NetworkStatsAccess.Level int accessLevel = checkAccessLevel(callingPackage);
DataUsageRequest normalizedRequest;
final long token = Binder.clearCallingIdentity();
try {
normalizedRequest = mStatsObservers.register(mContext,
- request, callback, callingUid, accessLevel);
+ request, callback, callingPid, callingUid, callingPackage, accessLevel);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -2061,25 +2113,21 @@
pw.decreaseIndent();
// Get the top openSession callers
- final SparseIntArray calls;
- synchronized (mOpenSessionCallsPerUid) {
- calls = mOpenSessionCallsPerUid.clone();
+ final HashMap calls;
+ synchronized (mOpenSessionCallsPerCaller) {
+ calls = new HashMap<>(mOpenSessionCallsPerCaller);
}
-
- final int N = calls.size();
- final long[] values = new long[N];
- for (int j = 0; j < N; j++) {
- values[j] = ((long) calls.valueAt(j) << 32) | calls.keyAt(j);
- }
- Arrays.sort(values);
-
- pw.println("Top openSession callers (uid=count):");
+ final List<Map.Entry<OpenSessionKey, Integer>> list = new ArrayList<>(calls.entrySet());
+ Collections.sort(list,
+ (left, right) -> Integer.compare(left.getValue(), right.getValue()));
+ final int num = list.size();
+ final int end = Math.max(0, num - DUMP_STATS_SESSION_COUNT);
+ pw.println("Top openSession callers:");
pw.increaseIndent();
- final int end = Math.max(0, N - DUMP_STATS_SESSION_COUNT);
- for (int j = N - 1; j >= end; j--) {
- final int uid = (int) (values[j] & 0xffffffff);
- final int count = (int) (values[j] >> 32);
- pw.print(uid); pw.print("="); pw.println(count);
+ for (int j = num - 1; j >= end; j--) {
+ final Map.Entry<OpenSessionKey, Integer> entry = list.get(j);
+ pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
+
}
pw.decreaseIndent();
pw.println();
@@ -2099,6 +2147,13 @@
}
});
pw.decreaseIndent();
+ pw.println();
+
+ pw.println("Stats Observers:");
+ pw.increaseIndent();
+ mStatsObservers.dump(pw);
+ pw.decreaseIndent();
+ pw.println();
pw.println("Dev stats:");
pw.increaseIndent();
diff --git a/service/ServiceConnectivityResources/Android.bp b/service/ServiceConnectivityResources/Android.bp
index f491cc7..02b2875 100644
--- a/service/ServiceConnectivityResources/Android.bp
+++ b/service/ServiceConnectivityResources/Android.bp
@@ -23,6 +23,7 @@
name: "ServiceConnectivityResources",
sdk_version: "module_30",
min_sdk_version: "30",
+ target_sdk_version: "33",
resource_dirs: [
"res",
],
diff --git a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
index 500c696..e2c5a63 100644
--- a/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
+++ b/service/jni/com_android_server_connectivity_ClatCoordinator.cpp
@@ -314,7 +314,8 @@
}
// TODO: use android::base::ScopeGuard.
- if (int ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK)) {
+ if (int ret = posix_spawnattr_setflags(&attr, POSIX_SPAWN_USEVFORK
+ | POSIX_SPAWN_CLOEXEC_DEFAULT)) {
posix_spawnattr_destroy(&attr);
throwIOException(env, "posix_spawnattr_setflags failed", ret);
return -1;
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 4835438..078fff0 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -108,6 +108,7 @@
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
import android.app.usage.NetworkStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -258,6 +259,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;
@@ -608,13 +610,6 @@
// Handle private DNS validation status updates.
private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
- /**
- * used to remove a network request, either a listener or a real request and call unavailable
- * arg1 = UID of caller
- * obj = NetworkRequest
- */
- private static final int EVENT_RELEASE_NETWORK_REQUEST_AND_CALL_UNAVAILABLE = 39;
-
/**
* Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
* been tested.
@@ -753,7 +748,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 +1400,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) {
@@ -2613,7 +2621,7 @@
verifyCallingUidAndPackage(callingPackageName, mDeps.getCallingUid());
enforceChangePermission(callingPackageName, callingAttributionTag);
if (mProtectedNetworks.contains(networkType)) {
- enforceConnectivityRestrictedNetworksPermission();
+ enforceConnectivityRestrictedNetworksPermission(true /* checkUidsAllowedList */);
}
InetAddress addr;
@@ -2865,7 +2873,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 +2881,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 +2890,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 +2904,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() {
@@ -2967,18 +2975,35 @@
android.Manifest.permission.NETWORK_SETTINGS);
}
- private void enforceConnectivityRestrictedNetworksPermission() {
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS,
- "ConnectivityService");
- return;
- } catch (SecurityException e) { /* fallback to ConnectivityInternalPermission */ }
- // TODO: Remove this fallback check after all apps have declared
- // CONNECTIVITY_USE_RESTRICTED_NETWORKS.
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "ConnectivityService");
+ private boolean checkConnectivityRestrictedNetworksPermission(int callingUid,
+ boolean checkUidsAllowedList) {
+ if (PermissionUtils.checkAnyPermissionOf(mContext,
+ android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS)) {
+ return true;
+ }
+
+ // fallback to ConnectivityInternalPermission
+ // TODO: Remove this fallback check after all apps have declared
+ // CONNECTIVITY_USE_RESTRICTED_NETWORKS.
+ if (PermissionUtils.checkAnyPermissionOf(mContext,
+ android.Manifest.permission.CONNECTIVITY_INTERNAL)) {
+ return true;
+ }
+
+ // Check whether uid is in allowed on restricted networks list.
+ if (checkUidsAllowedList
+ && mPermissionMonitor.isUidAllowedOnRestrictedNetworks(callingUid)) {
+ return true;
+ }
+ return false;
+ }
+
+ private void enforceConnectivityRestrictedNetworksPermission(boolean checkUidsAllowedList) {
+ final int callingUid = mDeps.getCallingUid();
+ if (!checkConnectivityRestrictedNetworksPermission(callingUid, checkUidsAllowedList)) {
+ throw new SecurityException("ConnectivityService: user " + callingUid
+ + " has no permission to access restricted network.");
+ }
}
private void enforceKeepalivePermission() {
@@ -3417,6 +3442,10 @@
pw.increaseIndent();
nai.dumpInactivityTimers(pw);
pw.decreaseIndent();
+ pw.println("Nat464Xlat:");
+ pw.increaseIndent();
+ nai.dumpNat464Xlat(pw);
+ pw.decreaseIndent();
pw.decreaseIndent();
}
}
@@ -4476,7 +4505,7 @@
private boolean hasCarrierPrivilegeForNetworkCaps(final int callingUid,
@NonNull final NetworkCapabilities caps) {
- if (SdkLevel.isAtLeastT() && mCarrierPrivilegeAuthenticator != null) {
+ if (mCarrierPrivilegeAuthenticator != null) {
return mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
callingUid, caps);
}
@@ -4506,7 +4535,6 @@
private void handleRegisterNetworkRequests(@NonNull final Set<NetworkRequestInfo> nris) {
ensureRunningOnConnectivityServiceThread();
- NetworkRequest requestToBeReleased = null;
for (final NetworkRequestInfo nri : nris) {
mNetworkRequestInfoLogs.log("REGISTER " + nri);
checkNrisConsistency(nri);
@@ -4521,13 +4549,6 @@
}
}
}
- if (req.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- if (!hasCarrierPrivilegeForNetworkCaps(nri.mUid, req.networkCapabilities)
- && !checkConnectivityRestrictedNetworksPermission(
- nri.mPid, nri.mUid)) {
- requestToBeReleased = req;
- }
- }
}
// If this NRI has a satisfier already, it is replacing an older request that
@@ -4539,11 +4560,6 @@
}
}
- if (requestToBeReleased != null) {
- releaseNetworkRequestAndCallOnUnavailable(requestToBeReleased);
- return;
- }
-
if (mFlags.noRematchAllRequestsOnRegister()) {
rematchNetworksAndRequests(nris);
} else {
@@ -5383,11 +5399,6 @@
/* callOnUnavailable */ false);
break;
}
- case EVENT_RELEASE_NETWORK_REQUEST_AND_CALL_UNAVAILABLE: {
- handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1,
- /* callOnUnavailable */ true);
- break;
- }
case EVENT_SET_ACCEPT_UNVALIDATED: {
Network network = (Network) msg.obj;
handleSetAcceptUnvalidated(network, toBool(msg.arg1), toBool(msg.arg2));
@@ -6606,7 +6617,7 @@
case REQUEST:
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
- callingAttributionTag);
+ callingAttributionTag, callingUid);
// TODO: this is incorrect. We mark the request as metered or not depending on
// the state of the app when the request is filed, but we never change the
// request if the app changes network state. http://b/29964605
@@ -6696,26 +6707,19 @@
}
private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
- String callingPackageName, String callingAttributionTag) {
+ String callingPackageName, String callingAttributionTag, final int callingUid) {
if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) == false) {
- if (!networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- enforceConnectivityRestrictedNetworksPermission();
+ // For T+ devices, callers with carrier privilege could request with CBS capabilities.
+ if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
+ && hasCarrierPrivilegeForNetworkCaps(callingUid, networkCapabilities)) {
+ return;
}
+ enforceConnectivityRestrictedNetworksPermission(true /* checkUidsAllowedList */);
} else {
enforceChangePermission(callingPackageName, callingAttributionTag);
}
}
- private boolean checkConnectivityRestrictedNetworksPermission(int callerPid, int callerUid) {
- if (checkAnyPermissionOf(callerPid, callerUid,
- android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS)
- || checkAnyPermissionOf(callerPid, callerUid,
- android.Manifest.permission.CONNECTIVITY_INTERNAL)) {
- return true;
- }
- return false;
- }
-
@Override
public boolean requestBandwidthUpdate(Network network) {
enforceAccessPermission();
@@ -6774,7 +6778,7 @@
final int callingUid = mDeps.getCallingUid();
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
- callingAttributionTag);
+ callingAttributionTag, callingUid);
enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
@@ -6897,13 +6901,6 @@
EVENT_RELEASE_NETWORK_REQUEST, mDeps.getCallingUid(), 0, networkRequest));
}
- private void releaseNetworkRequestAndCallOnUnavailable(NetworkRequest networkRequest) {
- ensureNetworkRequestHasType(networkRequest);
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_RELEASE_NETWORK_REQUEST_AND_CALL_UNAVAILABLE, mDeps.getCallingUid(), 0,
- networkRequest));
- }
-
private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
if (mNetworkProviderInfos.containsKey(npi.messenger)) {
// Avoid creating duplicates. even if an app makes a direct AIDL call.
@@ -8068,7 +8065,8 @@
&& nc.getOwnerUid() != Process.SYSTEM_UID
&& lp.getInterfaceName() != null
&& (lp.hasIpv4DefaultRoute() || lp.hasIpv4UnreachableDefaultRoute())
- && (lp.hasIpv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute());
+ && (lp.hasIpv6DefaultRoute() || lp.hasIpv6UnreachableDefaultRoute())
+ && !lp.hasExcludeRoute();
}
private static UidRangeParcel[] toUidRangeStableParcels(final @NonNull Set<UidRange> ranges) {
@@ -10605,7 +10603,11 @@
if (callback == null) throw new IllegalArgumentException("callback must be non-null");
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
- enforceConnectivityRestrictedNetworksPermission();
+ // TODO: Check allowed list here and ensure that either a) any QoS callback registered
+ // on this network is unregistered when the app loses permission or b) no QoS
+ // callbacks are sent for restricted networks unless the app currently has permission
+ // to access restricted networks.
+ enforceConnectivityRestrictedNetworksPermission(false /* checkUidsAllowedList */);
}
mQosCallbackTracker.registerCallback(callback, filter, nai);
}
@@ -10621,13 +10623,29 @@
mQosCallbackTracker.unregisterCallback(callback);
}
+ private boolean isNetworkPreferenceAllowedForProfile(@NonNull UserHandle profile) {
+ // UserManager.isManagedProfile returns true for all apps in managed user profiles.
+ // Enterprise device can be fully managed like device owner and such use case
+ // also should be supported. Calling app check for work profile and fully managed device
+ // is already done in DevicePolicyManager.
+ // This check is an extra caution to be sure device is fully managed or not.
+ final UserManager um = mContext.getSystemService(UserManager.class);
+ final DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ if (um.isManagedProfile(profile.getIdentifier())) {
+ return true;
+ }
+ if (SdkLevel.isAtLeastT() && dpm.getDeviceOwner() != null) return true;
+ return false;
+ }
+
/**
- * Request that a user profile is put by default on a network matching a given preference.
+ * Set a list of default network selection policies for a user profile or device owner.
*
* See the documentation for the individual preferences for a description of the supported
* behaviors.
*
- * @param profile the user profile for whih the preference is being set.
+ * @param profile If the device owner is set, any profile is allowed.
+ Otherwise, the given profile can only be managed profile.
* @param preferences the list of profile network preferences for the
* provided profile.
* @param listener an optional listener to listen for completion of the operation.
@@ -10652,9 +10670,9 @@
throw new IllegalArgumentException("Must explicitly specify a user handle ("
+ "UserHandle.CURRENT not supported)");
}
- final UserManager um = mContext.getSystemService(UserManager.class);
- if (!um.isManagedProfile(profile.getIdentifier())) {
- throw new IllegalArgumentException("Profile must be a managed profile");
+ if (!isNetworkPreferenceAllowedForProfile(profile)) {
+ throw new IllegalArgumentException("Profile must be a managed profile "
+ + "or the device owner must be set. ");
}
final List<ProfileNetworkPreferenceList.Preference> preferenceList =
@@ -10797,10 +10815,20 @@
private void handleSetProfileNetworkPreference(
@NonNull final List<ProfileNetworkPreferenceList.Preference> preferenceList,
@Nullable final IOnCompleteListener listener) {
+ /*
+ * handleSetProfileNetworkPreference is always called for single user.
+ * preferenceList only contains preferences for different uids within the same user
+ * (enforced by getUidListToBeAppliedForNetworkPreference).
+ * Clear all the existing preferences for the user before applying new preferences.
+ *
+ */
+ mProfileNetworkPreferences = mProfileNetworkPreferences.clearUser(
+ preferenceList.get(0).user);
for (final ProfileNetworkPreferenceList.Preference preference : preferenceList) {
validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
}
+
removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_PROFILE);
addPerAppDefaultNetworkRequests(
createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences));
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..3b0b3fd 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -36,6 +36,7 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.BpfMap;
import com.android.net.module.util.IBpfMap;
@@ -100,11 +101,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");
@@ -115,15 +116,21 @@
private final INetd mNetd;
@NonNull
private final Dependencies mDeps;
+ // IBpfMap objects {mIngressMap, mEgressMap} are initialized in #maybeStartBpf and closed in
+ // #maybeStopBpf.
@Nullable
- private final IBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap;
+ private IBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap = null;
@Nullable
- private final IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap;
+ private IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap = null;
@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.
*/
@@ -368,12 +375,35 @@
public ClatCoordinator(@NonNull Dependencies deps) {
mDeps = deps;
mNetd = mDeps.getNetd();
- mIngressMap = mDeps.getBpfIngress6Map();
- mEgressMap = mDeps.getBpfEgress4Map();
+ }
+
+ private void closeEgressMap() {
+ try {
+ mEgressMap.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot close egress4 map: " + e);
+ }
+ mEgressMap = null;
+ }
+
+ private void closeIngressMap() {
+ try {
+ mIngressMap.close();
+ } catch (Exception e) {
+ Log.e(TAG, "Cannot close ingress6 map: " + e);
+ }
+ mIngressMap = null;
}
private void maybeStartBpf(final ClatdTracker tracker) {
- if (mIngressMap == null || mEgressMap == null) return;
+ mEgressMap = mDeps.getBpfEgress4Map();
+ if (mEgressMap == null) return;
+
+ mIngressMap = mDeps.getBpfIngress6Map();
+ if (mIngressMap == null) {
+ closeEgressMap();
+ return;
+ }
final boolean isEthernet;
try {
@@ -717,6 +747,13 @@
} catch (ErrnoException | IllegalStateException e) {
Log.e(TAG, "Could not delete entry (" + rxKey + "): " + e);
}
+
+ // Manual close BPF map file descriptors. Just don't rely on that GC releasing to close
+ // the file descriptors even if class BpfMap supports close file descriptor in
+ // finalize(). If the interfaces are added and removed quickly, too many unclosed file
+ // descriptors may cause unexpected problem.
+ closeEgressMap();
+ closeIngressMap();
}
/**
@@ -738,6 +775,69 @@
mClatdTracker = null;
}
+ private void dumpBpfIngress(@NonNull IndentingPrintWriter pw) {
+ if (mIngressMap == null) {
+ pw.println("No BPF ingress6 map");
+ return;
+ }
+
+ try {
+ if (mIngressMap.isEmpty()) {
+ pw.println("<empty>");
+ }
+ pw.println("BPF ingress map: iif nat64Prefix v6Addr -> v4Addr oif");
+ pw.increaseIndent();
+ mIngressMap.forEach((k, v) -> {
+ // TODO: print interface name
+ pw.println(String.format("%d %s/96 %s -> %s %d", k.iif, k.pfx96, k.local6,
+ v.local4, v.oif));
+ });
+ pw.decreaseIndent();
+ } catch (ErrnoException e) {
+ pw.println("Error dumping BPF ingress6 map: " + e);
+ }
+ }
+
+ private void dumpBpfEgress(@NonNull IndentingPrintWriter pw) {
+ if (mEgressMap == null) {
+ pw.println("No BPF egress4 map");
+ return;
+ }
+
+ try {
+ if (mEgressMap.isEmpty()) {
+ pw.println("<empty>");
+ }
+ pw.println("BPF egress map: iif v4Addr -> v6Addr nat64Prefix oif");
+ pw.increaseIndent();
+ mEgressMap.forEach((k, v) -> {
+ // TODO: print interface name
+ pw.println(String.format("%d %s -> %s %s/96 %d %s", k.iif, k.local4, v.local6,
+ v.pfx96, v.oif, v.oifIsEthernet != 0 ? "ether" : "rawip"));
+ });
+ pw.decreaseIndent();
+ } catch (ErrnoException e) {
+ pw.println("Error dumping BPF egress4 map: " + e);
+ }
+ }
+
+ /**
+ * Dump the cordinator information. Only called when clat is started. See Nat464Xlat#dump.
+ *
+ * @param pw print writer.
+ */
+ public void dump(@NonNull IndentingPrintWriter pw) {
+ // TODO: dump ClatdTracker
+ // TODO: move map dump to a global place to avoid duplicate dump while there are two or
+ // more IPv6 only networks.
+ pw.println("Forwarding rules:");
+ pw.increaseIndent();
+ dumpBpfIngress(pw);
+ dumpBpfEgress(pw);
+ pw.decreaseIndent();
+ pw.println();
+ }
+
/**
* Get clatd tracker. For test only.
*/
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 de9dfe3..7829d1a 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
@@ -52,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");
@@ -61,7 +61,7 @@
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;
diff --git a/service/src/com/android/server/connectivity/Nat464Xlat.java b/service/src/com/android/server/connectivity/Nat464Xlat.java
index 7b06682..e8fc06d 100644
--- a/service/src/com/android/server/connectivity/Nat464Xlat.java
+++ b/service/src/com/android/server/connectivity/Nat464Xlat.java
@@ -36,9 +36,12 @@
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.NetworkStackConstants;
import com.android.server.ConnectivityService;
+import java.io.IOException;
import java.net.Inet6Address;
import java.util.Objects;
@@ -96,6 +99,7 @@
private String mIface;
private Inet6Address mIPv6Address;
private State mState = State.IDLE;
+ private ClatCoordinator mClatCoordinator;
private boolean mEnableClatOnCellular;
private boolean mPrefixDiscoveryRunning;
@@ -106,6 +110,7 @@
mNetd = netd;
mNetwork = nai;
mEnableClatOnCellular = deps.getCellular464XlatEnabled();
+ mClatCoordinator = deps.getClatCoordinator(mNetd);
}
/**
@@ -179,10 +184,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 +269,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;
@@ -506,6 +527,24 @@
mNetwork.handler().post(() -> handleInterfaceRemoved(iface));
}
+ /**
+ * Dump the NAT64 xlat information.
+ *
+ * @param pw print writer.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ if (SdkLevel.isAtLeastT()) {
+ if (isStarted()) {
+ pw.println("ClatCoordinator:");
+ pw.increaseIndent();
+ mClatCoordinator.dump(pw);
+ pw.decreaseIndent();
+ } else {
+ pw.println("<not start>");
+ }
+ }
+ }
+
@Override
public String toString() {
return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState;
diff --git a/service/src/com/android/server/connectivity/NetworkAgentInfo.java b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
index 1fc5a8f..323888a 100644
--- a/service/src/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/service/src/com/android/server/connectivity/NetworkAgentInfo.java
@@ -59,6 +59,7 @@
import android.util.Pair;
import android.util.SparseArray;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.WakeupMessage;
import com.android.modules.utils.build.SdkLevel;
import com.android.server.ConnectivityService;
@@ -1186,6 +1187,15 @@
}
/**
+ * Dump the NAT64 xlat information.
+ *
+ * @param pw print writer.
+ */
+ public void dumpNat464Xlat(IndentingPrintWriter pw) {
+ clatd.dump(pw);
+ }
+
+ /**
* Sets the most recent ConnectivityReport for this network.
*
* <p>This should only be called from the ConnectivityService thread.
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index 62b3add..8d99cb4 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<>();
@@ -419,7 +421,14 @@
if (appInfo == null) return false;
// Check whether package's uid is in allowed on restricted networks uid list. If so, this
// uid can have netd system permission.
- return mUidsAllowedOnRestrictedNetworks.contains(appInfo.uid);
+ return isUidAllowedOnRestrictedNetworks(appInfo.uid);
+ }
+
+ /**
+ * Returns whether the given uid is in allowed on restricted networks list.
+ */
+ public synchronized boolean isUidAllowedOnRestrictedNetworks(final int uid) {
+ return mUidsAllowedOnRestrictedNetworks.contains(uid);
}
@VisibleForTesting
@@ -545,17 +554,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 +663,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 +673,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 +997,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/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
index 71f342d..473a115 100644
--- a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
+++ b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
@@ -70,23 +70,33 @@
/**
* Returns a new object consisting of this object plus the passed preference.
*
- * If a preference already exists for the same user, it will be replaced by the passed
- * preference. Passing a Preference object containing a null capabilities object is equivalent
- * to (and indeed, implemented as) removing the preference for this user.
+ * It is not expected that unwanted preference already exists for the same user.
+ * All preferences for the user that were previously configured should be cleared before
+ * adding a new preference.
+ * Passing a Preference object containing a null capabilities object is equivalent
+ * to removing the preference for this user.
*/
public ProfileNetworkPreferenceList plus(@NonNull final Preference pref) {
- final ArrayList<Preference> newPrefs = new ArrayList<>();
- for (final Preference existingPref : preferences) {
- if (!existingPref.user.equals(pref.user)) {
- newPrefs.add(existingPref);
- }
- }
+ final ArrayList<Preference> newPrefs = new ArrayList<>(preferences);
if (null != pref.capabilities) {
newPrefs.add(pref);
}
return new ProfileNetworkPreferenceList(newPrefs);
}
+ /**
+ * Remove all preferences corresponding to a user.
+ */
+ public ProfileNetworkPreferenceList clearUser(UserHandle user) {
+ final ArrayList<Preference> newPrefs = new ArrayList<>();
+ for (final Preference existingPref : preferences) {
+ if (!existingPref.user.equals(user)) {
+ newPrefs.add(existingPref);
+ }
+ }
+ return new ProfileNetworkPreferenceList(newPrefs);
+ }
+
public boolean isEmpty() {
return preferences.isEmpty();
}
diff --git a/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java b/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java
index 534dbe7..e682026 100644
--- a/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java
+++ b/service/src/com/android/server/connectivity/QosCallbackAgentConnection.java
@@ -30,6 +30,8 @@
import android.telephony.data.NrQosSessionAttributes;
import android.util.Log;
+import com.android.modules.utils.build.SdkLevel;
+
import java.util.Objects;
/**
@@ -149,6 +151,7 @@
void sendEventEpsQosSessionAvailable(final QosSession session,
final EpsBearerQosSessionAttributes attributes) {
+ if (!validateOrSendErrorAndUnregister()) return;
try {
if (DBG) log("sendEventEpsQosSessionAvailable: sending...");
mCallback.onQosEpsBearerSessionAvailable(session, attributes);
@@ -159,6 +162,7 @@
void sendEventNrQosSessionAvailable(final QosSession session,
final NrQosSessionAttributes attributes) {
+ if (!validateOrSendErrorAndUnregister()) return;
try {
if (DBG) log("sendEventNrQosSessionAvailable: sending...");
mCallback.onNrQosSessionAvailable(session, attributes);
@@ -168,6 +172,7 @@
}
void sendEventQosSessionLost(@NonNull final QosSession session) {
+ if (!validateOrSendErrorAndUnregister()) return;
try {
if (DBG) log("sendEventQosSessionLost: sending...");
mCallback.onQosSessionLost(session);
@@ -185,6 +190,21 @@
}
}
+ private boolean validateOrSendErrorAndUnregister() {
+ final int exceptionType = mFilter.validate();
+ if (exceptionType != EX_TYPE_FILTER_NONE) {
+ log("validation fail before sending QosCallback.");
+ // Error callback is returned from Android T to prevent any disruption of application
+ // running on Android S.
+ if (SdkLevel.isAtLeastT()) {
+ sendEventQosCallbackError(exceptionType);
+ mQosCallbackTracker.unregisterCallback(mCallback);
+ }
+ return false;
+ }
+ return true;
+ }
+
private static void log(@NonNull final String msg) {
Log.d(TAG, msg);
}
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/EthernetNetworkManagementExceptionTest.java b/tests/common/java/android/net/EthernetNetworkManagementExceptionTest.java
new file mode 100644
index 0000000..84b6e54
--- /dev/null
+++ b/tests/common/java/android/net/EthernetNetworkManagementExceptionTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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 static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
+
+import static org.junit.Assert.assertEquals;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+public class EthernetNetworkManagementExceptionTest {
+ private static final String ERROR_MESSAGE = "Test error message";
+
+ @Test
+ public void testEthernetNetworkManagementExceptionParcelable() {
+ final EthernetNetworkManagementException e =
+ new EthernetNetworkManagementException(ERROR_MESSAGE);
+
+ assertParcelingIsLossless(e);
+ }
+
+ @Test
+ public void testEthernetNetworkManagementExceptionHasExpectedErrorMessage() {
+ final EthernetNetworkManagementException e =
+ new EthernetNetworkManagementException(ERROR_MESSAGE);
+
+ assertEquals(ERROR_MESSAGE, e.getMessage());
+ }
+}
diff --git a/tests/common/java/android/net/LinkPropertiesTest.java b/tests/common/java/android/net/LinkPropertiesTest.java
index 8fc636a..345a78d 100644
--- a/tests/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/common/java/android/net/LinkPropertiesTest.java
@@ -1261,6 +1261,17 @@
assertFalse(lp.hasIpv4UnreachableDefaultRoute());
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testHasExcludeRoute() {
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("VPN");
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV4, 2), RTN_UNICAST));
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 0), RTN_UNICAST));
+ assertFalse(lp.hasExcludeRoute());
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 2), RTN_THROW));
+ assertTrue(lp.hasExcludeRoute());
+ }
+
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
@EnableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
public void testRouteAddWithSameKey() throws Exception {
diff --git a/tests/common/java/android/net/netstats/NetworkStatsCollectionTest.kt b/tests/common/java/android/net/netstats/NetworkStatsCollectionTest.kt
new file mode 100644
index 0000000..368a519
--- /dev/null
+++ b/tests/common/java/android/net/netstats/NetworkStatsCollectionTest.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.netstats
+
+import android.net.NetworkIdentity
+import android.net.NetworkStatsCollection
+import android.net.NetworkStatsHistory
+import androidx.test.filters.SmallTest
+import com.android.testutils.ConnectivityModuleTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.SC_V2
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+import kotlin.test.fail
+
+@ConnectivityModuleTest
+@RunWith(JUnit4::class)
+@SmallTest
+class NetworkStatsCollectionTest {
+ @Rule
+ @JvmField
+ val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = SC_V2)
+
+ @Test
+ fun testBuilder() {
+ val ident = setOf<NetworkIdentity>()
+ val key1 = NetworkStatsCollection.Key(ident, /* uid */ 0, /* set */ 0, /* tag */ 0)
+ val key2 = NetworkStatsCollection.Key(ident, /* uid */ 1, /* set */ 0, /* tag */ 0)
+ val bucketDuration = 10L
+ val entry1 = NetworkStatsHistory.Entry(10, 10, 40, 4, 50, 5, 60)
+ val entry2 = NetworkStatsHistory.Entry(30, 10, 3, 41, 7, 1, 0)
+ val history1 = NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry1)
+ .addEntry(entry2)
+ .build()
+ val history2 = NetworkStatsHistory(10, 5)
+ val actualCollection = NetworkStatsCollection.Builder(bucketDuration)
+ .addEntry(key1, history1)
+ .addEntry(key2, history2)
+ .build()
+
+ // The builder will omit any entry with empty history. Thus, only history1
+ // is expected in the result collection.
+ val actualEntries = actualCollection.entries
+ assertEquals(1, actualEntries.size)
+ val actualHistory = actualEntries[key1] ?: fail("There should be an entry for $key1")
+ assertEquals(history1.entries, actualHistory.entries)
+ }
+}
diff --git a/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt b/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt
new file mode 100644
index 0000000..c2654c5
--- /dev/null
+++ b/tests/common/java/android/net/netstats/NetworkStatsHistoryTest.kt
@@ -0,0 +1,68 @@
+/*
+ * 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.netstats
+
+import android.net.NetworkStatsHistory
+import android.text.format.DateUtils
+import androidx.test.filters.SmallTest
+import com.android.testutils.ConnectivityModuleTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.SC_V2
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+
+@ConnectivityModuleTest
+@RunWith(JUnit4::class)
+@SmallTest
+class NetworkStatsHistoryTest {
+ @Rule
+ @JvmField
+ val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = SC_V2)
+
+ @Test
+ fun testBuilder() {
+ val entry1 = NetworkStatsHistory.Entry(10, 30, 40, 4, 50, 5, 60)
+ val entry2 = NetworkStatsHistory.Entry(30, 15, 3, 41, 7, 1, 0)
+ val entry3 = NetworkStatsHistory.Entry(7, 301, 11, 14, 31, 2, 80)
+ val statsEmpty = NetworkStatsHistory
+ .Builder(DateUtils.HOUR_IN_MILLIS, /* initialCapacity */ 10).build()
+ assertEquals(0, statsEmpty.entries.size)
+ assertEquals(DateUtils.HOUR_IN_MILLIS, statsEmpty.bucketDuration)
+ val statsSingle = NetworkStatsHistory
+ .Builder(DateUtils.HOUR_IN_MILLIS, /* initialCapacity */ 8)
+ .addEntry(entry1)
+ .build()
+ statsSingle.assertEntriesEqual(entry1)
+ assertEquals(DateUtils.HOUR_IN_MILLIS, statsSingle.bucketDuration)
+ val statsMultiple = NetworkStatsHistory
+ .Builder(DateUtils.SECOND_IN_MILLIS, /* initialCapacity */ 0)
+ .addEntry(entry1).addEntry(entry2).addEntry(entry3)
+ .build()
+ assertEquals(DateUtils.SECOND_IN_MILLIS, statsMultiple.bucketDuration)
+ statsMultiple.assertEntriesEqual(entry1, entry2, entry3)
+ }
+
+ fun NetworkStatsHistory.assertEntriesEqual(vararg entries: NetworkStatsHistory.Entry) {
+ assertEquals(entries.size, this.entries.size)
+ entries.forEachIndexed { i, element ->
+ assertEquals(element, this.entries[i])
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/common/java/android/net/netstats/NetworkTemplateTest.kt b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt
new file mode 100644
index 0000000..192694b
--- /dev/null
+++ b/tests/common/java/android/net/netstats/NetworkTemplateTest.kt
@@ -0,0 +1,205 @@
+/*
+ * 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.netstats
+
+import android.net.NetworkStats.DEFAULT_NETWORK_ALL
+import android.net.NetworkStats.METERED_ALL
+import android.net.NetworkStats.METERED_YES
+import android.net.NetworkStats.ROAMING_YES
+import android.net.NetworkStats.ROAMING_ALL
+import android.net.NetworkTemplate
+import android.net.NetworkTemplate.MATCH_BLUETOOTH
+import android.net.NetworkTemplate.MATCH_CARRIER
+import android.net.NetworkTemplate.MATCH_ETHERNET
+import android.net.NetworkTemplate.MATCH_MOBILE
+import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD
+import android.net.NetworkTemplate.MATCH_PROXY
+import android.net.NetworkTemplate.MATCH_WIFI
+import android.net.NetworkTemplate.MATCH_WIFI_WILDCARD
+import android.net.NetworkTemplate.NETWORK_TYPE_ALL
+import android.net.NetworkTemplate.OEM_MANAGED_ALL
+import android.telephony.TelephonyManager
+import com.android.net.module.util.NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL
+import com.android.net.module.util.NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT
+import com.android.testutils.ConnectivityModuleTest
+import com.android.testutils.DevSdkIgnoreRule
+import com.android.testutils.SC_V2
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
+
+private const val TEST_IMSI1 = "imsi"
+private const val TEST_WIFI_KEY1 = "wifiKey1"
+private const val TEST_WIFI_KEY2 = "wifiKey2"
+
+@RunWith(JUnit4::class)
+@ConnectivityModuleTest
+class NetworkTemplateTest {
+ @Rule
+ @JvmField
+ val ignoreRule = DevSdkIgnoreRule(ignoreClassUpTo = SC_V2)
+
+ @Test
+ fun testBuilderMatchRules() {
+ // Verify unknown match rules cannot construct templates.
+ listOf(Integer.MIN_VALUE, -1, Integer.MAX_VALUE).forEach {
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(it).build()
+ }
+ }
+
+ // Verify hidden match rules cannot construct templates.
+ listOf(MATCH_WIFI_WILDCARD, MATCH_MOBILE_WILDCARD, MATCH_PROXY).forEach {
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(it).build()
+ }
+ }
+
+ // Verify template which matches metered cellular and carrier networks with
+ // the given IMSI. See buildTemplateMobileAll and buildTemplateCarrierMetered.
+ listOf(MATCH_MOBILE, MATCH_CARRIER).forEach { matchRule ->
+ NetworkTemplate.Builder(matchRule).setSubscriberIds(setOf(TEST_IMSI1))
+ .setMeteredness(METERED_YES).build().let {
+ val expectedTemplate = NetworkTemplate(matchRule, TEST_IMSI1,
+ arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ assertEquals(expectedTemplate, it)
+ }
+ }
+
+ // Verify template which matches roaming cellular and carrier networks with
+ // the given IMSI.
+ listOf(MATCH_MOBILE, MATCH_CARRIER).forEach { matchRule ->
+ NetworkTemplate.Builder(matchRule).setSubscriberIds(setOf(TEST_IMSI1))
+ .setRoaming(ROAMING_YES).setMeteredness(METERED_YES).build().let {
+ val expectedTemplate = NetworkTemplate(matchRule, TEST_IMSI1,
+ arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
+ ROAMING_YES, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ assertEquals(expectedTemplate, it)
+ }
+ }
+
+ // Verify carrier template cannot be created without IMSI.
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(MATCH_CARRIER).build()
+ }
+
+ // Verify template which matches metered cellular networks,
+ // regardless of IMSI. See buildTemplateMobileWildcard.
+ NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_MOBILE_WILDCARD, null /*subscriberId*/,
+ null /*subscriberIds*/, arrayOf<String>(),
+ METERED_YES, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches metered cellular networks and ratType.
+ // See NetworkTemplate#buildTemplateMobileWithRatType.
+ NetworkTemplate.Builder(MATCH_MOBILE).setSubscriberIds(setOf(TEST_IMSI1))
+ .setMeteredness(METERED_YES).setRatType(TelephonyManager.NETWORK_TYPE_UMTS)
+ .build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1,
+ arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_UMTS,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches all wifi networks,
+ // regardless of Wifi Network Key. See buildTemplateWifiWildcard and buildTemplateWifi.
+ NetworkTemplate.Builder(MATCH_WIFI).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_WIFI_WILDCARD, null /*subscriberId*/,
+ null /*subscriberIds*/, arrayOf<String>(),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches wifi networks with the given Wifi Network Key.
+ // See buildTemplateWifi(wifiNetworkKey).
+ NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_WIFI, null /*subscriberId*/,
+ null /*subscriberIds*/, arrayOf(TEST_WIFI_KEY1),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches all wifi networks with the
+ // given Wifi Network Key, and IMSI. See buildTemplateWifi(wifiNetworkKey, subscriberId).
+ NetworkTemplate.Builder(MATCH_WIFI).setSubscriberIds(setOf(TEST_IMSI1))
+ .setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_WIFI, TEST_IMSI1,
+ arrayOf(TEST_IMSI1), arrayOf(TEST_WIFI_KEY1),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches ethernet and bluetooth networks.
+ // See buildTemplateEthernet and buildTemplateBluetooth.
+ listOf(MATCH_ETHERNET, MATCH_BLUETOOTH).forEach { matchRule ->
+ NetworkTemplate.Builder(matchRule).build().let {
+ val expectedTemplate = NetworkTemplate(matchRule, null /*subscriberId*/,
+ null /*subscriberIds*/, arrayOf<String>(),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
+ }
+ }
+
+ @Test
+ fun testBuilderWifiNetworkKeys() {
+ // Verify template builder which generates same template with the given different
+ // sequence keys.
+ NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(
+ setOf(TEST_WIFI_KEY1, TEST_WIFI_KEY2)).build().let {
+ val expectedTemplate = NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(
+ setOf(TEST_WIFI_KEY2, TEST_WIFI_KEY1)).build()
+ assertEquals(expectedTemplate, it)
+ }
+
+ // Verify template which matches non-wifi networks with the given key is invalid.
+ listOf(MATCH_MOBILE, MATCH_CARRIER, MATCH_ETHERNET, MATCH_BLUETOOTH, -1,
+ Integer.MAX_VALUE).forEach { matchRule ->
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(matchRule).setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build()
+ }
+ }
+
+ // Verify template which matches wifi networks with the given null key is invalid.
+ assertFailsWith<IllegalArgumentException> {
+ NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf(null)).build()
+ }
+
+ // Verify template which matches wifi wildcard with the given empty key set.
+ NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf<String>()).build().let {
+ val expectedTemplate = NetworkTemplate(MATCH_WIFI_WILDCARD, null /*subscriberId*/,
+ arrayOf<String>() /*subscriberIds*/, arrayOf<String>(),
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
+ assertEquals(expectedTemplate, it)
+ }
+ }
+}
diff --git a/tests/cts/hostside/Android.bp b/tests/cts/hostside/Android.bp
index b684068..ac84e57 100644
--- a/tests/cts/hostside/Android.bp
+++ b/tests/cts/hostside/Android.bp
@@ -26,6 +26,7 @@
"tradefed",
],
static_libs: [
+ "CompatChangeGatingTestBase",
"modules-utils-build-testing",
],
// Tag this module as a cts test artifact
@@ -34,4 +35,12 @@
"general-tests",
"sts"
],
+ data: [
+ ":CtsHostsideNetworkTestsApp",
+ ":CtsHostsideNetworkTestsApp2",
+ ":CtsHostsideNetworkTestsApp3",
+ ":CtsHostsideNetworkTestsApp3PreT",
+ ":CtsHostsideNetworkTestsAppNext",
+ ],
+ per_testcase_directory: true,
}
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/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index 96ce65f..524bd65 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
@@ -898,7 +898,7 @@
final Intent intent = new Intent();
if (type == TYPE_COMPONENT_ACTIVTIY) {
intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS))
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
} else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS))
.setFlags(1);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
new file mode 100644
index 0000000..098f295
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/ConnOnActivityStartTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getUiDevice;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class ConnOnActivityStartTest extends AbstractRestrictBackgroundNetworkTestCase {
+ private static final int TEST_ITERATION_COUNT = 5;
+
+ @Before
+ public final void setUp() throws Exception {
+ super.setUp();
+ resetDeviceState();
+ }
+
+ @After
+ public final void tearDown() throws Exception {
+ super.tearDown();
+ resetDeviceState();
+ }
+
+ private void resetDeviceState() throws Exception {
+ resetBatteryState();
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+
+
+ @Test
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ public void testStartActivity_batterySaver() throws Exception {
+ setBatterySaverMode(true);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_batterySaver");
+ }
+
+ @Test
+ @RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+ public void testStartActivity_dataSaver() throws Exception {
+ setRestrictBackground(true);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_dataSaver");
+ }
+
+ @Test
+ @RequiredProperties({DOZE_MODE})
+ public void testStartActivity_doze() throws Exception {
+ setDozeMode(true);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_doze");
+ }
+
+ @Test
+ @RequiredProperties({APP_STANDBY_MODE})
+ public void testStartActivity_appStandby() throws Exception {
+ turnBatteryOn();
+ setAppIdle(true);
+ assertLaunchedActivityHasNetworkAccess("testStartActivity_appStandby");
+ }
+
+ private void assertLaunchedActivityHasNetworkAccess(String testName) throws Exception {
+ for (int i = 0; i < TEST_ITERATION_COUNT; ++i) {
+ Log.i(TAG, testName + " start #" + i);
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
+ getUiDevice().pressHome();
+ assertBackgroundState();
+ Log.i(TAG, testName + " end #" + i);
+ }
+ }
+}
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 56be3e3..c53276b 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
@@ -57,6 +57,7 @@
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
import com.android.compatibility.common.util.AppStandbyUtils;
import com.android.compatibility.common.util.BatteryUtils;
@@ -438,6 +439,10 @@
return InstrumentationRegistry.getInstrumentation();
}
+ public static UiDevice getUiDevice() {
+ return UiDevice.getInstance(getInstrumentation());
+ }
+
// When power saver mode or restrict background enabled or adding any white/black list into
// those modes, NetworkPolicy may need to take some time to update the rules of uids. So having
// this function and using PollingCheck to try to make sure the uid has updated and reduce the
diff --git a/tests/cts/hostside/app2/Android.bp b/tests/cts/hostside/app2/Android.bp
index 01c8cd2..edfaf9f 100644
--- a/tests/cts/hostside/app2/Android.bp
+++ b/tests/cts/hostside/app2/Android.bp
@@ -23,6 +23,7 @@
defaults: ["cts_support_defaults"],
sdk_version: "test_current",
static_libs: [
+ "androidx.annotation_annotation",
"CtsHostsideNetworkTestsAidl",
"NetworkStackApiStableShims",
],
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
index 9fdb9c9..51acfdf 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
@@ -30,6 +30,8 @@
import android.os.RemoteException;
import android.util.Log;
+import androidx.annotation.GuardedBy;
+
import com.android.cts.net.hostside.INetworkStateObserver;
/**
@@ -37,27 +39,22 @@
*/
public class MyActivity extends Activity {
+ @GuardedBy("this")
private BroadcastReceiver finishCommandReceiver = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "MyActivity.onCreate()");
- Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
- finishCommandReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- Log.d(TAG, "Finishing MyActivity");
- MyActivity.this.finish();
- }
- };
- registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY));
}
@Override
public void finish() {
- if (finishCommandReceiver != null) {
- unregisterReceiver(finishCommandReceiver);
+ synchronized (this) {
+ if (finishCommandReceiver != null) {
+ unregisterReceiver(finishCommandReceiver);
+ finishCommandReceiver = null;
+ }
}
super.finish();
}
@@ -69,6 +66,31 @@
}
@Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ Log.d(TAG, "MyActivity.onNewIntent()");
+ setIntent(intent);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.d(TAG, "MyActivity.onResume(): " + getIntent());
+ Common.notifyNetworkStateObserver(this, getIntent(), TYPE_COMPONENT_ACTIVTY);
+ synchronized (this) {
+ finishCommandReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Finishing MyActivity");
+ MyActivity.this.finish();
+ }
+ };
+ registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY),
+ Context.RECEIVER_EXPORTED);
+ }
+ }
+
+ @Override
protected void onDestroy() {
Log.d(TAG, "MyActivity.onDestroy()");
super.onDestroy();
diff --git a/tests/cts/hostside/app3/Android.bp b/tests/cts/hostside/app3/Android.bp
new file mode 100644
index 0000000..141cf03
--- /dev/null
+++ b/tests/cts/hostside/app3/Android.bp
@@ -0,0 +1,54 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_defaults {
+ name: "CtsHostsideNetworkTestsApp3Defaults",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "junit",
+ ],
+ static_libs: [
+ "ctstestrunner-axt",
+ "truth-prebuilt",
+ ],
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkTestsApp3",
+ defaults: [
+ "cts_support_defaults",
+ "CtsHostsideNetworkTestsApp3Defaults",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkTestsApp3PreT",
+ target_sdk_version: "31",
+ defaults: [
+ "cts_support_defaults",
+ "CtsHostsideNetworkTestsApp3Defaults",
+ ],
+}
diff --git a/tests/cts/hostside/app3/AndroidManifest.xml b/tests/cts/hostside/app3/AndroidManifest.xml
new file mode 100644
index 0000000..eabcacb
--- /dev/null
+++ b/tests/cts/hostside/app3/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.net.hostside.app3">
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.net.hostside.app3" />
+
+</manifest>
diff --git a/tests/cts/hostside/app3/src/com/android/cts/net/hostside/app3/ExcludedRoutesGatingTest.java b/tests/cts/hostside/app3/src/com/android/cts/net/hostside/app3/ExcludedRoutesGatingTest.java
new file mode 100644
index 0000000..a1a8209
--- /dev/null
+++ b/tests/cts/hostside/app3/src/com/android/cts/net/hostside/app3/ExcludedRoutesGatingTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside.app3;
+
+import static org.junit.Assert.assertEquals;
+
+import android.Manifest;
+import android.net.IpPrefix;
+import android.net.LinkProperties;
+import android.net.RouteInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests to verify {@link LinkProperties#getRoutes} behavior, depending on
+ * {@LinkProperties#EXCLUDED_ROUTES} change state.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExcludedRoutesGatingTest {
+ @Before
+ public void setUp() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+ }
+
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testExcludedRoutesChangeEnabled() {
+ final LinkProperties lp = makeLinkPropertiesWithExcludedRoutes();
+
+ // Excluded routes change is enabled: non-RTN_UNICAST routes are visible.
+ assertEquals(2, lp.getRoutes().size());
+ assertEquals(2, lp.getAllRoutes().size());
+ }
+
+ @Test
+ public void testExcludedRoutesChangeDisabled() {
+ final LinkProperties lp = makeLinkPropertiesWithExcludedRoutes();
+
+ // Excluded routes change is disabled: non-RTN_UNICAST routes are filtered out.
+ assertEquals(0, lp.getRoutes().size());
+ assertEquals(0, lp.getAllRoutes().size());
+ }
+
+ private LinkProperties makeLinkPropertiesWithExcludedRoutes() {
+ final LinkProperties lp = new LinkProperties();
+
+ lp.addRoute(new RouteInfo(new IpPrefix("10.0.0.0/8"), null, null, RouteInfo.RTN_THROW));
+ lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"), null, null,
+ RouteInfo.RTN_UNREACHABLE));
+
+ return lp;
+ }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
new file mode 100644
index 0000000..3387fd7
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideConnOnActivityStartTest.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+public class HostsideConnOnActivityStartTest extends HostsideNetworkTestCase {
+ private static final String TEST_CLASS = TEST_PKG + ".ConnOnActivityStartTest";
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ uninstallPackage(TEST_APP2_PKG, false);
+ installPackage(TEST_APP2_APK);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ uninstallPackage(TEST_APP2_PKG, true);
+ }
+
+ public void testStartActivity_batterySaver() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_batterySaver");
+ }
+
+ public void testStartActivity_dataSaver() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_dataSaver");
+ }
+
+ public void testStartActivity_doze() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_doze");
+ }
+
+ public void testStartActivity_appStandby() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testStartActivity_appStandby");
+ }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java
new file mode 100644
index 0000000..b65fb6b
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java
@@ -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.
+ */
+
+package com.android.cts.net;
+
+import android.compat.cts.CompatChangeGatingTestCase;
+
+import java.util.Set;
+
+/**
+ * Tests for the {@link android.net.LinkProperties#EXCLUDED_ROUTES} compatibility change.
+ */
+public class HostsideLinkPropertiesGatingTests extends CompatChangeGatingTestCase {
+ private static final String TEST_APK = "CtsHostsideNetworkTestsApp3.apk";
+ private static final String TEST_APK_PRE_T = "CtsHostsideNetworkTestsApp3PreT.apk";
+ private static final String TEST_PKG = "com.android.cts.net.hostside.app3";
+ private static final String TEST_CLASS = ".ExcludedRoutesGatingTest";
+
+ private static final long EXCLUDED_ROUTES_CHANGE_ID = 186082280;
+
+ protected void tearDown() throws Exception {
+ uninstallPackage(TEST_PKG, true);
+ }
+
+ public void testExcludedRoutesChangeEnabled() throws Exception {
+ installPackage(TEST_APK, true);
+ runDeviceCompatTest("testExcludedRoutesChangeEnabled");
+ }
+
+ public void testExcludedRoutesChangeDisabledPreT() throws Exception {
+ installPackage(TEST_APK_PRE_T, true);
+ runDeviceCompatTest("testExcludedRoutesChangeDisabled");
+ }
+
+ public void testExcludedRoutesChangeDisabledByOverride() throws Exception {
+ installPackage(TEST_APK, true);
+ runDeviceCompatTestWithChangeDisabled("testExcludedRoutesChangeDisabled");
+ }
+
+ public void testExcludedRoutesChangeEnabledByOverridePreT() throws Exception {
+ installPackage(TEST_APK_PRE_T, true);
+ runDeviceCompatTestWithChangeEnabled("testExcludedRoutesChangeEnabled");
+ }
+
+ private void runDeviceCompatTest(String methodName) throws Exception {
+ runDeviceCompatTest(TEST_PKG, TEST_CLASS, methodName, Set.of(), Set.of());
+ }
+
+ private void runDeviceCompatTestWithChangeEnabled(String methodName) throws Exception {
+ runDeviceCompatTest(TEST_PKG, TEST_CLASS, methodName, Set.of(EXCLUDED_ROUTES_CHANGE_ID),
+ Set.of());
+ }
+
+ private void runDeviceCompatTestWithChangeDisabled(String methodName) throws Exception {
+ runDeviceCompatTest(TEST_PKG, TEST_CLASS, methodName, Set.of(),
+ Set.of(EXCLUDED_ROUTES_CHANGE_ID));
+ }
+}
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 48a1c79..33f3af5 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -38,4 +38,20 @@
<option name="hidden-api-checks" value="false" />
<option name="isolated-storage" value="false" />
</test>
+ <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+ one of the Mainline modules below is present on the device used for testing. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <!-- Tethering Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.tethering" />
+ <!-- Tethering Module (AOSP version). -->
+ <option name="mainline-module-package-name" value="com.android.tethering" />
+ <!-- NetworkStack Module (internal version). Should always be installed with CaptivePortalLogin. -->
+ <option name="mainline-module-package-name" value="com.google.android.networkstack" />
+ <!-- NetworkStack Module (AOSP version). Should always be installed with CaptivePortalLogin. -->
+ <option name="mainline-module-package-name" value="com.android.networkstack" />
+ <!-- Resolver Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.resolv" />
+ <!-- Resolver Module (AOSP version). -->
+ <option name="mainline-module-package-name" value="com.android.resolv" />
+ </object>
</configuration>
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index a129108..8f6786f 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -52,6 +52,7 @@
import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
+import static android.net.ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -164,7 +165,6 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
-import android.util.Pair;
import android.util.Range;
import androidx.test.InstrumentationRegistry;
@@ -182,6 +182,7 @@
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRuleKt;
+import com.android.testutils.DeviceInfoUtils;
import com.android.testutils.DumpTestUtils;
import com.android.testutils.RecorderCallback.CallbackEntry;
import com.android.testutils.TestHttpServer;
@@ -1605,51 +1606,7 @@
private static boolean isTcpKeepaliveSupportedByKernel() {
final String kVersionString = VintfRuntimeInfo.getKernelVersion();
- return compareMajorMinorVersion(kVersionString, "4.8") >= 0;
- }
-
- private static Pair<Integer, Integer> getVersionFromString(String version) {
- // Only gets major and minor number of the version string.
- final Pattern versionPattern = Pattern.compile("^(\\d+)(\\.(\\d+))?.*");
- final Matcher m = versionPattern.matcher(version);
- if (m.matches()) {
- final int major = Integer.parseInt(m.group(1));
- final int minor = TextUtils.isEmpty(m.group(3)) ? 0 : Integer.parseInt(m.group(3));
- return new Pair<>(major, minor);
- } else {
- return new Pair<>(0, 0);
- }
- }
-
- // TODO: Move to util class.
- private static int compareMajorMinorVersion(final String s1, final String s2) {
- final Pair<Integer, Integer> v1 = getVersionFromString(s1);
- final Pair<Integer, Integer> v2 = getVersionFromString(s2);
-
- if (v1.first == v2.first) {
- return Integer.compare(v1.second, v2.second);
- } else {
- return Integer.compare(v1.first, v2.first);
- }
- }
-
- /**
- * Verifies that version string compare logic returns expected result for various cases.
- * Note that only major and minor number are compared.
- */
- @Test
- public void testMajorMinorVersionCompare() {
- assertEquals(0, compareMajorMinorVersion("4.8.1", "4.8"));
- assertEquals(1, compareMajorMinorVersion("4.9", "4.8.1"));
- assertEquals(1, compareMajorMinorVersion("5.0", "4.8"));
- assertEquals(1, compareMajorMinorVersion("5", "4.8"));
- assertEquals(0, compareMajorMinorVersion("5", "5.0"));
- assertEquals(1, compareMajorMinorVersion("5-beta1", "4.8"));
- assertEquals(0, compareMajorMinorVersion("4.8.0.0", "4.8"));
- assertEquals(0, compareMajorMinorVersion("4.8-RC1", "4.8"));
- assertEquals(0, compareMajorMinorVersion("4.8", "4.8"));
- assertEquals(-1, compareMajorMinorVersion("3.10", "4.8.0"));
- assertEquals(-1, compareMajorMinorVersion("4.7.10.10", "4.8"));
+ return DeviceInfoUtils.compareMajorMinorVersion(kVersionString, "4.8") >= 0;
}
/**
@@ -3212,7 +3169,7 @@
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
@Test
public void testUidsAllowedOnRestrictedNetworks() throws Exception {
- assumeTrue(TestUtils.shouldTestSApis());
+ assumeTestSApis();
// TODO (b/175199465): figure out a reasonable permission check for
// setUidsAllowedOnRestrictedNetworks that allows tests but not system-external callers.
@@ -3225,10 +3182,10 @@
// because it has been just installed to device. In case the uid is existed in setting
// mistakenly, try to remove the uid and set correct uids to setting.
originalUidsAllowedOnRestrictedNetworks.remove(uid);
- runWithShellPermissionIdentity(() ->
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
- mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
+ runWithShellPermissionIdentity(() -> setUidsAllowedOnRestrictedNetworks(
+ mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
+ // File a restricted network request with permission first to hold the connection.
final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
@@ -3240,6 +3197,19 @@
runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ // File another restricted network request without permission.
+ final TestableNetworkCallback restrictedNetworkCb = new TestableNetworkCallback();
+ final NetworkRequest restrictedRequest = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
+ .build();
+ // Uid is not in allowed list and no permissions. Expect that SecurityException will throw.
+ assertThrows(SecurityException.class,
+ () -> mCm.requestNetwork(restrictedRequest, restrictedNetworkCb));
+
final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
final Network network = agent.getNetwork();
@@ -3259,19 +3229,26 @@
final Set<Integer> newUidsAllowedOnRestrictedNetworks =
new ArraySet<>(originalUidsAllowedOnRestrictedNetworks);
newUidsAllowedOnRestrictedNetworks.add(uid);
- runWithShellPermissionIdentity(() ->
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
- mContext, newUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
+ runWithShellPermissionIdentity(() -> setUidsAllowedOnRestrictedNetworks(
+ mContext, newUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
// Wait a while for sending allowed uids on the restricted network to netd.
- // TODD: Have a significant signal to know the uids has been send to netd.
+ // TODD: Have a significant signal to know the uids has been sent to netd.
assertBindSocketToNetworkSuccess(network);
+
+ // Uid is in allowed list. Try file network request again.
+ requestNetwork(restrictedRequest, restrictedNetworkCb);
+ // Verify that the network is restricted.
+ restrictedNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> network.equals(entry.getNetwork())
+ && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
} finally {
agent.unregister();
// Restore setting.
- runWithShellPermissionIdentity(() ->
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
- mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
+ runWithShellPermissionIdentity(() -> setUidsAllowedOnRestrictedNetworks(
+ mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
}
}
@@ -3295,6 +3272,12 @@
assertTrue(dumpOutput, dumpOutput.contains("BPF map content"));
}
+ private void assumeTestSApis() {
+ // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
+ // shims, and @IgnoreUpTo does not check that.
+ assumeTrue(TestUtils.shouldTestSApis());
+ }
+
private void unregisterRegisteredCallbacks() {
for (NetworkCallback callback: mRegisteredCallbacks) {
mCm.unregisterNetworkCallback(callback);
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
index c6fc38f..0c53411 100644
--- a/tests/cts/net/src/android/net/cts/DnsResolverTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -49,6 +49,7 @@
import android.os.Handler;
import android.os.Looper;
import android.platform.test.annotations.AppModeFull;
+import android.provider.Settings;
import android.system.ErrnoException;
import android.util.Log;
@@ -727,6 +728,18 @@
@Test
public void testPrivateDnsBypass() throws InterruptedException {
+ final String dataStallSetting = Settings.Global.getString(mCR,
+ Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK);
+ Settings.Global.putInt(mCR, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 0);
+ try {
+ doTestPrivateDnsBypass();
+ } finally {
+ Settings.Global.putString(mCR, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK,
+ dataStallSetting);
+ }
+ }
+
+ private void doTestPrivateDnsBypass() throws InterruptedException {
final Network[] testNetworks = getTestableNetworks();
// Set an invalid private DNS server
diff --git a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
index 30e0015..04434e5 100644
--- a/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/EthernetManagerTest.kt
@@ -75,7 +75,7 @@
private val em by lazy { EthernetManagerShimImpl.newInstance(context) }
private val createdIfaces = ArrayList<EthernetTestInterface>()
- private val addedListeners = ArrayList<InterfaceStateListener>()
+ private val addedListeners = ArrayList<EthernetStateListener>()
private class EthernetTestInterface(
context: Context,
@@ -171,13 +171,16 @@
}
}
- private fun addInterfaceStateListener(executor: Executor, listener: InterfaceStateListener) {
+ private fun addInterfaceStateListener(executor: Executor, listener: EthernetStateListener) {
em.addInterfaceStateListener(executor, listener)
addedListeners.add(listener)
}
private fun createInterface(): EthernetTestInterface {
- return EthernetTestInterface(context, Handler(Looper.getMainLooper()))
+ return EthernetTestInterface(
+ context,
+ Handler(Looper.getMainLooper())
+ ).also { createdIfaces.add(it) }
}
private fun setIncludeTestInterfaces(value: Boolean) {
@@ -209,15 +212,25 @@
listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT)
listener.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT)
+ // Register a new listener, it should see state of all existing interfaces immediately.
+ val listener2 = EthernetStateListener()
+ addInterfaceStateListener(executor, listener2)
+ listener2.expectCallback(iface, STATE_LINK_UP, ROLE_CLIENT)
+ listener2.expectCallback(iface2, STATE_LINK_UP, ROLE_CLIENT)
+
// Removing interfaces first sends link down, then STATE_ABSENT/ROLE_NONE.
removeInterface(iface)
- listener.expectCallback(iface, STATE_LINK_DOWN, ROLE_CLIENT)
- listener.expectCallback(iface, STATE_ABSENT, ROLE_NONE)
+ for (listener in addedListeners) {
+ listener.expectCallback(iface, STATE_LINK_DOWN, ROLE_CLIENT)
+ listener.expectCallback(iface, STATE_ABSENT, ROLE_NONE)
+ }
removeInterface(iface2)
- listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT)
- listener.expectCallback(iface2, STATE_ABSENT, ROLE_NONE)
- listener.assertNoCallback()
+ for (listener in addedListeners) {
+ listener.expectCallback(iface2, STATE_LINK_DOWN, ROLE_CLIENT)
+ listener.expectCallback(iface2, STATE_ABSENT, ROLE_NONE)
+ listener.assertNoCallback()
+ }
}
@Test
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/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 0504973..d4f3d57 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -1275,4 +1275,23 @@
matchAllCallback.expectCallback<Lost>(wifiNetwork)
wifiAgent.expectCallback<OnNetworkUnwanted>()
}
+
+ @Test
+ fun testUnregisterAgentBeforeAgentFullyConnected() {
+ val specifier = UUID.randomUUID().toString()
+ val callback = TestableNetworkCallback()
+ val transports = intArrayOf(TRANSPORT_CELLULAR)
+ // Ensure this NetworkAgent is never unneeded by filing a request with its specifier.
+ requestNetwork(makeTestNetworkRequest(specifier = specifier), callback)
+ val nc = makeTestNetworkCapabilities(specifier, transports)
+ val agent = createNetworkAgent(realContext, initialNc = nc)
+ // Connect the agent
+ agent.register()
+ // Mark agent connected then unregister agent immediately. Verify that both available and
+ // lost callback should be sent still.
+ agent.markConnected()
+ agent.unregister()
+ callback.expectCallback<Available>(agent.network!!)
+ callback.eventuallyExpect<Lost> { it.network == agent.network }
+ }
}
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/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/ConnectivityManagerTest.java b/tests/unit/java/android/net/ConnectivityManagerTest.java
index f324630..c327868 100644
--- a/tests/unit/java/android/net/ConnectivityManagerTest.java
+++ b/tests/unit/java/android/net/ConnectivityManagerTest.java
@@ -41,6 +41,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -72,6 +73,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -82,6 +84,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.lang.ref.WeakReference;
+
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
@@ -461,4 +465,49 @@
}
fail("expected exception of type " + throwableType);
}
+
+ private static class MockContext extends BroadcastInterceptingContext {
+ MockContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public Context getApplicationContext() {
+ return mock(Context.class);
+ }
+ }
+
+ private WeakReference<Context> makeConnectivityManagerAndReturnContext() {
+ // Mockito may have an internal reference to the mock, creating MockContext for testing.
+ final Context c = new MockContext(mock(Context.class));
+
+ new ConnectivityManager(c, mService);
+
+ return new WeakReference<>(c);
+ }
+
+ private void forceGC() {
+ // First GC ensures that objects are collected for finalization, then second GC ensures
+ // they're garbage-collected after being finalized.
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ }
+
+ @Test
+ public void testConnectivityManagerDoesNotLeakContext() throws Exception {
+ final WeakReference<Context> ref = makeConnectivityManagerAndReturnContext();
+
+ final int attempts = 100;
+ final long waitIntervalMs = 50;
+ for (int i = 0; i < attempts; i++) {
+ forceGC();
+ if (ref.get() == null) break;
+
+ Thread.sleep(waitIntervalMs);
+ }
+
+ assertNull("ConnectivityManager weak reference still not null after " + attempts
+ + " attempts", ref.get());
+ }
}
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/NetworkStatsCollectionTest.java b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
index 32c106d..0f02850 100644
--- a/tests/unit/java/android/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
@@ -38,13 +38,11 @@
import static org.junit.Assert.fail;
import android.content.res.Resources;
-import android.net.NetworkStatsCollection.Key;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
-import android.util.ArrayMap;
import android.util.RecurrenceRule;
import androidx.test.InstrumentationRegistry;
@@ -75,7 +73,6 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
/**
* Tests for {@link NetworkStatsCollection}.
@@ -534,52 +531,6 @@
assertThrows(ArithmeticException.class, () -> multiplySafeByRational(30, 3, 0));
}
- @Test
- public void testBuilder() {
- final Map<Key, NetworkStatsHistory> expectedEntries = new ArrayMap<>();
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- final NetworkIdentitySet ident = new NetworkIdentitySet();
- final Key key1 = new Key(ident, 0, 0, 0);
- final Key key2 = new Key(ident, 1, 0, 0);
- final long bucketDuration = 10;
-
- final NetworkStatsHistory.Entry entry1 = new NetworkStatsHistory.Entry(10, 10, 40,
- 4, 50, 5, 60);
- final NetworkStatsHistory.Entry entry2 = new NetworkStatsHistory.Entry(30, 10, 3,
- 41, 7, 1, 0);
-
- NetworkStatsHistory history1 = new NetworkStatsHistory.Builder(10, 5)
- .addEntry(entry1)
- .addEntry(entry2)
- .build();
-
- NetworkStatsHistory history2 = new NetworkStatsHistory(10, 5);
-
- NetworkStatsCollection actualCollection = new NetworkStatsCollection.Builder(bucketDuration)
- .addEntry(key1, history1)
- .addEntry(key2, history2)
- .build();
-
- // The builder will omit any entry with empty history. Thus, history2
- // is not expected in the result collection.
- expectedEntries.put(key1, history1);
-
- final Map<Key, NetworkStatsHistory> actualEntries = actualCollection.getEntries();
-
- assertEquals(expectedEntries.size(), actualEntries.size());
- for (Key expectedKey : expectedEntries.keySet()) {
- final NetworkStatsHistory expectedHistory = expectedEntries.get(expectedKey);
-
- final NetworkStatsHistory actualHistory = actualEntries.get(expectedKey);
- assertNotNull(actualHistory);
-
- assertEquals(expectedHistory.getEntries(), actualHistory.getEntries());
-
- actualEntries.remove(expectedKey);
- }
- assertEquals(0, actualEntries.size());
- }
-
/**
* Copy a {@link Resources#openRawResource(int)} into {@link File} for
* testing purposes.
diff --git a/tests/unit/java/android/net/NetworkStatsHistoryTest.java b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
index c170605..c5f8c00 100644
--- a/tests/unit/java/android/net/NetworkStatsHistoryTest.java
+++ b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
@@ -56,7 +56,6 @@
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
-import java.util.List;
import java.util.Random;
@RunWith(DevSdkIgnoreRunner.class)
@@ -533,40 +532,6 @@
assertEquals(512L + 4096L, stats.getTotalBytes());
}
- @Test
- public void testBuilder() {
- final NetworkStatsHistory.Entry entry1 = new NetworkStatsHistory.Entry(10, 30, 40,
- 4, 50, 5, 60);
- final NetworkStatsHistory.Entry entry2 = new NetworkStatsHistory.Entry(30, 15, 3,
- 41, 7, 1, 0);
- final NetworkStatsHistory.Entry entry3 = new NetworkStatsHistory.Entry(7, 301, 11,
- 14, 31, 2, 80);
-
- final NetworkStatsHistory statsEmpty = new NetworkStatsHistory
- .Builder(HOUR_IN_MILLIS, 10).build();
- assertEquals(0, statsEmpty.getEntries().size());
- assertEquals(HOUR_IN_MILLIS, statsEmpty.getBucketDuration());
-
- NetworkStatsHistory statsSingle = new NetworkStatsHistory
- .Builder(HOUR_IN_MILLIS, 8)
- .addEntry(entry1)
- .build();
- assertEquals(1, statsSingle.getEntries().size());
- assertEquals(HOUR_IN_MILLIS, statsSingle.getBucketDuration());
- assertEquals(entry1, statsSingle.getEntries().get(0));
-
- NetworkStatsHistory statsMultiple = new NetworkStatsHistory
- .Builder(SECOND_IN_MILLIS, 0)
- .addEntry(entry1).addEntry(entry2).addEntry(entry3)
- .build();
- final List<NetworkStatsHistory.Entry> entries = statsMultiple.getEntries();
- assertEquals(3, entries.size());
- assertEquals(SECOND_IN_MILLIS, statsMultiple.getBucketDuration());
- assertEquals(entry1, entries.get(0));
- assertEquals(entry2, entries.get(1));
- assertEquals(entry3, entries.get(2));
- }
-
private static void assertIndexBeforeAfter(
NetworkStatsHistory stats, int before, int after, long time) {
assertEquals("unexpected before", before, stats.getIndexBefore(time));
diff --git a/tests/unit/java/android/net/NetworkTemplateTest.kt b/tests/unit/java/android/net/NetworkTemplateTest.kt
index 453612f..abd1825 100644
--- a/tests/unit/java/android/net/NetworkTemplateTest.kt
+++ b/tests/unit/java/android/net/NetworkTemplateTest.kt
@@ -29,12 +29,8 @@
import android.net.NetworkStats.METERED_NO
import android.net.NetworkStats.METERED_YES
import android.net.NetworkStats.ROAMING_ALL
-import android.net.NetworkTemplate.MATCH_BLUETOOTH
-import android.net.NetworkTemplate.MATCH_CARRIER
-import android.net.NetworkTemplate.MATCH_ETHERNET
import android.net.NetworkTemplate.MATCH_MOBILE
import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD
-import android.net.NetworkTemplate.MATCH_PROXY
import android.net.NetworkTemplate.MATCH_WIFI
import android.net.NetworkTemplate.MATCH_WIFI_WILDCARD
import android.net.NetworkTemplate.NETWORK_TYPE_ALL
@@ -52,11 +48,9 @@
import android.net.wifi.WifiInfo
import android.os.Build
import android.telephony.TelephonyManager
-import com.android.net.module.util.NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_ALL
import com.android.net.module.util.NetworkStatsUtils.SUBSCRIBER_ID_MATCH_RULE_EXACT
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
-import com.android.testutils.SC_V2
import com.android.testutils.assertParcelSane
import org.junit.Before
import org.junit.Test
@@ -65,7 +59,6 @@
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
@@ -555,140 +548,4 @@
it.assertMatches(identMobileImsi3)
}
}
-
- @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
- @Test
- fun testBuilderMatchRules() {
- // Verify unknown match rules cannot construct templates.
- listOf(Integer.MIN_VALUE, -1, Integer.MAX_VALUE).forEach {
- assertFailsWith<IllegalArgumentException> {
- NetworkTemplate.Builder(it).build()
- }
- }
-
- // Verify hidden match rules cannot construct templates.
- listOf(MATCH_WIFI_WILDCARD, MATCH_MOBILE_WILDCARD, MATCH_PROXY).forEach {
- assertFailsWith<IllegalArgumentException> {
- NetworkTemplate.Builder(it).build()
- }
- }
-
- // Verify template which matches metered cellular and carrier networks with
- // the given IMSI. See buildTemplateMobileAll and buildTemplateCarrierMetered.
- listOf(MATCH_MOBILE, MATCH_CARRIER).forEach { matchRule ->
- NetworkTemplate.Builder(matchRule).setSubscriberIds(setOf(TEST_IMSI1))
- .setMeteredness(METERED_YES).build().let {
- val expectedTemplate = NetworkTemplate(matchRule, TEST_IMSI1,
- arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
- ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
- assertEquals(expectedTemplate, it)
- }
- }
-
- // Verify carrier template cannot be created without IMSI.
- assertFailsWith<IllegalArgumentException> {
- NetworkTemplate.Builder(MATCH_CARRIER).build()
- }
-
- // Verify template which matches metered cellular networks,
- // regardless of IMSI. See buildTemplateMobileWildcard.
- NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES).build().let {
- val expectedTemplate = NetworkTemplate(MATCH_MOBILE_WILDCARD, null /*subscriberId*/,
- null /*subscriberIds*/, arrayOf<String>(),
- METERED_YES, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
- assertEquals(expectedTemplate, it)
- }
-
- // Verify template which matches metered cellular networks and ratType.
- // See NetworkTemplate#buildTemplateMobileWithRatType.
- NetworkTemplate.Builder(MATCH_MOBILE).setSubscriberIds(setOf(TEST_IMSI1))
- .setMeteredness(METERED_YES).setRatType(TelephonyManager.NETWORK_TYPE_UMTS)
- .build().let {
- val expectedTemplate = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1,
- arrayOf(TEST_IMSI1), arrayOf<String>(), METERED_YES,
- ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_UMTS,
- OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
- assertEquals(expectedTemplate, it)
- }
-
- // Verify template which matches all wifi networks,
- // regardless of Wifi Network Key. See buildTemplateWifiWildcard and buildTemplateWifi.
- NetworkTemplate.Builder(MATCH_WIFI).build().let {
- val expectedTemplate = NetworkTemplate(MATCH_WIFI_WILDCARD, null /*subscriberId*/,
- null /*subscriberIds*/, arrayOf<String>(),
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
- assertEquals(expectedTemplate, it)
- }
-
- // Verify template which matches wifi networks with the given Wifi Network Key.
- // See buildTemplateWifi(wifiNetworkKey).
- NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build().let {
- val expectedTemplate = NetworkTemplate(MATCH_WIFI, null /*subscriberId*/,
- null /*subscriberIds*/, arrayOf(TEST_WIFI_KEY1),
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
- assertEquals(expectedTemplate, it)
- }
-
- // Verify template which matches all wifi networks with the
- // given Wifi Network Key, and IMSI. See buildTemplateWifi(wifiNetworkKey, subscriberId).
- NetworkTemplate.Builder(MATCH_WIFI).setSubscriberIds(setOf(TEST_IMSI1))
- .setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build().let {
- val expectedTemplate = NetworkTemplate(MATCH_WIFI, TEST_IMSI1,
- arrayOf(TEST_IMSI1), arrayOf(TEST_WIFI_KEY1),
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_EXACT)
- assertEquals(expectedTemplate, it)
- }
-
- // Verify template which matches ethernet and bluetooth networks.
- // See buildTemplateEthernet and buildTemplateBluetooth.
- listOf(MATCH_ETHERNET, MATCH_BLUETOOTH).forEach { matchRule ->
- NetworkTemplate.Builder(matchRule).build().let {
- val expectedTemplate = NetworkTemplate(matchRule, null /*subscriberId*/,
- null /*subscriberIds*/, arrayOf<String>(),
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
- assertEquals(expectedTemplate, it)
- }
- }
- }
-
- @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available
- @Test
- fun testBuilderWifiNetworkKeys() {
- // Verify template builder which generates same template with the given different
- // sequence keys.
- NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(
- setOf(TEST_WIFI_KEY1, TEST_WIFI_KEY2)).build().let {
- val expectedTemplate = NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(
- setOf(TEST_WIFI_KEY2, TEST_WIFI_KEY1)).build()
- assertEquals(expectedTemplate, it)
- }
-
- // Verify template which matches non-wifi networks with the given key is invalid.
- listOf(MATCH_MOBILE, MATCH_CARRIER, MATCH_ETHERNET, MATCH_BLUETOOTH, -1,
- Integer.MAX_VALUE).forEach { matchRule ->
- assertFailsWith<IllegalArgumentException> {
- NetworkTemplate.Builder(matchRule).setWifiNetworkKeys(setOf(TEST_WIFI_KEY1)).build()
- }
- }
-
- // Verify template which matches wifi networks with the given null key is invalid.
- assertFailsWith<IllegalArgumentException> {
- NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf(null)).build()
- }
-
- // Verify template which matches wifi wildcard with the given empty key set.
- NetworkTemplate.Builder(MATCH_WIFI).setWifiNetworkKeys(setOf<String>()).build().let {
- val expectedTemplate = NetworkTemplate(MATCH_WIFI_WILDCARD, null /*subscriberId*/,
- arrayOf<String>() /*subscriberIds*/, arrayOf<String>(),
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
- OEM_MANAGED_ALL, SUBSCRIBER_ID_MATCH_RULE_ALL)
- assertEquals(expectedTemplate, it)
- }
- }
}
diff --git a/tests/unit/java/android/net/QosSocketFilterTest.java b/tests/unit/java/android/net/QosSocketFilterTest.java
index 91f2cdd..6820b40 100644
--- a/tests/unit/java/android/net/QosSocketFilterTest.java
+++ b/tests/unit/java/android/net/QosSocketFilterTest.java
@@ -16,8 +16,17 @@
package android.net;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_CONNECTED;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import android.os.Build;
@@ -29,6 +38,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -36,14 +46,14 @@
@SmallTest
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
public class QosSocketFilterTest {
-
+ private static final int TEST_NET_ID = 1777;
+ private final Network mNetwork = new Network(TEST_NET_ID);
@Test
public void testPortExactMatch() {
final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
assertTrue(QosSocketFilter.matchesAddress(
new InetSocketAddress(addressA, 10), addressB, 10, 10));
-
}
@Test
@@ -77,5 +87,90 @@
assertFalse(QosSocketFilter.matchesAddress(
new InetSocketAddress(addressA, 10), addressB, 10, 10));
}
+
+ @Test
+ public void testAddressMatchWithAnyLocalAddresses() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("0.0.0.0");
+ assertTrue(QosSocketFilter.matchesAddress(
+ new InetSocketAddress(addressA, 10), addressB, 10, 10));
+ assertFalse(QosSocketFilter.matchesAddress(
+ new InetSocketAddress(addressB, 10), addressA, 10, 10));
+ }
+
+ @Test
+ public void testProtocolMatch() throws Exception {
+ DatagramSocket socket = new DatagramSocket(new InetSocketAddress("127.0.0.1", 0));
+ socket.connect(new InetSocketAddress("127.0.0.1", socket.getLocalPort() + 10));
+ DatagramSocket socketV6 = new DatagramSocket(new InetSocketAddress("::1", 0));
+ socketV6.connect(new InetSocketAddress("::1", socketV6.getLocalPort() + 10));
+ QosSocketInfo socketInfo = new QosSocketInfo(mNetwork, socket);
+ QosSocketFilter socketFilter = new QosSocketFilter(socketInfo);
+ QosSocketInfo socketInfo6 = new QosSocketInfo(mNetwork, socketV6);
+ QosSocketFilter socketFilter6 = new QosSocketFilter(socketInfo6);
+ assertTrue(socketFilter.matchesProtocol(IPPROTO_UDP));
+ assertTrue(socketFilter6.matchesProtocol(IPPROTO_UDP));
+ assertFalse(socketFilter.matchesProtocol(IPPROTO_TCP));
+ assertFalse(socketFilter6.matchesProtocol(IPPROTO_TCP));
+ socket.close();
+ socketV6.close();
+ }
+
+ @Test
+ public void testValidate() throws Exception {
+ DatagramSocket socket = new DatagramSocket(new InetSocketAddress("127.0.0.1", 0));
+ socket.connect(new InetSocketAddress("127.0.0.1", socket.getLocalPort() + 7));
+ DatagramSocket socketV6 = new DatagramSocket(new InetSocketAddress("::1", 0));
+
+ QosSocketInfo socketInfo = new QosSocketInfo(mNetwork, socket);
+ QosSocketFilter socketFilter = new QosSocketFilter(socketInfo);
+ QosSocketInfo socketInfo6 = new QosSocketInfo(mNetwork, socketV6);
+ QosSocketFilter socketFilter6 = new QosSocketFilter(socketInfo6);
+ assertEquals(EX_TYPE_FILTER_NONE, socketFilter.validate());
+ assertEquals(EX_TYPE_FILTER_NONE, socketFilter6.validate());
+ socket.close();
+ socketV6.close();
+ }
+
+ @Test
+ public void testValidateUnbind() throws Exception {
+ DatagramSocket socket;
+ socket = new DatagramSocket(null);
+ QosSocketInfo socketInfo = new QosSocketInfo(mNetwork, socket);
+ QosSocketFilter socketFilter = new QosSocketFilter(socketInfo);
+ assertEquals(EX_TYPE_FILTER_SOCKET_NOT_BOUND, socketFilter.validate());
+ socket.close();
+ }
+
+ @Test
+ public void testValidateLocalAddressChanged() throws Exception {
+ DatagramSocket socket = new DatagramSocket(null);
+ DatagramSocket socket6 = new DatagramSocket(null);
+ QosSocketInfo socketInfo = new QosSocketInfo(mNetwork, socket);
+ QosSocketFilter socketFilter = new QosSocketFilter(socketInfo);
+ QosSocketInfo socketInfo6 = new QosSocketInfo(mNetwork, socket6);
+ QosSocketFilter socketFilter6 = new QosSocketFilter(socketInfo6);
+ socket.bind(new InetSocketAddress("127.0.0.1", 0));
+ socket6.bind(new InetSocketAddress("::1", 0));
+ assertEquals(EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED, socketFilter.validate());
+ assertEquals(EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED, socketFilter6.validate());
+ socket.close();
+ socket6.close();
+ }
+
+ @Test
+ public void testValidateRemoteAddressChanged() throws Exception {
+ DatagramSocket socket;
+ socket = new DatagramSocket(new InetSocketAddress("127.0.0.1", 53137));
+ socket.connect(new InetSocketAddress("127.0.0.1", socket.getLocalPort() + 11));
+ QosSocketInfo socketInfo = new QosSocketInfo(mNetwork, socket);
+ QosSocketFilter socketFilter = new QosSocketFilter(socketInfo);
+ assertEquals(EX_TYPE_FILTER_NONE, socketFilter.validate());
+ socket.disconnect();
+ assertEquals(EX_TYPE_FILTER_SOCKET_NOT_CONNECTED, socketFilter.validate());
+ socket.connect(new InetSocketAddress("127.0.0.1", socket.getLocalPort() + 13));
+ assertEquals(EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED, socketFilter.validate());
+ socket.close();
+ }
}
diff --git a/tests/unit/java/android/net/QosSocketInfoTest.java b/tests/unit/java/android/net/QosSocketInfoTest.java
new file mode 100644
index 0000000..749c182
--- /dev/null
+++ b/tests/unit/java/android/net/QosSocketInfoTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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 static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_STREAM;
+
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+@RunWith(DevSdkIgnoreRunner.class)
+@SmallTest
+@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
+public class QosSocketInfoTest {
+ @Mock
+ private Network mMockNetwork = mock(Network.class);
+
+ @Test
+ public void testConstructWithSock() throws Exception {
+ ServerSocket server = new ServerSocket();
+ ServerSocket server6 = new ServerSocket();
+
+ InetSocketAddress clientAddr = new InetSocketAddress("127.0.0.1", 0);
+ InetSocketAddress serverAddr = new InetSocketAddress("127.0.0.1", 0);
+ InetSocketAddress clientAddr6 = new InetSocketAddress("::1", 0);
+ InetSocketAddress serverAddr6 = new InetSocketAddress("::1", 0);
+ server.bind(serverAddr);
+ server6.bind(serverAddr6);
+ Socket socket = new Socket(serverAddr.getAddress(), server.getLocalPort(),
+ clientAddr.getAddress(), clientAddr.getPort());
+ Socket socket6 = new Socket(serverAddr6.getAddress(), server6.getLocalPort(),
+ clientAddr6.getAddress(), clientAddr6.getPort());
+ QosSocketInfo sockInfo = new QosSocketInfo(mMockNetwork, socket);
+ QosSocketInfo sockInfo6 = new QosSocketInfo(mMockNetwork, socket6);
+ assertTrue(sockInfo.getLocalSocketAddress()
+ .equals(new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort())));
+ assertTrue(sockInfo.getRemoteSocketAddress()
+ .equals((InetSocketAddress) socket.getRemoteSocketAddress()));
+ assertEquals(SOCK_STREAM, sockInfo.getSocketType());
+ assertTrue(sockInfo6.getLocalSocketAddress()
+ .equals(new InetSocketAddress(socket6.getLocalAddress(), socket6.getLocalPort())));
+ assertTrue(sockInfo6.getRemoteSocketAddress()
+ .equals((InetSocketAddress) socket6.getRemoteSocketAddress()));
+ assertEquals(SOCK_STREAM, sockInfo6.getSocketType());
+ socket.close();
+ socket6.close();
+ server.close();
+ server6.close();
+ }
+
+ @Test
+ public void testConstructWithDatagramSock() throws Exception {
+ InetSocketAddress clientAddr = new InetSocketAddress("127.0.0.1", 0);
+ InetSocketAddress serverAddr = new InetSocketAddress("127.0.0.1", 0);
+ InetSocketAddress clientAddr6 = new InetSocketAddress("::1", 0);
+ InetSocketAddress serverAddr6 = new InetSocketAddress("::1", 0);
+ DatagramSocket socket = new DatagramSocket(null);
+ socket.setReuseAddress(true);
+ socket.bind(clientAddr);
+ socket.connect(serverAddr);
+ DatagramSocket socket6 = new DatagramSocket(null);
+ socket6.setReuseAddress(true);
+ socket6.bind(clientAddr);
+ socket6.connect(serverAddr);
+ QosSocketInfo sockInfo = new QosSocketInfo(mMockNetwork, socket);
+ QosSocketInfo sockInfo6 = new QosSocketInfo(mMockNetwork, socket6);
+ assertTrue(sockInfo.getLocalSocketAddress()
+ .equals((InetSocketAddress) socket.getLocalSocketAddress()));
+ assertTrue(sockInfo.getRemoteSocketAddress()
+ .equals((InetSocketAddress) socket.getRemoteSocketAddress()));
+ assertEquals(SOCK_DGRAM, sockInfo.getSocketType());
+ assertTrue(sockInfo6.getLocalSocketAddress()
+ .equals((InetSocketAddress) socket6.getLocalSocketAddress()));
+ assertTrue(sockInfo6.getRemoteSocketAddress()
+ .equals((InetSocketAddress) socket6.getRemoteSocketAddress()));
+ assertEquals(SOCK_DGRAM, sockInfo6.getSocketType());
+ socket.close();
+ }
+}
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..39a82bd 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -104,6 +104,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_2;
import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
@@ -136,6 +137,8 @@
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_PROFILE;
import static com.android.server.ConnectivityService.PREFERENCE_ORDER_VPN;
import static com.android.server.ConnectivityServiceTestUtils.transportToLegacyType;
+import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackRegister;
+import static com.android.server.NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister;
import static com.android.testutils.ConcurrentUtils.await;
import static com.android.testutils.ConcurrentUtils.durationOf;
import static com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
@@ -159,8 +162,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;
@@ -194,6 +197,7 @@
import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.admin.DevicePolicyManager;
import android.app.usage.NetworkStatsManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -338,10 +342,12 @@
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LocationPermissionChecker;
import com.android.networkstack.apishim.NetworkAgentConfigShimImpl;
+import com.android.networkstack.apishim.api29.ConstantsShim;
import com.android.server.ConnectivityService.ConnectivityDiagnosticsCallbackInfo;
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;
@@ -464,6 +470,7 @@
private static final int TEST_APP_ID_2 = 104;
private static final int TEST_WORK_PROFILE_APP_UID_2 =
UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID_2);
+ private static final int TEST_APP_ID_3 = 105;
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
@@ -539,7 +546,9 @@
@Mock NetworkPolicyManager mNetworkPolicyManager;
@Mock VpnProfileStore mVpnProfileStore;
@Mock SystemConfigManager mSystemConfigManager;
+ @Mock DevicePolicyManager mDevicePolicyManager;
@Mock Resources mResources;
+ @Mock ClatCoordinator mClatCoordinator;
@Mock PacProxyManager mPacProxyManager;
@Mock BpfNetMaps mBpfNetMaps;
@Mock CarrierPrivilegeAuthenticator mCarrierPrivilegeAuthenticator;
@@ -642,7 +651,8 @@
@Override
public ComponentName startService(Intent service) {
final String action = service.getAction();
- if (!VpnConfig.SERVICE_INTERFACE.equals(action)) {
+ if (!VpnConfig.SERVICE_INTERFACE.equals(action)
+ && !ConstantsShim.ACTION_VPN_MANAGER_EVENT.equals(action)) {
fail("Attempt to start unknown service, action=" + action);
}
return new ComponentName(service.getPackage(), "com.android.test.Service");
@@ -659,6 +669,7 @@
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager;
if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager;
+ if (Context.DEVICE_POLICY_SERVICE.equals(name)) return mDevicePolicyManager;
if (Context.SYSTEM_CONFIG_SERVICE.equals(name)) return mSystemConfigManager;
if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
if (Context.BATTERY_STATS_SERVICE.equals(name)) return mBatteryStatsManager;
@@ -687,6 +698,14 @@
doReturn(value).when(mUserManager).isManagedProfile(eq(userHandle.getIdentifier()));
}
+ public void setDeviceOwner(@NonNull final UserHandle userHandle, String value) {
+ // This relies on all contexts for a given user returning the same UM mock
+ final DevicePolicyManager dpmMock = createContextAsUser(userHandle, 0 /* flags */)
+ .getSystemService(DevicePolicyManager.class);
+ doReturn(value).when(dpmMock).getDeviceOwner();
+ doReturn(value).when(mDevicePolicyManager).getDeviceOwner();
+ }
+
@Override
public ContentResolver getContentResolver() {
return mContentResolver;
@@ -2004,6 +2023,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<>();
@@ -5851,7 +5875,7 @@
}
/**
- * Validate the callback flow CBS request without carrier privilege.
+ * Validate the service throws if request with CBS but without carrier privilege.
*/
@Test
public void testCBSRequestWithoutCarrierPrivilege() throws Exception {
@@ -5860,10 +5884,8 @@
final TestNetworkCallback networkCallback = new TestNetworkCallback();
mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_DENIED);
- // Now file the test request and expect it.
- mCm.requestNetwork(nr, networkCallback);
- networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
- mCm.unregisterNetworkCallback(networkCallback);
+ // Now file the test request and expect the service throws.
+ assertThrows(SecurityException.class, () -> mCm.requestNetwork(nr, networkCallback));
}
private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
@@ -9569,6 +9591,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 +9676,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 +9715,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 +9739,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 +9771,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 +9780,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 +9794,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 +9803,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 +9820,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 +9845,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 +9862,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 +9875,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 +9894,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 +9904,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 +9946,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 +9957,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 +9967,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 +9976,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 +9985,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 +10009,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 +10019,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 +10034,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 +10046,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 +10059,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 +10085,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());
@@ -11852,16 +11939,14 @@
mQosCallbackMockHelper.registerQosCallback(
mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
- final NetworkAgentWrapper.CallbackType.OnQosCallbackRegister cbRegister1 =
- (NetworkAgentWrapper.CallbackType.OnQosCallbackRegister)
- wrapper.getCallbackHistory().poll(1000, x -> true);
+ final OnQosCallbackRegister cbRegister1 =
+ (OnQosCallbackRegister) wrapper.getCallbackHistory().poll(1000, x -> true);
assertNotNull(cbRegister1);
final int registerCallbackId = cbRegister1.mQosCallbackId;
mService.unregisterQosCallback(mQosCallbackMockHelper.mCallback);
- final NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister cbUnregister;
- cbUnregister = (NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister)
- wrapper.getCallbackHistory().poll(1000, x -> true);
+ final OnQosCallbackUnregister cbUnregister =
+ (OnQosCallbackUnregister) wrapper.getCallbackHistory().poll(1000, x -> true);
assertNotNull(cbUnregister);
assertEquals(registerCallbackId, cbUnregister.mQosCallbackId);
assertNull(wrapper.getCallbackHistory().poll(200, x -> true));
@@ -11940,6 +12025,86 @@
&& session.getSessionType() == QosSession.TYPE_NR_BEARER));
}
+ @Test @IgnoreUpTo(SC_V2)
+ public void testQosCallbackAvailableOnValidationError() throws Exception {
+ mQosCallbackMockHelper = new QosCallbackMockHelper();
+ final NetworkAgentWrapper wrapper = mQosCallbackMockHelper.mAgentWrapper;
+ final int sessionId = 10;
+ final int qosCallbackId = 1;
+
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
+ mQosCallbackMockHelper.registerQosCallback(
+ mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+ OnQosCallbackRegister cbRegister1 =
+ (OnQosCallbackRegister) wrapper.getCallbackHistory().poll(1000, x -> true);
+ assertNotNull(cbRegister1);
+ final int registerCallbackId = cbRegister1.mQosCallbackId;
+
+ waitForIdle();
+
+ doReturn(QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED)
+ .when(mQosCallbackMockHelper.mFilter).validate();
+ final EpsBearerQosSessionAttributes attributes = new EpsBearerQosSessionAttributes(
+ 1, 2, 3, 4, 5, new ArrayList<>());
+ mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+ .sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
+ waitForIdle();
+
+ final NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister cbUnregister;
+ cbUnregister = (NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister)
+ wrapper.getCallbackHistory().poll(1000, x -> true);
+ assertNotNull(cbUnregister);
+ assertEquals(registerCallbackId, cbUnregister.mQosCallbackId);
+ waitForIdle();
+ verify(mQosCallbackMockHelper.mCallback)
+ .onError(eq(QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED));
+ }
+
+ @Test @IgnoreUpTo(SC_V2)
+ public void testQosCallbackLostOnValidationError() throws Exception {
+ mQosCallbackMockHelper = new QosCallbackMockHelper();
+ final int sessionId = 10;
+ final int qosCallbackId = 1;
+
+ doReturn(QosCallbackException.EX_TYPE_FILTER_NONE)
+ .when(mQosCallbackMockHelper.mFilter).validate();
+ mQosCallbackMockHelper.registerQosCallback(
+ mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+ waitForIdle();
+ EpsBearerQosSessionAttributes attributes =
+ sendQosSessionEvent(qosCallbackId, sessionId, true);
+ waitForIdle();
+
+ verify(mQosCallbackMockHelper.mCallback).onQosEpsBearerSessionAvailable(argThat(session ->
+ session.getSessionId() == sessionId
+ && session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes));
+
+ doReturn(QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED)
+ .when(mQosCallbackMockHelper.mFilter).validate();
+
+ sendQosSessionEvent(qosCallbackId, sessionId, false);
+ waitForIdle();
+ verify(mQosCallbackMockHelper.mCallback)
+ .onError(eq(QosCallbackException.EX_TYPE_FILTER_SOCKET_REMOTE_ADDRESS_CHANGED));
+ }
+
+ private EpsBearerQosSessionAttributes sendQosSessionEvent(
+ int qosCallbackId, int sessionId, boolean available) {
+ if (available) {
+ final EpsBearerQosSessionAttributes attributes = new EpsBearerQosSessionAttributes(
+ 1, 2, 3, 4, 5, new ArrayList<>());
+ mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+ .sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
+ return attributes;
+ } else {
+ mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+ .sendQosSessionLost(qosCallbackId, sessionId, QosSession.TYPE_EPS_BEARER);
+ return null;
+ }
+
+ }
+
@Test
public void testQosCallbackTooManyRequests() throws Exception {
mQosCallbackMockHelper = new QosCallbackMockHelper();
@@ -14463,7 +14628,7 @@
profileNetworkPreferenceBuilder.setPreference(
PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(
- NetworkCapabilities.NET_ENTERPRISE_ID_2);
+ NET_ENTERPRISE_ID_2);
registerDefaultNetworkCallbacks();
testPreferenceForUserNetworkUpDownForGivenPreference(
profileNetworkPreferenceBuilder.build(), true,
@@ -14488,6 +14653,146 @@
}
/**
+ * Make sure per profile network preferences behave as expected when two slices with
+ * two different apps within same user profile is configured
+ * Make sure per profile network preferences overrides with latest preference when
+ * same user preference is set twice
+ */
+ @Test
+ public void testSetPreferenceWithOverridingPreference()
+ throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ mServiceContext.setWorkProfile(testHandle, true);
+ registerDefaultNetworkCallbacks();
+
+ final TestNetworkCallback appCb1 = new TestNetworkCallback();
+ final TestNetworkCallback appCb2 = new TestNetworkCallback();
+ final TestNetworkCallback appCb3 = new TestNetworkCallback();
+
+ final int testWorkProfileAppUid1 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID);
+ final int testWorkProfileAppUid2 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_2);
+ final int testWorkProfileAppUid3 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_3);
+
+ registerDefaultNetworkCallbackAsUid(appCb1, testWorkProfileAppUid1);
+ registerDefaultNetworkCallbackAsUid(appCb2, testWorkProfileAppUid2);
+ registerDefaultNetworkCallbackAsUid(appCb3, testWorkProfileAppUid3);
+
+ // Connect both a regular cell agent and an enterprise network first.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ final TestNetworkAgentWrapper workAgent1 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_1);
+ final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_2);
+ workAgent1.connect(true);
+ workAgent2.connect(true);
+
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb1.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb2.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb3.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent1.getNetwork().netId, INetd.PERMISSION_SYSTEM));
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM));
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+
+ // Set preferences for testHandle to map testWorkProfileAppUid1 to
+ // NET_ENTERPRISE_ID_1 and testWorkProfileAppUid2 to NET_ENTERPRISE_ID_2.
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder1 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder1.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder1.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ profileNetworkPreferenceBuilder1.setIncludedUids(new int[]{testWorkProfileAppUid1});
+
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder2 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder2.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder2.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_2);
+ profileNetworkPreferenceBuilder2.setIncludedUids(new int[]{testWorkProfileAppUid2});
+
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder1.build(),
+ profileNetworkPreferenceBuilder2.build()),
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent2.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent1.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()),
+ PREFERENCE_ORDER_PROFILE));
+
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ appCb1.expectAvailableCallbacksValidated(workAgent1);
+ appCb2.expectAvailableCallbacksValidated(workAgent2);
+
+ // Set preferences for testHandle to map testWorkProfileAppUid3 to
+ // to NET_ENTERPRISE_ID_1.
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder3 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder3.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder3.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ profileNetworkPreferenceBuilder3.setIncludedUids(new int[]{testWorkProfileAppUid3});
+
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder3.build()),
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent1.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder3.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
+ workAgent2.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
+ workAgent1.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()),
+ PREFERENCE_ORDER_PROFILE));
+
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ appCb3.expectAvailableCallbacksValidated(workAgent1);
+ appCb2.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ appCb1.expectAvailableCallbacksValidated(mCellNetworkAgent);
+
+ // Set the preferences for testHandle to default.
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
+
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder.build()),
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
+ workAgent1.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder3.build()),
+ PREFERENCE_ORDER_PROFILE));
+
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, appCb1, appCb2);
+ appCb3.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ workAgent2.disconnect();
+ mCellNetworkAgent.disconnect();
+
+ mCm.unregisterNetworkCallback(appCb1);
+ mCm.unregisterNetworkCallback(appCb2);
+ mCm.unregisterNetworkCallback(appCb3);
+ // Other callbacks will be unregistered by tearDown()
+ }
+
+ /**
* Test that, in a given networking context, calling setPreferenceForUser to set per-profile
* defaults on then off works as expected.
*/
@@ -14657,12 +14962,42 @@
public void testProfileNetworkPrefWrongProfile() throws Exception {
final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
mServiceContext.setWorkProfile(testHandle, false);
- assertThrows("Should not be able to set a user pref for a non-work profile",
+ mServiceContext.setDeviceOwner(testHandle, null);
+ assertThrows("Should not be able to set a user pref for a non-work profile "
+ + "and non device owner",
IllegalArgumentException.class , () ->
mCm.setProfileNetworkPreference(testHandle,
PROFILE_NETWORK_PREFERENCE_ENTERPRISE, null, null));
}
+ /**
+ * Make sure requests for per-profile default networking for a device owner is
+ * accepted on T and not accepted on S
+ */
+ @Test
+ public void testProfileNetworkDeviceOwner() throws Exception {
+ final UserHandle testHandle = UserHandle.of(TEST_WORK_PROFILE_USER_ID);
+ mServiceContext.setWorkProfile(testHandle, false);
+ mServiceContext.setDeviceOwner(testHandle, "deviceOwnerPackage");
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ if (SdkLevel.isAtLeastT()) {
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder.build()),
+ r -> r.run(), listener);
+ } else {
+ // S should not allow setting preference on device owner
+ assertThrows("Should not be able to set a user pref for a non-work profile on S",
+ IllegalArgumentException.class , () ->
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder.build()),
+ r -> r.run(), listener));
+ }
+ }
+
@Test
public void testSubIdsClearedWithoutNetworkFactoryPermission() throws Exception {
mServiceContext.setPermission(NETWORK_FACTORY, PERMISSION_DENIED);
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index c3d64cb..a1eeaf4 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,
@@ -447,6 +447,8 @@
argThat(fd -> Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), fd)),
eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING));
+ inOrder.verify(mDeps).getBpfEgress4Map();
+ inOrder.verify(mDeps).getBpfIngress6Map();
inOrder.verify(mEgressMap).insertEntry(eq(EGRESS_KEY), eq(EGRESS_VALUE));
inOrder.verify(mIngressMap).insertEntry(eq(INGRESS_KEY), eq(INGRESS_VALUE));
inOrder.verify(mDeps).tcQdiscAddDevClsact(eq(STACKED_IFINDEX));
@@ -469,6 +471,8 @@
eq((short) PRIO_CLAT), eq((short) ETH_P_IP));
inOrder.verify(mEgressMap).deleteEntry(eq(EGRESS_KEY));
inOrder.verify(mIngressMap).deleteEntry(eq(INGRESS_KEY));
+ inOrder.verify(mEgressMap).close();
+ inOrder.verify(mIngressMap).close();
inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID));
inOrder.verify(mDeps).untagSocket(eq(RAW_SOCK_COOKIE));
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);
}
}
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 33c0868..9e79162 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -27,6 +27,7 @@
import static android.net.ConnectivityManager.NetworkCallback;
import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
+import static android.net.VpnManager.TYPE_VPN_PLATFORM;
import static android.os.UserHandle.PER_USER_RANGE;
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
@@ -54,6 +55,7 @@
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -65,6 +67,7 @@
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -83,19 +86,28 @@
import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkProvider;
import android.net.RouteInfo;
import android.net.UidRangeParcel;
import android.net.VpnManager;
+import android.net.VpnProfileState;
import android.net.VpnService;
import android.net.VpnTransportInfo;
import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeNetworkLostException;
+import android.net.ipsec.ike.exceptions.IkeNonProtocolException;
import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.net.ipsec.ike.exceptions.IkeTimeoutException;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.INetworkManagementService;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.UserHandle;
@@ -113,11 +125,13 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.internal.util.HexDump;
import com.android.modules.utils.build.SdkLevel;
import com.android.server.IpSecService;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -135,6 +149,7 @@
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -188,6 +203,20 @@
* - One pair of packages have consecutive UIDs.
*/
static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"};
+ static final String PKGS_BYTES =
+ "3C62756E646C653E0A3C696E74206E616D653D22434F4C4C454354494F4E5F4C454E4754482220"
+ + "76616C75653D223422202F3E0A3C7062756E646C655F61735F6D6170206E616D653D224C4953"
+ + "545F4954454D5F30223E0A3C737472696E67206E616D653D22535452494E475F4B4559223E63"
+ + "6F6D2E6578616D706C653C2F737472696E673E0A3C2F7062756E646C655F61735F6D61703E0A"
+ + "3C7062756E646C655F61735F6D6170206E616D653D224C4953545F4954454D5F31223E0A3C73"
+ + "7472696E67206E616D653D22535452494E475F4B4559223E6F72672E6578616D706C653C2F73"
+ + "7472696E673E0A3C2F7062756E646C655F61735F6D61703E0A3C7062756E646C655F61735F6D"
+ + "6170206E616D653D224C4953545F4954454D5F32223E0A3C737472696E67206E616D653D2253"
+ + "5452494E475F4B4559223E6E65742E6578616D706C653C2F737472696E673E0A3C2F7062756E"
+ + "646C655F61735F6D61703E0A3C7062756E646C655F61735F6D6170206E616D653D224C495354"
+ + "5F4954454D5F33223E0A3C737472696E67206E616D653D22535452494E475F4B4559223E7765"
+ + "622E76706E3C2F737472696E673E0A3C2F7062756E646C655F61735F6D61703E0A3C2F62756E"
+ + "646C653E0A";
static final int[] PKG_UIDS = {66, 77, 78, 400};
// Mock packages
@@ -271,6 +300,11 @@
doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(any());
}
+ @After
+ public void tearDown() throws Exception {
+ doReturn(PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
+ }
+
private <T> void mockService(Class<T> clazz, String name, T service) {
doReturn(service).when(mContext).getSystemService(name);
doReturn(name).when(mContext).getSystemServiceName(clazz);
@@ -696,6 +730,47 @@
}
}
+ private Vpn prepareVpnForVerifyAppExclusionList() throws Exception {
+ assumeTrue(isAtLeastT());
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+ when(mVpnProfileStore.get(vpn.getVpnAppExcludedForPackage(TEST_VPN_PKG)))
+ .thenReturn(HexDump.hexStringToByteArray(PKGS_BYTES));
+
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ vpn.mNetworkAgent = new NetworkAgent(mContext, Looper.getMainLooper(), TAG,
+ new NetworkCapabilities.Builder().build(), new LinkProperties(), 10 /* score */,
+ new NetworkAgentConfig.Builder().build(),
+ new NetworkProvider(mContext, Looper.getMainLooper(), TAG)) {};
+ return vpn;
+ }
+
+ @Test
+ public void testSetAndGetAppExclusionList() throws Exception {
+ final Vpn vpn = prepareVpnForVerifyAppExclusionList();
+ vpn.setAppExclusionList(TEST_VPN_PKG, Arrays.asList(PKGS));
+ verify(mVpnProfileStore)
+ .put(eq(vpn.getVpnAppExcludedForPackage(TEST_VPN_PKG)),
+ eq(HexDump.hexStringToByteArray(PKGS_BYTES)));
+ assertEquals(vpn.createUserAndRestrictedProfilesRanges(
+ primaryUser.id, null, Arrays.asList(PKGS)),
+ vpn.mNetworkCapabilities.getUids());
+ assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
+ }
+
+ @Test
+ public void testSetAndGetAppExclusionListRestrictedUser() throws Exception {
+ final Vpn vpn = prepareVpnForVerifyAppExclusionList();
+ // Mock it to restricted profile
+ when(mUserManager.getUserInfo(anyInt())).thenReturn(restrictedProfileA);
+ // Restricted users cannot configure VPNs
+ assertThrows(SecurityException.class,
+ () -> vpn.setAppExclusionList(TEST_VPN_PKG, new ArrayList<>()));
+ assertThrows(SecurityException.class, () -> vpn.getAppExclusionList(TEST_VPN_PKG));
+ }
+
@Test
public void testProvisionVpnProfilePreconsented() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
@@ -783,6 +858,30 @@
verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
}
+ private void verifyPlatformVpnIsActivated(String packageName) {
+ verify(mAppOps).noteOpNoThrow(
+ eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
+ eq(Process.myUid()),
+ eq(packageName),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ verify(mAppOps).startOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(Process.myUid()),
+ eq(packageName),
+ eq(null) /* attributionTag */,
+ eq(null) /* message */);
+ }
+
+ private void verifyPlatformVpnIsDeactivated(String packageName) {
+ // Add a small delay to double confirm that finishOp is only called once.
+ verify(mAppOps, after(100)).finishOp(
+ eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
+ eq(Process.myUid()),
+ eq(packageName),
+ eq(null) /* attributionTag */);
+ }
+
@Test
public void testStartVpnProfile() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
@@ -793,13 +892,7 @@
vpn.startVpnProfile(TEST_VPN_PKG);
verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
- verify(mAppOps)
- .noteOpNoThrow(
- eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
- eq(Process.myUid()),
- eq(TEST_VPN_PKG),
- eq(null) /* attributionTag */,
- eq(null) /* message */);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
}
@Test
@@ -811,7 +904,7 @@
vpn.startVpnProfile(TEST_VPN_PKG);
- // Verify that the the ACTIVATE_VPN appop was checked, but no error was thrown.
+ // Verify that the ACTIVATE_VPN appop was checked, but no error was thrown.
verify(mAppOps).noteOpNoThrow(AppOpsManager.OPSTR_ACTIVATE_VPN, Process.myUid(),
TEST_VPN_PKG, null /* attributionTag */, null /* message */);
}
@@ -896,18 +989,7 @@
when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
.thenReturn(mVpnProfile.encode());
vpn.startVpnProfile(TEST_VPN_PKG);
- verify(mAppOps).noteOpNoThrow(
- eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
- eq(Process.myUid()),
- eq(TEST_VPN_PKG),
- eq(null) /* attributionTag */,
- eq(null) /* message */);
- verify(mAppOps).startOp(
- eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
- eq(Process.myUid()),
- eq(TEST_VPN_PKG),
- eq(null) /* attributionTag */,
- eq(null) /* message */);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
// Add a small delay to make sure that startOp is only called once.
verify(mAppOps, after(100).times(1)).startOp(
eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
@@ -923,12 +1005,7 @@
eq(null) /* attributionTag */,
eq(null) /* message */);
vpn.stopVpnProfile(TEST_VPN_PKG);
- // Add a small delay to double confirm that startOp is only called once.
- verify(mAppOps, after(100)).finishOp(
- eq(AppOpsManager.OPSTR_ESTABLISH_VPN_MANAGER),
- eq(Process.myUid()),
- eq(TEST_VPN_PKG),
- eq(null) /* attributionTag */);
+ verifyPlatformVpnIsDeactivated(TEST_VPN_PKG);
}
@Test
@@ -964,6 +1041,128 @@
eq(null) /* message */);
}
+ private void verifyVpnManagerEvent(String sessionKey, String category, int errorClass,
+ int errorCode, VpnProfileState... profileState) {
+ final Context userContext =
+ mContext.createContextAsUser(UserHandle.of(primaryUser.id), 0 /* flags */);
+ final ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+
+ final int verifyTimes = (profileState == null) ? 1 : profileState.length;
+ verify(userContext, times(verifyTimes)).startService(intentArgumentCaptor.capture());
+
+ for (int i = 0; i < verifyTimes; i++) {
+ final Intent intent = intentArgumentCaptor.getAllValues().get(i);
+ assertEquals(sessionKey, intent.getStringExtra(VpnManager.EXTRA_SESSION_KEY));
+ final Set<String> categories = intent.getCategories();
+ assertTrue(categories.contains(category));
+ assertEquals(errorClass,
+ intent.getIntExtra(VpnManager.EXTRA_ERROR_CLASS, -1 /* defaultValue */));
+ assertEquals(errorCode,
+ intent.getIntExtra(VpnManager.EXTRA_ERROR_CODE, -1 /* defaultValue */));
+ if (profileState != null) {
+ assertEquals(profileState[i], intent.getParcelableExtra(
+ VpnManager.EXTRA_VPN_PROFILE_STATE, VpnProfileState.class));
+ }
+ }
+ reset(userContext);
+ }
+
+ @Test
+ public void testVpnManagerEventForUserDeactivated() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastT());
+ // For security reasons, Vpn#prepare() will check that oldPackage and newPackage are either
+ // null or the package of the caller. This test will call Vpn#prepare() to pretend the old
+ // VPN is replaced by a new one. But only Settings can change to some other packages, and
+ // this is checked with CONTROL_VPN so simulate holding CONTROL_VPN in order to pass the
+ // security checks.
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ // Test the case that the user deactivates the vpn in vpn app.
+ final String sessionKey1 = vpn.startVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsDeactivated(TEST_VPN_PKG);
+ // CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and
+ // errorCode won't be set.
+ verifyVpnManagerEvent(sessionKey1, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
+ -1 /* errorClass */, -1 /* errorCode */, null /* profileState */);
+ reset(mAppOps);
+
+ // Test the case that the user chooses another vpn and the original one is replaced.
+ final String sessionKey2 = vpn.startVpnProfile(TEST_VPN_PKG);
+ verifyPlatformVpnIsActivated(TEST_VPN_PKG);
+ vpn.prepare(TEST_VPN_PKG, "com.new.vpn" /* newPackage */, TYPE_VPN_PLATFORM);
+ verifyPlatformVpnIsDeactivated(TEST_VPN_PKG);
+ // CATEGORY_EVENT_DEACTIVATED_BY_USER is not an error event, so both of errorClass and
+ // errorCode won't be set.
+ verifyVpnManagerEvent(sessionKey2, VpnManager.CATEGORY_EVENT_DEACTIVATED_BY_USER,
+ -1 /* errorClass */, -1 /* errorCode */, null /* profileState */);
+ }
+
+ @Test
+ public void testVpnManagerEventForAlwaysOnChanged() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastT());
+ // Calling setAlwaysOnPackage() needs to hold CONTROL_VPN.
+ doReturn(PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(CONTROL_VPN);
+ final Vpn vpn = createVpn(primaryUser.id);
+ // Enable VPN always-on for PKGS[1].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyVpnManagerEvent(null /* sessionKey */,
+ VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
+ -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
+
+ // Enable VPN lockdown for PKGS[1].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyVpnManagerEvent(null /* sessionKey */,
+ VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
+ -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, true /* lockdown */));
+
+ // Disable VPN lockdown for PKGS[1].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyVpnManagerEvent(null /* sessionKey */,
+ VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
+ -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
+
+ // Disable VPN always-on.
+ assertTrue(vpn.setAlwaysOnPackage(null, false /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyVpnManagerEvent(null /* sessionKey */,
+ VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
+ -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, false /* alwaysOn */, false /* lockdown */));
+
+ // Enable VPN always-on for PKGS[1] again.
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false /* lockdown */,
+ null /* lockdownAllowlist */));
+ verifyVpnManagerEvent(null /* sessionKey */,
+ VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
+ -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
+
+ // Enable VPN always-on for PKGS[2].
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[2], false /* lockdown */,
+ null /* lockdownAllowlist */));
+ // PKGS[1] is replaced with PKGS[2].
+ // Pass 2 VpnProfileState objects to verifyVpnManagerEvent(), the first one is sent to
+ // PKGS[1] to notify PKGS[1] that the VPN always-on is disabled, the second one is sent to
+ // PKGS[2] to notify PKGS[2] that the VPN always-on is enabled.
+ verifyVpnManagerEvent(null /* sessionKey */,
+ VpnManager.CATEGORY_EVENT_ALWAYS_ON_STATE_CHANGED, -1 /* errorClass */,
+ -1 /* errorCode */, new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, false /* alwaysOn */, false /* lockdown */),
+ new VpnProfileState(VpnProfileState.STATE_DISCONNECTED,
+ null /* sessionKey */, true /* alwaysOn */, false /* lockdown */));
+ }
+
@Test
public void testSetPackageAuthorizationVpnService() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
@@ -981,7 +1180,7 @@
public void testSetPackageAuthorizationPlatformVpn() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
- assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, VpnManager.TYPE_VPN_PLATFORM));
+ assertTrue(vpn.setPackageAuthorization(TEST_VPN_PKG, TYPE_VPN_PLATFORM));
verify(mAppOps)
.setMode(
eq(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN),
@@ -1031,15 +1230,16 @@
config -> Arrays.asList(config.flags).contains(flag)));
}
- @Test
- public void testStartPlatformVpnAuthenticationFailed() throws Exception {
+ private void setupPlatformVpnWithSpecificExceptionAndItsErrorCode(IkeException exception,
+ String category, int errorType, int errorCode) throws Exception {
final ArgumentCaptor<IkeSessionCallback> captor =
ArgumentCaptor.forClass(IkeSessionCallback.class);
- final IkeProtocolException exception = mock(IkeProtocolException.class);
- when(exception.getErrorType())
- .thenReturn(IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED);
- final Vpn vpn = startLegacyVpn(createVpn(primaryUser.id), (mVpnProfile));
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ final String sessionKey = vpn.startVpnProfile(TEST_VPN_PKG);
final NetworkCallback cb = triggerOnAvailableAndGetCallback();
verifyInterfaceSetCfgWithFlags(IF_STATE_UP);
@@ -1049,10 +1249,75 @@
verify(mIkev2SessionCreator, timeout(TEST_TIMEOUT_MS))
.createIkeSession(any(), any(), any(), any(), captor.capture(), any());
final IkeSessionCallback ikeCb = captor.getValue();
- ikeCb.onClosedExceptionally(exception);
+ ikeCb.onClosedWithException(exception);
- verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS)).unregisterNetworkCallback(eq(cb));
- assertEquals(LegacyVpnInfo.STATE_FAILED, vpn.getLegacyVpnInfo().state);
+ verifyVpnManagerEvent(sessionKey, category, errorType, errorCode, null /* profileState */);
+ if (errorType == VpnManager.ERROR_CLASS_NOT_RECOVERABLE) {
+ verify(mConnectivityManager, timeout(TEST_TIMEOUT_MS))
+ .unregisterNetworkCallback(eq(cb));
+ }
+ }
+
+ @Test
+ public void testStartPlatformVpnAuthenticationFailed() throws Exception {
+ final IkeProtocolException exception = mock(IkeProtocolException.class);
+ final int errorCode = IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED;
+ when(exception.getErrorType()).thenReturn(errorCode);
+ setupPlatformVpnWithSpecificExceptionAndItsErrorCode(exception,
+ VpnManager.CATEGORY_EVENT_IKE_ERROR, VpnManager.ERROR_CLASS_NOT_RECOVERABLE,
+ errorCode);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithRecoverableError() throws Exception {
+ final IkeProtocolException exception = mock(IkeProtocolException.class);
+ final int errorCode = IkeProtocolException.ERROR_TYPE_TEMPORARY_FAILURE;
+ when(exception.getErrorType()).thenReturn(errorCode);
+ setupPlatformVpnWithSpecificExceptionAndItsErrorCode(exception,
+ VpnManager.CATEGORY_EVENT_IKE_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE, errorCode);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithUnknownHostException() throws Exception {
+ final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
+ final UnknownHostException unknownHostException = new UnknownHostException();
+ final int errorCode = VpnManager.ERROR_CODE_NETWORK_UNKNOWN_HOST;
+ when(exception.getCause()).thenReturn(unknownHostException);
+ setupPlatformVpnWithSpecificExceptionAndItsErrorCode(exception,
+ VpnManager.CATEGORY_EVENT_NETWORK_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE,
+ errorCode);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithIkeTimeoutException() throws Exception {
+ final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
+ final IkeTimeoutException ikeTimeoutException =
+ new IkeTimeoutException("IkeTimeoutException");
+ final int errorCode = VpnManager.ERROR_CODE_NETWORK_PROTOCOL_TIMEOUT;
+ when(exception.getCause()).thenReturn(ikeTimeoutException);
+ setupPlatformVpnWithSpecificExceptionAndItsErrorCode(exception,
+ VpnManager.CATEGORY_EVENT_NETWORK_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE,
+ errorCode);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithIkeNetworkLostException() throws Exception {
+ final IkeNetworkLostException exception = new IkeNetworkLostException(
+ new Network(100));
+ setupPlatformVpnWithSpecificExceptionAndItsErrorCode(exception,
+ VpnManager.CATEGORY_EVENT_NETWORK_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE,
+ VpnManager.ERROR_CODE_NETWORK_LOST);
+ }
+
+ @Test
+ public void testStartPlatformVpnFailedWithIOException() throws Exception {
+ final IkeNonProtocolException exception = mock(IkeNonProtocolException.class);
+ final IOException ioException = new IOException();
+ final int errorCode = VpnManager.ERROR_CODE_NETWORK_IO;
+ when(exception.getCause()).thenReturn(ioException);
+ setupPlatformVpnWithSpecificExceptionAndItsErrorCode(exception,
+ VpnManager.CATEGORY_EVENT_NETWORK_ERROR, VpnManager.ERROR_CLASS_RECOVERABLE,
+ errorCode);
}
@Test
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
index 5f9d1ff..13a85e8 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -86,11 +86,19 @@
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
+ private static final int PID_SYSTEM = 1234;
+ private static final int PID_RED = 1235;
+ private static final int PID_BLUE = 1236;
+
private static final int UID_RED = UserHandle.PER_USER_RANGE + 1;
private static final int UID_BLUE = UserHandle.PER_USER_RANGE + 2;
private static final int UID_GREEN = UserHandle.PER_USER_RANGE + 3;
private static final int UID_ANOTHER_USER = 2 * UserHandle.PER_USER_RANGE + 4;
+ private static final String PACKAGE_SYSTEM = "android";
+ private static final String PACKAGE_RED = "RED";
+ private static final String PACKAGE_BLUE = "BLUE";
+
private static final long WAIT_TIMEOUT_MS = 500;
private static final long THRESHOLD_BYTES = 2 * MB_IN_BYTES;
private static final long BASE_BYTES = 7 * MB_IN_BYTES;
@@ -131,14 +139,15 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, thresholdTooLowBytes);
final DataUsageRequest requestByApp = mStatsObservers.register(mContext, inputRequest,
- mUsageCallback, UID_RED, NetworkStatsAccess.Level.DEVICE);
+ mUsageCallback, PID_RED , UID_RED, PACKAGE_RED, NetworkStatsAccess.Level.DEVICE);
assertTrue(requestByApp.requestId > 0);
assertTrue(Objects.equals(sTemplateWifi, requestByApp.template));
assertEquals(thresholdTooLowBytes, requestByApp.thresholdInBytes);
// Verify the threshold requested by system uid won't be overridden.
final DataUsageRequest requestBySystem = mStatsObservers.register(mContext, inputRequest,
- mUsageCallback, Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ mUsageCallback, PID_SYSTEM, Process.SYSTEM_UID, PACKAGE_SYSTEM,
+ NetworkStatsAccess.Level.DEVICE);
assertTrue(requestBySystem.requestId > 0);
assertTrue(Objects.equals(sTemplateWifi, requestBySystem.template));
assertEquals(1, requestBySystem.thresholdInBytes);
@@ -151,7 +160,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, highThresholdBytes);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ PID_SYSTEM, Process.SYSTEM_UID, PACKAGE_SYSTEM, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateWifi, request.template));
assertEquals(highThresholdBytes, request.thresholdInBytes);
@@ -163,13 +172,13 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, THRESHOLD_BYTES);
DataUsageRequest request1 = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ PID_SYSTEM, Process.SYSTEM_UID, PACKAGE_SYSTEM, NetworkStatsAccess.Level.DEVICE);
assertTrue(request1.requestId > 0);
assertTrue(Objects.equals(sTemplateWifi, request1.template));
assertEquals(THRESHOLD_BYTES, request1.thresholdInBytes);
DataUsageRequest request2 = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ PID_SYSTEM, Process.SYSTEM_UID, PACKAGE_SYSTEM, NetworkStatsAccess.Level.DEVICE);
assertTrue(request2.requestId > request1.requestId);
assertTrue(Objects.equals(sTemplateWifi, request2.template));
assertEquals(THRESHOLD_BYTES, request2.thresholdInBytes);
@@ -189,7 +198,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ PID_SYSTEM, Process.SYSTEM_UID, PACKAGE_SYSTEM, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
@@ -209,7 +218,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- UID_RED, NetworkStatsAccess.Level.DEVICE);
+ PID_RED, UID_RED, PACKAGE_RED, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
@@ -237,7 +246,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ PID_SYSTEM, Process.SYSTEM_UID, PACKAGE_SYSTEM, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
@@ -261,7 +270,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ PID_SYSTEM, Process.SYSTEM_UID, PACKAGE_SYSTEM, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
@@ -291,7 +300,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- Process.SYSTEM_UID, NetworkStatsAccess.Level.DEVICE);
+ PID_SYSTEM, Process.SYSTEM_UID, PACKAGE_SYSTEM, NetworkStatsAccess.Level.DEVICE);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
@@ -322,7 +331,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- UID_RED, NetworkStatsAccess.Level.DEFAULT);
+ PID_RED, UID_RED, PACKAGE_SYSTEM , NetworkStatsAccess.Level.DEFAULT);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
@@ -355,7 +364,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- UID_BLUE, NetworkStatsAccess.Level.DEFAULT);
+ PID_BLUE, UID_BLUE, PACKAGE_BLUE, NetworkStatsAccess.Level.DEFAULT);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
@@ -387,7 +396,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- UID_BLUE, NetworkStatsAccess.Level.USER);
+ PID_BLUE, UID_BLUE, PACKAGE_BLUE, NetworkStatsAccess.Level.USER);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);
@@ -420,7 +429,7 @@
DataUsageRequest.REQUEST_ID_UNSET, sTemplateImsi1, THRESHOLD_BYTES);
DataUsageRequest request = mStatsObservers.register(mContext, inputRequest, mUsageCallback,
- UID_RED, NetworkStatsAccess.Level.USER);
+ PID_RED, UID_RED, PACKAGE_RED, NetworkStatsAccess.Level.USER);
assertTrue(request.requestId > 0);
assertTrue(Objects.equals(sTemplateImsi1, request.template));
assertEquals(THRESHOLD_BYTES, request.thresholdInBytes);