[automerger skipped] Merge changes from topic "am-6bf6d04b-31af-4d11-9286-c78baea6dc7f" into oc-dev am: 704a9a46d8 -s ours am: ecaabe8f3e -s ours
am: 9b319aa0ef -s ours
am skip reason: change_id I91b4ab018a9e7fc73dcb7969e4a6520d6b27d629 with SHA1 d51ae5760d is in history
Change-Id: Ia0a4c5b6dacdb18ddfeead56ebbc9d5d843f9a3d
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index 4a2e2e3..0656cae 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -14,8 +14,15 @@
limitations under the License.
-->
<configuration description="Config for CTS net host test cases">
+ <option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="networking" />
<target_preparer class="com.android.cts.net.NetPolicyTestsPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="teardown-command" value="cmd power set-mode 0" />
+ <option name="teardown-command" value="cmd battery reset" />
+ </target_preparer>
+
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsHostsideNetworkTests.jar" />
<option name="runtime-hint" value="3m56s" />
diff --git a/tests/cts/hostside/app/Android.mk b/tests/cts/hostside/app/Android.mk
index f094f3f..6d89e58 100644
--- a/tests/cts/hostside/app/Android.mk
+++ b/tests/cts/hostside/app/Android.mk
@@ -20,9 +20,11 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ub-uiautomator \
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt ctstestrunner-axt ub-uiautomator \
CtsHostsideNetworkTestsAidl
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
+
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_PACKAGE_NAME := CtsHostsideNetworkTestsApp
diff --git a/tests/cts/hostside/app/AndroidManifest.xml b/tests/cts/hostside/app/AndroidManifest.xml
index 7466cb8..ba0e242 100644
--- a/tests/cts/hostside/app/AndroidManifest.xml
+++ b/tests/cts/hostside/app/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
@@ -44,7 +45,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.cts.net.hostside" />
</manifest>
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 d2c0873..1f5984a 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
@@ -34,7 +34,7 @@
removePowerSaveModeWhitelist(TEST_APP2_PKG);
removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
setAppIdle(false);
- turnBatteryOff();
+ turnBatteryOn();
registerBroadcastReceiver();
}
@@ -48,7 +48,7 @@
try {
tearDownMeteredNetwork();
} finally {
- turnBatteryOn();
+ turnBatteryOff();
setAppIdle(false);
}
}
@@ -127,6 +127,19 @@
assertBackgroundNetworkAccess(false);
}
+ public void testBackgroundNetworkAccess_tempWhitelisted() throws Exception {
+ if (!isSupported()) return;
+
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ }
+
public void testBackgroundNetworkAccess_disabled() throws Exception {
if (!isSupported()) return;
@@ -137,14 +150,19 @@
}
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);
- turnBatteryOn();
- assertBackgroundNetworkAccess(true);
turnBatteryOff();
+ assertBackgroundNetworkAccess(true);
+ turnBatteryOn();
assertBackgroundNetworkAccess(false);
// Check that app is restricted when not idle but power-save is on
@@ -154,11 +172,11 @@
assertBackgroundNetworkAccess(false);
// Use setBatterySaverMode API to leave power-save mode instead of plugging in charger
setBatterySaverMode(false);
- turnBatteryOn();
+ turnBatteryOff();
assertBackgroundNetworkAccess(true);
// And when no longer charging, it still has network access, since it's not idle
- turnBatteryOff();
+ turnBatteryOn();
assertBackgroundNetworkAccess(true);
}
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 28175b8..931376b 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,6 +16,7 @@
package com.android.cts.net.hostside;
+import android.text.TextUtils;
import android.util.Log;
/**
@@ -52,12 +53,19 @@
@Override
protected boolean isSupported() throws Exception {
- boolean supported = isDozeModeEnabled();
- if (!supported) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Doze Mode");
+ String unSupported = "";
+ if (!isDozeModeEnabled()) {
+ unSupported += "Doze mode,";
}
- return supported;
+ 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;
}
/**
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 ce56d25..1844878 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
@@ -20,6 +20,10 @@
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 java.util.concurrent.CountDownLatch;
@@ -34,6 +38,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;
@@ -49,7 +54,7 @@
import android.text.TextUtils;
import android.util.Log;
-import com.android.cts.net.hostside.INetworkStateObserver;
+import com.android.compatibility.common.util.BatteryUtils;
/**
* Superclass for tests related to background network restrictions.
@@ -83,11 +88,14 @@
protected static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
protected static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
+ // TODO: Update BatteryManager.BATTERY_PLUGGED_ANY as @TestApi
+ public static final int BATTERY_PLUGGED_ANY =
+ BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS;
private static final String NETWORK_STATUS_SEPARATOR = "\\|";
private static final int SECOND_IN_MS = 1000;
static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
- private static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
+ private static final int PROCESS_STATE_FOREGROUND_SERVICE = 3;
private static final int PROCESS_STATE_TOP = 2;
private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
@@ -108,6 +116,8 @@
private static final String APP_NOT_FOREGROUND_ERROR = "app_not_fg";
+ protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 5_000; // 5 sec
+
protected Context mContext;
protected Instrumentation mInstrumentation;
protected ConnectivityManager mCm;
@@ -138,6 +148,7 @@
enableLocation();
}
mSupported = setUpActiveNetworkMeteringState();
+ setAppIdle(false);
Log.i(TAG, "Apps status on " + getName() + ":\n"
+ "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
@@ -258,17 +269,21 @@
protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
assertBackgroundState(); // Sanity check.
- assertNetworkAccess(expectAllowed);
+ assertNetworkAccess(expectAllowed /* expectAvailable */, false /* needScreenOn */);
}
protected void assertForegroundNetworkAccess() throws Exception {
assertForegroundState(); // Sanity check.
- assertNetworkAccess(true);
+ // We verified that app is in foreground state but if the screen turns-off while
+ // verifying for network access, the app will go into background state (in case app's
+ // foreground status was due to top activity). So, turn the screen on when verifying
+ // network connectivity.
+ assertNetworkAccess(true /* expectAvailable */, true /* needScreenOn */);
}
protected void assertForegroundServiceNetworkAccess() throws Exception {
assertForegroundServiceState(); // Sanity check.
- assertNetworkAccess(true);
+ assertNetworkAccess(true /* expectAvailable */, false /* needScreenOn */);
}
/**
@@ -289,6 +304,10 @@
return mSupported;
}
+ protected boolean isBatterySaverSupported() throws Exception {
+ return BatteryUtils.isBatterySaverSupported();
+ }
+
/**
* Asserts that an app always have access while on foreground or running a foreground service.
*
@@ -367,7 +386,8 @@
/**
* Asserts whether the active network is available or not.
*/
- private void assertNetworkAccess(boolean expectAvailable) throws Exception {
+ private void assertNetworkAccess(boolean expectAvailable, boolean needScreenOn)
+ throws Exception {
final int maxTries = 5;
String error = null;
int timeoutMs = 500;
@@ -385,6 +405,9 @@
Log.w(TAG, "Network status didn't match for expectAvailable=" + expectAvailable
+ " on attempt #" + i + ": " + error + "\n"
+ "Sleeping " + timeoutMs + "ms before trying again");
+ if (needScreenOn) {
+ turnScreenOn();
+ }
// No sleep after the last turn
if (i < maxTries) {
SystemClock.sleep(timeoutMs);
@@ -751,6 +774,12 @@
+ ". Full list: " + uids);
}
+ protected void addTempPowerSaveModeWhitelist(String packageName, long duration)
+ throws Exception {
+ Log.i(TAG, "Adding pkg " + packageName + " to temp-power-save-mode whitelist");
+ executeShellCommand("dumpsys deviceidle tempwhitelist -d " + duration + " " + packageName);
+ }
+
protected void assertPowerSaveModeWhitelist(String packageName, boolean expected)
throws Exception {
// TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
@@ -800,15 +829,19 @@
assertPowerSaveModeExceptIdleWhitelist(packageName, false); // Sanity check
}
- protected void turnBatteryOff() throws Exception {
+ protected void turnBatteryOn() throws Exception {
executeSilentShellCommand("cmd battery unplug");
+ executeSilentShellCommand("cmd battery set status "
+ + BatteryManager.BATTERY_STATUS_DISCHARGING);
assertBatteryState(false);
}
- protected void turnBatteryOn() throws Exception {
- executeSilentShellCommand("cmd battery reset");
+ protected void turnBatteryOff() throws Exception {
+ executeSilentShellCommand("cmd battery set ac " + BATTERY_PLUGGED_ANY);
+ executeSilentShellCommand("cmd battery set level 100");
+ executeSilentShellCommand("cmd battery set status "
+ + BatteryManager.BATTERY_STATUS_CHARGING);
assertBatteryState(true);
-
}
private void assertBatteryState(boolean pluggedIn) throws Exception {
@@ -839,11 +872,11 @@
protected void setBatterySaverMode(boolean enabled) throws Exception {
Log.i(TAG, "Setting Battery Saver Mode to " + enabled);
if (enabled) {
- turnBatteryOff();
+ turnBatteryOn();
executeSilentShellCommand("cmd power set-mode 1");
} else {
executeSilentShellCommand("cmd power set-mode 0");
- turnBatteryOn();
+ turnBatteryOff();
}
}
@@ -853,12 +886,12 @@
Log.i(TAG, "Setting Doze Mode to " + enabled);
if (enabled) {
- turnBatteryOff();
+ turnBatteryOn();
turnScreenOff();
executeShellCommand("dumpsys deviceidle force-idle deep");
} else {
turnScreenOn();
- turnBatteryOn();
+ turnBatteryOff();
executeShellCommand("dumpsys deviceidle unforce");
}
// Sanity check.
@@ -1004,7 +1037,8 @@
private Intent getIntentForComponent(int type) {
final Intent intent = new Intent();
if (type == TYPE_COMPONENT_ACTIVTIY) {
- intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS));
+ intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS))
.setFlags(1);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
index 5248255..7f65b55 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,6 +15,7 @@
*/
package com.android.cts.net.hostside;
+import android.os.SystemClock;
import android.util.Log;
/**
@@ -56,17 +57,26 @@
}
}
+ @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.
*/
public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
- if (!isSupported()) return;
-
- if (!isDozeModeEnabled()) {
- Log.w(TAG, "testDataAndBatterySaverModes_meteredNetwork() skipped because "
- + "device does not support Doze Mode");
+ 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()) {
@@ -136,13 +146,12 @@
* networks.
*/
public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
- if (!isSupported()) return;
-
- if (!isDozeModeEnabled()) {
- Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because "
- + "device does not support Doze Mode");
+ if (!isBatterySaverSupported()) {
+ Log.i(TAG, "Skipping " + getClass() + "." + getName()
+ + "() because device does not support Battery saver mode");
return;
}
+ if (!isSupported()) return;
if (!setUnmeteredNetwork()) {
Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because network"
@@ -207,12 +216,12 @@
* are enabled.
*/
public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
- if (!isSupported()) {
+ if (!isBatterySaverSupported()) {
+ Log.i(TAG, "Skipping " + getClass() + "." + getName()
+ + "() because device does not support Battery saver mode");
return;
}
- if (!isDozeModeEnabled()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Doze Mode");
+ if (!isSupported()) {
return;
}
@@ -245,11 +254,6 @@
if (!isSupported()) {
return;
}
- if (!isDozeModeEnabled()) {
- Log.i(TAG, "Skipping " + getClass() + "." + getName()
- + "() because device does not support Doze Mode");
- return;
- }
setDozeMode(true);
setAppIdle(true);
@@ -271,4 +275,55 @@
setDozeMode(false);
}
}
+
+ public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
+ if (!isSupported()) {
+ return;
+ }
+
+ setDozeMode(true);
+ setAppIdle(true);
+
+ try {
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+ }
+
+ 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);
+
+ try {
+ assertBackgroundNetworkAccess(false);
+
+ addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(true);
+
+ // Wait until the whitelist duration is expired.
+ SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setBatterySaverMode(false);
+ }
+ }
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
index ff05d8c..e2976c2 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -62,10 +62,10 @@
final Intent intent = new Intent();
intent.setComponent(new ComponentName(APP2_PACKAGE, SERVICE_NAME));
- // Needs to use BIND_ALLOW_OOM_MANAGEMENT and BIND_NOT_FOREGROUND so app2 does not run in
+ // Needs to use BIND_NOT_FOREGROUND so app2 does not run in
// the same process state as app
mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE
- | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_FOREGROUND);
+ | Context.BIND_NOT_FOREGROUND);
cv.block(TIMEOUT_MS);
if (mService == null) {
throw new IllegalStateException(
diff --git a/tests/cts/hostside/app2/AndroidManifest.xml b/tests/cts/hostside/app2/AndroidManifest.xml
index adf0045..ad270b3 100644
--- a/tests/cts/hostside/app2/AndroidManifest.xml
+++ b/tests/cts/hostside/app2/AndroidManifest.xml
@@ -19,6 +19,7 @@
package="com.android.cts.net.hostside.app2" >
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET" />
<!--
@@ -33,7 +34,7 @@
This application also provides a service, RemoteSocketFactoryService, that the test app can
use to open sockets to remote hosts as a different user ID.
-->
- <application>
+ <application android:usesCleartextTraffic="true">
<activity android:name=".MyActivity" android:exported="true"/>
<service android:name=".MyService" android:exported="true"/>
<service android:name=".MyForegroundService" android:exported="true"/>
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 c6df893..a2443b3 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -19,13 +19,13 @@
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.ddmlib.testrunner.TestResult;
import com.android.ddmlib.testrunner.TestResult.TestStatus;
-import com.android.ddmlib.testrunner.TestRunResult;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.result.TestResult;
+import com.android.tradefed.result.TestRunResult;
import com.android.tradefed.testtype.DeviceTestCase;
import com.android.tradefed.testtype.IAbi;
import com.android.tradefed.testtype.IAbiReceiver;
@@ -128,7 +128,7 @@
protected void runDeviceTests(String packageName, String testClassName, String methodName)
throws DeviceNotAvailableException {
RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
- "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
+ "androidx.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
if (testClassName != null) {
if (methodName != null) {
@@ -150,7 +150,7 @@
if (result.hasFailedTests()) {
// build a meaningful error message
StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
- for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+ for (Map.Entry<TestDescription, TestResult> resultEntry :
result.getTestResults().entrySet()) {
if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
errorBuilder.append(resultEntry.getKey().toString());
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index bf3fc08..fe9d36c 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -146,6 +146,11 @@
"testBackgroundNetworkAccess_whitelisted");
}
+ public void testAppIdleMetered_tempWhitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testBackgroundNetworkAccess_tempWhitelisted");
+ }
+
public void testAppIdleMetered_enabled() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
"testBackgroundNetworkAccess_enabled");
@@ -166,6 +171,11 @@
"testBackgroundNetworkAccess_whitelisted");
}
+ public void testAppIdleNonMetered_tempWhitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testBackgroundNetworkAccess_tempWhitelisted");
+ }
+
public void testAppIdleNonMetered_enabled() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
"testBackgroundNetworkAccess_enabled");
@@ -261,6 +271,16 @@
"testDozeAndAppIdle_powerSaveWhitelists");
}
+ public void testAppIdleAndDoze_tempPowerSaveWhitelists() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndDoze_tempPowerSaveWhitelists");
+ }
+
+ public void testAppIdleAndBatterySaver_tempPowerSaveWhitelists() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testAppIdleAndBatterySaver_tempPowerSaveWhitelists");
+ }
+
/*******************
* Helper methods. *
*******************/
diff --git a/tests/cts/hostside/src/com/android/cts/net/NetPolicyTestsPreparer.java b/tests/cts/hostside/src/com/android/cts/net/NetPolicyTestsPreparer.java
index 9b19554..ca14c27 100644
--- a/tests/cts/hostside/src/com/android/cts/net/NetPolicyTestsPreparer.java
+++ b/tests/cts/hostside/src/com/android/cts/net/NetPolicyTestsPreparer.java
@@ -25,50 +25,38 @@
public class NetPolicyTestsPreparer implements ITargetPreparer, ITargetCleaner {
private final static String KEY_PAROLE_DURATION = "parole_duration";
- private final static String DESIRED_PAROLE_DURATION = "0";
+ private final static int DESIRED_PAROLE_DURATION = 0;
+ private final static String KEY_STABLE_CHARGING_THRESHOLD = "stable_charging_threshold";
+ private final static int DESIRED_STABLE_CHARGING_THRESHOLD = 0;
- private boolean mAppIdleConstsUpdated;
+ private ITestDevice mDevice;
private String mOriginalAppIdleConsts;
@Override
public void setUp(ITestDevice device, IBuildInfo buildInfo) throws DeviceNotAvailableException {
- updateParoleDuration(device);
+ mDevice = device;
+ mOriginalAppIdleConsts = getAppIdleConstants();
+ setAppIdleConstants(KEY_PAROLE_DURATION + "=" + DESIRED_PAROLE_DURATION + ","
+ + KEY_STABLE_CHARGING_THRESHOLD + "=" + DESIRED_STABLE_CHARGING_THRESHOLD);
LogUtil.CLog.d("Original app_idle_constants: " + mOriginalAppIdleConsts);
}
@Override
public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable throwable)
throws DeviceNotAvailableException {
- if (mAppIdleConstsUpdated) {
- executeCmd(device, "settings put global app_idle_constants " + mOriginalAppIdleConsts);
- }
+ setAppIdleConstants(mOriginalAppIdleConsts);
}
- /**
- * Updates parole_duration with the desired value.
- */
- private void updateParoleDuration(ITestDevice device) throws DeviceNotAvailableException {
- mOriginalAppIdleConsts = executeCmd(device, "settings get global app_idle_constants");
- String newAppIdleConstants;
- final String newConstant = KEY_PAROLE_DURATION + "=" + DESIRED_PAROLE_DURATION;
- if (mOriginalAppIdleConsts == null || "null".equals(mOriginalAppIdleConsts)) {
- // app_idle_constants is initially empty, so just assign the desired value.
- newAppIdleConstants = newConstant;
- } else if (mOriginalAppIdleConsts.contains(KEY_PAROLE_DURATION)) {
- // app_idle_constants contains parole_duration, so replace it with the desired value.
- newAppIdleConstants = mOriginalAppIdleConsts.replaceAll(
- KEY_PAROLE_DURATION + "=\\d+", newConstant);
- } else {
- // app_idle_constants didn't have parole_duration, so append the desired value.
- newAppIdleConstants = mOriginalAppIdleConsts + "," + newConstant;
- }
- executeCmd(device, "settings put global app_idle_constants " + newAppIdleConstants);
- mAppIdleConstsUpdated = true;
+ private void setAppIdleConstants(String appIdleConstants) throws DeviceNotAvailableException {
+ executeCmd("settings put global app_idle_constants " + appIdleConstants);
}
- private String executeCmd(ITestDevice device, String cmd)
- throws DeviceNotAvailableException {
- final String output = device.executeShellCommand(cmd).trim();
+ private String getAppIdleConstants() throws DeviceNotAvailableException {
+ return executeCmd("settings get global app_idle_constants");
+ }
+
+ 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/Android.mk b/tests/cts/net/Android.mk
index 4aeab38..45941a7 100644
--- a/tests/cts/net/Android.mk
+++ b/tests/cts/net/Android.mk
@@ -24,7 +24,12 @@
# Include both the 32 and 64 bit versions
LOCAL_MULTILIB := both
-LOCAL_JAVA_LIBRARIES := voip-common conscrypt org.apache.http.legacy
+LOCAL_JAVA_LIBRARIES := \
+ voip-common \
+ conscrypt \
+ org.apache.http.legacy \
+ android.test.base.stubs \
+
LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libnativedns_jni \
libnativemultinetwork_jni libnativehelper_compat_libc++
@@ -36,15 +41,16 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
core-tests-support \
- compatibility-device-util \
- ctstestrunner \
+ compatibility-device-util-axt \
+ ctstestrunner-axt \
ctstestserver \
mockwebserver \
junit \
- legacy-android-test
+ truth-prebuilt
# uncomment when b/13249961 is fixed
#LOCAL_SDK_VERSION := current
+LOCAL_PRIVATE_PLATFORM_APIS := true
# Tag this module as a cts test artifact
LOCAL_COMPATIBILITY_SUITE := cts vts general-tests
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index dd310a1..b261b39 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
@@ -30,7 +31,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
- <application>
+ <application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
@@ -41,7 +42,7 @@
</receiver>
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="android.net.cts"
android:label="CTS tests of android.net">
<meta-data android:name="listener"
diff --git a/tests/cts/net/AndroidTest.xml b/tests/cts/net/AndroidTest.xml
index 4a578ea..1326970 100644
--- a/tests/cts/net/AndroidTest.xml
+++ b/tests/cts/net/AndroidTest.xml
@@ -13,6 +13,7 @@
limitations under the License.
-->
<configuration description="Config for CTS Net test cases">
+ <option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="networking" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
@@ -22,5 +23,6 @@
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.cts" />
<option name="runtime-hint" value="9m4s" />
+ <option name="hidden-api-checks" value="false" />
</test>
</configuration>
diff --git a/tests/cts/net/OWNERS b/tests/cts/net/OWNERS
new file mode 100644
index 0000000..dc82bb0
--- /dev/null
+++ b/tests/cts/net/OWNERS
@@ -0,0 +1,2 @@
+lorenzo@google.com
+satk@google.com
\ No newline at end of file
diff --git a/tests/cts/net/assets/network_watchlist_config_empty_for_test.xml b/tests/cts/net/assets/network_watchlist_config_empty_for_test.xml
new file mode 100644
index 0000000..19628d1
--- /dev/null
+++ b/tests/cts/net/assets/network_watchlist_config_empty_for_test.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright (C) 2018 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.
+*/
+-->
+<!-- This test config file is for NetworkWatchlistTest tests -->
+<watchlist-config>
+ <sha256-domain>
+ </sha256-domain>
+ <sha256-ip>
+ </sha256-ip>
+ <crc32-domain>
+ </crc32-domain>
+ <crc32-ip>
+ </crc32-ip>
+</watchlist-config>
diff --git a/tests/cts/net/assets/network_watchlist_config_for_test.xml b/tests/cts/net/assets/network_watchlist_config_for_test.xml
new file mode 100644
index 0000000..835ae0f
--- /dev/null
+++ b/tests/cts/net/assets/network_watchlist_config_for_test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright (C) 2018 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.
+*/
+-->
+<!-- This test config file just contains some random hashes for testing
+ConnectivityManager.getWatchlistConfigHash() -->
+<watchlist-config>
+ <sha256-domain>
+ <hash>F0905DA7549614957B449034C281EF7BDEFDBC2B6E050AD1E78D6DE18FBD0D5F</hash>
+ </sha256-domain>
+ <sha256-ip>
+ <hash>18DD41C9F2E8E4879A1575FB780514EF33CF6E1F66578C4AE7CCA31F49B9F2EC</hash>
+ </sha256-ip>
+ <crc32-domain>
+ <hash>AAAAAAAA</hash>
+ </crc32-domain>
+ <crc32-ip>
+ <hash>BBBBBBBB</hash>
+ </crc32-ip>
+</watchlist-config>
diff --git a/tests/cts/net/jni/Android.mk b/tests/cts/net/jni/Android.mk
index 887e95e..727a44d 100644
--- a/tests/cts/net/jni/Android.mk
+++ b/tests/cts/net/jni/Android.mk
@@ -28,7 +28,7 @@
LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog
LOCAL_CXX_STL := libc++_static
-LOCAL_CFLAGS := -Wno-unused-parameter
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
include $(BUILD_SHARED_LIBRARY)
@@ -37,6 +37,7 @@
# Don't include this package in any configuration by default.
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := NativeMultinetworkJni.c
+LOCAL_CFLAGS := -Wall -Werror -Wno-format
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog
LOCAL_CXX_STL := libc++_static
diff --git a/tests/cts/net/jni/NativeMultinetworkJni.c b/tests/cts/net/jni/NativeMultinetworkJni.c
index 9156504..2fa5291 100644
--- a/tests/cts/net/jni/NativeMultinetworkJni.c
+++ b/tests/cts/net/jni/NativeMultinetworkJni.c
@@ -47,7 +47,7 @@
const int saved_errno = errno;
freeaddrinfo(res);
- ALOGD("android_getaddrinfofornetwork(%llu, %s) returned rval=%d errno=%d",
+ ALOGD("android_getaddrinfofornetwork(%" PRIu64 ", %s) returned rval=%d errno=%d",
handle, kHostname, rval, saved_errno);
return rval == 0 ? 0 : -saved_errno;
}
@@ -61,7 +61,7 @@
errno = 0;
int rval = android_setprocnetwork(handle);
const int saved_errno = errno;
- ALOGD("android_setprocnetwork(%llu) returned rval=%d errno=%d",
+ ALOGD("android_setprocnetwork(%" PRIu64 ") returned rval=%d errno=%d",
handle, rval, saved_errno);
return rval == 0 ? 0 : -saved_errno;
}
@@ -82,7 +82,7 @@
errno = 0;
int rval = android_setsocknetwork(handle, fd);
const int saved_errno = errno;
- ALOGD("android_setprocnetwork(%llu, %d) returned rval=%d errno=%d",
+ ALOGD("android_setprocnetwork(%" PRIu64 ", %d) returned rval=%d errno=%d",
handle, fd, rval, saved_errno);
close(fd);
return rval == 0 ? 0 : -saved_errno;
diff --git a/tests/cts/net/native/qtaguid/Android.mk b/tests/cts/net/native/qtaguid/Android.mk
index 6c92b5c..bf89e5f 100644
--- a/tests/cts/net/native/qtaguid/Android.mk
+++ b/tests/cts/net/native/qtaguid/Android.mk
@@ -29,10 +29,10 @@
LOCAL_SHARED_LIBRARIES := \
libutils \
liblog \
- libcutils \
LOCAL_STATIC_LIBRARIES := \
- libgtest
+ libgtest \
+ libqtaguid \
LOCAL_CTS_TEST_PACKAGE := android.net.native
# Tag this module as a cts test artifact
diff --git a/tests/cts/net/native/qtaguid/AndroidTest.xml b/tests/cts/net/native/qtaguid/AndroidTest.xml
index 2eea82e..7591c87 100644
--- a/tests/cts/net/native/qtaguid/AndroidTest.xml
+++ b/tests/cts/net/native/qtaguid/AndroidTest.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Config for CTS Native Network xt_qtaguid test cases">
+ <option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="networking" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
diff --git a/tests/cts/net/native/qtaguid/src/NativeQtaguidTest.cpp b/tests/cts/net/native/qtaguid/src/NativeQtaguidTest.cpp
index 9009c24..1892a44 100644
--- a/tests/cts/net/native/qtaguid/src/NativeQtaguidTest.cpp
+++ b/tests/cts/net/native/qtaguid/src/NativeQtaguidTest.cpp
@@ -15,13 +15,38 @@
*/
#include <arpa/inet.h>
+#include <error.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <sys/socket.h>
+#include <sys/utsname.h>
#include <gtest/gtest.h>
-#include <cutils/qtaguid.h>
+#include <qtaguid/qtaguid.h>
+
+int hasQtaguidKernelSupport() {
+ struct utsname buf;
+ int kernel_version_major;
+ int kernel_version_minor;
+
+ int ret = uname(&buf);
+ if (ret) {
+ ret = -errno;
+ return ret;
+ }
+ char dummy;
+ ret = sscanf(buf.release, "%d.%d%c", &kernel_version_major, &kernel_version_minor, &dummy);
+ if (ret < 3)
+ return -EINVAL;
+
+ if ((kernel_version_major == 4 && kernel_version_minor < 9) ||
+ (kernel_version_major < 4)) {
+ return 1;
+ } else {
+ return access("/proc/net/xt_qtaguid/ctrl", F_OK) != -1;
+ }
+}
int getCtrlSkInfo(int tag, uid_t uid, uint64_t* sk_addr, int* ref_cnt) {
FILE *fp;
@@ -62,7 +87,7 @@
uint64_t sk_addr;
uint64_t expect_addr = 0;
- EXPECT_EQ(0, qtaguid_tagSocket(sockfd, tag, uid));
+ EXPECT_EQ(0, legacy_tagSocket(sockfd, tag, uid));
EXPECT_EQ(0, getCtrlSkInfo(tag, uid, &sk_addr, &ref_cnt));
EXPECT_EQ(expect_addr, sk_addr);
close(sockfd);
@@ -70,12 +95,18 @@
}
TEST (NativeQtaguidTest, close_socket_without_untag) {
+ int res = hasQtaguidKernelSupport();
+ ASSERT_LE(0, res);
+ if (!res) {
+ GTEST_LOG_(INFO) << "This test is skipped since kernel may not have the module\n";
+ return;
+ }
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
uid_t uid = getuid();
int tag = arc4random();
int ref_cnt;
uint64_t dummy_sk;
- EXPECT_EQ(0, qtaguid_tagSocket(sockfd, tag, uid));
+ EXPECT_EQ(0, legacy_tagSocket(sockfd, tag, uid));
EXPECT_EQ(0, getCtrlSkInfo(tag, uid, &dummy_sk, &ref_cnt));
EXPECT_EQ(2, ref_cnt);
close(sockfd);
@@ -83,12 +114,18 @@
}
TEST (NativeQtaguidTest, close_socket_without_untag_ipv6) {
+ int res = hasQtaguidKernelSupport();
+ ASSERT_LE(0, res);
+ if (!res) {
+ GTEST_LOG_(INFO) << "This test is skipped since kernel may not have the module\n";
+ return;
+ }
int sockfd = socket(AF_INET6, SOCK_STREAM, 0);
uid_t uid = getuid();
int tag = arc4random();
int ref_cnt;
uint64_t dummy_sk;
- EXPECT_EQ(0, qtaguid_tagSocket(sockfd, tag, uid));
+ EXPECT_EQ(0, legacy_tagSocket(sockfd, tag, uid));
EXPECT_EQ(0, getCtrlSkInfo(tag, uid, &dummy_sk, &ref_cnt));
EXPECT_EQ(2, ref_cnt);
close(sockfd);
@@ -96,12 +133,17 @@
}
TEST (NativeQtaguidTest, no_socket_addr_leak) {
+ int res = hasQtaguidKernelSupport();
+ ASSERT_LE(0, res);
+ if (!res) {
+ GTEST_LOG_(INFO) << "This test is skipped since kernel may not have the module\n";
+ return;
+ }
checkNoSocketPointerLeaks(AF_INET);
checkNoSocketPointerLeaks(AF_INET6);
}
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
-
return RUN_ALL_TESTS();
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index c885942..810b5df 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -16,19 +16,29 @@
package android.net.cts;
+import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
+import static android.content.pm.PackageManager.FEATURE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import android.app.Instrumentation;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
@@ -38,26 +48,44 @@
import android.net.NetworkRequest;
import android.net.wifi.WifiManager;
import android.os.Looper;
+import android.os.SystemClock;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.system.Os;
import android.system.OsConstants;
import android.test.AndroidTestCase;
+import android.text.TextUtils;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.internal.R;
import com.android.internal.telephony.PhoneConstants;
+import libcore.io.Streams;
+
import java.io.File;
import java.io.FileNotFoundException;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
-import java.net.Socket;
+import java.net.HttpURLConnection;
+import java.net.Inet6Address;
+import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
import java.util.HashMap;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class ConnectivityManagerTest extends AndroidTestCase {
@@ -72,6 +100,9 @@
private static final String TEST_HOST = "connectivitycheck.gstatic.com";
private static final int SOCKET_TIMEOUT_MS = 2000;
private static final int SEND_BROADCAST_TIMEOUT = 30000;
+ private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 5000;
+ private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
+ private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
private static final int HTTP_PORT = 80;
private static final String HTTP_REQUEST =
"GET /generate_204 HTTP/1.0\r\n" +
@@ -101,18 +132,22 @@
private static final int MIN_NUM_NETWORK_TYPES = 1;
private Context mContext;
+ private Instrumentation mInstrumentation;
private ConnectivityManager mCm;
private WifiManager mWifiManager;
private PackageManager mPackageManager;
private final HashMap<Integer, NetworkConfig> mNetworks =
new HashMap<Integer, NetworkConfig>();
boolean mWifiConnectAttempted;
+ private TestNetworkCallback mCellNetworkCallback;
+
@Override
protected void setUp() throws Exception {
super.setUp();
Looper.prepare();
mContext = getContext();
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mPackageManager = mContext.getPackageManager();
@@ -140,6 +175,10 @@
if (mWifiConnectAttempted) {
disconnectFromWifi(null);
}
+ if (cellConnectAttempted()) {
+ disconnectFromCell();
+ }
+ super.tearDown();
}
/**
@@ -246,6 +285,95 @@
}
}
+ /**
+ * Tests that connections can be opened on WiFi and cellphone networks,
+ * and that they are made from different IP addresses.
+ */
+ public void testOpenConnection() throws Exception {
+ boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
+ && mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
+ if (!canRunTest) {
+ Log.i(TAG,"testOpenConnection cannot execute unless device supports both WiFi "
+ + "and a cellular connection");
+ return;
+ }
+
+ Network wifiNetwork = connectToWifi();
+ Network cellNetwork = connectToCell();
+ // This server returns the requestor's IP address as the response body.
+ URL url = new URL("http://google-ipv6test.appspot.com/ip.js?fmt=text");
+ String wifiAddressString = httpGet(wifiNetwork, url);
+ String cellAddressString = httpGet(cellNetwork, url);
+
+ assertFalse(String.format("Same address '%s' on two different networks (%s, %s)",
+ wifiAddressString, wifiNetwork, cellNetwork),
+ wifiAddressString.equals(cellAddressString));
+
+ // Sanity check that the IP addresses that the requests appeared to come from
+ // are actually on the respective networks.
+ assertOnNetwork(wifiAddressString, wifiNetwork);
+ assertOnNetwork(cellAddressString, cellNetwork);
+
+ assertFalse("Unexpectedly equal: " + wifiNetwork, wifiNetwork.equals(cellNetwork));
+ }
+
+ private Network connectToCell() throws InterruptedException {
+ if (cellConnectAttempted()) {
+ throw new IllegalStateException("Already connected");
+ }
+ NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ mCellNetworkCallback = new TestNetworkCallback();
+ mCm.requestNetwork(cellRequest, mCellNetworkCallback);
+ final Network cellNetwork = mCellNetworkCallback.waitForAvailable();
+ assertNotNull("Cell network not available within timeout", cellNetwork);
+ return cellNetwork;
+ }
+
+ private boolean cellConnectAttempted() {
+ return mCellNetworkCallback != null;
+ }
+
+ private void disconnectFromCell() {
+ if (!cellConnectAttempted()) {
+ throw new IllegalStateException("Cell connection not attempted");
+ }
+ mCm.unregisterNetworkCallback(mCellNetworkCallback);
+ mCellNetworkCallback = null;
+ }
+
+ /**
+ * Performs a HTTP GET to the specified URL on the specified Network, and returns
+ * the response body decoded as UTF-8.
+ */
+ private static String httpGet(Network network, URL httpUrl) throws IOException {
+ HttpURLConnection connection = (HttpURLConnection) network.openConnection(httpUrl);
+ try {
+ InputStream inputStream = connection.getInputStream();
+ return Streams.readFully(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ private void assertOnNetwork(String adressString, Network network) throws UnknownHostException {
+ InetAddress address = InetAddress.getByName(adressString);
+ LinkProperties linkProperties = mCm.getLinkProperties(network);
+ // To make sure that the request went out on the right network, check that
+ // the IP address seen by the server is assigned to the expected network.
+ // We can only do this for IPv6 addresses, because in IPv4 we will likely
+ // have a private IPv4 address, and that won't match what the server sees.
+ if (address instanceof Inet6Address) {
+ assertContains(linkProperties.getAddresses(), address);
+ }
+ }
+
+ private static<T> void assertContains(Collection<T> collection, T element) {
+ assertTrue(element + " not found in " + collection, collection.contains(element));
+ }
+
private void assertStartUsingNetworkFeatureUnsupported(int networkType, String feature) {
try {
mCm.startUsingNetworkFeature(networkType, feature);
@@ -324,7 +452,7 @@
* that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
*/
public void testRegisterNetworkCallback() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
return;
}
@@ -364,7 +492,7 @@
* of a {@code NetworkCallback}.
*/
public void testRegisterNetworkCallback_withPendingIntent() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
return;
}
@@ -458,7 +586,7 @@
* Tests reporting of connectivity changed.
*/
public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent cannot execute unless device supports WiFi");
return;
}
@@ -475,7 +603,7 @@
}
public void testConnectivityChanged_whenRegistered_shouldReceiveIntent() {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testConnectivityChanged_whenRegistered_shouldReceiveIntent cannot execute unless device supports WiFi");
return;
}
@@ -495,14 +623,14 @@
public void testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent()
throws InterruptedException {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
return;
}
- Intent startIntent = new Intent();
- startIntent.setComponent(new ComponentName("android.net.cts.appForApi23",
- "android.net.cts.appForApi23.ConnectivityListeningActivity"));
- mContext.startActivity(startIntent);
+ mContext.startActivity(new Intent()
+ .setComponent(new ComponentName("android.net.cts.appForApi23",
+ "android.net.cts.appForApi23.ConnectivityListeningActivity"))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
Thread.sleep(200);
toggleWifi();
@@ -835,4 +963,146 @@
assertTrue(lowerBoundSec <= interval);
assertTrue(interval <= upperBoundSec);
}
+
+ // Returns "true", "false" or "none"
+ private String getWifiMeteredStatus(String ssid) throws Exception {
+ // Interestingly giving the SSID as an argument to list wifi-networks
+ // only works iff the network in question has the "false" policy.
+ // Also unfortunately runShellCommand does not pass the command to the interpreter
+ // so it's not possible to | grep the ssid.
+ final String command = "cmd netpolicy list wifi-networks";
+ final String policyString = runShellCommand(mInstrumentation, command);
+
+ final Matcher m = Pattern.compile("^" + ssid + ";(true|false|none)$",
+ Pattern.MULTILINE | Pattern.UNIX_LINES).matcher(policyString);
+ if (!m.find()) {
+ fail("Unexpected format from cmd netpolicy");
+ }
+ return m.group(1);
+ }
+
+ // metered should be "true", "false" or "none"
+ private void setWifiMeteredStatus(String ssid, String metered) throws Exception {
+ final String setCommand = "cmd netpolicy set metered-network " + ssid + " " + metered;
+ runShellCommand(mInstrumentation, setCommand);
+ assertEquals(getWifiMeteredStatus(ssid), metered);
+ }
+
+ private 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;
+ }
+ }
+
+ private void waitForActiveNetworkMetered(boolean requestedMeteredness) 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 == requestedMeteredness) {
+ 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.
+ mCm.registerDefaultNetworkCallback(networkCallback);
+ if (!latch.await(NETWORK_CHANGE_METEREDNESS_TIMEOUT, TimeUnit.MILLISECONDS)) {
+ fail("Timed out waiting for active network metered status to change to "
+ + requestedMeteredness + " ; network = " + mCm.getActiveNetwork());
+ }
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
+
+ private void assertMultipathPreferenceIsEventually(Network network, int oldValue,
+ int expectedValue) {
+ // Sanity check : if oldValue == expectedValue, there is no way to guarantee the test
+ // is not flaky.
+ assertNotSame(oldValue, expectedValue);
+
+ for (int i = 0; i < NUM_TRIES_MULTIPATH_PREF_CHECK; ++i) {
+ final int actualValue = mCm.getMultipathPreference(network);
+ if (actualValue == expectedValue) {
+ return;
+ }
+ if (actualValue != oldValue) {
+ fail("Multipath preference is neither previous (" + oldValue
+ + ") nor expected (" + expectedValue + ")");
+ }
+ SystemClock.sleep(INTERVAL_MULTIPATH_PREF_CHECK_MS);
+ }
+ fail("Timed out waiting for multipath preference to change. expected = "
+ + expectedValue + " ; actual = " + mCm.getMultipathPreference(network));
+ }
+
+ private int getCurrentMeteredMultipathPreference(ContentResolver resolver) {
+ final String rawMeteredPref = Settings.Global.getString(resolver,
+ NETWORK_METERED_MULTIPATH_PREFERENCE);
+ return TextUtils.isEmpty(rawMeteredPref)
+ ? mContext.getResources().getInteger(R.integer.config_networkMeteredMultipathPreference)
+ : Integer.parseInt(rawMeteredPref);
+ }
+
+ private int findNextPrefValue(ContentResolver resolver) {
+ // A bit of a nuclear hammer, but race conditions in CTS are bad. To be able to
+ // detect a correct setting value without race conditions, the next pref must
+ // be a valid value (range 0..3) that is different from the old setting of the
+ // metered preference and from the unmetered preference.
+ final int meteredPref = getCurrentMeteredMultipathPreference(resolver);
+ final int unmeteredPref = ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED;
+ if (0 != meteredPref && 0 != unmeteredPref) return 0;
+ if (1 != meteredPref && 1 != unmeteredPref) return 1;
+ return 2;
+ }
+
+ /**
+ * Verify that getMultipathPreference does return appropriate values
+ * for metered and unmetered networks.
+ */
+ public void testGetMultipathPreference() throws Exception {
+ final ContentResolver resolver = mContext.getContentResolver();
+ final Network network = ensureWifiConnected();
+ final String ssid = unquoteSSID(mWifiManager.getConnectionInfo().getSSID());
+ final String oldMeteredSetting = getWifiMeteredStatus(ssid);
+ final String oldMeteredMultipathPreference = Settings.Global.getString(
+ resolver, NETWORK_METERED_MULTIPATH_PREFERENCE);
+ try {
+ final int initialMeteredPreference = getCurrentMeteredMultipathPreference(resolver);
+ int newMeteredPreference = findNextPrefValue(resolver);
+ Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
+ Integer.toString(newMeteredPreference));
+ setWifiMeteredStatus(ssid, "true");
+ waitForActiveNetworkMetered(true);
+ assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
+ NET_CAPABILITY_NOT_METERED), false);
+ assertMultipathPreferenceIsEventually(network, initialMeteredPreference,
+ newMeteredPreference);
+
+ final int oldMeteredPreference = newMeteredPreference;
+ newMeteredPreference = findNextPrefValue(resolver);
+ Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
+ Integer.toString(newMeteredPreference));
+ assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
+ NET_CAPABILITY_NOT_METERED), false);
+ assertMultipathPreferenceIsEventually(network,
+ oldMeteredPreference, newMeteredPreference);
+
+ setWifiMeteredStatus(ssid, "false");
+ waitForActiveNetworkMetered(false);
+ assertEquals(mCm.getNetworkCapabilities(network).hasCapability(
+ NET_CAPABILITY_NOT_METERED), true);
+ assertMultipathPreferenceIsEventually(network, newMeteredPreference,
+ ConnectivityManager.MULTIPATH_PREFERENCE_UNMETERED);
+ } finally {
+ Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
+ oldMeteredMultipathPreference);
+ setWifiMeteredStatus(ssid, oldMeteredSetting);
+ }
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/DnsTest.java b/tests/cts/net/src/android/net/cts/DnsTest.java
index 8575c33..84231c2 100644
--- a/tests/cts/net/src/android/net/cts/DnsTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsTest.java
@@ -19,6 +19,9 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
+import android.net.Network;
import android.net.NetworkInfo;
import android.os.SystemClock;
import android.test.AndroidTestCase;
@@ -29,6 +32,8 @@
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
public class DnsTest extends AndroidTestCase {
@@ -40,6 +45,12 @@
private static final String TAG = "DnsTest";
private static final String PROXY_NETWORK_TYPE = "PROXY";
+ private ConnectivityManager mCm;
+
+ public void setUp() {
+ mCm = getContext().getSystemService(ConnectivityManager.class);
+ }
+
/**
* @return true on success
*/
@@ -57,7 +68,9 @@
* Perf - measure size of first and second tier caches and their effect
* Assert requires network permission
*/
- public void testDnsWorks() {
+ public void testDnsWorks() throws Exception {
+ ensureIpv6Connectivity();
+
InetAddress addrs[] = {};
try {
addrs = InetAddress.getAllByName("www.google.com");
@@ -88,11 +101,14 @@
try {
addrs = InetAddress.getAllByName("ipv6.google.com");
} catch (UnknownHostException e) {}
- assertTrue("[RERUN] DNS could not resolve ipv6.google.com, check the network supports IPv6",
- addrs.length != 0);
+ String msg =
+ "[RERUN] DNS could not resolve ipv6.google.com, check the network supports IPv6. lp=" +
+ mCm.getActiveLinkProperties();
+ assertTrue(msg, addrs.length != 0);
for (InetAddress addr : addrs) {
- assertFalse ("[RERUN] ipv6.google.com returned IPv4 address: " + addr.getHostAddress() +
- ", check your network's DNS server", addr instanceof Inet4Address);
+ msg = "[RERUN] ipv6.google.com returned IPv4 address: " + addr.getHostAddress() +
+ ", check your network's DNS server. lp=" + mCm.getActiveLinkProperties();
+ assertFalse (msg, addr instanceof Inet4Address);
foundV6 |= (addr instanceof Inet6Address);
if (DBG) Log.e(TAG, "ipv6.google.com gave " + addr.toString());
}
@@ -256,13 +272,35 @@
}
private boolean activeNetworkInfoIsProxy() {
- ConnectivityManager cm = (ConnectivityManager)
- getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
- NetworkInfo info = cm.getActiveNetworkInfo();
+ NetworkInfo info = mCm.getActiveNetworkInfo();
if (PROXY_NETWORK_TYPE.equals(info.getTypeName())) {
return true;
}
return false;
}
+
+ private void ensureIpv6Connectivity() throws InterruptedException {
+ CountDownLatch latch = new CountDownLatch(1);
+ final int TIMEOUT_MS = 5_000;
+
+ final NetworkCallback callback = new NetworkCallback() {
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
+ if (lp.hasGlobalIPv6Address()) {
+ latch.countDown();
+ }
+ }
+ };
+ mCm.registerDefaultNetworkCallback(callback);
+
+ String msg = "Default network did not provide IPv6 connectivity after " + TIMEOUT_MS
+ + "ms. Please connect to an IPv6-capable network. lp="
+ + mCm.getActiveLinkProperties();
+ try {
+ assertTrue(msg, latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ } finally {
+ mCm.unregisterNetworkCallback(callback);
+ }
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
new file mode 100644
index 0000000..7132ecf
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2018 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.cts;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.content.Context;
+import android.net.IpSecAlgorithm;
+import android.net.IpSecManager;
+import android.net.IpSecTransform;
+import android.system.Os;
+import android.system.OsConstants;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class IpSecBaseTest extends AndroidTestCase {
+
+ private static final String TAG = IpSecBaseTest.class.getSimpleName();
+
+ protected static final String IPV4_LOOPBACK = "127.0.0.1";
+ protected static final String IPV6_LOOPBACK = "::1";
+ protected static final String[] LOOPBACK_ADDRS = new String[] {IPV4_LOOPBACK, IPV6_LOOPBACK};
+ protected static final int[] DIRECTIONS =
+ new int[] {IpSecManager.DIRECTION_IN, IpSecManager.DIRECTION_OUT};
+
+ protected static final byte[] TEST_DATA = "Best test data ever!".getBytes();
+ protected static final int DATA_BUFFER_LEN = 4096;
+ protected static final int SOCK_TIMEOUT = 500;
+
+ private static final byte[] KEY_DATA = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x20, 0x21, 0x22, 0x23
+ };
+
+ protected static final byte[] AUTH_KEY = getKey(256);
+ protected static final byte[] CRYPT_KEY = getKey(256);
+
+ protected IpSecManager mISM;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mISM = (IpSecManager) getContext().getSystemService(Context.IPSEC_SERVICE);
+ }
+
+ protected static byte[] getKey(int bitLength) {
+ return Arrays.copyOf(KEY_DATA, bitLength / 8);
+ }
+
+ protected static int getDomain(InetAddress address) {
+ int domain;
+ if (address instanceof Inet6Address) {
+ domain = OsConstants.AF_INET6;
+ } else {
+ domain = OsConstants.AF_INET;
+ }
+ return domain;
+ }
+
+ protected static int getPort(FileDescriptor sock) throws Exception {
+ return ((InetSocketAddress) Os.getsockname(sock)).getPort();
+ }
+
+ public static interface GenericSocket extends AutoCloseable {
+ void send(byte[] data) throws Exception;
+
+ byte[] receive() throws Exception;
+
+ int getPort() throws Exception;
+
+ void close() throws Exception;
+
+ void applyTransportModeTransform(
+ IpSecManager ism, int direction, IpSecTransform transform) throws Exception;
+
+ void removeTransportModeTransforms(IpSecManager ism) throws Exception;
+ }
+
+ public static interface GenericTcpSocket extends GenericSocket {}
+
+ public static interface GenericUdpSocket extends GenericSocket {
+ void sendTo(byte[] data, InetAddress dstAddr, int port) throws Exception;
+ }
+
+ public abstract static class NativeSocket implements GenericSocket {
+ public FileDescriptor mFd;
+
+ public NativeSocket(FileDescriptor fd) {
+ mFd = fd;
+ }
+
+ @Override
+ public void send(byte[] data) throws Exception {
+ Os.write(mFd, data, 0, data.length);
+ }
+
+ @Override
+ public byte[] receive() throws Exception {
+ byte[] in = new byte[DATA_BUFFER_LEN];
+ AtomicInteger bytesRead = new AtomicInteger(-1);
+
+ Thread readSockThread = new Thread(() -> {
+ long startTime = System.currentTimeMillis();
+ while (bytesRead.get() < 0 && System.currentTimeMillis() < startTime + SOCK_TIMEOUT) {
+ try {
+ bytesRead.set(Os.recvfrom(mFd, in, 0, DATA_BUFFER_LEN, 0, null));
+ } catch (Exception e) {
+ Log.e(TAG, "Error encountered reading from socket", e);
+ }
+ }
+ });
+
+ readSockThread.start();
+ readSockThread.join(SOCK_TIMEOUT);
+
+ if (bytesRead.get() < 0) {
+ throw new IOException("No data received from socket");
+ }
+
+ return Arrays.copyOfRange(in, 0, bytesRead.get());
+ }
+
+ @Override
+ public int getPort() throws Exception {
+ return IpSecBaseTest.getPort(mFd);
+ }
+
+ @Override
+ public void close() throws Exception {
+ Os.close(mFd);
+ }
+
+ @Override
+ public void applyTransportModeTransform(
+ IpSecManager ism, int direction, IpSecTransform transform) throws Exception {
+ ism.applyTransportModeTransform(mFd, direction, transform);
+ }
+
+ @Override
+ public void removeTransportModeTransforms(IpSecManager ism) throws Exception {
+ ism.removeTransportModeTransforms(mFd);
+ }
+ }
+
+ public static class NativeTcpSocket extends NativeSocket implements GenericTcpSocket {
+ public NativeTcpSocket(FileDescriptor fd) {
+ super(fd);
+ }
+ }
+
+ public static class NativeUdpSocket extends NativeSocket implements GenericUdpSocket {
+ public NativeUdpSocket(FileDescriptor fd) {
+ super(fd);
+ }
+
+ @Override
+ public void sendTo(byte[] data, InetAddress dstAddr, int port) throws Exception {
+ Os.sendto(mFd, data, 0, data.length, 0, dstAddr, port);
+ }
+ }
+
+ public static class JavaUdpSocket implements GenericUdpSocket {
+ public final DatagramSocket mSocket;
+
+ public JavaUdpSocket(InetAddress localAddr) {
+ try {
+ mSocket = new DatagramSocket(0, localAddr);
+ mSocket.setSoTimeout(SOCK_TIMEOUT);
+ } catch (SocketException e) {
+ // Fail loudly if we can't set up sockets properly. And without the timeout, we
+ // could easily end up in an endless wait.
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void send(byte[] data) throws Exception {
+ mSocket.send(new DatagramPacket(data, data.length));
+ }
+
+ @Override
+ public void sendTo(byte[] data, InetAddress dstAddr, int port) throws Exception {
+ mSocket.send(new DatagramPacket(data, data.length, dstAddr, port));
+ }
+
+ @Override
+ public int getPort() throws Exception {
+ return mSocket.getLocalPort();
+ }
+
+ @Override
+ public void close() throws Exception {
+ mSocket.close();
+ }
+
+ @Override
+ public byte[] receive() throws Exception {
+ DatagramPacket data = new DatagramPacket(new byte[DATA_BUFFER_LEN], DATA_BUFFER_LEN);
+ mSocket.receive(data);
+ return Arrays.copyOfRange(data.getData(), 0, data.getLength());
+ }
+
+ @Override
+ public void applyTransportModeTransform(
+ IpSecManager ism, int direction, IpSecTransform transform) throws Exception {
+ ism.applyTransportModeTransform(mSocket, direction, transform);
+ }
+
+ @Override
+ public void removeTransportModeTransforms(IpSecManager ism) throws Exception {
+ ism.removeTransportModeTransforms(mSocket);
+ }
+ }
+
+ public static class JavaTcpSocket implements GenericTcpSocket {
+ public final Socket mSocket;
+
+ public JavaTcpSocket(Socket socket) {
+ mSocket = socket;
+ try {
+ mSocket.setSoTimeout(SOCK_TIMEOUT);
+ } catch (SocketException e) {
+ // Fail loudly if we can't set up sockets properly. And without the timeout, we
+ // could easily end up in an endless wait.
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void send(byte[] data) throws Exception {
+ mSocket.getOutputStream().write(data);
+ }
+
+ @Override
+ public byte[] receive() throws Exception {
+ byte[] in = new byte[DATA_BUFFER_LEN];
+ int bytesRead = mSocket.getInputStream().read(in);
+ return Arrays.copyOfRange(in, 0, bytesRead);
+ }
+
+ @Override
+ public int getPort() throws Exception {
+ return mSocket.getLocalPort();
+ }
+
+ @Override
+ public void close() throws Exception {
+ mSocket.close();
+ }
+
+ @Override
+ public void applyTransportModeTransform(
+ IpSecManager ism, int direction, IpSecTransform transform) throws Exception {
+ ism.applyTransportModeTransform(mSocket, direction, transform);
+ }
+
+ @Override
+ public void removeTransportModeTransforms(IpSecManager ism) throws Exception {
+ ism.removeTransportModeTransforms(mSocket);
+ }
+ }
+
+ public static class SocketPair<T> {
+ public final T mLeftSock;
+ public final T mRightSock;
+
+ public SocketPair(T leftSock, T rightSock) {
+ mLeftSock = leftSock;
+ mRightSock = rightSock;
+ }
+ }
+
+ protected static void applyTransformBidirectionally(
+ IpSecManager ism, IpSecTransform transform, GenericSocket socket) throws Exception {
+ for (int direction : DIRECTIONS) {
+ socket.applyTransportModeTransform(ism, direction, transform);
+ }
+ }
+
+ public static SocketPair<NativeUdpSocket> getNativeUdpSocketPair(
+ InetAddress localAddr, IpSecManager ism, IpSecTransform transform, boolean connected)
+ throws Exception {
+ int domain = getDomain(localAddr);
+
+ NativeUdpSocket leftSock = new NativeUdpSocket(
+ Os.socket(domain, OsConstants.SOCK_DGRAM, OsConstants.IPPROTO_UDP));
+ NativeUdpSocket rightSock = new NativeUdpSocket(
+ Os.socket(domain, OsConstants.SOCK_DGRAM, OsConstants.IPPROTO_UDP));
+
+ for (NativeUdpSocket sock : new NativeUdpSocket[] {leftSock, rightSock}) {
+ applyTransformBidirectionally(ism, transform, sock);
+ Os.bind(sock.mFd, localAddr, 0);
+ }
+
+ if (connected) {
+ Os.connect(leftSock.mFd, localAddr, rightSock.getPort());
+ Os.connect(rightSock.mFd, localAddr, leftSock.getPort());
+ }
+
+ return new SocketPair<>(leftSock, rightSock);
+ }
+
+ public static SocketPair<NativeTcpSocket> getNativeTcpSocketPair(
+ InetAddress localAddr, IpSecManager ism, IpSecTransform transform) throws Exception {
+ int domain = getDomain(localAddr);
+
+ NativeTcpSocket server = new NativeTcpSocket(
+ Os.socket(domain, OsConstants.SOCK_STREAM, OsConstants.IPPROTO_TCP));
+ NativeTcpSocket client = new NativeTcpSocket(
+ Os.socket(domain, OsConstants.SOCK_STREAM, OsConstants.IPPROTO_TCP));
+
+ Os.bind(server.mFd, localAddr, 0);
+
+ applyTransformBidirectionally(ism, transform, server);
+ applyTransformBidirectionally(ism, transform, client);
+
+ Os.listen(server.mFd, 10);
+ Os.connect(client.mFd, localAddr, server.getPort());
+ NativeTcpSocket accepted = new NativeTcpSocket(Os.accept(server.mFd, null));
+
+ applyTransformBidirectionally(ism, transform, accepted);
+ server.close();
+
+ return new SocketPair<>(client, accepted);
+ }
+
+ public static SocketPair<JavaUdpSocket> getJavaUdpSocketPair(
+ InetAddress localAddr, IpSecManager ism, IpSecTransform transform, boolean connected)
+ throws Exception {
+ JavaUdpSocket leftSock = new JavaUdpSocket(localAddr);
+ JavaUdpSocket rightSock = new JavaUdpSocket(localAddr);
+
+ applyTransformBidirectionally(ism, transform, leftSock);
+ applyTransformBidirectionally(ism, transform, rightSock);
+
+ if (connected) {
+ leftSock.mSocket.connect(localAddr, rightSock.mSocket.getLocalPort());
+ rightSock.mSocket.connect(localAddr, leftSock.mSocket.getLocalPort());
+ }
+
+ return new SocketPair<>(leftSock, rightSock);
+ }
+
+ public static SocketPair<JavaTcpSocket> getJavaTcpSocketPair(
+ InetAddress localAddr, IpSecManager ism, IpSecTransform transform) throws Exception {
+ JavaTcpSocket clientSock = new JavaTcpSocket(new Socket());
+ ServerSocket serverSocket = new ServerSocket();
+ serverSocket.bind(new InetSocketAddress(localAddr, 0));
+
+ // While technically the client socket does not need to be bound, the OpenJDK implementation
+ // of Socket only allocates an FD when bind() or connect() or other similar methods are
+ // called. So we call bind to force the FD creation, so that we can apply a transform to it
+ // prior to socket connect.
+ clientSock.mSocket.bind(new InetSocketAddress(localAddr, 0));
+
+ // IpSecService doesn't support serverSockets at the moment; workaround using FD
+ FileDescriptor serverFd = serverSocket.getImpl().getFD$();
+
+ applyTransformBidirectionally(ism, transform, new NativeTcpSocket(serverFd));
+ applyTransformBidirectionally(ism, transform, clientSock);
+
+ clientSock.mSocket.connect(new InetSocketAddress(localAddr, serverSocket.getLocalPort()));
+ JavaTcpSocket acceptedSock = new JavaTcpSocket(serverSocket.accept());
+
+ applyTransformBidirectionally(ism, transform, acceptedSock);
+ serverSocket.close();
+
+ return new SocketPair<>(clientSock, acceptedSock);
+ }
+
+ private void checkSocketPair(GenericSocket left, GenericSocket right) throws Exception {
+ left.send(TEST_DATA);
+ assertArrayEquals(TEST_DATA, right.receive());
+
+ right.send(TEST_DATA);
+ assertArrayEquals(TEST_DATA, left.receive());
+
+ left.close();
+ right.close();
+ }
+
+ private void checkUnconnectedUdpSocketPair(
+ GenericUdpSocket left, GenericUdpSocket right, InetAddress localAddr) throws Exception {
+ left.sendTo(TEST_DATA, localAddr, right.getPort());
+ assertArrayEquals(TEST_DATA, right.receive());
+
+ right.sendTo(TEST_DATA, localAddr, left.getPort());
+ assertArrayEquals(TEST_DATA, left.receive());
+
+ left.close();
+ right.close();
+ }
+
+ protected static IpSecTransform buildIpSecTransform(
+ Context mContext,
+ IpSecManager.SecurityParameterIndex spi,
+ IpSecManager.UdpEncapsulationSocket encapSocket,
+ InetAddress remoteAddr)
+ throws Exception {
+ String localAddr = (remoteAddr instanceof Inet4Address) ? IPV4_LOOPBACK : IPV6_LOOPBACK;
+ IpSecTransform.Builder builder =
+ new IpSecTransform.Builder(mContext)
+ .setEncryption(new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY))
+ .setAuthentication(
+ new IpSecAlgorithm(
+ IpSecAlgorithm.AUTH_HMAC_SHA256,
+ AUTH_KEY,
+ AUTH_KEY.length * 4));
+
+ if (encapSocket != null) {
+ builder.setIpv4Encapsulation(encapSocket, encapSocket.getPort());
+ }
+
+ return builder.buildTransportModeTransform(InetAddress.getByName(localAddr), spi);
+ }
+
+ private IpSecTransform buildDefaultTransform(InetAddress localAddr) throws Exception {
+ try (IpSecManager.SecurityParameterIndex spi =
+ mISM.allocateSecurityParameterIndex(localAddr)) {
+ return buildIpSecTransform(mContext, spi, null, localAddr);
+ }
+ }
+
+ public void testJavaTcpSocketPair() throws Exception {
+ for (String addr : LOOPBACK_ADDRS) {
+ InetAddress local = InetAddress.getByName(addr);
+ try (IpSecTransform transform = buildDefaultTransform(local)) {
+ SocketPair<JavaTcpSocket> sockets = getJavaTcpSocketPair(local, mISM, transform);
+ checkSocketPair(sockets.mLeftSock, sockets.mRightSock);
+ }
+ }
+ }
+
+ public void testJavaUdpSocketPair() throws Exception {
+ for (String addr : LOOPBACK_ADDRS) {
+ InetAddress local = InetAddress.getByName(addr);
+ try (IpSecTransform transform = buildDefaultTransform(local)) {
+ SocketPair<JavaUdpSocket> sockets =
+ getJavaUdpSocketPair(local, mISM, transform, true);
+ checkSocketPair(sockets.mLeftSock, sockets.mRightSock);
+ }
+ }
+ }
+
+ public void testJavaUdpSocketPairUnconnected() throws Exception {
+ for (String addr : LOOPBACK_ADDRS) {
+ InetAddress local = InetAddress.getByName(addr);
+ try (IpSecTransform transform = buildDefaultTransform(local)) {
+ SocketPair<JavaUdpSocket> sockets =
+ getJavaUdpSocketPair(local, mISM, transform, false);
+ checkUnconnectedUdpSocketPair(sockets.mLeftSock, sockets.mRightSock, local);
+ }
+ }
+ }
+
+ public void testNativeTcpSocketPair() throws Exception {
+ for (String addr : LOOPBACK_ADDRS) {
+ InetAddress local = InetAddress.getByName(addr);
+ try (IpSecTransform transform = buildDefaultTransform(local)) {
+ SocketPair<NativeTcpSocket> sockets =
+ getNativeTcpSocketPair(local, mISM, transform);
+ checkSocketPair(sockets.mLeftSock, sockets.mRightSock);
+ }
+ }
+ }
+
+ public void testNativeUdpSocketPair() throws Exception {
+ for (String addr : LOOPBACK_ADDRS) {
+ InetAddress local = InetAddress.getByName(addr);
+ try (IpSecTransform transform = buildDefaultTransform(local)) {
+ SocketPair<NativeUdpSocket> sockets =
+ getNativeUdpSocketPair(local, mISM, transform, true);
+ checkSocketPair(sockets.mLeftSock, sockets.mRightSock);
+ }
+ }
+ }
+
+ public void testNativeUdpSocketPairUnconnected() throws Exception {
+ for (String addr : LOOPBACK_ADDRS) {
+ InetAddress local = InetAddress.getByName(addr);
+ try (IpSecTransform transform = buildDefaultTransform(local)) {
+ SocketPair<NativeUdpSocket> sockets =
+ getNativeUdpSocketPair(local, mISM, transform, false);
+ checkUnconnectedUdpSocketPair(sockets.mLeftSock, sockets.mRightSock, local);
+ }
+ }
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
new file mode 100644
index 0000000..a18b2f0
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -0,0 +1,1140 @@
+/*
+ * Copyright (C) 2017 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.cts;
+
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.IpSecAlgorithm;
+import android.net.IpSecManager;
+import android.net.IpSecTransform;
+import android.net.TrafficStats;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+
+public class IpSecManagerTest extends IpSecBaseTest {
+
+ private static final String TAG = IpSecManagerTest.class.getSimpleName();
+
+ private ConnectivityManager mCM;
+
+ private static InetAddress IpAddress(String addrString) {
+ try {
+ return InetAddress.getByName(addrString);
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException("Invalid IP address: " + e);
+ }
+ }
+
+ private static final InetAddress GOOGLE_DNS_4 = IpAddress("8.8.8.8");
+ private static final InetAddress GOOGLE_DNS_6 = IpAddress("2001:4860:4860::8888");
+
+ private static final InetAddress[] GOOGLE_DNS_LIST =
+ new InetAddress[] {GOOGLE_DNS_4, GOOGLE_DNS_6};
+
+ private static final int DROID_SPI = 0xD1201D;
+ private static final int MAX_PORT_BIND_ATTEMPTS = 10;
+
+ private static final byte[] AEAD_KEY = getKey(288);
+
+ private static final int TCP_HDRLEN_WITH_OPTIONS = 32;
+ private static final int UDP_HDRLEN = 8;
+ private static final int IP4_HDRLEN = 20;
+ private static final int IP6_HDRLEN = 40;
+
+ // Encryption parameters
+ private static final int AES_GCM_IV_LEN = 8;
+ private static final int AES_CBC_IV_LEN = 16;
+ private static final int AES_GCM_BLK_SIZE = 4;
+ private static final int AES_CBC_BLK_SIZE = 16;
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ /*
+ * Allocate a random SPI
+ * Allocate a specific SPI using previous randomly created SPI value
+ * Realloc the same SPI that was specifically created (expect SpiUnavailable)
+ * Close SPIs
+ */
+ public void testAllocSpi() throws Exception {
+ for (InetAddress addr : GOOGLE_DNS_LIST) {
+ IpSecManager.SecurityParameterIndex randomSpi = null, droidSpi = null;
+ randomSpi = mISM.allocateSecurityParameterIndex(addr);
+ assertTrue(
+ "Failed to receive a valid SPI",
+ randomSpi.getSpi() != IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
+
+ droidSpi = mISM.allocateSecurityParameterIndex(addr, DROID_SPI);
+ assertTrue("Failed to allocate specified SPI, " + DROID_SPI,
+ droidSpi.getSpi() == DROID_SPI);
+
+ try {
+ mISM.allocateSecurityParameterIndex(addr, DROID_SPI);
+ fail("Duplicate SPI was allowed to be created");
+ } catch (IpSecManager.SpiUnavailableException expected) {
+ // This is a success case because we expect a dupe SPI to throw
+ }
+
+ randomSpi.close();
+ droidSpi.close();
+ }
+ }
+
+ /** This function finds an available port */
+ private static int findUnusedPort() throws Exception {
+ // Get an available port.
+ DatagramSocket s = new DatagramSocket();
+ int port = s.getLocalPort();
+ s.close();
+ return port;
+ }
+
+ private static FileDescriptor getBoundUdpSocket(InetAddress address) throws Exception {
+ FileDescriptor sock =
+ Os.socket(getDomain(address), OsConstants.SOCK_DGRAM, OsConstants.IPPROTO_UDP);
+
+ for (int i = 0; i < MAX_PORT_BIND_ATTEMPTS; i++) {
+ try {
+ int port = findUnusedPort();
+ Os.bind(sock, address, port);
+ break;
+ } catch (ErrnoException e) {
+ // Someone claimed the port since we called findUnusedPort.
+ if (e.errno == OsConstants.EADDRINUSE) {
+ if (i == MAX_PORT_BIND_ATTEMPTS - 1) {
+
+ fail("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
+ }
+ continue;
+ }
+ throw e.rethrowAsIOException();
+ }
+ }
+ return sock;
+ }
+
+ private void checkUnconnectedUdp(IpSecTransform transform, InetAddress local, int sendCount,
+ boolean useJavaSockets) throws Exception {
+ GenericUdpSocket sockLeft = null, sockRight = null;
+ if (useJavaSockets) {
+ SocketPair<JavaUdpSocket> sockets = getJavaUdpSocketPair(local, mISM, transform, false);
+ sockLeft = sockets.mLeftSock;
+ sockRight = sockets.mRightSock;
+ } else {
+ SocketPair<NativeUdpSocket> sockets =
+ getNativeUdpSocketPair(local, mISM, transform, false);
+ sockLeft = sockets.mLeftSock;
+ sockRight = sockets.mRightSock;
+ }
+
+ for (int i = 0; i < sendCount; i++) {
+ byte[] in;
+
+ sockLeft.sendTo(TEST_DATA, local, sockRight.getPort());
+ in = sockRight.receive();
+ assertArrayEquals("Left-to-right encrypted data did not match.", TEST_DATA, in);
+
+ sockRight.sendTo(TEST_DATA, local, sockLeft.getPort());
+ in = sockLeft.receive();
+ assertArrayEquals("Right-to-left encrypted data did not match.", TEST_DATA, in);
+ }
+
+ sockLeft.close();
+ sockRight.close();
+ }
+
+ private void checkTcp(IpSecTransform transform, InetAddress local, int sendCount,
+ boolean useJavaSockets) throws Exception {
+ GenericTcpSocket client = null, accepted = null;
+ if (useJavaSockets) {
+ SocketPair<JavaTcpSocket> sockets = getJavaTcpSocketPair(local, mISM, transform);
+ client = sockets.mLeftSock;
+ accepted = sockets.mRightSock;
+ } else {
+ SocketPair<NativeTcpSocket> sockets = getNativeTcpSocketPair(local, mISM, transform);
+ client = sockets.mLeftSock;
+ accepted = sockets.mRightSock;
+ }
+
+ // Wait for TCP handshake packets to be counted
+ StatsChecker.waitForNumPackets(3); // (SYN, SYN+ACK, ACK)
+
+ // Reset StatsChecker, to ignore negotiation overhead.
+ StatsChecker.initStatsChecker();
+ for (int i = 0; i < sendCount; i++) {
+ byte[] in;
+
+ client.send(TEST_DATA);
+ in = accepted.receive();
+ assertArrayEquals("Client-to-server encrypted data did not match.", TEST_DATA, in);
+
+ // Allow for newest data + ack packets to be returned before sending next packet
+ // Also add the number of expected packets in each of the previous runs (4 per run)
+ StatsChecker.waitForNumPackets(2 + (4 * i));
+
+ accepted.send(TEST_DATA);
+ in = client.receive();
+ assertArrayEquals("Server-to-client encrypted data did not match.", TEST_DATA, in);
+
+ // Allow for all data + ack packets to be returned before sending next packet
+ // Also add the number of expected packets in each of the previous runs (4 per run)
+ StatsChecker.waitForNumPackets(4 * (i + 1));
+ }
+
+ // Transforms should not be removed from the sockets, otherwise FIN packets will be sent
+ // unencrypted.
+ // This test also unfortunately happens to rely on a nuance of the cleanup order. By
+ // keeping the policy on the socket, but removing the SA before lingering FIN packets
+ // are sent (at an undetermined later time), the FIN packets are dropped. Without this,
+ // we run into all kinds of headaches trying to test data accounting (unsolicited
+ // packets mysteriously appearing and messing up our counters)
+ // The right way to close sockets is to set SO_LINGER to ensure synchronous closure,
+ // closing the sockets, and then closing the transforms. See documentation for the
+ // Socket or FileDescriptor flavors of applyTransportModeTransform() in IpSecManager
+ // for more details.
+
+ client.close();
+ accepted.close();
+ }
+
+ /*
+ * Alloc outbound SPI
+ * Alloc inbound SPI
+ * Create transport mode transform
+ * open socket
+ * apply transform to socket
+ * send data on socket
+ * release transform
+ * send data (expect exception)
+ */
+ public void testCreateTransform() throws Exception {
+ InetAddress localAddr = InetAddress.getByName(IPV4_LOOPBACK);
+ IpSecManager.SecurityParameterIndex spi =
+ mISM.allocateSecurityParameterIndex(localAddr);
+
+ IpSecTransform transform =
+ new IpSecTransform.Builder(mContext)
+ .setEncryption(new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY))
+ .setAuthentication(
+ new IpSecAlgorithm(
+ IpSecAlgorithm.AUTH_HMAC_SHA256,
+ AUTH_KEY,
+ AUTH_KEY.length * 8))
+ .buildTransportModeTransform(localAddr, spi);
+
+ final boolean [][] applyInApplyOut = {
+ {false, false}, {false, true}, {true, false}, {true,true}};
+ final byte[] data = new String("Best test data ever!").getBytes("UTF-8");
+ final DatagramPacket outPacket = new DatagramPacket(data, 0, data.length, localAddr, 0);
+
+ byte[] in = new byte[data.length];
+ DatagramPacket inPacket = new DatagramPacket(in, in.length);
+ DatagramSocket localSocket;
+ int localPort;
+
+ for(boolean[] io : applyInApplyOut) {
+ boolean applyIn = io[0];
+ boolean applyOut = io[1];
+ // Bind localSocket to a random available port.
+ localSocket = new DatagramSocket(0);
+ localPort = localSocket.getLocalPort();
+ localSocket.setSoTimeout(200);
+ outPacket.setPort(localPort);
+ if (applyIn) {
+ mISM.applyTransportModeTransform(
+ localSocket, IpSecManager.DIRECTION_IN, transform);
+ }
+ if (applyOut) {
+ mISM.applyTransportModeTransform(
+ localSocket, IpSecManager.DIRECTION_OUT, transform);
+ }
+ if (applyIn == applyOut) {
+ localSocket.send(outPacket);
+ localSocket.receive(inPacket);
+ assertTrue("Encapsulated data did not match.",
+ Arrays.equals(outPacket.getData(), inPacket.getData()));
+ mISM.removeTransportModeTransforms(localSocket);
+ localSocket.close();
+ } else {
+ try {
+ localSocket.send(outPacket);
+ localSocket.receive(inPacket);
+ } catch (IOException e) {
+ continue;
+ } finally {
+ mISM.removeTransportModeTransforms(localSocket);
+ localSocket.close();
+ }
+ // FIXME: This check is disabled because sockets currently receive data
+ // if there is a valid SA for decryption, even when the input policy is
+ // not applied to a socket.
+ // fail("Data IO should fail on asymmetrical transforms! + Input="
+ // + applyIn + " Output=" + applyOut);
+ }
+ }
+ transform.close();
+ }
+
+ /** Snapshot of TrafficStats as of initStatsChecker call for later comparisons */
+ private static class StatsChecker {
+ private static final double ERROR_MARGIN_BYTES = 1.05;
+ private static final double ERROR_MARGIN_PKTS = 1.05;
+ private static final int MAX_WAIT_TIME_MILLIS = 1000;
+
+ private static long uidTxBytes;
+ private static long uidRxBytes;
+ private static long uidTxPackets;
+ private static long uidRxPackets;
+
+ private static long ifaceTxBytes;
+ private static long ifaceRxBytes;
+ private static long ifaceTxPackets;
+ private static long ifaceRxPackets;
+
+ /**
+ * This method counts the number of incoming packets, polling intermittently up to
+ * MAX_WAIT_TIME_MILLIS.
+ */
+ private static void waitForNumPackets(int numPackets) throws Exception {
+ long uidTxDelta = 0;
+ long uidRxDelta = 0;
+ for (int i = 0; i < 100; i++) {
+ uidTxDelta = TrafficStats.getUidTxPackets(Os.getuid()) - uidTxPackets;
+ uidRxDelta = TrafficStats.getUidRxPackets(Os.getuid()) - uidRxPackets;
+
+ // TODO: Check Rx packets as well once kernel security policy bug is fixed.
+ // (b/70635417)
+ if (uidTxDelta >= numPackets) {
+ return;
+ }
+ Thread.sleep(MAX_WAIT_TIME_MILLIS / 100);
+ }
+ fail(
+ "Not enough traffic was recorded to satisfy the provided conditions: wanted "
+ + numPackets
+ + ", got "
+ + uidTxDelta
+ + " tx and "
+ + uidRxDelta
+ + " rx packets");
+ }
+
+ private static void assertUidStatsDelta(
+ int expectedTxByteDelta,
+ int expectedTxPacketDelta,
+ int minRxByteDelta,
+ int maxRxByteDelta,
+ int expectedRxPacketDelta) {
+ long newUidTxBytes = TrafficStats.getUidTxBytes(Os.getuid());
+ long newUidRxBytes = TrafficStats.getUidRxBytes(Os.getuid());
+ long newUidTxPackets = TrafficStats.getUidTxPackets(Os.getuid());
+ long newUidRxPackets = TrafficStats.getUidRxPackets(Os.getuid());
+
+ assertEquals(expectedTxByteDelta, newUidTxBytes - uidTxBytes);
+ assertTrue(
+ newUidRxBytes - uidRxBytes >= minRxByteDelta
+ && newUidRxBytes - uidRxBytes <= maxRxByteDelta);
+ assertEquals(expectedTxPacketDelta, newUidTxPackets - uidTxPackets);
+ assertEquals(expectedRxPacketDelta, newUidRxPackets - uidRxPackets);
+ }
+
+ private static void assertIfaceStatsDelta(
+ int expectedTxByteDelta,
+ int expectedTxPacketDelta,
+ int expectedRxByteDelta,
+ int expectedRxPacketDelta)
+ throws IOException {
+ long newIfaceTxBytes = TrafficStats.getLoopbackTxBytes();
+ long newIfaceRxBytes = TrafficStats.getLoopbackRxBytes();
+ long newIfaceTxPackets = TrafficStats.getLoopbackTxPackets();
+ long newIfaceRxPackets = TrafficStats.getLoopbackRxPackets();
+
+ // Check that iface stats are within an acceptable range; data might be sent
+ // on the local interface by other apps.
+ assertApproxEquals(
+ ifaceTxBytes, newIfaceTxBytes, expectedTxByteDelta, ERROR_MARGIN_BYTES);
+ assertApproxEquals(
+ ifaceRxBytes, newIfaceRxBytes, expectedRxByteDelta, ERROR_MARGIN_BYTES);
+ assertApproxEquals(
+ ifaceTxPackets, newIfaceTxPackets, expectedTxPacketDelta, ERROR_MARGIN_PKTS);
+ assertApproxEquals(
+ ifaceRxPackets, newIfaceRxPackets, expectedRxPacketDelta, ERROR_MARGIN_PKTS);
+ }
+
+ private static void assertApproxEquals(
+ long oldStats, long newStats, int expectedDelta, double errorMargin) {
+ assertTrue(expectedDelta <= newStats - oldStats);
+ assertTrue((expectedDelta * errorMargin) > newStats - oldStats);
+ }
+
+ private static void initStatsChecker() throws Exception {
+ uidTxBytes = TrafficStats.getUidTxBytes(Os.getuid());
+ uidRxBytes = TrafficStats.getUidRxBytes(Os.getuid());
+ uidTxPackets = TrafficStats.getUidTxPackets(Os.getuid());
+ uidRxPackets = TrafficStats.getUidRxPackets(Os.getuid());
+
+ ifaceTxBytes = TrafficStats.getLoopbackTxBytes();
+ ifaceRxBytes = TrafficStats.getLoopbackRxBytes();
+ ifaceTxPackets = TrafficStats.getLoopbackTxPackets();
+ ifaceRxPackets = TrafficStats.getLoopbackRxPackets();
+ }
+ }
+
+ private int getTruncLenBits(IpSecAlgorithm authOrAead) {
+ return authOrAead == null ? 0 : authOrAead.getTruncationLengthBits();
+ }
+
+ private int getIvLen(IpSecAlgorithm cryptOrAead) {
+ if (cryptOrAead == null) { return 0; }
+
+ switch (cryptOrAead.getName()) {
+ case IpSecAlgorithm.CRYPT_AES_CBC:
+ return AES_CBC_IV_LEN;
+ case IpSecAlgorithm.AUTH_CRYPT_AES_GCM:
+ return AES_GCM_IV_LEN;
+ default:
+ throw new IllegalArgumentException(
+ "IV length unknown for algorithm" + cryptOrAead.getName());
+ }
+ }
+
+ private int getBlkSize(IpSecAlgorithm cryptOrAead) {
+ // RFC 4303, section 2.4 states that ciphertext plus pad_len, next_header fields must
+ // terminate on a 4-byte boundary. Thus, the minimum ciphertext block size is 4 bytes.
+ if (cryptOrAead == null) { return 4; }
+
+ switch (cryptOrAead.getName()) {
+ case IpSecAlgorithm.CRYPT_AES_CBC:
+ return AES_CBC_BLK_SIZE;
+ case IpSecAlgorithm.AUTH_CRYPT_AES_GCM:
+ return AES_GCM_BLK_SIZE;
+ default:
+ throw new IllegalArgumentException(
+ "Blk size unknown for algorithm" + cryptOrAead.getName());
+ }
+ }
+
+ /** Helper function to calculate expected ESP packet size. */
+ private 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
+ payloadLen += 2; // ESP trailer
+
+ // Align to block size of encryption algorithm
+ payloadLen += (cryptBlockSize - (payloadLen % cryptBlockSize)) % cryptBlockSize;
+ return payloadLen + ESP_HDRLEN + ICV_LEN;
+ }
+
+ public void checkTransform(
+ int protocol,
+ String localAddress,
+ IpSecAlgorithm crypt,
+ IpSecAlgorithm auth,
+ IpSecAlgorithm aead,
+ boolean doUdpEncap,
+ int sendCount,
+ boolean useJavaSockets)
+ throws Exception {
+ StatsChecker.initStatsChecker();
+ InetAddress local = InetAddress.getByName(localAddress);
+
+ try (IpSecManager.UdpEncapsulationSocket encapSocket = mISM.openUdpEncapsulationSocket();
+ IpSecManager.SecurityParameterIndex spi =
+ mISM.allocateSecurityParameterIndex(local)) {
+
+ IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(mContext);
+ if (crypt != null) {
+ transformBuilder.setEncryption(crypt);
+ }
+ if (auth != null) {
+ transformBuilder.setAuthentication(auth);
+ }
+ if (aead != null) {
+ transformBuilder.setAuthenticatedEncryption(aead);
+ }
+
+ if (doUdpEncap) {
+ transformBuilder =
+ transformBuilder.setIpv4Encapsulation(encapSocket, encapSocket.getPort());
+ }
+
+ int ipHdrLen = local instanceof Inet6Address ? IP6_HDRLEN : IP4_HDRLEN;
+ int transportHdrLen = 0;
+ int udpEncapLen = doUdpEncap ? UDP_HDRLEN : 0;
+
+ try (IpSecTransform transform =
+ transformBuilder.buildTransportModeTransform(local, spi)) {
+ if (protocol == IPPROTO_TCP) {
+ transportHdrLen = TCP_HDRLEN_WITH_OPTIONS;
+ checkTcp(transform, local, sendCount, useJavaSockets);
+ } else if (protocol == IPPROTO_UDP) {
+ transportHdrLen = UDP_HDRLEN;
+
+ // TODO: Also check connected udp.
+ checkUnconnectedUdp(transform, local, sendCount, useJavaSockets);
+ } else {
+ throw new IllegalArgumentException("Invalid protocol");
+ }
+ }
+
+ checkStatsChecker(
+ protocol,
+ ipHdrLen,
+ transportHdrLen,
+ udpEncapLen,
+ sendCount,
+ getIvLen(crypt != null ? crypt : aead),
+ getBlkSize(crypt != null ? crypt : aead),
+ getTruncLenBits(auth != null ? auth : aead));
+ }
+ }
+
+ private void checkStatsChecker(
+ int protocol,
+ int ipHdrLen,
+ int transportHdrLen,
+ int udpEncapLen,
+ int sendCount,
+ int ivLen,
+ int blkSize,
+ int truncLenBits)
+ throws Exception {
+
+ int innerPacketSize = TEST_DATA.length + transportHdrLen + ipHdrLen;
+ int outerPacketSize =
+ calculateEspPacketSize(
+ TEST_DATA.length + transportHdrLen, ivLen, blkSize, truncLenBits)
+ + udpEncapLen
+ + ipHdrLen;
+
+ int expectedOuterBytes = outerPacketSize * sendCount;
+ int expectedInnerBytes = innerPacketSize * sendCount;
+ int expectedPackets = sendCount;
+
+ // Each run sends two packets, one in each direction.
+ sendCount *= 2;
+ expectedOuterBytes *= 2;
+ expectedInnerBytes *= 2;
+ expectedPackets *= 2;
+
+ // Add TCP ACKs for data packets
+ if (protocol == IPPROTO_TCP) {
+ int encryptedTcpPktSize =
+ calculateEspPacketSize(TCP_HDRLEN_WITH_OPTIONS, ivLen, blkSize, truncLenBits);
+
+
+ // Add data packet ACKs
+ expectedOuterBytes += (encryptedTcpPktSize + udpEncapLen + ipHdrLen) * (sendCount);
+ expectedInnerBytes += (TCP_HDRLEN_WITH_OPTIONS + ipHdrLen) * (sendCount);
+ expectedPackets += sendCount;
+ }
+
+ StatsChecker.waitForNumPackets(expectedPackets);
+
+ // eBPF only counts inner packets, whereas xt_qtaguid counts outer packets. Allow both
+ StatsChecker.assertUidStatsDelta(
+ expectedOuterBytes,
+ expectedPackets,
+ expectedInnerBytes,
+ expectedOuterBytes,
+ expectedPackets);
+
+ // Unreliable at low numbers due to potential interference from other processes.
+ if (sendCount >= 1000) {
+ StatsChecker.assertIfaceStatsDelta(
+ expectedOuterBytes, expectedPackets, expectedOuterBytes, expectedPackets);
+ }
+ }
+
+ private void checkIkePacket(
+ NativeUdpSocket wrappedEncapSocket, InetAddress localAddr) throws Exception {
+ StatsChecker.initStatsChecker();
+
+ try (NativeUdpSocket remoteSocket = new NativeUdpSocket(getBoundUdpSocket(localAddr))) {
+
+ // Append IKE/ESP header - 4 bytes of SPI, 4 bytes of seq number, all zeroed out
+ // If the first four bytes are zero, assume non-ESP (IKE traffic)
+ byte[] dataWithEspHeader = new byte[TEST_DATA.length + 8];
+ System.arraycopy(TEST_DATA, 0, dataWithEspHeader, 8, TEST_DATA.length);
+
+ // Send the IKE packet from remoteSocket to wrappedEncapSocket. Since IKE packets
+ // are multiplexed over the socket, we expect them to appear on the encap socket
+ // (as opposed to being decrypted and received on the non-encap socket)
+ remoteSocket.sendTo(dataWithEspHeader, localAddr, wrappedEncapSocket.getPort());
+ byte[] in = wrappedEncapSocket.receive();
+ assertArrayEquals("Encapsulated data did not match.", dataWithEspHeader, in);
+
+ // Also test that the IKE socket can send data out.
+ wrappedEncapSocket.sendTo(dataWithEspHeader, localAddr, remoteSocket.getPort());
+ in = remoteSocket.receive();
+ assertArrayEquals("Encapsulated data did not match.", dataWithEspHeader, in);
+
+ // Calculate expected packet sizes. Always use IPv4 header, since our kernels only
+ // guarantee support of UDP encap on IPv4.
+ int expectedNumPkts = 2;
+ int expectedPacketSize =
+ expectedNumPkts * (dataWithEspHeader.length + UDP_HDRLEN + IP4_HDRLEN);
+
+ StatsChecker.waitForNumPackets(expectedNumPkts);
+ StatsChecker.assertUidStatsDelta(
+ expectedPacketSize,
+ expectedNumPkts,
+ expectedPacketSize,
+ expectedPacketSize,
+ expectedNumPkts);
+ StatsChecker.assertIfaceStatsDelta(
+ expectedPacketSize, expectedNumPkts, expectedPacketSize, expectedNumPkts);
+ }
+ }
+
+ public void testIkeOverUdpEncapSocket() throws Exception {
+ // IPv6 not supported for UDP-encap-ESP
+ InetAddress local = InetAddress.getByName(IPV4_LOOPBACK);
+ try (IpSecManager.UdpEncapsulationSocket encapSocket = mISM.openUdpEncapsulationSocket()) {
+ NativeUdpSocket wrappedEncapSocket =
+ new NativeUdpSocket(encapSocket.getFileDescriptor());
+ checkIkePacket(wrappedEncapSocket, local);
+
+ // Now try with a transform applied to a socket using this Encap socket
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+
+ try (IpSecManager.SecurityParameterIndex spi =
+ mISM.allocateSecurityParameterIndex(local);
+ IpSecTransform transform =
+ new IpSecTransform.Builder(mContext)
+ .setEncryption(crypt)
+ .setAuthentication(auth)
+ .setIpv4Encapsulation(encapSocket, encapSocket.getPort())
+ .buildTransportModeTransform(local, spi);
+ JavaUdpSocket localSocket = new JavaUdpSocket(local)) {
+ applyTransformBidirectionally(mISM, transform, localSocket);
+
+ checkIkePacket(wrappedEncapSocket, local);
+ }
+ }
+ }
+
+ // TODO: Check IKE over ESP sockets (IPv4, IPv6) - does this need SOCK_RAW?
+
+ /* TODO: Re-enable these when policy matcher works for reflected packets
+ *
+ * The issue here is that A sends to B, and everything is new; therefore PREROUTING counts
+ * correctly. But it appears that the security path is not cleared afterwards, thus when A
+ * sends an ACK back to B, the policy matcher flags it as a "IPSec" packet. See b/70635417
+ */
+
+ // public void testInterfaceCountersTcp4() throws Exception {
+ // IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ // IpSecAlgorithm auth = new IpSecAlgorithm(
+ // IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ // checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, false, 1000);
+ // }
+
+ // public void testInterfaceCountersTcp6() throws Exception {
+ // IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ // IpSecAlgorithm auth = new IpSecAlgorithm(
+ // IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ // checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, false, 1000);
+ // }
+
+ // public void testInterfaceCountersTcp4UdpEncap() throws Exception {
+ // IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ // IpSecAlgorithm auth =
+ // new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ // checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, true, 1000);
+ // }
+
+ public void testInterfaceCountersUdp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1000, false);
+ }
+
+ public void testInterfaceCountersUdp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1000, false);
+ }
+
+ public void testInterfaceCountersUdp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1000, false);
+ }
+
+ public void testAesCbcHmacMd5Tcp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacMd5Tcp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacMd5Udp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacMd5Udp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha1Tcp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, getKey(160), 96);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha1Tcp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, getKey(160), 96);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha1Udp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, getKey(160), 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha1Udp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, getKey(160), 96);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha256Tcp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha256Tcp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha256Udp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha256Udp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha384Tcp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA384, getKey(384), 192);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha384Tcp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA384, getKey(384), 192);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha384Udp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA384, getKey(384), 192);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha384Udp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA384, getKey(384), 192);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha512Tcp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA512, getKey(512), 256);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha512Tcp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA512, getKey(512), 256);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha512Udp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA512, getKey(512), 256);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesCbcHmacSha512Udp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA512, getKey(512), 256);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
+ }
+
+ public void testAesGcm64Tcp4() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm64Tcp6() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm64Udp4() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm64Udp6() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm96Tcp4() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm96Tcp6() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm96Udp4() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm96Udp6() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm128Tcp4() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm128Tcp6() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm128Udp4() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesGcm128Udp6() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
+ }
+
+ public void testAesCbcHmacMd5Tcp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesCbcHmacMd5Udp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesCbcHmacSha1Tcp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, getKey(160), 96);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesCbcHmacSha1Udp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, getKey(160), 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesCbcHmacSha256Tcp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesCbcHmacSha256Udp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesCbcHmacSha384Tcp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA384, getKey(384), 192);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesCbcHmacSha384Udp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA384, getKey(384), 192);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesCbcHmacSha512Tcp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA512, getKey(512), 256);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesCbcHmacSha512Udp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA512, getKey(512), 256);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
+ }
+
+ public void testAesGcm64Tcp4UdpEncap() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
+ }
+
+ public void testAesGcm64Udp4UdpEncap() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
+ }
+
+ public void testAesGcm96Tcp4UdpEncap() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
+ }
+
+ public void testAesGcm96Udp4UdpEncap() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
+ }
+
+ public void testAesGcm128Tcp4UdpEncap() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
+ }
+
+ public void testAesGcm128Udp4UdpEncap() throws Exception {
+ IpSecAlgorithm authCrypt =
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
+ }
+
+ public void testCryptUdp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, null, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, null, null, false, 1, true);
+ }
+
+ public void testAuthUdp4() throws Exception {
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, auth, null, false, 1, true);
+ }
+
+ public void testCryptUdp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, null, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, null, null, false, 1, true);
+ }
+
+ public void testAuthUdp6() throws Exception {
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, auth, null, false, 1, false);
+ checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, auth, null, false, 1, true);
+ }
+
+ public void testCryptTcp4() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, null, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, null, null, false, 1, true);
+ }
+
+ public void testAuthTcp4() throws Exception {
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, auth, null, false, 1, true);
+ }
+
+ public void testCryptTcp6() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, null, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, null, null, false, 1, true);
+ }
+
+ public void testAuthTcp6() throws Exception {
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, auth, null, false, 1, false);
+ checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, auth, null, false, 1, true);
+ }
+
+ public void testCryptUdp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, null, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, null, null, true, 1, true);
+ }
+
+ public void testAuthUdp4UdpEncap() throws Exception {
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, auth, null, true, 1, false);
+ checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, auth, null, true, 1, true);
+ }
+
+ public void testCryptTcp4UdpEncap() throws Exception {
+ IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, null, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, null, null, true, 1, true);
+ }
+
+ public void testAuthTcp4UdpEncap() throws Exception {
+ IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, auth, null, true, 1, false);
+ checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, auth, null, true, 1, true);
+ }
+
+ public void testOpenUdpEncapSocketSpecificPort() throws Exception {
+ IpSecManager.UdpEncapsulationSocket encapSocket = null;
+ int port = -1;
+ for (int i = 0; i < MAX_PORT_BIND_ATTEMPTS; i++) {
+ try {
+ port = findUnusedPort();
+ encapSocket = mISM.openUdpEncapsulationSocket(port);
+ break;
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.EADDRINUSE) {
+ // Someone claimed the port since we called findUnusedPort.
+ continue;
+ }
+ throw e;
+ } finally {
+ if (encapSocket != null) {
+ encapSocket.close();
+ }
+ }
+ }
+
+ if (encapSocket == null) {
+ fail("Failed " + MAX_PORT_BIND_ATTEMPTS + " attempts to bind to a port");
+ }
+
+ assertTrue("Returned invalid port", encapSocket.getPort() == port);
+ }
+
+ public void testOpenUdpEncapSocketRandomPort() throws Exception {
+ try (IpSecManager.UdpEncapsulationSocket encapSocket = mISM.openUdpEncapsulationSocket()) {
+ assertTrue("Returned invalid port", encapSocket.getPort() != 0);
+ }
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/IpSecSysctlTest.java b/tests/cts/net/src/android/net/cts/IpSecSysctlTest.java
new file mode 100644
index 0000000..b362282
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/IpSecSysctlTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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.cts;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * Tests for multinetwork sysctl functionality.
+ */
+public class IpSecSysctlTest extends SysctlBaseTest {
+
+ // SPI expiration sysctls. Must be present and set greater than 1h.
+ private static final String SPI_TIMEOUT_SYSCTL = "/proc/sys/net/core/xfrm_acq_expires";
+ private static final int MIN_ACQ_EXPIRES = 3600;
+
+ /**
+ * Checks that SPI default timeouts are overridden, and set to a reasonable length of time
+ */
+ public void testProcFiles() throws ErrnoException, IOException, NumberFormatException {
+ int value = getIntValue(SPI_TIMEOUT_SYSCTL);
+ assertAtLeast(SPI_TIMEOUT_SYSCTL, value, MIN_ACQ_EXPIRES);
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/MacAddressTest.java b/tests/cts/net/src/android/net/cts/MacAddressTest.java
new file mode 100644
index 0000000..af1e760
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/MacAddressTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 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.cts;
+
+import static android.net.MacAddress.TYPE_BROADCAST;
+import static android.net.MacAddress.TYPE_MULTICAST;
+import static android.net.MacAddress.TYPE_UNICAST;
+
+import static org.junit.Assert.fail;
+
+import android.net.MacAddress;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MacAddressTest {
+
+ static class TestCase {
+ final String macAddress;
+ final String ouiString;
+ final int addressType;
+ final boolean isLocallyAssigned;
+
+ TestCase(String macAddress, String ouiString, int addressType, boolean isLocallyAssigned) {
+ this.macAddress = macAddress;
+ this.ouiString = ouiString;
+ this.addressType = addressType;
+ this.isLocallyAssigned = isLocallyAssigned;
+ }
+ }
+
+ static final boolean LOCALLY_ASSIGNED = true;
+ static final boolean GLOBALLY_UNIQUE = false;
+
+ static String typeToString(int addressType) {
+ switch (addressType) {
+ case TYPE_UNICAST:
+ return "TYPE_UNICAST";
+ case TYPE_BROADCAST:
+ return "TYPE_BROADCAST";
+ case TYPE_MULTICAST:
+ return "TYPE_MULTICAST";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ static String localAssignedToString(boolean isLocallyAssigned) {
+ return isLocallyAssigned ? "LOCALLY_ASSIGNED" : "GLOBALLY_UNIQUE";
+ }
+
+ @Test
+ public void testMacAddress() {
+ TestCase[] tests = {
+ new TestCase("ff:ff:ff:ff:ff:ff", "ff:ff:ff", TYPE_BROADCAST, LOCALLY_ASSIGNED),
+ new TestCase("d2:c4:22:4d:32:a8", "d2:c4:22", TYPE_UNICAST, LOCALLY_ASSIGNED),
+ new TestCase("33:33:aa:bb:cc:dd", "33:33:aa", TYPE_MULTICAST, LOCALLY_ASSIGNED),
+ new TestCase("06:00:00:00:00:00", "06:00:00", TYPE_UNICAST, LOCALLY_ASSIGNED),
+ new TestCase("07:00:d3:56:8a:c4", "07:00:d3", TYPE_MULTICAST, LOCALLY_ASSIGNED),
+ new TestCase("00:01:44:55:66:77", "00:01:44", TYPE_UNICAST, GLOBALLY_UNIQUE),
+ new TestCase("08:00:22:33:44:55", "08:00:22", TYPE_UNICAST, GLOBALLY_UNIQUE),
+ };
+
+ for (TestCase tc : tests) {
+ MacAddress mac = MacAddress.fromString(tc.macAddress);
+
+ if (!tc.ouiString.equals(mac.toOuiString())) {
+ fail(String.format("expected OUI string %s, got %s",
+ tc.ouiString, mac.toOuiString()));
+ }
+
+ if (tc.isLocallyAssigned != mac.isLocallyAssigned()) {
+ fail(String.format("expected %s to be %s, got %s", mac,
+ localAssignedToString(tc.isLocallyAssigned),
+ localAssignedToString(mac.isLocallyAssigned())));
+ }
+
+ if (tc.addressType != mac.getAddressType()) {
+ fail(String.format("expected %s address type to be %s, got %s", mac,
+ typeToString(tc.addressType), typeToString(mac.getAddressType())));
+ }
+
+ if (!tc.macAddress.equals(mac.toString())) {
+ fail(String.format("expected toString() to return %s, got %s",
+ tc.macAddress, mac.toString()));
+ }
+
+ if (!mac.equals(MacAddress.fromBytes(mac.toByteArray()))) {
+ byte[] bytes = mac.toByteArray();
+ fail(String.format("expected mac address from bytes %s to be %s, got %s",
+ Arrays.toString(bytes),
+ MacAddress.fromBytes(bytes),
+ mac));
+ }
+ }
+ }
+
+ @Test
+ public void testConstructorInputValidation() {
+ String[] invalidStringAddresses = {
+ "",
+ "abcd",
+ "1:2:3:4:5",
+ "1:2:3:4:5:6:7",
+ "10000:2:3:4:5:6",
+ };
+
+ for (String s : invalidStringAddresses) {
+ try {
+ MacAddress mac = MacAddress.fromString(s);
+ fail("MacAddress.fromString(" + s + ") should have failed, but returned " + mac);
+ } catch (IllegalArgumentException excepted) {
+ }
+ }
+
+ try {
+ MacAddress mac = MacAddress.fromString(null);
+ fail("MacAddress.fromString(null) should have failed, but returned " + mac);
+ } catch (NullPointerException excepted) {
+ }
+
+ byte[][] invalidBytesAddresses = {
+ {},
+ {1,2,3,4,5},
+ {1,2,3,4,5,6,7},
+ };
+
+ for (byte[] b : invalidBytesAddresses) {
+ try {
+ MacAddress mac = MacAddress.fromBytes(b);
+ fail("MacAddress.fromBytes(" + Arrays.toString(b)
+ + ") should have failed, but returned " + mac);
+ } catch (IllegalArgumentException excepted) {
+ }
+ }
+
+ try {
+ MacAddress mac = MacAddress.fromBytes(null);
+ fail("MacAddress.fromBytes(null) should have failed, but returned " + mac);
+ } catch (NullPointerException excepted) {
+ }
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
index 51ee50e..b2c9d9b 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
@@ -152,4 +152,27 @@
// to query on the default network.
// assertEquals(-OsConstants.ENONET, runGetaddrinfoCheck(eNoNetHandle));
}
+
+ public void testNetworkHandle() {
+ // Test Network -> NetworkHandle -> Network results in the same Network.
+ for (Network network : getTestableNetworks()) {
+ long networkHandle = network.getNetworkHandle();
+ Network newNetwork = Network.fromNetworkHandle(networkHandle);
+ assertEquals(newNetwork, network);
+ }
+
+ // Test that only obfuscated handles are allowed.
+ try {
+ Network.fromNetworkHandle(100);
+ fail();
+ } catch (IllegalArgumentException e) {}
+ try {
+ Network.fromNetworkHandle(-1);
+ fail();
+ } catch (IllegalArgumentException e) {}
+ try {
+ Network.fromNetworkHandle(0);
+ fail();
+ } catch (IllegalArgumentException e) {}
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkSysctlTest.java b/tests/cts/net/src/android/net/cts/MultinetworkSysctlTest.java
index c091a13..1d0c111 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkSysctlTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkSysctlTest.java
@@ -29,7 +29,7 @@
/**
* Tests for multinetwork sysctl functionality.
*/
-public class MultinetworkSysctlTest extends AndroidTestCase {
+public class MultinetworkSysctlTest extends SysctlBaseTest {
// Global sysctls. Must be present and set to 1.
private static final String[] GLOBAL_SYSCTLS = {
@@ -42,30 +42,6 @@
private static final String IPV6_SYSCTL_DIR = "/proc/sys/net/ipv6/conf";
private static final String AUTOCONF_SYSCTL = "accept_ra_rt_table";
- // Expected mode, UID, and GID of sysctl files.
- private static final int SYSCTL_MODE = 0100644;
- private static final int SYSCTL_UID = 0;
- private static final int SYSCTL_GID = 0;
-
- private void checkSysctlPermissions(String fileName) throws ErrnoException {
- StructStat stat = Os.stat(fileName);
- assertEquals("mode of " + fileName + ":", SYSCTL_MODE, stat.st_mode);
- assertEquals("UID of " + fileName + ":", SYSCTL_UID, stat.st_uid);
- assertEquals("GID of " + fileName + ":", SYSCTL_GID, stat.st_gid);
- }
-
- private void assertLess(String what, int a, int b) {
- assertTrue(what + " expected < " + b + " but was: " + a, a < b);
- }
-
- private String readFile(String fileName) throws ErrnoException, IOException {
- byte[] buf = new byte[1024];
- FileDescriptor fd = Os.open(fileName, 0, OsConstants.O_RDONLY);
- int bytesRead = Os.read(fd, buf, 0, buf.length);
- assertLess("length of " + fileName + ":", bytesRead, buf.length);
- return new String(buf);
- }
-
/**
* Checks that the sysctls for multinetwork kernel features are present and
* enabled. The necessary kernel commits are:
@@ -80,9 +56,8 @@
*/
public void testProcFiles() throws ErrnoException, IOException, NumberFormatException {
for (String sysctl : GLOBAL_SYSCTLS) {
- checkSysctlPermissions(sysctl);
- int value = Integer.parseInt(readFile(sysctl).trim());
- assertEquals("value of " + sysctl + ":", 1, value);
+ int value = getIntValue(sysctl);
+ assertEquals(sysctl, 1, value);
}
File[] interfaceDirs = new File(IPV6_SYSCTL_DIR).listFiles();
@@ -91,9 +66,8 @@
continue;
}
String sysctl = new File(interfaceDir, AUTOCONF_SYSCTL).getAbsolutePath();
- checkSysctlPermissions(sysctl);
- int value = Integer.parseInt(readFile(sysctl).trim());
- assertLess("value of " + sysctl + ":", value, 0);
+ int value = getIntValue(sysctl);
+ assertLess(sysctl, value, 0);
}
}
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
new file mode 100644
index 0000000..c862c77
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2018 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.cts;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
+
+import android.net.NetworkRequest;
+import android.test.AndroidTestCase;
+
+public class NetworkRequestTest extends AndroidTestCase {
+ public void testCapabilities() {
+ assertTrue(new NetworkRequest.Builder().addCapability(NET_CAPABILITY_MMS).build()
+ .hasCapability(NET_CAPABILITY_MMS));
+ assertFalse(new NetworkRequest.Builder().removeCapability(NET_CAPABILITY_MMS).build()
+ .hasCapability(NET_CAPABILITY_MMS));
+ }
+
+ public void testTransports() {
+ assertTrue(new NetworkRequest.Builder().addTransportType(TRANSPORT_BLUETOOTH).build()
+ .hasTransport(TRANSPORT_BLUETOOTH));
+ assertFalse(new NetworkRequest.Builder().removeTransportType(TRANSPORT_BLUETOOTH).build()
+ .hasTransport(TRANSPORT_BLUETOOTH));
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java b/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java
new file mode 100644
index 0000000..8ad7820
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2018 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.cts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.os.FileUtils;
+import android.os.ParcelFileDescriptor;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.compatibility.common.util.SystemUtil;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Formatter;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class NetworkWatchlistTest {
+
+ private static final String TEST_WATCHLIST_XML = "assets/network_watchlist_config_for_test.xml";
+ private static final String TEST_EMPTY_WATCHLIST_XML =
+ "assets/network_watchlist_config_empty_for_test.xml";
+ private static final String TMP_CONFIG_PATH =
+ "/data/local/tmp/network_watchlist_config_for_test.xml";
+ // Generated from sha256sum network_watchlist_config_for_test.xml
+ private static final String TEST_WATCHLIST_CONFIG_HASH =
+ "B5FC4636994180D54E1E912F78178AB1D8BD2BE71D90CA9F5BBC3284E4D04ED4";
+
+ private ConnectivityManager mConnectivityManager;
+ private boolean mHasFeature;
+
+ @Before
+ public void setUp() throws Exception {
+ mHasFeature = isAtLeastP();
+ mConnectivityManager =
+ (ConnectivityManager) InstrumentationRegistry.getContext().getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ assumeTrue(mHasFeature);
+ // Set empty watchlist test config before testing
+ setWatchlistConfig(TEST_EMPTY_WATCHLIST_XML);
+ // Verify test watchlist config is not set before testing
+ byte[] result = mConnectivityManager.getNetworkWatchlistConfigHash();
+ assertNotEquals(TEST_WATCHLIST_CONFIG_HASH, byteArrayToHexString(result));
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (mHasFeature) {
+ // Set empty watchlist test config after testing
+ setWatchlistConfig(TEST_EMPTY_WATCHLIST_XML);
+ }
+ }
+
+ private void cleanup() throws IOException {
+ runCommand("rm " + TMP_CONFIG_PATH);
+ }
+
+ private boolean isAtLeastP() throws Exception {
+ // TODO: replace with ApiLevelUtil.isAtLeast(Build.VERSION_CODES.P) when the P API level
+ // constant is defined.
+ return ApiLevelUtil.getCodename().compareToIgnoreCase("P") >= 0;
+ }
+
+ /**
+ * Test if ConnectivityManager.getNetworkWatchlistConfigHash() correctly
+ * returns the hash of config we set.
+ */
+ @Test
+ public void testGetWatchlistConfigHash() throws Exception {
+ // Set watchlist config file for test
+ setWatchlistConfig(TEST_WATCHLIST_XML);
+ // Test if watchlist config hash value is correct
+ byte[] result = mConnectivityManager.getNetworkWatchlistConfigHash();
+ Assert.assertEquals(TEST_WATCHLIST_CONFIG_HASH, byteArrayToHexString(result));
+ }
+
+ private static String byteArrayToHexString(byte[] bytes) {
+ Formatter formatter = new Formatter();
+ for (byte b : bytes) {
+ formatter.format("%02X", b);
+ }
+ return formatter.toString();
+ }
+
+ private void saveResourceToFile(String res, String filePath) throws IOException {
+ // App can't access /data/local/tmp directly, so we pipe resource to file through stdin.
+ ParcelFileDescriptor stdin = pipeFromStdin(filePath);
+ pipeResourceToFileDescriptor(res, stdin);
+ }
+
+ /* Pipe stdin to a file in filePath. Returns PFD for stdin. */
+ private ParcelFileDescriptor pipeFromStdin(String filePath) {
+ // Not all devices have symlink for /dev/stdin, so use /proc/self/fd/0 directly.
+ // /dev/stdin maps to /proc/self/fd/0.
+ return runRwCommand("cp /proc/self/fd/0 " + filePath)[1];
+ }
+
+ private void pipeResourceToFileDescriptor(String res, ParcelFileDescriptor pfd)
+ throws IOException {
+ InputStream resStream = getClass().getClassLoader().getResourceAsStream(res);
+ FileOutputStream fdStream = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);
+
+ FileUtils.copy(resStream, fdStream);
+
+ try {
+ fdStream.close();
+ } catch (IOException e) {
+ }
+ }
+
+ private static String runCommand(String command) throws IOException {
+ return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
+ }
+
+ private static ParcelFileDescriptor[] runRwCommand(String command) {
+ return InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation().executeShellCommandRw(command);
+ }
+
+ private void setWatchlistConfig(String watchlistConfigFile) throws Exception {
+ cleanup();
+ saveResourceToFile(watchlistConfigFile, TMP_CONFIG_PATH);
+ final String cmdResult = runCommand(
+ "cmd network_watchlist set-test-config " + TMP_CONFIG_PATH).trim();
+ assertThat(cmdResult).contains("Success");
+ cleanup();
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/SysctlBaseTest.java b/tests/cts/net/src/android/net/cts/SysctlBaseTest.java
new file mode 100644
index 0000000..a5966d4
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/SysctlBaseTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2018 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.cts;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * Tests for multinetwork sysctl functionality.
+ */
+public class SysctlBaseTest extends AndroidTestCase {
+
+ // Expected mode, UID, and GID of sysctl files.
+ private static final int SYSCTL_MODE = 0100644;
+ private static final int SYSCTL_UID = 0;
+ private static final int SYSCTL_GID = 0;
+
+ private void checkSysctlPermissions(String fileName) throws ErrnoException {
+ StructStat stat = Os.stat(fileName);
+ assertEquals("mode of " + fileName + ":", SYSCTL_MODE, stat.st_mode);
+ assertEquals("UID of " + fileName + ":", SYSCTL_UID, stat.st_uid);
+ assertEquals("GID of " + fileName + ":", SYSCTL_GID, stat.st_gid);
+ }
+
+ protected void assertLess(String sysctl, int a, int b) {
+ assertTrue("value of " + sysctl + ": expected < " + b + " but was: " + a, a < b);
+ }
+
+ protected void assertAtLeast(String sysctl, int a, int b) {
+ assertTrue("value of " + sysctl + ": expected >= " + b + " but was: " + a, a >= b);
+ }
+
+ private String readFile(String fileName) throws ErrnoException, IOException {
+ byte[] buf = new byte[1024];
+ FileDescriptor fd = Os.open(fileName, 0, OsConstants.O_RDONLY);
+ int bytesRead = Os.read(fd, buf, 0, buf.length);
+ assertLess("length of " + fileName + ":", bytesRead, buf.length);
+ return new String(buf);
+ }
+
+ /*
+ * Checks permissions and retrieves the sysctl's value. Retrieval of value should always use
+ * this method
+ */
+ protected int getIntValue(String filename) throws ErrnoException, IOException {
+ checkSysctlPermissions(filename);
+ return Integer.parseInt(readFile(filename).trim());
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
index 930c742..a8743fa 100755
--- a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
+++ b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
@@ -81,26 +81,6 @@
return packetCount * (20 + 32 + bytes);
}
- private void accessOwnTrafficStats() throws IOException {
- final int ownAppUid = getContext().getApplicationInfo().uid;
- Log.d(LOG_TAG, "accesOwnTrafficStatsWithTags(): about to read qtaguid stats for own uid " + ownAppUid);
-
- boolean foundOwnDetailedStats = false;
- try {
- BufferedReader qtaguidReader = new BufferedReader(new FileReader("/proc/net/xt_qtaguid/stats"));
- String line;
- while ((line = qtaguidReader.readLine()) != null) {
- String tokens[] = line.split(" ");
- if (tokens.length > 3 && tokens[3].equals(String.valueOf(ownAppUid))) {
- Log.d(LOG_TAG, "accessOwnTrafficStatsWithTags(): got own stats: " + line);
- }
- }
- qtaguidReader.close();
- } catch (FileNotFoundException e) {
- fail("Was not able to access qtaguid/stats: " + e);
- }
- }
-
public void testTrafficStatsForLocalhost() throws IOException {
final long mobileTxPacketsBefore = TrafficStats.getMobileTxPackets();
final long mobileRxPacketsBefore = TrafficStats.getMobileRxPackets();
@@ -132,7 +112,6 @@
byte[] buf = new byte[byteCount];
TrafficStats.setThreadStatsTag(0x42);
TrafficStats.tagSocket(socket);
- accessOwnTrafficStats();
for (int i = 0; i < packetCount; i++) {
out.write(buf);
out.flush();
@@ -145,7 +124,6 @@
}
out.close();
socket.close();
- accessOwnTrafficStats();
} catch (IOException e) {
Log.i(LOG_TAG, "Badness during writes to socket: " + e);
}
diff --git a/tests/cts/net/src/android/net/wifi/aware/OWNERS b/tests/cts/net/src/android/net/wifi/aware/OWNERS
new file mode 100644
index 0000000..4afc47f
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/aware/OWNERS
@@ -0,0 +1,2 @@
+etancohen@google.com
+satk@google.com
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
index 87e22d8..7277553 100644
--- a/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
+++ b/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
@@ -20,7 +20,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.location.LocationManager;
import android.net.ConnectivityManager;
+import android.net.MacAddress;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.wifi.WifiManager;
@@ -37,14 +39,11 @@
import android.net.wifi.aware.WifiAwareSession;
import android.os.Handler;
import android.os.HandlerThread;
-import android.provider.Settings;
import android.test.AndroidTestCase;
-import android.util.Log;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
@@ -76,13 +75,6 @@
// used to store any WifiAwareSession allocated during tests - will clean-up after tests
private List<WifiAwareSession> mSessions = new ArrayList<>();
- // Return true if location is enabled.
- private boolean isLocationEnabled() {
- return Settings.Secure.getInt(getContext().getContentResolver(),
- Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) !=
- Settings.Secure.LOCATION_MODE_OFF;
- }
-
private class WifiAwareBroadcastReceiver extends BroadcastReceiver {
private CountDownLatch mBlocker = new CountDownLatch(1);
@@ -358,6 +350,10 @@
return;
}
+ assertTrue("Wi-Fi Aware requires Location to be Enabled",
+ ((LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE)).isLocationEnabled());
+
mWifiAwareManager = (WifiAwareManager) getContext().getSystemService(
Context.WIFI_AWARE_SERVICE);
assertNotNull("Wi-Fi Aware Manager", mWifiAwareManager);
@@ -431,18 +427,6 @@
return;
}
- if (isLocationEnabled()) {
- /* Can't execute this test with location on since it means that Aware will not get
- * disabled even if we disable Wi-Fi (which when location is enabled does not correspond
- * to disabling the Wi-Fi chip).
- *
- * Considering other tests may require locationing to be enable we can't also fail the
- * test in such a case. Hence it is skipped.
- */
- Log.d(TAG, "Skipping test since location scans are enabled");
- return;
- }
-
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
@@ -717,82 +701,14 @@
}
/**
- * Request an Aware data-path (open) on a Publish discovery session (which can be done with a
- * null peer - to accept all requests). Validate that times-out.
- */
- public void testDataPathOpenInContextOfDiscoveryFail() {
- if (!TestUtils.shouldTestWifiAware(getContext())) {
- return;
- }
-
- WifiAwareSession session = attachAndGetSession();
-
- PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
- "ValidName").build();
- DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
- NetworkCallbackTest networkCb = new NetworkCallbackTest();
-
- // 1. publish
- session.publish(publishConfig, discoveryCb, mHandler);
- assertTrue("Publish started",
- discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
- PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
- assertNotNull("Publish session", discoverySession);
-
- // 2. request an AWARE network
- NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
- NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
- discoverySession.createNetworkSpecifierOpen(null)).build();
- mConnectivityManager.requestNetwork(nr, networkCb, 2000);
- assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
-
- discoverySession.close();
- session.close();
- }
-
- /**
- * Request an Aware data-path (encrypted) on a Publish discovery session (which can be done
- * with a null peer - to accept all requests). Validate that times-out.
- */
- public void testDataPathPassphraseInContextOfDiscoveryFail() {
- if (!TestUtils.shouldTestWifiAware(getContext())) {
- return;
- }
-
- WifiAwareSession session = attachAndGetSession();
-
- PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
- "ValidName").build();
- DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
- NetworkCallbackTest networkCb = new NetworkCallbackTest();
-
- // 1. publish
- session.publish(publishConfig, discoveryCb, mHandler);
- assertTrue("Publish started",
- discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
- PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
- assertNotNull("Publish session", discoverySession);
-
- // 2. request an AWARE network
- NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
- NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
- discoverySession.createNetworkSpecifierPassphrase(null,
- "Some very long but not very good passphrase")).build();
- mConnectivityManager.requestNetwork(nr, networkCb, 2000);
- assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
-
- discoverySession.close();
- session.close();
- }
-
- /**
- * Request an Aware data-path (open) as a Responder with no peer MAC address (i.e. accept any
- * peer request). Validate that times-out.
+ * Request an Aware data-path (open) as a Responder with an arbitrary peer MAC address. Validate
+ * that times-out.
*/
public void testDataPathOpenOutOfBandFail() {
if (!TestUtils.shouldTestWifiAware(getContext())) {
return;
}
+ MacAddress mac = MacAddress.fromString("00:01:02:03:04:05");
WifiAwareSession session = attachAndGetSession();
@@ -805,7 +721,8 @@
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
session.createNetworkSpecifierOpen(
- WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, null)).build();
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+ mac.toByteArray())).build();
mConnectivityManager.requestNetwork(nr, networkCb, 2000);
assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
@@ -813,13 +730,14 @@
}
/**
- * Request an Aware data-path (encrypted) as a Responder with no peer MAC address (i.e.
- * accept any peer request). Validate that times-out.
+ * Request an Aware data-path (encrypted) as a Responder with an arbitrary peer MAC address.
+ * Validate that times-out.
*/
public void testDataPathPassphraseOutOfBandFail() {
if (!TestUtils.shouldTestWifiAware(getContext())) {
return;
}
+ MacAddress mac = MacAddress.fromString("00:01:02:03:04:05");
WifiAwareSession session = attachAndGetSession();
@@ -832,7 +750,7 @@
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
session.createNetworkSpecifierPassphrase(
- WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, null,
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, mac.toByteArray(),
"abcdefghihk")).build();
mConnectivityManager.requestNetwork(nr, networkCb, 2000);
assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
diff --git a/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java b/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
index c9b82ee..8a22bef 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
@@ -42,12 +42,14 @@
private static final int STATE_WIFI_CHANGED = 2;
private static final int STATE_START_SCAN = 3;
private static final int STATE_SCAN_RESULTS_AVAILABLE = 4;
+ private static final int STATE_SCAN_FAILURE = 5;
private static final String TAG = "WifiInfoTest";
private static final int TIMEOUT_MSEC = 6000;
private static final int WAIT_MSEC = 60;
private static final int ENABLE_WAIT_MSEC = 10000;
private static final int SCAN_WAIT_MSEC = 10000;
+ private static final int SCAN_MAX_RETRY_COUNT = 6;
private IntentFilter mIntentFilter;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -60,7 +62,11 @@
}
} else if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
synchronized (mMySync) {
- mMySync.expectedState = STATE_SCAN_RESULTS_AVAILABLE;
+ if (intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)) {
+ mMySync.expectedState = STATE_SCAN_RESULTS_AVAILABLE;
+ } else {
+ mMySync.expectedState = STATE_SCAN_FAILURE;
+ }
mMySync.notify();
}
}
@@ -120,11 +126,12 @@
}
}
- private void waitForBroadcast(long timeout, int expectedState) throws Exception {
+ private boolean waitForBroadcast(long timeout, int expectedState) throws Exception {
long waitTime = System.currentTimeMillis() + timeout;
while (System.currentTimeMillis() < waitTime
&& mMySync.expectedState != expectedState)
mMySync.wait(WAIT_MSEC);
+ return mMySync.expectedState == expectedState;
}
public void testScanResultProperties() {
@@ -140,11 +147,16 @@
}
}
+ /* Multiple scans to ensure bssid is updated */
private void scanAndWait() throws Exception {
synchronized (mMySync) {
- mMySync.expectedState = STATE_START_SCAN;
- mWifiManager.startScan();
- waitForBroadcast(SCAN_WAIT_MSEC, STATE_SCAN_RESULTS_AVAILABLE);
+ for (int retry = 0; retry < SCAN_MAX_RETRY_COUNT; retry++) {
+ mMySync.expectedState = STATE_START_SCAN;
+ mWifiManager.startScan();
+ if (waitForBroadcast(SCAN_WAIT_MSEC, STATE_SCAN_RESULTS_AVAILABLE)) {
+ break;
+ }
+ }
}
}
@@ -157,9 +169,6 @@
long timestamp = 0;
String BSSID = null;
- /* Multiple scans to ensure bssid is updated */
- scanAndWait();
- scanAndWait();
scanAndWait();
List<ScanResult> scanResults = mWifiManager.getScanResults();
@@ -171,8 +180,6 @@
}
scanAndWait();
- scanAndWait();
- scanAndWait();
scanResults = mWifiManager.getScanResults();
for (ScanResult result : scanResults) {
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
index 3965d0e..2ed0124 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -70,7 +70,7 @@
private static final int STATE_WIFI_ENABLED = 2;
private static final int STATE_WIFI_DISABLED = 3;
private static final int STATE_SCANNING = 4;
- private static final int STATE_SCAN_RESULTS_AVAILABLE = 5;
+ private static final int STATE_SCAN_DONE = 5;
private static final String TAG = "WifiManagerTest";
private static final String SSID1 = "\"WifiManagerTest\"";
@@ -96,13 +96,15 @@
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+
synchronized (mMySync) {
- if (mWifiManager.getScanResults() != null) {
+ if (intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false)) {
mScanResults = mWifiManager.getScanResults();
- mMySync.expectedState = STATE_SCAN_RESULTS_AVAILABLE;
- mScanResults = mWifiManager.getScanResults();
- mMySync.notifyAll();
+ } else {
+ mScanResults = null;
}
+ mMySync.expectedState = STATE_SCAN_DONE;
+ mMySync.notifyAll();
}
} else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
int newState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
@@ -201,6 +203,25 @@
}
}
+ // Get the current scan status from sticky broadcast.
+ private boolean isScanCurrentlyAvailable() {
+ boolean isAvailable = false;
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(WifiManager.WIFI_SCAN_AVAILABLE);
+ Intent intent = mContext.registerReceiver(null, intentFilter);
+ assertNotNull(intent);
+ if (intent.getAction().equals(WifiManager.WIFI_SCAN_AVAILABLE)) {
+ int state = intent.getIntExtra(
+ WifiManager.EXTRA_SCAN_AVAILABLE, WifiManager.WIFI_STATE_UNKNOWN);
+ if (state == WifiManager.WIFI_STATE_ENABLED) {
+ isAvailable = true;
+ } else if (state == WifiManager.WIFI_STATE_DISABLED) {
+ isAvailable = false;
+ }
+ }
+ return isAvailable;
+ }
+
private void startScan() throws Exception {
synchronized (mMySync) {
mMySync.expectedState = STATE_SCANNING;
@@ -245,8 +266,7 @@
* 1.reconnect
* 2.reassociate
* 3.disconnect
- * 4.pingSupplicant
- * 5.satrtScan
+ * 4.createWifiLock
*/
public void testWifiManagerActions() throws Exception {
if (!WifiFeature.isWifiSupported(getContext())) {
@@ -256,10 +276,31 @@
assertTrue(mWifiManager.reconnect());
assertTrue(mWifiManager.reassociate());
assertTrue(mWifiManager.disconnect());
+ final String TAG = "Test";
+ assertNotNull(mWifiManager.createWifiLock(TAG));
+ assertNotNull(mWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG));
+ }
+
+ /**
+ * Test wifi scanning when location scan is turned off.
+ */
+ public void testWifiManagerScanWhenWifiOffLocationTurnedOn() throws Exception {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ if (!hasLocationFeature()) {
+ Log.d(TAG, "Skipping test as location is not supported");
+ return;
+ }
+ if (!isLocationEnabled()) {
+ fail("Please enable location for this test - since Marshmallow WiFi scan results are"
+ + " empty when location is disabled!");
+ }
setWifiEnabled(false);
- startScan();
Thread.sleep(DURATION);
- if (mWifiManager.isScanAlwaysAvailable()) {
+ startScan();
+ if (mWifiManager.isScanAlwaysAvailable() && isScanCurrentlyAvailable()) {
// Make sure at least one AP is found.
assertNotNull("mScanResult should not be null!", mScanResults);
assertFalse("empty scan results!", mScanResults.isEmpty());
@@ -833,6 +874,15 @@
TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
+ // add sleep to avoid calling stopLocalOnlyHotspot before TetherController initialization.
+ // TODO: remove this sleep as soon as b/124330089 is fixed.
+ try {
+ Log.d(TAG, "Sleep for 2 seconds");
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Thread InterruptedException!");
+ }
+
stopLocalOnlyHotspot(callback, wifiEnabled);
// wifi should either stay on, or come back on
@@ -908,6 +958,15 @@
}
assertTrue(caughtException);
+ // add sleep to avoid calling stopLocalOnlyHotspot before TetherController initialization.
+ // TODO: remove this sleep as soon as b/124330089 is fixed.
+ try {
+ Log.d(TAG, "Sleep for 2 seconds");
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ Log.d(TAG, "Thread InterruptedException!");
+ }
+
stopLocalOnlyHotspot(callback, wifiEnabled);
}
}
diff --git a/tests/cts/net/src/android/net/wifi/rtt/OWNERS b/tests/cts/net/src/android/net/wifi/rtt/OWNERS
new file mode 100644
index 0000000..4afc47f
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/rtt/OWNERS
@@ -0,0 +1,2 @@
+etancohen@google.com
+satk@google.com
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/wifi/rtt/cts/TestBase.java b/tests/cts/net/src/android/net/wifi/rtt/cts/TestBase.java
new file mode 100644
index 0000000..57ea2a5
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/rtt/cts/TestBase.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2018 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.wifi.rtt.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.RangingResultCallback;
+import android.net.wifi.rtt.WifiRttManager;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Base class for Wi-Fi RTT CTS test cases. Provides a uniform configuration and event management
+ * facility.
+ */
+public class TestBase extends AndroidTestCase {
+ protected static final String TAG = "WifiRttCtsTests";
+
+ // wait for Wi-Fi RTT to become available
+ private static final int WAIT_FOR_RTT_CHANGE_SECS = 10;
+
+ // wait for Wi-Fi scan results to become available
+ private static final int WAIT_FOR_SCAN_RESULTS_SECS = 20;
+
+ protected WifiRttManager mWifiRttManager;
+ protected WifiManager mWifiManager;
+ private LocationManager mLocationManager;
+ private WifiManager.WifiLock mWifiLock;
+
+ private final HandlerThread mHandlerThread = new HandlerThread("SingleDeviceTest");
+ protected final Executor mExecutor;
+ {
+ mHandlerThread.start();
+ mExecutor = new HandlerExecutor(new Handler(mHandlerThread.getLooper()));
+ }
+
+ /**
+ * Returns a flag indicating whether or not Wi-Fi RTT should be tested. Wi-Fi RTT
+ * should be tested if the feature is supported on the current device.
+ */
+ static boolean shouldTestWifiRtt(Context context) {
+ final PackageManager pm = context.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_WIFI_RTT);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ if (!shouldTestWifiRtt(getContext())) {
+ return;
+ }
+
+ mLocationManager = (LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE);
+ assertTrue("RTT testing requires Location to be enabled",
+ mLocationManager.isLocationEnabled());
+
+ mWifiRttManager = (WifiRttManager) getContext().getSystemService(
+ Context.WIFI_RTT_RANGING_SERVICE);
+ assertNotNull("Wi-Fi RTT Manager", mWifiRttManager);
+
+ mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+ assertNotNull("Wi-Fi Manager", mWifiManager);
+ mWifiLock = mWifiManager.createWifiLock(TAG);
+ mWifiLock.acquire();
+ if (!mWifiManager.isWifiEnabled()) {
+ mWifiManager.setWifiEnabled(true);
+ }
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED);
+ WifiRttBroadcastReceiver receiver = new WifiRttBroadcastReceiver();
+ mContext.registerReceiver(receiver, intentFilter);
+ if (!mWifiRttManager.isAvailable()) {
+ assertTrue("Timeout waiting for Wi-Fi RTT to change status",
+ receiver.waitForStateChange());
+ assertTrue("Wi-Fi RTT is not available (should be)", mWifiRttManager.isAvailable());
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (!shouldTestWifiRtt(getContext())) {
+ super.tearDown();
+ return;
+ }
+
+ super.tearDown();
+ }
+
+ class WifiRttBroadcastReceiver extends BroadcastReceiver {
+ private CountDownLatch mBlocker = new CountDownLatch(1);
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED.equals(intent.getAction())) {
+ mBlocker.countDown();
+ }
+ }
+
+ boolean waitForStateChange() throws InterruptedException {
+ return mBlocker.await(WAIT_FOR_RTT_CHANGE_SECS, TimeUnit.SECONDS);
+ }
+ }
+
+ class WifiScansBroadcastReceiver extends BroadcastReceiver {
+ private CountDownLatch mBlocker = new CountDownLatch(1);
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) {
+ mBlocker.countDown();
+ }
+ }
+
+ boolean waitForStateChange() throws InterruptedException {
+ return mBlocker.await(WAIT_FOR_SCAN_RESULTS_SECS, TimeUnit.SECONDS);
+ }
+ }
+
+ class ResultCallback extends RangingResultCallback {
+ private CountDownLatch mBlocker = new CountDownLatch(1);
+ private int mCode; // 0: success, otherwise RangingResultCallback STATUS_CODE_*.
+ private List<RangingResult> mResults;
+
+ @Override
+ public void onRangingFailure(int code) {
+ mCode = code;
+ mResults = null; // not necessary since intialized to null - but for completeness
+ mBlocker.countDown();
+ }
+
+ @Override
+ public void onRangingResults(List<RangingResult> results) {
+ mCode = 0; // not necessary since initialized to 0 - but for completeness
+ mResults = results;
+ mBlocker.countDown();
+ }
+
+ /**
+ * Waits for the listener callback to be called - or an error (timeout, interruption).
+ * Returns true on callback called, false on error (timeout, interruption).
+ */
+ boolean waitForCallback() throws InterruptedException {
+ return mBlocker.await(WAIT_FOR_RTT_CHANGE_SECS, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Returns the code of the callback operation. Will be 0 for success (onRangingResults
+ * called), else (if onRangingFailure called) will be one of the STATUS_CODE_* values.
+ */
+ int getCode() {
+ return mCode;
+ }
+
+ /**
+ * Returns the list of ranging results. In cases of error (getCode() != 0) will return null.
+ */
+ List<RangingResult> getResults() {
+ return mResults;
+ }
+ }
+
+ /**
+ * Start a scan and return a list of observed ScanResults (APs).
+ */
+ protected List<ScanResult> scanAps() throws InterruptedException {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ WifiScansBroadcastReceiver receiver = new WifiScansBroadcastReceiver();
+ mContext.registerReceiver(receiver, intentFilter);
+
+ mWifiManager.startScan();
+ receiver.waitForStateChange();
+ mContext.unregisterReceiver(receiver);
+ return mWifiManager.getScanResults();
+ }
+
+ /**
+ * Start a scan and return a test AP which supports IEEE 802.11mc and which has the highest
+ * RSSI. Will perform N (parameterized) scans and get the best AP across both scans.
+ *
+ * Returns null if test AP is not found in the specified number of scans.
+ *
+ * @param numScanRetries Maximum number of scans retries (in addition to first scan).
+ */
+ protected ScanResult scanForTestAp(int numScanRetries)
+ throws InterruptedException {
+ int scanCount = 0;
+ ScanResult bestTestAp = null;
+ while (scanCount <= numScanRetries) {
+ for (ScanResult scanResult : scanAps()) {
+ if (!scanResult.is80211mcResponder()) {
+ continue;
+ }
+ if (bestTestAp == null || scanResult.level > bestTestAp.level) {
+ bestTestAp = scanResult;
+ }
+ }
+
+ scanCount++;
+ }
+
+ return bestTestAp;
+ }
+}
diff --git a/tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java b/tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java
new file mode 100644
index 0000000..74a0c3d
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2018 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.wifi.rtt.cts;
+
+import android.content.IntentFilter;
+import android.net.wifi.ScanResult;
+import android.net.wifi.rtt.RangingRequest;
+import android.net.wifi.rtt.RangingResult;
+import android.net.wifi.rtt.WifiRttManager;
+
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Wi-Fi RTT CTS test: range to all available Access Points which support IEEE 802.11mc.
+ */
+public class WifiRttTest extends TestBase {
+ // Number of scans to do while searching for APs supporting IEEE 802.11mc
+ private static final int NUM_SCANS_SEARCHING_FOR_IEEE80211MC_AP = 2;
+
+ // Number of RTT measurements per AP
+ private static final int NUM_OF_RTT_ITERATIONS = 10;
+
+ // Maximum failure rate of RTT measurements (percentage)
+ private static final int MAX_FAILURE_RATE_PERCENT = 10;
+
+ // Maximum variation from the average measurement (measures consistency)
+ private static final int MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM = 1000;
+
+ // Minimum valid RSSI value
+ private static final int MIN_VALID_RSSI = -100;
+
+ /**
+ * Test Wi-Fi RTT ranging operation:
+ * - Scan for visible APs for the test AP (which is validated to support IEEE 802.11mc)
+ * - Perform N (constant) RTT operations
+ * - Validate:
+ * - Failure ratio < threshold (constant)
+ * - Result margin < threshold (constant)
+ */
+ public void testRangingToTestAp() throws InterruptedException {
+ if (!shouldTestWifiRtt(getContext())) {
+ return;
+ }
+
+ // Scan for IEEE 802.11mc supporting APs
+ ScanResult testAp = scanForTestAp(NUM_SCANS_SEARCHING_FOR_IEEE80211MC_AP);
+ assertTrue("Cannot find test AP", testAp != null);
+
+ // Perform RTT operations
+ RangingRequest request = new RangingRequest.Builder().addAccessPoint(testAp).build();
+ List<RangingResult> allResults = new ArrayList<>();
+ int numFailures = 0;
+ int distanceSum = 0;
+ int distanceMin = 0;
+ int distanceMax = 0;
+ int[] statuses = new int[NUM_OF_RTT_ITERATIONS];
+ int[] distanceMms = new int[NUM_OF_RTT_ITERATIONS];
+ int[] distanceStdDevMms = new int[NUM_OF_RTT_ITERATIONS];
+ int[] rssis = new int[NUM_OF_RTT_ITERATIONS];
+ int[] numAttempted = new int[NUM_OF_RTT_ITERATIONS];
+ int[] numSuccessful = new int[NUM_OF_RTT_ITERATIONS];
+ long[] timestampsMs = new long[NUM_OF_RTT_ITERATIONS];
+ byte[] lastLci = null;
+ byte[] lastLcr = null;
+ for (int i = 0; i < NUM_OF_RTT_ITERATIONS; ++i) {
+ ResultCallback callback = new ResultCallback();
+ mWifiRttManager.startRanging(request, mExecutor, callback);
+ assertTrue("Wi-Fi RTT results: no callback on iteration " + i,
+ callback.waitForCallback());
+
+ List<RangingResult> currentResults = callback.getResults();
+ assertTrue("Wi-Fi RTT results: null results (onRangingFailure) on iteration " + i,
+ currentResults != null);
+ assertTrue("Wi-Fi RTT results: unexpected # of results (expect 1) on iteration " + i,
+ currentResults.size() == 1);
+ RangingResult result = currentResults.get(0);
+ assertTrue("Wi-Fi RTT results: invalid result (wrong BSSID) entry on iteration " + i,
+ result.getMacAddress().toString().equals(testAp.BSSID));
+ assertEquals(
+ "Wi-Fi RTT results: invalid result (non-null PeerHandle) entry on iteration "
+ + i, null, result.getPeerHandle());
+
+ allResults.add(result);
+ int status = result.getStatus();
+ statuses[i] = status;
+ if (status == RangingResult.STATUS_SUCCESS) {
+ distanceSum += result.getDistanceMm();
+ if (i == 0) {
+ distanceMin = result.getDistanceMm();
+ distanceMax = result.getDistanceMm();
+ } else {
+ distanceMin = Math.min(distanceMin, result.getDistanceMm());
+ distanceMax = Math.max(distanceMax, result.getDistanceMm());
+ }
+
+ assertTrue("Wi-Fi RTT results: invalid RSSI on iteration " + i,
+ result.getRssi() >= MIN_VALID_RSSI);
+
+ distanceMms[i - numFailures] = result.getDistanceMm();
+ distanceStdDevMms[i - numFailures] = result.getDistanceStdDevMm();
+ rssis[i - numFailures] = result.getRssi();
+ numAttempted[i - numFailures] = result.getNumAttemptedMeasurements();
+ numSuccessful[i - numFailures] = result.getNumSuccessfulMeasurements();
+ timestampsMs[i - numFailures] = result.getRangingTimestampMillis();
+
+ byte[] currentLci = result.getLci();
+ byte[] currentLcr = result.getLcr();
+ if (i - numFailures > 0) {
+ assertTrue("Wi-Fi RTT results: invalid result (LCI mismatch) on iteration " + i,
+ Arrays.equals(currentLci, lastLci));
+ assertTrue("Wi-Fi RTT results: invalid result (LCR mismatch) on iteration " + i,
+ Arrays.equals(currentLcr, lastLcr));
+ }
+ lastLci = currentLci;
+ lastLcr = currentLcr;
+ } else {
+ numFailures++;
+ }
+ }
+
+ // Save results to log
+ int numGoodResults = NUM_OF_RTT_ITERATIONS - numFailures;
+ DeviceReportLog reportLog = new DeviceReportLog(TAG, "testRangingToTestAp");
+ reportLog.addValues("status_codes", statuses, ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValues("distance_mm", Arrays.copyOf(distanceMms, numGoodResults),
+ ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValues("distance_stddev_mm", Arrays.copyOf(distanceStdDevMms, numGoodResults),
+ ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValues("rssi_dbm", Arrays.copyOf(rssis, numGoodResults), ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ reportLog.addValues("num_attempted", Arrays.copyOf(numAttempted, numGoodResults),
+ ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValues("num_successful", Arrays.copyOf(numSuccessful, numGoodResults),
+ ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.addValues("timestamps", Arrays.copyOf(timestampsMs, numGoodResults),
+ ResultType.NEUTRAL, ResultUnit.NONE);
+ reportLog.submit();
+
+ // Analyze results
+ assertTrue("Wi-Fi RTT failure rate exceeds threshold: FAIL=" + numFailures + ", ITERATIONS="
+ + NUM_OF_RTT_ITERATIONS,
+ numFailures <= NUM_OF_RTT_ITERATIONS * MAX_FAILURE_RATE_PERCENT / 100);
+ if (numFailures != NUM_OF_RTT_ITERATIONS) {
+ double distanceAvg = distanceSum / (NUM_OF_RTT_ITERATIONS - numFailures);
+ assertTrue("Wi-Fi RTT: Variation (max direction) exceeds threshold",
+ (distanceMax - distanceAvg) <= MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM);
+ assertTrue("Wi-Fi RTT: Variation (min direction) exceeds threshold",
+ (distanceAvg - distanceMin) <= MAX_VARIATION_FROM_AVERAGE_DISTANCE_MM);
+ for (int i = 0; i < numGoodResults; ++i) {
+ assertNotSame("Number of attempted measurements is 0", 0, numAttempted[i]);
+ assertNotSame("Number of successful measurements is 0", 0, numSuccessful[i]);
+ }
+ }
+ }
+
+ /**
+ * Validate that when a request contains more range operations than allowed (by API) that we
+ * get an exception.
+ */
+ public void testRequestTooLarge() {
+ if (!shouldTestWifiRtt(getContext())) {
+ return;
+ }
+
+ ScanResult dummy = new ScanResult();
+ dummy.BSSID = "00:01:02:03:04:05";
+
+ RangingRequest.Builder builder = new RangingRequest.Builder();
+ for (int i = 0; i < RangingRequest.getMaxPeers() - 2; ++i) {
+ builder.addAccessPoint(dummy);
+ }
+
+ List<ScanResult> scanResults = new ArrayList<>();
+ scanResults.add(dummy);
+ scanResults.add(dummy);
+ scanResults.add(dummy);
+
+ builder.addAccessPoints(scanResults);
+
+ try {
+ mWifiRttManager.startRanging(builder.build(), mExecutor, new ResultCallback());
+ } catch (IllegalArgumentException e) {
+ return;
+ }
+
+ assertTrue(
+ "Did not receive expected IllegalArgumentException when tried to range to too "
+ + "many peers",
+ false);
+ }
+}