Merge "Add testTetheringUpstream cts test"
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index dbff179..6a7f1b2 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -21,6 +21,8 @@
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.cts.net.NetPolicyTestsPreparer" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
+ <target_preparer class="com.android.cts.net.NetworkPolicyTestsPreparer" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="teardown-command" value="cmd power set-mode 0" />
@@ -31,4 +33,9 @@
<option name="jar" value="CtsHostsideNetworkTests.jar" />
<option name="runtime-hint" value="3m56s" />
</test>
+
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="directory-keys" value="/sdcard/CtsHostsideNetworkTests" />
+ <option name="collect-on-run-ended-only" value="true" />
+ </metrics_collector>
</configuration>
diff --git a/tests/cts/hostside/app/Android.bp b/tests/cts/hostside/app/Android.bp
index 2362389..7a11456 100644
--- a/tests/cts/hostside/app/Android.bp
+++ b/tests/cts/hostside/app/Android.bp
@@ -20,6 +20,8 @@
//sdk_version: "current",
platform_apis: true,
static_libs: [
+ "androidx.test.rules",
+ "androidx.test.ext.junit",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"ub-uiautomator",
diff --git a/tests/cts/hostside/app/AndroidManifest.xml b/tests/cts/hostside/app/AndroidManifest.xml
index f54b5c1..3b00713 100644
--- a/tests/cts/hostside/app/AndroidManifest.xml
+++ b/tests/cts/hostside/app/AndroidManifest.xml
@@ -26,8 +26,9 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <application>
+ <application android:requestLegacyExternalStorage="true" >
<uses-library android:name="android.test.runner" />
<activity android:name=".MyActivity" />
<service android:name=".MyVpnService"
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 55bd406..d06ab13 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -16,20 +16,27 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.APP_STANDBY_MODE;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+
import android.os.SystemClock;
-import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
/**
* Base class for metered and non-metered tests on idle apps.
*/
+@RequiredProperties({APP_STANDBY_MODE})
abstract class AbstractAppIdleTestCase extends AbstractRestrictBackgroundNetworkTestCase {
- @Override
- protected final void setUp() throws Exception {
+ @Before
+ public final void setUp() throws Exception {
super.setUp();
- if (!isSupported()) return;
-
// Set initial state.
removePowerSaveModeWhitelist(TEST_APP2_PKG);
removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -39,41 +46,16 @@
registerBroadcastReceiver();
}
- @Override
- protected final void tearDown() throws Exception {
+ @After
+ public final void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
- try {
- tearDownMeteredNetwork();
- } finally {
- turnBatteryOff();
- setAppIdle(false);
- }
+ turnBatteryOff();
+ setAppIdle(false);
}
- @Override
- protected boolean isSupported() throws Exception {
- boolean supported = isDozeModeEnabled();
- if (!supported) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Doze Mode");
- }
- return supported;
- }
-
- /**
- * Resets the (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void tearDownMeteredNetwork() throws Exception {
- }
-
+ @Test
public void testBackgroundNetworkAccess_enabled() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -98,9 +80,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_whitelisted() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -127,9 +108,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_tempWhitelisted() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -140,23 +120,17 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_disabled() throws Exception {
- if (!isSupported()) return;
-
assertBackgroundNetworkAccess(true);
assertsForegroundAlwaysHasNetworkAccess();
assertBackgroundNetworkAccess(true);
}
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ @Test
public void testAppIdleNetworkAccess_whenCharging() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) return;
-
// Check that app is paroled when charging
setAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -180,9 +154,8 @@
assertBackgroundNetworkAccess(true);
}
+ @Test
public void testAppIdleNetworkAccess_idleWhitelisted() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -199,9 +172,8 @@
removeAppIdleWhitelist(mUid + 1);
}
+ @Test
public void testAppIdle_toast() throws Exception {
- if (!isSupported()) return;
-
setAppIdle(true);
assertAppIdle(true);
assertEquals("Shown", showToast());
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
index 931376b..04d054d 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -16,20 +16,22 @@
package com.android.cts.net.hostside;
-import android.text.TextUtils;
-import android.util.Log;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
/**
* Base class for metered and non-metered Battery Saver Mode tests.
*/
+@RequiredProperties({BATTERY_SAVER_MODE})
abstract class AbstractBatterySaverModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
- @Override
- protected final void setUp() throws Exception {
+ @Before
+ public final void setUp() throws Exception {
super.setUp();
- if (!isSupported()) return;
-
// Set initial state.
removePowerSaveModeWhitelist(TEST_APP2_PKG);
removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -38,55 +40,15 @@
registerBroadcastReceiver();
}
- @Override
- protected final void tearDown() throws Exception {
+ @After
+ public final void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
- try {
- tearDownMeteredNetwork();
- } finally {
- setBatterySaverMode(false);
- }
+ setBatterySaverMode(false);
}
- @Override
- protected boolean isSupported() throws Exception {
- String unSupported = "";
- if (!isDozeModeEnabled()) {
- unSupported += "Doze mode,";
- }
- if (!isBatterySaverSupported()) {
- unSupported += "Battery saver mode,";
- }
- if (!TextUtils.isEmpty(unSupported)) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support " + unSupported);
- return false;
- }
- return true;
- }
-
- /**
- * Sets the initial (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void setUpMeteredNetwork() throws Exception {
- }
-
- /**
- * Resets the (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void tearDownMeteredNetwork() throws Exception {
- }
-
+ @Test
public void testBackgroundNetworkAccess_enabled() throws Exception {
- if (!isSupported()) return;
-
setBatterySaverMode(true);
assertBackgroundNetworkAccess(false);
@@ -118,9 +80,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_whitelisted() throws Exception {
- if (!isSupported()) return;
-
setBatterySaverMode(true);
assertBackgroundNetworkAccess(false);
@@ -140,9 +101,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_disabled() throws Exception {
- if (!isSupported()) return;
-
assertBackgroundNetworkAccess(true);
assertsForegroundAlwaysHasNetworkAccess();
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
index f20f1d1..6f32c56 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -16,20 +16,25 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.DOZE_MODE;
+import static com.android.cts.net.hostside.Property.NOT_LOW_RAM_DEVICE;
+
import android.os.SystemClock;
-import android.util.Log;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
/**
* Base class for metered and non-metered Doze Mode tests.
*/
+@RequiredProperties({DOZE_MODE})
abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
- @Override
- protected final void setUp() throws Exception {
+ @Before
+ public final void setUp() throws Exception {
super.setUp();
- if (!isSupported()) return;
-
// Set initial state.
removePowerSaveModeWhitelist(TEST_APP2_PKG);
removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
@@ -38,48 +43,15 @@
registerBroadcastReceiver();
}
- @Override
- protected final void tearDown() throws Exception {
+ @After
+ public final void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
- try {
- tearDownMeteredNetwork();
- } finally {
- setDozeMode(false);
- }
+ setDozeMode(false);
}
- @Override
- protected boolean isSupported() throws Exception {
- boolean supported = isDozeModeEnabled();
- if (!supported) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Doze Mode");
- }
- return supported;
- }
-
- /**
- * Sets the initial (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void setUpMeteredNetwork() throws Exception {
- }
-
- /**
- * Resets the (non) metered network state.
- *
- * <p>By default is empty - it's up to subclasses to override.
- */
- protected void tearDownMeteredNetwork() throws Exception {
- }
-
+ @Test
public void testBackgroundNetworkAccess_enabled() throws Exception {
- if (!isSupported()) return;
-
setDozeMode(true);
assertBackgroundNetworkAccess(false);
@@ -96,9 +68,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_whitelisted() throws Exception {
- if (!isSupported()) return;
-
setDozeMode(true);
assertBackgroundNetworkAccess(false);
@@ -118,19 +89,18 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testBackgroundNetworkAccess_disabled() throws Exception {
- if (!isSupported()) return;
-
assertBackgroundNetworkAccess(true);
assertsForegroundAlwaysHasNetworkAccess();
assertBackgroundNetworkAccess(true);
}
+ @RequiredProperties({NOT_LOW_RAM_DEVICE})
+ @Test
public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
throws Exception {
- if (!isSupported() || isLowRamDevice()) return;
-
setPendingIntentWhitelistDuration(NETWORK_TIMEOUT_MS);
try {
registerNotificationListenerService();
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 40d7e34..c8fe624 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
@@ -17,14 +17,22 @@
package com.android.cts.net.hostside;
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
-import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
import static android.os.BatteryManager.BATTERY_PLUGGED_AC;
import static android.os.BatteryManager.BATTERY_PLUGGED_USB;
import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getContext;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getInstrumentation;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getWifiManager;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.app.ActivityManager;
import android.app.Instrumentation;
@@ -34,9 +42,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.wifi.WifiManager;
@@ -44,24 +50,27 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
-import android.test.InstrumentationTestCase;
-import android.text.TextUtils;
import android.util.Log;
-import com.android.compatibility.common.util.BatteryUtils;
+import org.junit.Rule;
+import org.junit.rules.RuleChain;
+import org.junit.runner.RunWith;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
/**
* Superclass for tests related to background network restrictions.
*/
-abstract class AbstractRestrictBackgroundNetworkTestCase extends InstrumentationTestCase {
- protected static final String TAG = "RestrictBackgroundNetworkTests";
+@RunWith(AndroidJUnit4.class)
+public abstract class AbstractRestrictBackgroundNetworkTestCase {
+ public static final String TAG = "RestrictBackgroundNetworkTests";
protected static final String TEST_PKG = "com.android.cts.net.hostside";
protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
@@ -98,8 +107,6 @@
static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
private static int PROCESS_STATE_FOREGROUND_SERVICE;
- private static final int PROCESS_STATE_TOP = 2;
-
private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
@@ -123,38 +130,31 @@
protected Context mContext;
protected Instrumentation mInstrumentation;
protected ConnectivityManager mCm;
- protected WifiManager mWfm;
protected int mUid;
private int mMyUid;
- private String mMeteredWifi;
private MyServiceClient mServiceClient;
private String mDeviceIdleConstantsSetting;
- private boolean mSupported;
- private boolean mIsLocationOn;
- @Override
+ @Rule
+ public final RuleChain mRuleChain = RuleChain.outerRule(new DumpOnFailureRule())
+ .around(new RequiredPropertiesRule())
+ .around(new MeterednessConfigurationRule());
+
protected void setUp() throws Exception {
- super.setUp();
PROCESS_STATE_FOREGROUND_SERVICE = (Integer) ActivityManager.class
.getDeclaredField("PROCESS_STATE_FOREGROUND_SERVICE").get(null);
mInstrumentation = getInstrumentation();
- mContext = mInstrumentation.getContext();
- mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
- mWfm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mContext = getContext();
+ mCm = getConnectivityManager();
mUid = getUid(TEST_APP2_PKG);
mMyUid = getUid(mContext.getPackageName());
mServiceClient = new MyServiceClient(mContext);
mServiceClient.bind();
mDeviceIdleConstantsSetting = "device_idle_constants";
- mIsLocationOn = isLocationOn();
- if (!mIsLocationOn) {
- enableLocation();
- }
- mSupported = setUpActiveNetworkMeteringState();
setAppIdle(false);
- Log.i(TAG, "Apps status on " + getName() + ":\n"
+ Log.i(TAG, "Apps status:\n"
+ "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
+ "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
@@ -165,37 +165,10 @@
final String currentConstants =
executeShellCommand("settings get global app_idle_constants");
assertEquals(appIdleConstants, currentConstants);
- }
+ }
- @Override
protected void tearDown() throws Exception {
- if (!mIsLocationOn) {
- disableLocation();
- }
mServiceClient.unbind();
-
- super.tearDown();
- }
-
- private void enableLocation() throws Exception {
- Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_SENSORS_ONLY);
- assertEquals(Settings.Secure.LOCATION_MODE_SENSORS_ONLY,
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE));
- }
-
- private void disableLocation() throws Exception {
- Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE,
- Settings.Secure.LOCATION_MODE_OFF);
- assertEquals(Settings.Secure.LOCATION_MODE_OFF,
- Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE));
- }
-
- private boolean isLocationOn() throws Exception {
- return Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.LOCATION_MODE) != Settings.Secure.LOCATION_MODE_OFF;
}
protected int getUid(String packageName) throws Exception {
@@ -259,23 +232,8 @@
protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception {
final String status = mServiceClient.getRestrictBackgroundStatus();
assertNotNull("didn't get API status from app2", status);
- final String actualStatus = toString(Integer.parseInt(status));
- assertEquals("wrong status", toString(expectedStatus), actualStatus);
- }
-
- protected void assertMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
- final int actualStatus = mCm.getRestrictBackgroundStatus();
- assertEquals("Wrong status", toString(expectedStatus), toString(actualStatus));
- }
-
- protected boolean isMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
- final int actualStatus = mCm.getRestrictBackgroundStatus();
- if (expectedStatus != actualStatus) {
- Log.d(TAG, "Expected: " + toString(expectedStatus)
- + " but actual: " + toString(actualStatus));
- return false;
- }
- return true;
+ assertEquals(restrictBackgroundValueToString(expectedStatus),
+ restrictBackgroundValueToString(Integer.parseInt(status)));
}
protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
@@ -298,28 +256,6 @@
}
/**
- * Whether this device suport this type of test.
- *
- * <p>Should be overridden when necessary (but always calling
- * {@code super.isSupported()} first), and explicitly used before each test
- * Example:
- *
- * <pre><code>
- * public void testSomething() {
- * if (!isSupported()) return;
- * </code></pre>
- *
- * @return {@code true} by default.
- */
- protected boolean isSupported() throws Exception {
- return mSupported;
- }
-
- protected boolean isBatterySaverSupported() {
- return BatteryUtils.isBatterySaverSupported();
- }
-
- /**
* Asserts that an app always have access while on foreground or running a foreground service.
*
* <p>This method will launch an activity and a foreground service to make the assertion, but
@@ -388,23 +324,6 @@
}
/**
- * As per CDD requirements, if the device doesn't support data saver mode then
- * ConnectivityManager.getRestrictBackgroundStatus() will always return
- * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
- * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
- * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
- */
- protected boolean isDataSaverSupported() throws Exception {
- assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
- try {
- setRestrictBackground(true);
- return !isMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
- } finally {
- setRestrictBackground(false);
- }
- }
-
- /**
* Returns whether an app state should be considered "background" for restriction purposes.
*/
protected boolean isBackground(int state) {
@@ -443,40 +362,10 @@
// Exponential back-off.
timeoutMs = Math.min(timeoutMs*2, NETWORK_TIMEOUT_MS);
}
- dumpOnFailure();
fail("Invalid state for expectAvailable=" + expectAvailable + " after " + maxTries
+ " attempts.\nLast error: " + error);
}
- private void dumpOnFailure() throws Exception {
- dumpAllNetworkRules();
- Log.d(TAG, "Usagestats dump: " + getUsageStatsDump());
- executeShellCommand("settings get global app_idle_constants");
- }
-
- private void dumpAllNetworkRules() throws Exception {
- final String networkManagementDump = runShellCommand(mInstrumentation,
- "dumpsys network_management").trim();
- final String networkPolicyDump = runShellCommand(mInstrumentation,
- "dumpsys netpolicy").trim();
- TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
- splitter.setString(networkManagementDump);
- String next;
- Log.d(TAG, ">>> Begin network_management dump");
- while (splitter.hasNext()) {
- next = splitter.next();
- Log.d(TAG, next);
- }
- Log.d(TAG, "<<< End network_management dump");
- splitter.setString(networkPolicyDump);
- Log.d(TAG, ">>> Begin netpolicy dump");
- while (splitter.hasNext()) {
- next = splitter.next();
- Log.d(TAG, next);
- }
- Log.d(TAG, "<<< End netpolicy dump");
- }
-
/**
* Checks whether the network is available as expected.
*
@@ -528,22 +417,10 @@
return errors.toString();
}
- protected boolean isLowRamDevice() {
- final ActivityManager am = (ActivityManager) mContext.getSystemService(
- Context.ACTIVITY_SERVICE);
- return am.isLowRamDevice();
- }
-
- protected String executeShellCommand(String command) throws Exception {
- final String result = runShellCommand(mInstrumentation, command).trim();
- if (DEBUG) Log.d(TAG, "Command '" + command + "' returned '" + result + "'");
- return result;
- }
-
/**
* Runs a Shell command which is not expected to generate output.
*/
- protected void executeSilentShellCommand(String command) throws Exception {
+ protected void executeSilentShellCommand(String command) {
final String result = executeShellCommand(command);
assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty());
}
@@ -572,10 +449,6 @@
});
}
- protected void assertDelayedShellCommand(String command, ExpectResultChecker checker)
- throws Exception {
- assertDelayedShellCommand(command, 5, 1, checker);
- }
protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
ExpectResultChecker checker) throws Exception {
String result = "";
@@ -592,159 +465,6 @@
+ " attempts. Last result: '" + result + "'");
}
- /**
- * Sets the initial metering state for the active network.
- *
- * <p>It's called on setup and by default does nothing - it's up to the
- * subclasses to override.
- *
- * @return whether the tests in the subclass are supported on this device.
- */
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return true;
- }
-
- /**
- * Makes sure the active network is not metered.
- *
- * <p>If the device does not supoprt un-metered networks (for example if it
- * only has cellular data but not wi-fi), it should return {@code false};
- * otherwise, it should return {@code true} (or fail if the un-metered
- * network could not be set).
- *
- * @return {@code true} if the network is now unmetered.
- */
- protected boolean setUnmeteredNetwork() throws Exception {
- final NetworkInfo info = mCm.getActiveNetworkInfo();
- assertNotNull("Could not get active network", info);
- if (!mCm.isActiveNetworkMetered()) {
- Log.d(TAG, "Active network is not metered: " + info);
- } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {
- Log.i(TAG, "Setting active WI-FI network as not metered: " + info );
- setWifiMeteredStatus(false);
- } else {
- Log.d(TAG, "Active network cannot be set to un-metered: " + info);
- return false;
- }
- assertActiveNetworkMetered(false); // Sanity check.
- return true;
- }
-
- /**
- * Enables metering on the active network if supported.
- *
- * <p>If the device does not support metered networks it should return
- * {@code false}; otherwise, it should return {@code true} (or fail if the
- * metered network could not be set).
- *
- * @return {@code true} if the network is now metered.
- */
- protected boolean setMeteredNetwork() throws Exception {
- final NetworkInfo info = mCm.getActiveNetworkInfo();
- final boolean metered = mCm.isActiveNetworkMetered();
- if (metered) {
- Log.d(TAG, "Active network already metered: " + info);
- return true;
- } else if (info.getType() != ConnectivityManager.TYPE_WIFI) {
- Log.w(TAG, "Active network does not support metering: " + info);
- return false;
- } else {
- Log.w(TAG, "Active network not metered: " + info);
- }
- final String netId = setWifiMeteredStatus(true);
-
- // Set flag so status is reverted on resetMeteredNetwork();
- mMeteredWifi = netId;
- // Sanity check.
- assertWifiMeteredStatus(netId, true);
- assertActiveNetworkMetered(true);
- return true;
- }
-
- /**
- * Resets the device metering state to what it was before the test started.
- *
- * <p>This reverts any metering changes made by {@code setMeteredNetwork}.
- */
- protected void resetMeteredNetwork() throws Exception {
- if (mMeteredWifi != null) {
- Log.i(TAG, "resetMeteredNetwork(): SID '" + mMeteredWifi
- + "' was set as metered by test case; resetting it");
- setWifiMeteredStatus(mMeteredWifi, false);
- assertActiveNetworkMetered(false); // Sanity check.
- }
- }
-
- private void assertActiveNetworkMetered(boolean expected) throws Exception {
- final int maxTries = 5;
- NetworkInfo info = null;
- for (int i = 1; i <= maxTries; i++) {
- info = mCm.getActiveNetworkInfo();
- if (info == null) {
- Log.v(TAG, "No active network info on attempt #" + i
- + "; sleeping 1s before polling again");
- } else if (mCm.isActiveNetworkMetered() != expected) {
- Log.v(TAG, "Wrong metered status for active network " + info + "; expected="
- + expected + "; sleeping 1s before polling again");
- } else {
- break;
- }
- Thread.sleep(SECOND_IN_MS);
- }
- assertNotNull("No active network after " + maxTries + " attempts", info);
- assertEquals("Wrong metered status for active network " + info, expected,
- mCm.isActiveNetworkMetered());
- }
-
- private String setWifiMeteredStatus(boolean metered) throws Exception {
- // We could call setWifiEnabled() here, but it might take sometime to be in a consistent
- // state (for example, if one of the saved network is not properly authenticated), so it's
- // better to let the hostside test take care of that.
- assertTrue("wi-fi is disabled", mWfm.isWifiEnabled());
- // TODO: if it's not guaranteed the device has wi-fi, we need to change the tests
- // to make the actual verification of restrictions optional.
- final String ssid = mWfm.getConnectionInfo().getSSID();
- return setWifiMeteredStatus(ssid, metered);
- }
-
- private String setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
- assertNotNull("null SSID", ssid);
- final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any.
- assertFalse("empty SSID", ssid.isEmpty());
-
- Log.i(TAG, "Setting wi-fi network " + netId + " metered status to " + metered);
- final String setCommand = "cmd netpolicy set metered-network " + netId + " " + metered;
- assertDelayedShellCommand(setCommand, "");
-
- return netId;
- }
-
- private void assertWifiMeteredStatus(String netId, boolean status) throws Exception {
- final String command = "cmd netpolicy list wifi-networks";
- final String expectedLine = netId + ";" + status;
- assertDelayedShellCommand(command, new ExpectResultChecker() {
-
- @Override
- public boolean isExpected(String result) {
- return result.contains(expectedLine);
- }
-
- @Override
- public String getExpected() {
- return "line containing " + expectedLine;
- }
- });
- }
-
- protected void setRestrictBackground(boolean enabled) throws Exception {
- executeShellCommand("cmd netpolicy set restrict-background " + enabled);
- final String output = executeShellCommand("cmd netpolicy get restrict-background ");
- final String expectedSuffix = enabled ? "enabled" : "disabled";
- // TODO: use MoreAsserts?
- assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
- output.endsWith(expectedSuffix));
- }
-
protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
assertRestrictBackgroundWhitelist(uid, true);
@@ -924,7 +644,7 @@
protected void setDozeMode(boolean enabled) throws Exception {
// Sanity check, since tests should check beforehand....
- assertTrue("Device does not support Doze Mode", isDozeModeEnabled());
+ assertTrue("Device does not support Doze Mode", isDozeModeSupported());
Log.i(TAG, "Setting Doze Mode to " + enabled);
if (enabled) {
@@ -944,43 +664,16 @@
assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
}
- protected boolean isDozeModeEnabled() throws Exception {
- final String result = executeShellCommand("cmd deviceidle enabled deep").trim();
- return result.equals("1");
- }
-
protected void setAppIdle(boolean enabled) throws Exception {
Log.i(TAG, "Setting app idle to " + enabled);
executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled );
assertAppIdle(enabled); // Sanity check
}
- private String getUsageStatsDump() throws Exception {
- final String output = runShellCommand(mInstrumentation, "dumpsys usagestats").trim();
- final StringBuilder sb = new StringBuilder();
- final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
- splitter.setString(output);
- String str;
- while (splitter.hasNext()) {
- str = splitter.next();
- if (str.contains("package=")
- && !str.contains(TEST_PKG) && !str.contains(TEST_APP2_PKG)) {
- continue;
- }
- if (str.trim().startsWith("config=") || str.trim().startsWith("time=")) {
- continue;
- }
- sb.append(str).append('\n');
- }
- return sb.toString();
- }
-
protected void assertAppIdle(boolean enabled) throws Exception {
try {
assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 15, 2, "Idle=" + enabled);
} catch (Throwable e) {
- Log.d(TAG, "UsageStats dump:\n" + getUsageStatsDump());
- executeShellCommand("settings get global app_idle_constants");
throw e;
}
}
@@ -1061,12 +754,10 @@
// App didn't come to foreground when the activity is started, so try again.
assertForegroundNetworkAccess();
} else {
- dumpOnFailure();
fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
}
}
} else {
- dumpOnFailure();
fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
}
} else {
@@ -1150,19 +841,6 @@
}
}
- private String toString(int status) {
- switch (status) {
- case RESTRICT_BACKGROUND_STATUS_DISABLED:
- return "DISABLED";
- case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
- return "WHITELISTED";
- case RESTRICT_BACKGROUND_STATUS_ENABLED:
- return "ENABLED";
- default:
- return "UNKNOWN_STATUS_" + status;
- }
- }
-
private ProcessState getProcessStateByUid(int uid) throws Exception {
return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid));
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
index 622d993..f1858d6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
@@ -16,15 +16,8 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
-
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setMeteredNetwork();
- }
-
- @Override
- protected void tearDownMeteredNetwork() throws Exception {
- resetMeteredNetwork();
- }
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
index bde71f9..e737a6d 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
@@ -16,9 +16,8 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setUnmeteredNetwork();
- }
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
index 3071cfe..c78ca2e 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
@@ -16,15 +16,8 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
-
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setMeteredNetwork();
- }
-
- @Override
- protected void tearDownMeteredNetwork() throws Exception {
- resetMeteredNetwork();
- }
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
index 6d3076f..fb52a54 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
@@ -16,10 +16,9 @@
package com.android.cts.net.hostside;
-public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setUnmeteredNetwork();
- }
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+@RequiredProperties({NON_METERED_NETWORK})
+public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index cfe6a73..aa2c914 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -20,24 +20,33 @@
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
-import android.util.Log;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NO_DATA_SAVER_MODE;
+
+import static org.junit.Assert.fail;
import com.android.compatibility.common.util.CddTest;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import androidx.test.filters.LargeTest;
+
+@RequiredProperties({DATA_SAVER_MODE, METERED_NETWORK})
+@LargeTest
public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
"com.android.providers.downloads"
};
- private boolean mIsDataSaverSupported;
-
- @Override
+ @Before
public void setUp() throws Exception {
super.setUp();
- mIsDataSaverSupported = isDataSaverSupported();
-
// Set initial state.
setRestrictBackground(false);
removeRestrictBackgroundWhitelist(mUid);
@@ -47,36 +56,15 @@
assertRestrictBackgroundChangedReceived(0);
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
- try {
- resetMeteredNetwork();
- } finally {
- setRestrictBackground(false);
- }
+ setRestrictBackground(false);
}
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setMeteredNetwork();
- }
-
- @Override
- protected boolean isSupported() throws Exception {
- if (!mIsDataSaverSupported) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Data Saver Mode");
- }
- return mIsDataSaverSupported && super.isSupported();
- }
-
+ @Test
public void testGetRestrictBackgroundStatus_disabled() throws Exception {
- if (!isSupported()) return;
-
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
// Sanity check: make sure status is always disabled, never whitelisted
@@ -88,9 +76,8 @@
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
}
+ @Test
public void testGetRestrictBackgroundStatus_whitelisted() throws Exception {
- if (!isSupported()) return;
-
setRestrictBackground(true);
assertRestrictBackgroundChangedReceived(1);
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -107,9 +94,8 @@
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
}
+ @Test
public void testGetRestrictBackgroundStatus_enabled() throws Exception {
- if (!isSupported()) return;
-
setRestrictBackground(true);
assertRestrictBackgroundChangedReceived(1);
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -142,9 +128,8 @@
assertBackgroundNetworkAccess(false);
}
+ @Test
public void testGetRestrictBackgroundStatus_blacklisted() throws Exception {
- if (!isSupported()) return;
-
addRestrictBackgroundBlacklist(mUid);
assertRestrictBackgroundChangedReceived(1);
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
@@ -180,9 +165,8 @@
assertsForegroundAlwaysHasNetworkAccess();
}
+ @Test
public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
- if (!isSupported()) return;
-
final StringBuilder error = new StringBuilder();
for (String packageName : REQUIRED_WHITELISTED_PACKAGES) {
int uid = -1;
@@ -202,10 +186,10 @@
}
}
+ @RequiredProperties({NO_DATA_SAVER_MODE})
@CddTest(requirement="7.4.7/C-2-2")
+ @Test
public void testBroadcastNotSentOnUnsupportedDevices() throws Exception {
- if (isSupported()) return;
-
setRestrictBackground(true);
assertRestrictBackgroundChangedReceived(0);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
index e4189af..4306c99 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
@@ -16,15 +16,8 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+
+@RequiredProperties({METERED_NETWORK})
public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
-
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setMeteredNetwork();
- }
-
- @Override
- protected void tearDownMeteredNetwork() throws Exception {
- resetMeteredNetwork();
- }
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
index edbbb9e..1e89f15 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
@@ -16,10 +16,8 @@
package com.android.cts.net.hostside;
-public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
- @Override
- protected boolean setUpActiveNetworkMeteringState() throws Exception {
- return setUnmeteredNetwork();
- }
+@RequiredProperties({NON_METERED_NETWORK})
+public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
new file mode 100644
index 0000000..cedd62a
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DumpOnFailureRule.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 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.AbstractRestrictBackgroundNetworkTestCase.TAG;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TEST_PKG;
+
+import android.os.Environment;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import com.android.compatibility.common.util.OnFailureRule;
+
+import org.junit.AssumptionViolatedException;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+public class DumpOnFailureRule extends OnFailureRule {
+ private File mDumpDir = new File(Environment.getExternalStorageDirectory(),
+ "CtsHostsideNetworkTests");
+
+ @Override
+ public void onTestFailure(Statement base, Description description, Throwable throwable) {
+ final String testName = description.getClassName() + "_" + description.getMethodName();
+
+ if (throwable instanceof AssumptionViolatedException) {
+ Log.d(TAG, "Skipping test " + testName + ": " + throwable);
+ return;
+ }
+
+ prepareDumpRootDir();
+ final File dumpFile = new File(mDumpDir, "dump-" + testName);
+ Log.i(TAG, "Dumping debug info for " + description + ": " + dumpFile.getPath());
+ try (FileOutputStream out = new FileOutputStream(dumpFile)) {
+ for (String cmd : new String[] {
+ "dumpsys netpolicy",
+ "dumpsys network_management",
+ "dumpsys usagestats " + TEST_PKG,
+ "dumpsys usagestats appstandby",
+ }) {
+ dumpCommandOutput(out, cmd);
+ }
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Error opening file: " + dumpFile, e);
+ } catch (IOException e) {
+ Log.e(TAG, "Error closing file: " + dumpFile, e);
+ }
+ }
+
+ void dumpCommandOutput(FileOutputStream out, String cmd) {
+ final ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().executeShellCommand(cmd);
+ try (FileInputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
+ out.write(("Output of '" + cmd + "':\n").getBytes(StandardCharsets.UTF_8));
+ FileUtils.copy(in, out);
+ out.write("\n\n=================================================================\n\n"
+ .getBytes(StandardCharsets.UTF_8));
+ } catch (IOException e) {
+ Log.e(TAG, "Error dumping '" + cmd + "'", e);
+ }
+ }
+
+ void prepareDumpRootDir() {
+ if (!mDumpDir.exists() && !mDumpDir.mkdir()) {
+ Log.e(TAG, "Error creating " + mDumpDir);
+ }
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
new file mode 100644
index 0000000..8fadf9e
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MeterednessConfigurationRule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 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.resetMeteredNetwork;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setupMeteredNetwork;
+import static com.android.cts.net.hostside.Property.METERED_NETWORK;
+import static com.android.cts.net.hostside.Property.NON_METERED_NETWORK;
+
+import android.util.ArraySet;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+public class MeterednessConfigurationRule extends BeforeAfterRule {
+ private Pair<String, Boolean> mSsidAndInitialMeteredness;
+
+ @Override
+ public void onBefore(Statement base, Description description) throws Throwable {
+ final ArraySet<Property> requiredProperties
+ = RequiredPropertiesRule.getRequiredProperties();
+ if (requiredProperties.contains(METERED_NETWORK)) {
+ configureNetworkMeteredness(true);
+ } else if (requiredProperties.contains(NON_METERED_NETWORK)) {
+ configureNetworkMeteredness(false);
+ }
+ }
+
+ @Override
+ public void onAfter(Statement base, Description description) throws Throwable {
+ resetNetworkMeteredness();
+ }
+
+ public void configureNetworkMeteredness(boolean metered) throws Exception {
+ mSsidAndInitialMeteredness = setupMeteredNetwork(metered);
+ }
+
+ public void resetNetworkMeteredness() throws Exception {
+ if (mSsidAndInitialMeteredness != null) {
+ resetMeteredNetwork(mSsidAndInitialMeteredness.first,
+ mSsidAndInitialMeteredness.second);
+ }
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
index b1a2186..c9edda6 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -15,9 +15,21 @@
*/
package com.android.cts.net.hostside;
+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.os.SystemClock;
import android.util.Log;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
/**
* Test cases for the more complex scenarios where multiple restrictions (like Battery Saver Mode
* and Data Saver Mode) are applied simultaneously.
@@ -29,12 +41,10 @@
public class MixedModesTest extends AbstractRestrictBackgroundNetworkTestCase {
private static final String TAG = "MixedModesTest";
- @Override
+ @Before
public void setUp() throws Exception {
super.setUp();
- if (!isSupported()) return;
-
// Set initial state.
removeRestrictBackgroundWhitelist(mUid);
removeRestrictBackgroundBlacklist(mUid);
@@ -44,12 +54,10 @@
registerBroadcastReceiver();
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
super.tearDown();
- if (!isSupported()) return;
-
try {
setRestrictBackground(false);
} finally {
@@ -57,34 +65,15 @@
}
}
- @Override
- public boolean isSupported() throws Exception {
- if (!isDozeModeEnabled()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Doze Mode");
- return false;
- }
- return true;
- }
-
/**
* Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on metered networks.
*/
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, METERED_NETWORK})
+ @Test
public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) return;
-
- Log.i(TAG, "testDataAndBatterySaverModes_meteredNetwork() tests");
- if (!setMeteredNetwork()) {
- Log.w(TAG, "testDataAndBatterySaverModes_meteredNetwork() skipped because "
- + "device cannot use a metered network");
- return;
- }
-
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(true);
try {
setRestrictBackground(true);
setBatterySaverMode(true);
@@ -137,7 +126,7 @@
removeRestrictBackgroundBlacklist(mUid);
removePowerSaveModeWhitelist(TEST_APP2_PKG);
} finally {
- resetMeteredNetwork();
+ meterednessConfiguration.resetNetworkMeteredness();
}
}
@@ -145,86 +134,75 @@
* Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on non-metered
* networks.
*/
+ @RequiredProperties({DATA_SAVER_MODE, BATTERY_SAVER_MODE, NON_METERED_NETWORK})
+ @Test
public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(false);
+ try {
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+
+ Log.v(TAG, "Not whitelisted for any.");
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+ addRestrictBackgroundWhitelist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ removeRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+ Log.v(TAG, "Whitelisted for both.");
+ addRestrictBackgroundWhitelist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundBlacklist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removeRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
}
- if (!isSupported()) return;
-
- if (!setUnmeteredNetwork()) {
- Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because network"
- + " is metered");
- return;
- }
- Log.i(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() tests");
- setRestrictBackground(true);
- setBatterySaverMode(true);
-
- Log.v(TAG, "Not whitelisted for any.");
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
-
- Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
- addRestrictBackgroundWhitelist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundWhitelist(mUid);
-
- Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- removeRestrictBackgroundWhitelist(mUid);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
-
- Log.v(TAG, "Whitelisted for both.");
- addRestrictBackgroundWhitelist(mUid);
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundWhitelist(mUid);
-
- Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
- addRestrictBackgroundBlacklist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(false);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(false);
- removeRestrictBackgroundBlacklist(mUid);
-
- Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
- addRestrictBackgroundBlacklist(mUid);
- addPowerSaveModeWhitelist(TEST_APP2_PKG);
- assertBackgroundNetworkAccess(true);
- assertsForegroundAlwaysHasNetworkAccess();
- assertBackgroundNetworkAccess(true);
- removeRestrictBackgroundBlacklist(mUid);
- removePowerSaveModeWhitelist(TEST_APP2_PKG);
}
/**
* Tests that powersave whitelists works as expected when doze and battery saver modes
* are enabled.
*/
+ @RequiredProperties({DOZE_MODE, BATTERY_SAVER_MODE})
+ @Test
public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) {
- return;
- }
-
setBatterySaverMode(true);
setDozeMode(true);
@@ -250,11 +228,9 @@
* Tests that powersave whitelists works as expected when doze and appIdle modes
* are enabled.
*/
+ @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+ @Test
public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
- if (!isSupported()) {
- return;
- }
-
setDozeMode(true);
setAppIdle(true);
@@ -276,11 +252,9 @@
}
}
+ @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+ @Test
public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
- if (!isSupported()) {
- return;
- }
-
setDozeMode(true);
setAppIdle(true);
@@ -299,16 +273,9 @@
}
}
+ @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+ @Test
public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) {
- return;
- }
-
setBatterySaverMode(true);
setAppIdle(true);
@@ -330,11 +297,9 @@
/**
* Tests that the app idle whitelist works as expected when doze and appIdle mode are enabled.
*/
+ @RequiredProperties({DOZE_MODE, APP_STANDBY_MODE})
+ @Test
public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
- if (!isSupported()) {
- return;
- }
-
setDozeMode(true);
setAppIdle(true);
@@ -353,11 +318,9 @@
}
}
+ @RequiredProperties({APP_STANDBY_MODE, DOZE_MODE})
+ @Test
public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- if (!isSupported()) {
- return;
- }
-
setDozeMode(true);
setAppIdle(true);
@@ -380,16 +343,9 @@
}
}
+ @RequiredProperties({APP_STANDBY_MODE, BATTERY_SAVER_MODE})
+ @Test
public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
- if (!isBatterySaverSupported()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Battery saver mode");
- return;
- }
- if (!isSupported()) {
- return;
- }
-
setBatterySaverMode(true);
setAppIdle(true);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
index 24dde9d..ed397b9 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -16,15 +16,26 @@
package com.android.cts.net.hostside;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.setRestrictBackground;
+import static com.android.cts.net.hostside.Property.BATTERY_SAVER_MODE;
+import static com.android.cts.net.hostside.Property.DATA_SAVER_MODE;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
import android.net.Network;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
import java.util.Objects;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
- private boolean mIsDataSaverSupported;
private Network mNetwork;
private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
@@ -132,108 +143,122 @@
}
}
- @Override
+ @Before
public void setUp() throws Exception {
super.setUp();
- mIsDataSaverSupported = isDataSaverSupported();
-
mNetwork = mCm.getActiveNetwork();
- // Set initial state.
- setBatterySaverMode(false);
registerBroadcastReceiver();
- if (!mIsDataSaverSupported) return;
- setRestrictBackground(false);
removeRestrictBackgroundWhitelist(mUid);
removeRestrictBackgroundBlacklist(mUid);
assertRestrictBackgroundChangedReceived(0);
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
super.tearDown();
- if (!mIsDataSaverSupported) return;
+ setRestrictBackground(false);
+ setBatterySaverMode(false);
+ }
+ @RequiredProperties({DATA_SAVER_MODE})
+ @Test
+ public void testOnBlockedStatusChanged_dataSaver() throws Exception {
+ // Initial state
+ setBatterySaverMode(false);
+ setRestrictBackground(false);
+
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(true);
try {
- resetMeteredNetwork();
+ // Register callback
+ registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+ mTestNetworkCallback.expectAvailableCallback(mNetwork);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+ // Enable restrict background
+ setRestrictBackground(true);
+ assertBackgroundNetworkAccess(false);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+
+ // Add to whitelist
+ addRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+ // Remove from whitelist
+ removeRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(false);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
} finally {
+ meterednessConfiguration.resetNetworkMeteredness();
+ }
+
+ // Set to non-metered network
+ meterednessConfiguration.configureNetworkMeteredness(false);
+ try {
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+ // Disable restrict background, should not trigger callback
setRestrictBackground(false);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.assertNoCallback();
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
}
}
- public void testOnBlockedStatusChanged_data_saver() throws Exception {
- if (!mIsDataSaverSupported) return;
-
- // Prepare metered wifi
- if (!setMeteredNetwork()) return;
-
- // Register callback
- registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
- mTestNetworkCallback.expectAvailableCallback(mNetwork);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
- // Enable restrict background
- setRestrictBackground(true);
- assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
- // Add to whitelist
- addRestrictBackgroundWhitelist(mUid);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
- // Remove from whitelist
- removeRestrictBackgroundWhitelist(mUid);
- assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
- // Set to non-metered network
- setUnmeteredNetwork();
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
- // Disable restrict background, should not trigger callback
+ @RequiredProperties({BATTERY_SAVER_MODE})
+ @Test
+ public void testOnBlockedStatusChanged_powerSaver() throws Exception {
+ // Set initial state.
+ setBatterySaverMode(false);
setRestrictBackground(false);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.assertNoCallback();
- }
+ final MeterednessConfigurationRule meterednessConfiguration
+ = new MeterednessConfigurationRule();
+ meterednessConfiguration.configureNetworkMeteredness(true);
+ try {
+ // Register callback
+ registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+ mTestNetworkCallback.expectAvailableCallback(mNetwork);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
- public void testOnBlockedStatusChanged_power_saver() throws Exception {
- // Prepare metered wifi
- if (!setMeteredNetwork()) return;
+ // Enable Power Saver
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
- // Register callback
- registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
- mTestNetworkCallback.expectAvailableCallback(mNetwork);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
-
- // Enable Power Saver
- setBatterySaverMode(true);
- assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
-
- // Disable Power Saver
- setBatterySaverMode(false);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ // Disable Power Saver
+ setBatterySaverMode(false);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
+ }
// Set to non-metered network
- setUnmeteredNetwork();
- mTestNetworkCallback.assertNoCallback();
+ meterednessConfiguration.configureNetworkMeteredness(false);
+ try {
+ mTestNetworkCallback.assertNoCallback();
- // Enable Power Saver
- setBatterySaverMode(true);
- assertBackgroundNetworkAccess(false);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+ // Enable Power Saver
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
- // Disable Power Saver
- setBatterySaverMode(false);
- assertBackgroundNetworkAccess(true);
- mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ // Disable Power Saver
+ setBatterySaverMode(false);
+ assertBackgroundNetworkAccess(true);
+ mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+ } finally {
+ meterednessConfiguration.resetNetworkMeteredness();
+ }
}
// TODO: 1. test against VPN lockdown.
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
new file mode 100644
index 0000000..3807d79
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkPolicyTestUtils.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2019 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 android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static com.android.cts.net.hostside.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.location.LocationManager;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.wifi.WifiManager;
+import android.os.Process;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.compatibility.common.util.AppStandbyUtils;
+import com.android.compatibility.common.util.BatteryUtils;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+public class NetworkPolicyTestUtils {
+
+ private static final int TIMEOUT_CHANGE_METEREDNESS_MS = 5000;
+
+ private static ConnectivityManager mCm;
+ private static WifiManager mWm;
+
+ private static Boolean mBatterySaverSupported;
+ private static Boolean mDataSaverSupported;
+ private static Boolean mDozeModeSupported;
+ private static Boolean mAppStandbySupported;
+
+ private NetworkPolicyTestUtils() {}
+
+ public static boolean isBatterySaverSupported() {
+ if (mBatterySaverSupported == null) {
+ mBatterySaverSupported = BatteryUtils.isBatterySaverSupported();
+ }
+ return mBatterySaverSupported;
+ }
+
+ /**
+ * As per CDD requirements, if the device doesn't support data saver mode then
+ * ConnectivityManager.getRestrictBackgroundStatus() will always return
+ * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
+ * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
+ * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
+ */
+ public static boolean isDataSaverSupported() {
+ if (mDataSaverSupported == null) {
+ assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+ try {
+ setRestrictBackground(true);
+ mDataSaverSupported = !isMyRestrictBackgroundStatus(
+ RESTRICT_BACKGROUND_STATUS_DISABLED);
+ } finally {
+ setRestrictBackground(false);
+ }
+ }
+ return mDataSaverSupported;
+ }
+
+ public static boolean isDozeModeSupported() {
+ if (mDozeModeSupported == null) {
+ final String result = executeShellCommand("cmd deviceidle enabled deep");
+ mDozeModeSupported = result.equals("1");
+ }
+ return mDozeModeSupported;
+ }
+
+ public static boolean isAppStandbySupported() {
+ if (mAppStandbySupported == null) {
+ mAppStandbySupported = AppStandbyUtils.isAppStandbyEnabled();
+ }
+ return mAppStandbySupported;
+ }
+
+ public static boolean isLowRamDevice() {
+ final ActivityManager am = (ActivityManager) getContext().getSystemService(
+ Context.ACTIVITY_SERVICE);
+ return am.isLowRamDevice();
+ }
+
+ public static boolean isLocationEnabled() {
+ final LocationManager lm = (LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE);
+ return lm.isLocationEnabled();
+ }
+
+ public static void setLocationEnabled(boolean enabled) {
+ final LocationManager lm = (LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE);
+ lm.setLocationEnabledForUser(enabled, Process.myUserHandle());
+ assertEquals("Couldn't change location enabled state", lm.isLocationEnabled(), enabled);
+ Log.d(TAG, "Changed location enabled state to " + enabled);
+ }
+
+ public static boolean isActiveNetworkMetered(boolean metered) {
+ return getConnectivityManager().isActiveNetworkMetered() == metered;
+ }
+
+ public static boolean canChangeActiveNetworkMeteredness() {
+ final Network activeNetwork = getConnectivityManager().getActiveNetwork();
+ final NetworkCapabilities networkCapabilities
+ = getConnectivityManager().getNetworkCapabilities(activeNetwork);
+ return networkCapabilities.hasTransport(TRANSPORT_WIFI);
+ }
+
+ public static Pair<String, Boolean> setupMeteredNetwork(boolean metered) throws Exception {
+ if (isActiveNetworkMetered(metered)) {
+ return null;
+ }
+ final boolean isLocationEnabled = isLocationEnabled();
+ try {
+ if (!isLocationEnabled) {
+ setLocationEnabled(true);
+ }
+ final String ssid = unquoteSSID(getWifiManager().getConnectionInfo().getSSID());
+ assertNotEquals(WifiManager.UNKNOWN_SSID, ssid);
+ setWifiMeteredStatus(ssid, metered);
+ return Pair.create(ssid, !metered);
+ } finally {
+ // Reset the location enabled state
+ if (!isLocationEnabled) {
+ setLocationEnabled(false);
+ }
+ }
+ }
+
+ public static void resetMeteredNetwork(String ssid, boolean metered) throws Exception {
+ setWifiMeteredStatus(ssid, metered);
+ }
+
+ public static void setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
+ assertFalse("SSID should not be empty", TextUtils.isEmpty(ssid));
+ final String cmd = "cmd netpolicy set metered-network " + ssid + " " + metered;
+ executeShellCommand(cmd);
+ assertWifiMeteredStatus(ssid, metered);
+ assertActiveNetworkMetered(metered);
+ }
+
+ public static void assertWifiMeteredStatus(String ssid, boolean expectedMeteredStatus) {
+ final String result = executeShellCommand("cmd netpolicy list wifi-networks");
+ final String expectedLine = ssid + ";" + expectedMeteredStatus;
+ assertTrue("Expected line: " + expectedLine + "; Actual result: " + result,
+ result.contains(expectedLine));
+ }
+
+ // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+ public static void assertActiveNetworkMetered(boolean expectedMeteredStatus) throws Exception {
+ final CountDownLatch latch = new CountDownLatch(1);
+ final NetworkCallback networkCallback = new NetworkCallback() {
+ @Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+ final boolean metered = !nc.hasCapability(NET_CAPABILITY_NOT_METERED);
+ if (metered == expectedMeteredStatus) {
+ latch.countDown();
+ }
+ }
+ };
+ // Registering a callback here guarantees onCapabilitiesChanged is called immediately
+ // with the current setting. Therefore, if the setting has already been changed,
+ // this method will return right away, and if not it will wait for the setting to change.
+ getConnectivityManager().registerDefaultNetworkCallback(networkCallback);
+ if (!latch.await(TIMEOUT_CHANGE_METEREDNESS_MS, TimeUnit.MILLISECONDS)) {
+ fail("Timed out waiting for active network metered status to change to "
+ + expectedMeteredStatus + " ; network = "
+ + getConnectivityManager().getActiveNetwork());
+ }
+ getConnectivityManager().unregisterNetworkCallback(networkCallback);
+ }
+
+ public static void setRestrictBackground(boolean enabled) {
+ executeShellCommand("cmd netpolicy set restrict-background " + enabled);
+ final String output = executeShellCommand("cmd netpolicy get restrict-background");
+ final String expectedSuffix = enabled ? "enabled" : "disabled";
+ assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
+ output.endsWith(expectedSuffix));
+ }
+
+ public static boolean isMyRestrictBackgroundStatus(int expectedStatus) {
+ final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+ if (expectedStatus != actualStatus) {
+ Log.d(TAG, "MyRestrictBackgroundStatus: "
+ + "Expected: " + restrictBackgroundValueToString(expectedStatus)
+ + "; Actual: " + restrictBackgroundValueToString(actualStatus));
+ return false;
+ }
+ return true;
+ }
+
+ // Copied from cts/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+ private static String unquoteSSID(String ssid) {
+ // SSID is returned surrounded by quotes if it can be decoded as UTF-8.
+ // Otherwise it's guaranteed not to start with a quote.
+ if (ssid.charAt(0) == '"') {
+ return ssid.substring(1, ssid.length() - 1);
+ } else {
+ return ssid;
+ }
+ }
+
+ public static String restrictBackgroundValueToString(int status) {
+ switch (status) {
+ case RESTRICT_BACKGROUND_STATUS_DISABLED:
+ return "DISABLED";
+ case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+ return "WHITELISTED";
+ case RESTRICT_BACKGROUND_STATUS_ENABLED:
+ return "ENABLED";
+ default:
+ return "UNKNOWN_STATUS_" + status;
+ }
+ }
+
+ public static String executeShellCommand(String command) {
+ final String result = runShellCommand(command).trim();
+ Log.d(TAG, "Output of '" + command + "': '" + result + "'");
+ return result;
+ }
+
+ public static void assertMyRestrictBackgroundStatus(int expectedStatus) {
+ final int actualStatus = getConnectivityManager().getRestrictBackgroundStatus();
+ assertEquals(restrictBackgroundValueToString(expectedStatus),
+ restrictBackgroundValueToString(actualStatus));
+ }
+
+ public static ConnectivityManager getConnectivityManager() {
+ if (mCm == null) {
+ mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+ return mCm;
+ }
+
+ public static WifiManager getWifiManager() {
+ if (mWm == null) {
+ mWm = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+ }
+ return mWm;
+ }
+
+ public static Context getContext() {
+ return getInstrumentation().getContext();
+ }
+
+ public static Instrumentation getInstrumentation() {
+ return InstrumentationRegistry.getInstrumentation();
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/Property.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/Property.java
new file mode 100644
index 0000000..18805f9
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/Property.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 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.canChangeActiveNetworkMeteredness;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isActiveNetworkMetered;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isAppStandbySupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isBatterySaverSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDataSaverSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported;
+import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isLowRamDevice;
+
+public enum Property {
+ BATTERY_SAVER_MODE(1 << 0) {
+ public boolean isSupported() { return isBatterySaverSupported(); }
+ },
+
+ DATA_SAVER_MODE(1 << 1) {
+ public boolean isSupported() { return isDataSaverSupported(); }
+ },
+
+ NO_DATA_SAVER_MODE(~DATA_SAVER_MODE.getValue()) {
+ public boolean isSupported() { return !isDataSaverSupported(); }
+ },
+
+ DOZE_MODE(1 << 2) {
+ public boolean isSupported() { return isDozeModeSupported(); }
+ },
+
+ APP_STANDBY_MODE(1 << 3) {
+ public boolean isSupported() { return isAppStandbySupported(); }
+ },
+
+ NOT_LOW_RAM_DEVICE(1 << 4) {
+ public boolean isSupported() { return !isLowRamDevice(); }
+ },
+
+ METERED_NETWORK(1 << 5) {
+ public boolean isSupported() {
+ return isActiveNetworkMetered(true) || canChangeActiveNetworkMeteredness();
+ }
+ },
+
+ NON_METERED_NETWORK(~METERED_NETWORK.getValue()) {
+ public boolean isSupported() {
+ return isActiveNetworkMetered(false) || canChangeActiveNetworkMeteredness();
+ }
+ };
+
+ private int mValue;
+
+ Property(int value) { mValue = value; }
+
+ public int getValue() { return mValue; }
+
+ abstract boolean isSupported();
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredProperties.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredProperties.java
new file mode 100644
index 0000000..96838bb
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredProperties.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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 java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target({METHOD, TYPE})
+@Inherited
+public @interface RequiredProperties {
+ Property[] value();
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java
new file mode 100644
index 0000000..01f9f3e
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RequiredPropertiesRule.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 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.AbstractRestrictBackgroundNetworkTestCase.TAG;
+
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.compatibility.common.util.BeforeAfterRule;
+
+import org.junit.Assume;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class RequiredPropertiesRule extends BeforeAfterRule {
+
+ private static ArraySet<Property> mRequiredProperties;
+
+ @Override
+ public void onBefore(Statement base, Description description) {
+ mRequiredProperties = getAllRequiredProperties(description);
+
+ final String testName = description.getClassName() + "#" + description.getMethodName();
+ assertTestIsValid(testName, mRequiredProperties);
+ Log.i(TAG, "Running test " + testName + " with required properties: "
+ + propertiesToString(mRequiredProperties));
+ }
+
+ private ArraySet<Property> getAllRequiredProperties(Description description) {
+ final ArraySet<Property> allRequiredProperties = new ArraySet<>();
+ RequiredProperties requiredProperties = description.getAnnotation(RequiredProperties.class);
+ if (requiredProperties != null) {
+ Collections.addAll(allRequiredProperties, requiredProperties.value());
+ }
+
+ for (Class<?> clazz = description.getTestClass();
+ clazz != null; clazz = clazz.getSuperclass()) {
+ requiredProperties = clazz.getDeclaredAnnotation(RequiredProperties.class);
+ if (requiredProperties == null) {
+ continue;
+ }
+ for (Property requiredProperty : requiredProperties.value()) {
+ for (Property p : Property.values()) {
+ if (p.getValue() == ~requiredProperty.getValue()
+ && allRequiredProperties.contains(p)) {
+ continue;
+ }
+ }
+ allRequiredProperties.add(requiredProperty);
+ }
+ }
+ return allRequiredProperties;
+ }
+
+ private void assertTestIsValid(String testName, ArraySet<Property> requiredProperies) {
+ if (requiredProperies == null) {
+ return;
+ }
+ final ArrayList<Property> unsupportedProperties = new ArrayList<>();
+ for (Property property : requiredProperies) {
+ if (!property.isSupported()) {
+ unsupportedProperties.add(property);
+ }
+ }
+ Assume.assumeTrue("Unsupported properties: "
+ + propertiesToString(unsupportedProperties), unsupportedProperties.isEmpty());
+ }
+
+ public static ArraySet<Property> getRequiredProperties() {
+ return mRequiredProperties;
+ }
+
+ private static String propertiesToString(Iterable<Property> properties) {
+ return "[" + TextUtils.join(",", properties) + "]";
+ }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
index 8d6c4ac..1312085 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
@@ -29,14 +29,14 @@
uninstallPackage(TEST_APP2_PKG, true);
}
- public void testOnBlockedStatusChanged_data_saver() throws Exception {
+ public void testOnBlockedStatusChanged_dataSaver() throws Exception {
runDeviceTests(TEST_PKG,
- TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_data_saver");
+ TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_dataSaver");
}
- public void testOnBlockedStatusChanged_power_saver() throws Exception {
+ public void testOnBlockedStatusChanged_powerSaver() throws Exception {
runDeviceTests(TEST_PKG,
- TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_power_saver");
+ TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_powerSaver");
}
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
index a2443b3..ce20379 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -79,7 +79,8 @@
protected void installPackage(String apk) throws FileNotFoundException,
DeviceNotAvailableException {
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
- assertNull(getDevice().installPackage(buildHelper.getTestFile(apk), false));
+ assertNull(getDevice().installPackage(buildHelper.getTestFile(apk),
+ false /* reinstall */, true /* grantPermissions */));
}
protected void uninstallPackage(String packageName, boolean shouldSucceed)
diff --git a/tests/cts/hostside/src/com/android/cts/net/NetworkPolicyTestsPreparer.java b/tests/cts/hostside/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
new file mode 100644
index 0000000..dfce7da
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/NetworkPolicyTestsPreparer.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.TestInformation;
+import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.targetprep.ITargetPreparer;
+
+public class NetworkPolicyTestsPreparer implements ITargetPreparer {
+ private ITestDevice mDevice;
+ private String mOriginalAppStandbyEnabled;
+
+ @Override
+ public void setUp(TestInformation testInformation) throws DeviceNotAvailableException {
+ mDevice = testInformation.getDevice();
+ mOriginalAppStandbyEnabled = getAppStandbyEnabled();
+ setAppStandbyEnabled("1");
+ LogUtil.CLog.d("Original app_standby_enabled: " + mOriginalAppStandbyEnabled);
+ }
+
+ @Override
+ public void tearDown(TestInformation testInformation, Throwable e)
+ throws DeviceNotAvailableException {
+ setAppStandbyEnabled(mOriginalAppStandbyEnabled);
+ }
+
+ private void setAppStandbyEnabled(String appStandbyEnabled) throws DeviceNotAvailableException {
+ if ("null".equals(appStandbyEnabled)) {
+ executeCmd("settings delete global app_standby_enabled");
+ } else {
+ executeCmd("settings put global app_standby_enabled " + appStandbyEnabled);
+ }
+ }
+
+ private String getAppStandbyEnabled() throws DeviceNotAvailableException {
+ return executeCmd("settings get global app_standby_enabled").trim();
+ }
+
+ private String executeCmd(String cmd) throws DeviceNotAvailableException {
+ final String output = mDevice.executeShellCommand(cmd).trim();
+ LogUtil.CLog.d("Output for '%s': %s", cmd, output);
+ return output;
+ }
+}
diff --git a/tests/cts/net/ipsec/src/android/net/eap/cts/EapSessionConfigTest.java b/tests/cts/net/ipsec/src/android/net/eap/cts/EapSessionConfigTest.java
new file mode 100644
index 0000000..c24379d
--- /dev/null
+++ b/tests/cts/net/ipsec/src/android/net/eap/cts/EapSessionConfigTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.eap.cts;
+
+import static android.telephony.TelephonyManager.APPTYPE_USIM;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.eap.EapSessionConfig;
+import android.net.eap.EapSessionConfig.EapAkaConfig;
+import android.net.eap.EapSessionConfig.EapAkaPrimeConfig;
+import android.net.eap.EapSessionConfig.EapMsChapV2Config;
+import android.net.eap.EapSessionConfig.EapSimConfig;
+import android.net.eap.EapSessionConfig.EapUiccConfig;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class EapSessionConfigTest {
+ // These constants are IANA-defined values and are copies of hidden constants in
+ // frameworks/opt/net/ike/src/java/com/android/internal/net/eap/message/EapData.java.
+ private static final int EAP_TYPE_SIM = 18;
+ private static final int EAP_TYPE_AKA = 23;
+ private static final int EAP_TYPE_MSCHAP_V2 = 26;
+ private static final int EAP_TYPE_AKA_PRIME = 50;
+
+ private static final int SUB_ID = 1;
+ private static final byte[] EAP_IDENTITY = "test@android.net".getBytes();
+ private static final String NETWORK_NAME = "android.net";
+ private static final String EAP_MSCHAPV2_USERNAME = "username";
+ private static final String EAP_MSCHAPV2_PASSWORD = "password";
+
+ @Test
+ public void testBuildWithAllEapMethods() {
+ EapSessionConfig result =
+ new EapSessionConfig.Builder()
+ .setEapIdentity(EAP_IDENTITY)
+ .setEapSimConfig(SUB_ID, APPTYPE_USIM)
+ .setEapAkaConfig(SUB_ID, APPTYPE_USIM)
+ .setEapAkaPrimeConfig(
+ SUB_ID,
+ APPTYPE_USIM,
+ NETWORK_NAME,
+ true /* allowMismatchedNetworkNames */)
+ .setEapMsChapV2Config(EAP_MSCHAPV2_USERNAME, EAP_MSCHAPV2_PASSWORD)
+ .build();
+
+ assertArrayEquals(EAP_IDENTITY, result.getEapIdentity());
+
+ EapSimConfig eapSimConfig = result.getEapSimConfig();
+ assertNotNull(eapSimConfig);
+ assertEquals(EAP_TYPE_SIM, eapSimConfig.getMethodType());
+ verifyEapUiccConfigCommon(eapSimConfig);
+
+ EapAkaConfig eapAkaConfig = result.getEapAkaConfig();
+ assertNotNull(eapAkaConfig);
+ assertEquals(EAP_TYPE_AKA, eapAkaConfig.getMethodType());
+ verifyEapUiccConfigCommon(eapAkaConfig);
+
+ EapAkaPrimeConfig eapAkaPrimeConfig = result.getEapAkaPrimeConfig();
+ assertNotNull(eapAkaPrimeConfig);
+ assertEquals(EAP_TYPE_AKA_PRIME, eapAkaPrimeConfig.getMethodType());
+ assertEquals(NETWORK_NAME, eapAkaPrimeConfig.getNetworkName());
+ assertTrue(NETWORK_NAME, eapAkaPrimeConfig.allowsMismatchedNetworkNames());
+ verifyEapUiccConfigCommon(eapAkaPrimeConfig);
+
+ EapMsChapV2Config eapMsChapV2Config = result.getEapMsChapV2onfig();
+ assertNotNull(eapMsChapV2Config);
+ assertEquals(EAP_TYPE_MSCHAP_V2, eapMsChapV2Config.getMethodType());
+ assertEquals(EAP_MSCHAPV2_USERNAME, eapMsChapV2Config.getUsername());
+ assertEquals(EAP_MSCHAPV2_PASSWORD, eapMsChapV2Config.getPassword());
+ }
+
+ private void verifyEapUiccConfigCommon(EapUiccConfig config) {
+ assertEquals(SUB_ID, config.getSubId());
+ assertEquals(APPTYPE_USIM, config.getAppType());
+ }
+}
diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java
new file mode 100644
index 0000000..c3e3ba3
--- /dev/null
+++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/IkeSessionParamsTestBase.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipsec.ike.cts;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkAddress;
+import android.net.Network;
+import android.net.TestNetworkInterface;
+import android.net.TestNetworkManager;
+import android.net.ipsec.ike.cts.TestNetworkUtils.TestNetworkCallback;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "MANAGE_TEST_NETWORKS permission can't be granted to instant apps")
+abstract class IkeSessionParamsTestBase extends IkeTestBase {
+ // Static state to reduce setup/teardown
+ static ConnectivityManager sCM;
+ static TestNetworkManager sTNM;
+ static ParcelFileDescriptor sTunFd;
+ static TestNetworkCallback sTunNetworkCallback;
+ static Network sTunNetwork;
+
+ static Context sContext = InstrumentationRegistry.getContext();
+ static IBinder sBinder = new Binder();
+
+ // This method is guaranteed to run in subclasses and will run before subclasses' @BeforeClass
+ // methods.
+ @BeforeClass
+ public static void setUpTestNetworkBeforeClass() throws Exception {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity();
+ sCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ sTNM = (TestNetworkManager) sContext.getSystemService(Context.TEST_NETWORK_SERVICE);
+
+ TestNetworkInterface testIface =
+ sTNM.createTunInterface(
+ new LinkAddress[] {new LinkAddress(IPV4_ADDRESS_LOCAL, IP4_PREFIX_LEN)});
+
+ sTunFd = testIface.getFileDescriptor();
+ sTunNetworkCallback =
+ TestNetworkUtils.setupAndGetTestNetwork(
+ sCM, sTNM, testIface.getInterfaceName(), sBinder);
+ sTunNetwork = sTunNetworkCallback.getNetworkBlocking();
+ }
+
+ // This method is guaranteed to run in subclasses and will run after subclasses' @AfterClass
+ // methods.
+ @AfterClass
+ public static void tearDownTestNetworkAfterClass() throws Exception {
+ sCM.unregisterNetworkCallback(sTunNetworkCallback);
+
+ sTNM.teardownTestNetwork(sTunNetwork);
+ sTunFd.close();
+
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+}
diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/PacketUtils.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/PacketUtils.java
new file mode 100644
index 0000000..35e6719
--- /dev/null
+++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/PacketUtils.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ipsec.ike.cts;
+
+import static android.system.OsConstants.IPPROTO_IPV6;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * This code is a exact copy of {@link PacketUtils} in
+ * cts/tests/tests/net/src/android/net/cts/PacketUtils.java.
+ *
+ * <p>TODO(b/148689509): Statically include the PacketUtils source file instead of copying it.
+ */
+public class PacketUtils {
+ private static final String TAG = PacketUtils.class.getSimpleName();
+
+ private static final int DATA_BUFFER_LEN = 4096;
+
+ static final int IP4_HDRLEN = 20;
+ static final int IP6_HDRLEN = 40;
+ static final int UDP_HDRLEN = 8;
+ static final int TCP_HDRLEN = 20;
+ static final int TCP_HDRLEN_WITH_TIMESTAMP_OPT = TCP_HDRLEN + 12;
+
+ // Not defined in OsConstants
+ static final int IPPROTO_IPV4 = 4;
+ static final int IPPROTO_ESP = 50;
+
+ // Encryption parameters
+ static final int AES_GCM_IV_LEN = 8;
+ static final int AES_CBC_IV_LEN = 16;
+ static final int AES_GCM_BLK_SIZE = 4;
+ static final int AES_CBC_BLK_SIZE = 16;
+
+ // Encryption algorithms
+ static final String AES = "AES";
+ static final String AES_CBC = "AES/CBC/NoPadding";
+ static final String HMAC_SHA_256 = "HmacSHA256";
+
+ public interface Payload {
+ byte[] getPacketBytes(IpHeader header) throws Exception;
+
+ void addPacketBytes(IpHeader header, ByteBuffer resultBuffer) throws Exception;
+
+ short length();
+
+ int getProtocolId();
+ }
+
+ public abstract static class IpHeader {
+
+ public final byte proto;
+ public final InetAddress srcAddr;
+ public final InetAddress dstAddr;
+ public final Payload payload;
+
+ public IpHeader(int proto, InetAddress src, InetAddress dst, Payload payload) {
+ this.proto = (byte) proto;
+ this.srcAddr = src;
+ this.dstAddr = dst;
+ this.payload = payload;
+ }
+
+ public abstract byte[] getPacketBytes() throws Exception;
+
+ public abstract int getProtocolId();
+ }
+
+ public static class Ip4Header extends IpHeader {
+ private short checksum;
+
+ public Ip4Header(int proto, Inet4Address src, Inet4Address dst, Payload payload) {
+ super(proto, src, dst, payload);
+ }
+
+ public byte[] getPacketBytes() throws Exception {
+ ByteBuffer resultBuffer = buildHeader();
+ payload.addPacketBytes(this, resultBuffer);
+
+ return getByteArrayFromBuffer(resultBuffer);
+ }
+
+ public ByteBuffer buildHeader() {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ // Version, IHL
+ bb.put((byte) (0x45));
+
+ // DCSP, ECN
+ bb.put((byte) 0);
+
+ // Total Length
+ bb.putShort((short) (IP4_HDRLEN + payload.length()));
+
+ // Empty for Identification, Flags and Fragment Offset
+ bb.putShort((short) 0);
+ bb.put((byte) 0x40);
+ bb.put((byte) 0x00);
+
+ // TTL
+ bb.put((byte) 64);
+
+ // Protocol
+ bb.put(proto);
+
+ // Header Checksum
+ final int ipChecksumOffset = bb.position();
+ bb.putShort((short) 0);
+
+ // Src/Dst addresses
+ bb.put(srcAddr.getAddress());
+ bb.put(dstAddr.getAddress());
+
+ bb.putShort(ipChecksumOffset, calculateChecksum(bb));
+
+ return bb;
+ }
+
+ private short calculateChecksum(ByteBuffer bb) {
+ int checksum = 0;
+
+ // Calculate sum of 16-bit values, excluding checksum. IPv4 headers are always 32-bit
+ // aligned, so no special cases needed for unaligned values.
+ ShortBuffer shortBuffer = ByteBuffer.wrap(getByteArrayFromBuffer(bb)).asShortBuffer();
+ while (shortBuffer.hasRemaining()) {
+ short val = shortBuffer.get();
+
+ // Wrap as needed
+ checksum = addAndWrapForChecksum(checksum, val);
+ }
+
+ return onesComplement(checksum);
+ }
+
+ public int getProtocolId() {
+ return IPPROTO_IPV4;
+ }
+ }
+
+ public static class Ip6Header extends IpHeader {
+ public Ip6Header(int nextHeader, Inet6Address src, Inet6Address dst, Payload payload) {
+ super(nextHeader, src, dst, payload);
+ }
+
+ public byte[] getPacketBytes() throws Exception {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ // Version | Traffic Class (First 4 bits)
+ bb.put((byte) 0x60);
+
+ // Traffic class (Last 4 bits), Flow Label
+ bb.put((byte) 0);
+ bb.put((byte) 0);
+ bb.put((byte) 0);
+
+ // Payload Length
+ bb.putShort((short) payload.length());
+
+ // Next Header
+ bb.put(proto);
+
+ // Hop Limit
+ bb.put((byte) 64);
+
+ // Src/Dst addresses
+ bb.put(srcAddr.getAddress());
+ bb.put(dstAddr.getAddress());
+
+ // Payload
+ payload.addPacketBytes(this, bb);
+
+ return getByteArrayFromBuffer(bb);
+ }
+
+ public int getProtocolId() {
+ return IPPROTO_IPV6;
+ }
+ }
+
+ public static class BytePayload implements Payload {
+ public final byte[] payload;
+
+ public BytePayload(byte[] payload) {
+ this.payload = payload;
+ }
+
+ public int getProtocolId() {
+ return -1;
+ }
+
+ public byte[] getPacketBytes(IpHeader header) {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ addPacketBytes(header, bb);
+ return getByteArrayFromBuffer(bb);
+ }
+
+ public void addPacketBytes(IpHeader header, ByteBuffer resultBuffer) {
+ resultBuffer.put(payload);
+ }
+
+ public short length() {
+ return (short) payload.length;
+ }
+ }
+
+ public static class UdpHeader implements Payload {
+
+ public final short srcPort;
+ public final short dstPort;
+ public final Payload payload;
+
+ public UdpHeader(int srcPort, int dstPort, Payload payload) {
+ this.srcPort = (short) srcPort;
+ this.dstPort = (short) dstPort;
+ this.payload = payload;
+ }
+
+ public int getProtocolId() {
+ return IPPROTO_UDP;
+ }
+
+ public short length() {
+ return (short) (payload.length() + 8);
+ }
+
+ public byte[] getPacketBytes(IpHeader header) throws Exception {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ addPacketBytes(header, bb);
+ return getByteArrayFromBuffer(bb);
+ }
+
+ public void addPacketBytes(IpHeader header, ByteBuffer resultBuffer) throws Exception {
+ // Source, Destination port
+ resultBuffer.putShort(srcPort);
+ resultBuffer.putShort(dstPort);
+
+ // Payload Length
+ resultBuffer.putShort(length());
+
+ // Get payload bytes for checksum + payload
+ ByteBuffer payloadBuffer = ByteBuffer.allocate(DATA_BUFFER_LEN);
+ payload.addPacketBytes(header, payloadBuffer);
+ byte[] payloadBytes = getByteArrayFromBuffer(payloadBuffer);
+
+ // Checksum
+ resultBuffer.putShort(calculateChecksum(header, payloadBytes));
+
+ // Payload
+ resultBuffer.put(payloadBytes);
+ }
+
+ private short calculateChecksum(IpHeader header, byte[] payloadBytes) throws Exception {
+ int newChecksum = 0;
+ ShortBuffer srcBuffer = ByteBuffer.wrap(header.srcAddr.getAddress()).asShortBuffer();
+ ShortBuffer dstBuffer = ByteBuffer.wrap(header.dstAddr.getAddress()).asShortBuffer();
+
+ while (srcBuffer.hasRemaining() || dstBuffer.hasRemaining()) {
+ short val = srcBuffer.hasRemaining() ? srcBuffer.get() : dstBuffer.get();
+
+ // Wrap as needed
+ newChecksum = addAndWrapForChecksum(newChecksum, val);
+ }
+
+ // Add pseudo-header values. Proto is 0-padded, so just use the byte.
+ newChecksum = addAndWrapForChecksum(newChecksum, header.proto);
+ newChecksum = addAndWrapForChecksum(newChecksum, length());
+ newChecksum = addAndWrapForChecksum(newChecksum, srcPort);
+ newChecksum = addAndWrapForChecksum(newChecksum, dstPort);
+ newChecksum = addAndWrapForChecksum(newChecksum, length());
+
+ ShortBuffer payloadShortBuffer = ByteBuffer.wrap(payloadBytes).asShortBuffer();
+ while (payloadShortBuffer.hasRemaining()) {
+ newChecksum = addAndWrapForChecksum(newChecksum, payloadShortBuffer.get());
+ }
+ if (payload.length() % 2 != 0) {
+ newChecksum =
+ addAndWrapForChecksum(
+ newChecksum, (payloadBytes[payloadBytes.length - 1] << 8));
+ }
+
+ return onesComplement(newChecksum);
+ }
+ }
+
+ public static class EspHeader implements Payload {
+ public final int nextHeader;
+ public final int spi;
+ public final int seqNum;
+ public final byte[] key;
+ public final byte[] payload;
+
+ /**
+ * Generic constructor for ESP headers.
+ *
+ * <p>For Tunnel mode, payload will be a full IP header + attached payloads
+ *
+ * <p>For Transport mode, payload will be only the attached payloads, but with the checksum
+ * calculated using the pre-encryption IP header
+ */
+ public EspHeader(int nextHeader, int spi, int seqNum, byte[] key, byte[] payload) {
+ this.nextHeader = nextHeader;
+ this.spi = spi;
+ this.seqNum = seqNum;
+ this.key = key;
+ this.payload = payload;
+ }
+
+ public int getProtocolId() {
+ return IPPROTO_ESP;
+ }
+
+ public short length() {
+ // ALWAYS uses AES-CBC, HMAC-SHA256 (128b trunc len)
+ return (short)
+ calculateEspPacketSize(payload.length, AES_CBC_IV_LEN, AES_CBC_BLK_SIZE, 128);
+ }
+
+ public byte[] getPacketBytes(IpHeader header) throws Exception {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ addPacketBytes(header, bb);
+ return getByteArrayFromBuffer(bb);
+ }
+
+ public void addPacketBytes(IpHeader header, ByteBuffer resultBuffer) throws Exception {
+ ByteBuffer espPayloadBuffer = ByteBuffer.allocate(DATA_BUFFER_LEN);
+ espPayloadBuffer.putInt(spi);
+ espPayloadBuffer.putInt(seqNum);
+ espPayloadBuffer.put(getCiphertext(key));
+
+ espPayloadBuffer.put(getIcv(getByteArrayFromBuffer(espPayloadBuffer)), 0, 16);
+ resultBuffer.put(getByteArrayFromBuffer(espPayloadBuffer));
+ }
+
+ private byte[] getIcv(byte[] authenticatedSection) throws GeneralSecurityException {
+ Mac sha256HMAC = Mac.getInstance(HMAC_SHA_256);
+ SecretKeySpec authKey = new SecretKeySpec(key, HMAC_SHA_256);
+ sha256HMAC.init(authKey);
+
+ return sha256HMAC.doFinal(authenticatedSection);
+ }
+
+ /**
+ * Encrypts and builds ciphertext block. Includes the IV, Padding and Next-Header blocks
+ *
+ * <p>The ciphertext does NOT include the SPI/Sequence numbers, or the ICV.
+ */
+ private byte[] getCiphertext(byte[] key) throws GeneralSecurityException {
+ int paddedLen = calculateEspEncryptedLength(payload.length, AES_CBC_BLK_SIZE);
+ ByteBuffer paddedPayload = ByteBuffer.allocate(paddedLen);
+ paddedPayload.put(payload);
+
+ // Add padding - consecutive integers from 0x01
+ int pad = 1;
+ while (paddedPayload.position() < paddedPayload.limit()) {
+ paddedPayload.put((byte) pad++);
+ }
+
+ paddedPayload.position(paddedPayload.limit() - 2);
+ paddedPayload.put((byte) (paddedLen - 2 - payload.length)); // Pad length
+ paddedPayload.put((byte) nextHeader);
+
+ // Generate Initialization Vector
+ byte[] iv = new byte[AES_CBC_IV_LEN];
+ new SecureRandom().nextBytes(iv);
+ IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
+ SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES);
+
+ // Encrypt payload
+ Cipher cipher = Cipher.getInstance(AES_CBC);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
+ byte[] encrypted = cipher.doFinal(getByteArrayFromBuffer(paddedPayload));
+
+ // Build ciphertext
+ ByteBuffer cipherText = ByteBuffer.allocate(AES_CBC_IV_LEN + encrypted.length);
+ cipherText.put(iv);
+ cipherText.put(encrypted);
+
+ return getByteArrayFromBuffer(cipherText);
+ }
+ }
+
+ private static int addAndWrapForChecksum(int currentChecksum, int value) {
+ currentChecksum += value & 0x0000ffff;
+
+ // Wrap anything beyond the first 16 bits, and add to lower order bits
+ return (currentChecksum >>> 16) + (currentChecksum & 0x0000ffff);
+ }
+
+ private static short onesComplement(int val) {
+ val = (val >>> 16) + (val & 0xffff);
+
+ if (val == 0) return 0;
+ return (short) ((~val) & 0xffff);
+ }
+
+ public static int calculateEspPacketSize(
+ int payloadLen, int cryptIvLength, int cryptBlockSize, int authTruncLen) {
+ final int ESP_HDRLEN = 4 + 4; // SPI + Seq#
+ final int ICV_LEN = authTruncLen / 8; // Auth trailer; based on truncation length
+ payloadLen += cryptIvLength; // Initialization Vector
+
+ // Align to block size of encryption algorithm
+ payloadLen = calculateEspEncryptedLength(payloadLen, cryptBlockSize);
+ return payloadLen + ESP_HDRLEN + ICV_LEN;
+ }
+
+ private static int calculateEspEncryptedLength(int payloadLen, int cryptBlockSize) {
+ payloadLen += 2; // ESP trailer
+
+ // Align to block size of encryption algorithm
+ return payloadLen + calculateEspPadLen(payloadLen, cryptBlockSize);
+ }
+
+ private static int calculateEspPadLen(int payloadLen, int cryptBlockSize) {
+ return (cryptBlockSize - (payloadLen % cryptBlockSize)) % cryptBlockSize;
+ }
+
+ private static byte[] getByteArrayFromBuffer(ByteBuffer buffer) {
+ return Arrays.copyOfRange(buffer.array(), 0, buffer.position());
+ }
+
+ /*
+ * Debug printing
+ */
+ private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+ public static String bytesToHex(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(hexArray[b >>> 4]);
+ sb.append(hexArray[b & 0x0F]);
+ sb.append(' ');
+ }
+ return sb.toString();
+ }
+}
diff --git a/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
new file mode 100644
index 0000000..71450ea
--- /dev/null
+++ b/tests/cts/net/ipsec/src/android/net/ipsec/ike/cts/TunUtils.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net.ipsec.ike.cts;
+
+import static android.net.ipsec.ike.cts.PacketUtils.IP4_HDRLEN;
+import static android.net.ipsec.ike.cts.PacketUtils.IP6_HDRLEN;
+import static android.net.ipsec.ike.cts.PacketUtils.IPPROTO_ESP;
+import static android.net.ipsec.ike.cts.PacketUtils.UDP_HDRLEN;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import android.os.ParcelFileDescriptor;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Predicate;
+
+/**
+ * This code is a exact copy of {@link TunUtils} in
+ * cts/tests/tests/net/src/android/net/cts/TunUtils.java, except the import path of PacketUtils is
+ * the path to the copy of PacktUtils.
+ *
+ * <p>TODO(b/148689509): Statically include the TunUtils source file instead of copying it.
+ */
+public class TunUtils {
+ private static final String TAG = TunUtils.class.getSimpleName();
+
+ private static final int DATA_BUFFER_LEN = 4096;
+ private static final int TIMEOUT = 100;
+
+ private static final int IP4_PROTO_OFFSET = 9;
+ private static final int IP6_PROTO_OFFSET = 6;
+
+ private static final int IP4_ADDR_OFFSET = 12;
+ private static final int IP4_ADDR_LEN = 4;
+ private static final int IP6_ADDR_OFFSET = 8;
+ private static final int IP6_ADDR_LEN = 16;
+
+ private final ParcelFileDescriptor mTunFd;
+ private final List<byte[]> mPackets = new ArrayList<>();
+ private final Thread mReaderThread;
+
+ public TunUtils(ParcelFileDescriptor tunFd) {
+ mTunFd = tunFd;
+
+ // Start background reader thread
+ mReaderThread =
+ new Thread(
+ () -> {
+ try {
+ // Loop will exit and thread will quit when tunFd is closed.
+ // Receiving either EOF or an exception will exit this reader loop.
+ // FileInputStream in uninterruptable, so there's no good way to
+ // ensure that this thread shuts down except upon FD closure.
+ while (true) {
+ byte[] intercepted = receiveFromTun();
+ if (intercepted == null) {
+ // Exit once we've hit EOF
+ return;
+ } else if (intercepted.length > 0) {
+ // Only save packet if we've received any bytes.
+ synchronized (mPackets) {
+ mPackets.add(intercepted);
+ mPackets.notifyAll();
+ }
+ }
+ }
+ } catch (IOException ignored) {
+ // Simply exit this reader thread
+ return;
+ }
+ });
+ mReaderThread.start();
+ }
+
+ private byte[] receiveFromTun() throws IOException {
+ FileInputStream in = new FileInputStream(mTunFd.getFileDescriptor());
+ byte[] inBytes = new byte[DATA_BUFFER_LEN];
+ int bytesRead = in.read(inBytes);
+
+ if (bytesRead < 0) {
+ return null; // return null for EOF
+ } else if (bytesRead >= DATA_BUFFER_LEN) {
+ throw new IllegalStateException("Too big packet. Fragmentation unsupported");
+ }
+ return Arrays.copyOf(inBytes, bytesRead);
+ }
+
+ private byte[] getFirstMatchingPacket(Predicate<byte[]> verifier, int startIndex) {
+ synchronized (mPackets) {
+ for (int i = startIndex; i < mPackets.size(); i++) {
+ byte[] pkt = mPackets.get(i);
+ if (verifier.test(pkt)) {
+ return pkt;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks if the specified bytes were ever sent in plaintext.
+ *
+ * <p>Only checks for known plaintext bytes to prevent triggering on ICMP/RA packets or the like
+ *
+ * @param plaintext the plaintext bytes to check for
+ * @param startIndex the index in the list to check for
+ */
+ public boolean hasPlaintextPacket(byte[] plaintext, int startIndex) {
+ Predicate<byte[]> verifier =
+ (pkt) -> {
+ return Collections.indexOfSubList(Arrays.asList(pkt), Arrays.asList(plaintext))
+ != -1;
+ };
+ return getFirstMatchingPacket(verifier, startIndex) != null;
+ }
+
+ public byte[] getEspPacket(int spi, boolean encap, int startIndex) {
+ return getFirstMatchingPacket(
+ (pkt) -> {
+ return isEsp(pkt, spi, encap);
+ },
+ startIndex);
+ }
+
+ public byte[] awaitEspPacketNoPlaintext(
+ int spi, byte[] plaintext, boolean useEncap, int expectedPacketSize) throws Exception {
+ long endTime = System.currentTimeMillis() + TIMEOUT;
+ int startIndex = 0;
+
+ synchronized (mPackets) {
+ while (System.currentTimeMillis() < endTime) {
+ byte[] espPkt = getEspPacket(spi, useEncap, startIndex);
+ if (espPkt != null) {
+ // Validate packet size
+ assertEquals(expectedPacketSize, espPkt.length);
+
+ // Always check plaintext from start
+ assertFalse(hasPlaintextPacket(plaintext, 0));
+ return espPkt; // We've found the packet we're looking for.
+ }
+
+ startIndex = mPackets.size();
+
+ // Try to prevent waiting too long. If waitTimeout <= 0, we've already hit timeout
+ long waitTimeout = endTime - System.currentTimeMillis();
+ if (waitTimeout > 0) {
+ mPackets.wait(waitTimeout);
+ }
+ }
+
+ fail("No such ESP packet found with SPI " + spi);
+ }
+ return null;
+ }
+
+ private static boolean isSpiEqual(byte[] pkt, int espOffset, int spi) {
+ // Check SPI byte by byte.
+ return pkt[espOffset] == (byte) ((spi >>> 24) & 0xff)
+ && pkt[espOffset + 1] == (byte) ((spi >>> 16) & 0xff)
+ && pkt[espOffset + 2] == (byte) ((spi >>> 8) & 0xff)
+ && pkt[espOffset + 3] == (byte) (spi & 0xff);
+ }
+
+ private static boolean isEsp(byte[] pkt, int spi, boolean encap) {
+ if (isIpv6(pkt)) {
+ // IPv6 UDP encap not supported by kernels; assume non-encap.
+ return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP6_HDRLEN, spi);
+ } else {
+ // Use default IPv4 header length (assuming no options)
+ if (encap) {
+ return pkt[IP4_PROTO_OFFSET] == IPPROTO_UDP
+ && isSpiEqual(pkt, IP4_HDRLEN + UDP_HDRLEN, spi);
+ } else {
+ return pkt[IP4_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP4_HDRLEN, spi);
+ }
+ }
+ }
+
+ private static boolean isIpv6(byte[] pkt) {
+ // First nibble shows IP version. 0x60 for IPv6
+ return (pkt[0] & (byte) 0xF0) == (byte) 0x60;
+ }
+
+ private static byte[] getReflectedPacket(byte[] pkt) {
+ byte[] reflected = Arrays.copyOf(pkt, pkt.length);
+
+ if (isIpv6(pkt)) {
+ // Set reflected packet's dst to that of the original's src
+ System.arraycopy(
+ pkt, // src
+ IP6_ADDR_OFFSET + IP6_ADDR_LEN, // src offset
+ reflected, // dst
+ IP6_ADDR_OFFSET, // dst offset
+ IP6_ADDR_LEN); // len
+ // Set reflected packet's src IP to that of the original's dst IP
+ System.arraycopy(
+ pkt, // src
+ IP6_ADDR_OFFSET, // src offset
+ reflected, // dst
+ IP6_ADDR_OFFSET + IP6_ADDR_LEN, // dst offset
+ IP6_ADDR_LEN); // len
+ } else {
+ // Set reflected packet's dst to that of the original's src
+ System.arraycopy(
+ pkt, // src
+ IP4_ADDR_OFFSET + IP4_ADDR_LEN, // src offset
+ reflected, // dst
+ IP4_ADDR_OFFSET, // dst offset
+ IP4_ADDR_LEN); // len
+ // Set reflected packet's src IP to that of the original's dst IP
+ System.arraycopy(
+ pkt, // src
+ IP4_ADDR_OFFSET, // src offset
+ reflected, // dst
+ IP4_ADDR_OFFSET + IP4_ADDR_LEN, // dst offset
+ IP4_ADDR_LEN); // len
+ }
+ return reflected;
+ }
+
+ /** Takes all captured packets, flips the src/dst, and re-injects them. */
+ public void reflectPackets() throws IOException {
+ synchronized (mPackets) {
+ for (byte[] pkt : mPackets) {
+ injectPacket(getReflectedPacket(pkt));
+ }
+ }
+ }
+
+ public void injectPacket(byte[] pkt) throws IOException {
+ FileOutputStream out = new FileOutputStream(mTunFd.getFileDescriptor());
+ out.write(pkt);
+ out.flush();
+ }
+
+ /** Resets the intercepted packets. */
+ public void reset() throws IOException {
+ synchronized (mPackets) {
+ mPackets.clear();
+ }
+ }
+}