Merge "Minor changes for DnsResolverTest" into qt-dev
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index 0656cae..c7cab7b 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -16,6 +16,9 @@
<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" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+
<target_preparer class="com.android.cts.net.NetPolicyTestsPreparer" />
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
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 4e4d1f6..6dfa2f3 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
@@ -153,7 +153,14 @@
Log.i(TAG, "Apps status on " + getName() + ":\n"
+ "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
+ "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
- executeShellCommand("settings get global app_idle_constants");
+
+ // app_idle_constants set in NetPolicyTestsPreparer.setUp() is not always sucessful (suspect
+ // timing issue), here we set it again to make sure.
+ final String appIdleConstants = "parole_duration=0,stable_charging_threshold=0";
+ executeShellCommand("settings put global app_idle_constants " + appIdleConstants);
+ final String currentConstants =
+ executeShellCommand("settings get global app_idle_constants");
+ assertEquals(appIdleConstants, currentConstants);
}
@Override
@@ -204,7 +211,7 @@
do {
attempts++;
count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED);
- if (count == expectedCount) {
+ if (count >= expectedCount) {
break;
}
Log.d(TAG, "Expecting count " + expectedCount + " but actual is " + count + " after "
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index 72563d4..cfe6a73 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -22,6 +22,8 @@
import android.util.Log;
+import com.android.compatibility.common.util.CddTest;
+
public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
@@ -35,7 +37,6 @@
super.setUp();
mIsDataSaverSupported = isDataSaverSupported();
- if (!isSupported()) return;
// Set initial state.
setRestrictBackground(false);
@@ -201,6 +202,20 @@
}
}
+ @CddTest(requirement="7.4.7/C-2-2")
+ public void testBroadcastNotSentOnUnsupportedDevices() throws Exception {
+ if (isSupported()) return;
+
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(0);
+
+ setRestrictBackground(false);
+ assertRestrictBackgroundChangedReceived(0);
+
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(0);
+ }
+
private void assertDataSaverStatusOnBackground(int expectedStatus) throws Exception {
assertRestrictBackgroundStatus(expectedStatus);
assertBackgroundNetworkAccess(expectedStatus != RESTRICT_BACKGROUND_STATUS_ENABLED);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
index 7d91574..7d3d4fc 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
@@ -17,6 +17,7 @@
package com.android.cts.net.hostside;
import android.content.Intent;
+import android.net.Network;
import android.net.ProxyInfo;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
@@ -27,6 +28,7 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.ArrayList;
public class MyVpnService extends VpnService {
@@ -118,6 +120,18 @@
}
}
+ ArrayList<Network> underlyingNetworks =
+ intent.getParcelableArrayListExtra(packageName + ".underlyingNetworks");
+ if (underlyingNetworks == null) {
+ // VPN tracks default network
+ builder.setUnderlyingNetworks(null);
+ } else {
+ builder.setUnderlyingNetworks(underlyingNetworks.toArray(new Network[0]));
+ }
+
+ boolean isAlwaysMetered = intent.getBooleanExtra(packageName + ".isAlwaysMetered", false);
+ builder.setMetered(isAlwaysMetered);
+
ProxyInfo vpnProxy = intent.getParcelableExtra(packageName + ".httpProxy");
builder.setHttpProxy(vpnProxy);
builder.setMtu(MTU);
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 17e1347..2fc85f6 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -19,6 +19,7 @@
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.*;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -31,6 +32,7 @@
import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.VpnService;
+import android.net.wifi.WifiManager;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemProperties;
@@ -61,7 +63,9 @@
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Random;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
@@ -100,6 +104,7 @@
private MyActivity mActivity;
private String mPackageName;
private ConnectivityManager mCM;
+ private WifiManager mWifiManager;
private RemoteSocketFactoryClient mRemoteSocketFactoryClient;
Network mNetwork;
@@ -123,7 +128,8 @@
mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
MyActivity.class, null);
mPackageName = mActivity.getPackageName();
- mCM = (ConnectivityManager) mActivity.getSystemService(mActivity.CONNECTIVITY_SERVICE);
+ mCM = (ConnectivityManager) mActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mWifiManager = (WifiManager) mActivity.getSystemService(Context.WIFI_SERVICE);
mRemoteSocketFactoryClient = new RemoteSocketFactoryClient(mActivity);
mRemoteSocketFactoryClient.bind();
mDevice.waitForIdle();
@@ -192,9 +198,11 @@
}
}
+ // TODO: Consider replacing arguments with a Builder.
private void startVpn(
String[] addresses, String[] routes, String allowedApplications,
- String disallowedApplications, ProxyInfo proxyInfo) throws Exception {
+ String disallowedApplications, @Nullable ProxyInfo proxyInfo,
+ @Nullable ArrayList<Network> underlyingNetworks, boolean isAlwaysMetered) throws Exception {
prepareVpn();
// Register a callback so we will be notified when our VPN comes up.
@@ -221,7 +229,10 @@
.putExtra(mPackageName + ".routes", TextUtils.join(",", routes))
.putExtra(mPackageName + ".allowedapplications", allowedApplications)
.putExtra(mPackageName + ".disallowedapplications", disallowedApplications)
- .putExtra(mPackageName + ".httpProxy", proxyInfo);
+ .putExtra(mPackageName + ".httpProxy", proxyInfo)
+ .putParcelableArrayListExtra(
+ mPackageName + ".underlyingNetworks", underlyingNetworks)
+ .putExtra(mPackageName + ".isAlwaysMetered", isAlwaysMetered);
mActivity.startService(intent);
synchronized (mLock) {
@@ -251,7 +262,8 @@
mCallback = new NetworkCallback() {
public void onLost(Network network) {
synchronized (mLockShutdown) {
- Log.i(TAG, "Got lost callback for network=" + network + ",mNetwork = " + mNetwork);
+ Log.i(TAG, "Got lost callback for network=" + network
+ + ",mNetwork = " + mNetwork);
if( mNetwork == network){
mLockShutdown.notify();
}
@@ -574,7 +586,7 @@
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"0.0.0.0/0", "::/0"},
- "", "", null);
+ "", "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */);
final Intent intent = receiver.awaitForBroadcast(TimeUnit.MINUTES.toMillis(1));
assertNotNull("Failed to receive broadcast from VPN service", intent);
@@ -598,7 +610,7 @@
String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"192.0.2.0/24", "2001:db8::/32"},
- allowedApps, "", null);
+ allowedApps, "", null, null /* underlyingNetworks */, false /* isAlwaysMetered */);
assertSocketClosed(fd, TEST_HOST);
@@ -620,7 +632,8 @@
Log.i(TAG, "Append shell app to disallowedApps: " + disallowedApps);
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"192.0.2.0/24", "2001:db8::/32"},
- "", disallowedApps, null);
+ "", disallowedApps, null, null /* underlyingNetworks */,
+ false /* isAlwaysMetered */);
assertSocketStillOpen(localFd, TEST_HOST);
assertSocketStillOpen(remoteFd, TEST_HOST);
@@ -657,7 +670,7 @@
ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "",
- testProxyInfo);
+ testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */);
// Check that the proxy change broadcast is received
try {
@@ -697,7 +710,7 @@
ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"0.0.0.0/0", "::/0"}, "", disallowedApps,
- testProxyInfo);
+ testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */);
// The disallowed app does has the proxy configs of the default network.
assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork());
@@ -711,7 +724,8 @@
proxyBroadcastReceiver.register();
String allowedApps = mPackageName;
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
- new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null);
+ new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
+ null /* underlyingNetworks */, false /* isAlwaysMetered */);
try {
assertNotNull("No proxy change was broadcast.",
@@ -748,7 +762,7 @@
proxyBroadcastReceiver.register();
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "",
- testProxyInfo);
+ testProxyInfo, null /* underlyingNetworks */, false /* isAlwaysMetered */);
assertDefaultProxy(testProxyInfo);
mCM.bindProcessToNetwork(initialNetwork);
@@ -761,6 +775,145 @@
assertDefaultProxy(initialProxy);
}
+ public void testVpnMeterednessWithNoUnderlyingNetwork() throws Exception {
+ if (!supportedHardware()) {
+ return;
+ }
+ // VPN is not routing any traffic i.e. its underlying networks is an empty array.
+ ArrayList<Network> underlyingNetworks = new ArrayList<>();
+ String allowedApps = mPackageName;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
+ underlyingNetworks, false /* isAlwaysMetered */);
+
+ // VPN should now be the active network.
+ assertEquals(mNetwork, mCM.getActiveNetwork());
+ assertVpnTransportContains(NetworkCapabilities.TRANSPORT_VPN);
+ // VPN with no underlying networks should be metered by default.
+ assertTrue(isNetworkMetered(mNetwork));
+ assertTrue(mCM.isActiveNetworkMetered());
+ }
+
+ public void testVpnMeterednessWithNullUnderlyingNetwork() throws Exception {
+ if (!supportedHardware()) {
+ return;
+ }
+ Network underlyingNetwork = mCM.getActiveNetwork();
+ if (underlyingNetwork == null) {
+ Log.i(TAG, "testVpnMeterednessWithNullUnderlyingNetwork cannot execute"
+ + " unless there is an active network");
+ return;
+ }
+ // VPN tracks platform default.
+ ArrayList<Network> underlyingNetworks = null;
+ String allowedApps = mPackageName;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
+ underlyingNetworks, false /*isAlwaysMetered */);
+
+ // Ensure VPN transports contains underlying network's transports.
+ assertVpnTransportContains(underlyingNetwork);
+ // Its meteredness should be same as that of underlying network.
+ assertEquals(isNetworkMetered(underlyingNetwork), isNetworkMetered(mNetwork));
+ // Meteredness based on VPN capabilities and CM#isActiveNetworkMetered should be in sync.
+ assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered());
+ }
+
+ public void testVpnMeterednessWithNonNullUnderlyingNetwork() throws Exception {
+ if (!supportedHardware()) {
+ return;
+ }
+ Network underlyingNetwork = mCM.getActiveNetwork();
+ if (underlyingNetwork == null) {
+ Log.i(TAG, "testVpnMeterednessWithNonNullUnderlyingNetwork cannot execute"
+ + " unless there is an active network");
+ return;
+ }
+ // VPN explicitly declares WiFi to be its underlying network.
+ ArrayList<Network> underlyingNetworks = new ArrayList<>(1);
+ underlyingNetworks.add(underlyingNetwork);
+ String allowedApps = mPackageName;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
+ underlyingNetworks, false /* isAlwaysMetered */);
+
+ // Ensure VPN transports contains underlying network's transports.
+ assertVpnTransportContains(underlyingNetwork);
+ // Its meteredness should be same as that of underlying network.
+ assertEquals(isNetworkMetered(underlyingNetwork), isNetworkMetered(mNetwork));
+ // Meteredness based on VPN capabilities and CM#isActiveNetworkMetered should be in sync.
+ assertEquals(isNetworkMetered(mNetwork), mCM.isActiveNetworkMetered());
+ }
+
+ public void testAlwaysMeteredVpnWithNullUnderlyingNetwork() throws Exception {
+ if (!supportedHardware()) {
+ return;
+ }
+ Network underlyingNetwork = mCM.getActiveNetwork();
+ if (underlyingNetwork == null) {
+ Log.i(TAG, "testAlwaysMeteredVpnWithNullUnderlyingNetwork cannot execute"
+ + " unless there is an active network");
+ return;
+ }
+ // VPN tracks platform default.
+ ArrayList<Network> underlyingNetworks = null;
+ String allowedApps = mPackageName;
+ boolean isAlwaysMetered = true;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
+ underlyingNetworks, isAlwaysMetered);
+
+ // VPN's meteredness does not depend on underlying network since it is always metered.
+ assertTrue(isNetworkMetered(mNetwork));
+ assertTrue(mCM.isActiveNetworkMetered());
+ }
+
+ public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception {
+ if (!supportedHardware()) {
+ return;
+ }
+ Network underlyingNetwork = mCM.getActiveNetwork();
+ if (underlyingNetwork == null) {
+ Log.i(TAG, "testAlwaysMeteredVpnWithNonNullUnderlyingNetwork cannot execute"
+ + " unless there is an active network");
+ return;
+ }
+ // VPN explicitly declares its underlying network.
+ ArrayList<Network> underlyingNetworks = new ArrayList<>(1);
+ underlyingNetworks.add(underlyingNetwork);
+ String allowedApps = mPackageName;
+ boolean isAlwaysMetered = true;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null,
+ underlyingNetworks, isAlwaysMetered);
+
+ // VPN's meteredness does not depend on underlying network since it is always metered.
+ assertTrue(isNetworkMetered(mNetwork));
+ assertTrue(mCM.isActiveNetworkMetered());
+ }
+
+ private boolean isNetworkMetered(Network network) {
+ NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
+ return !nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ }
+
+ private void assertVpnTransportContains(Network underlyingNetwork) {
+ int[] transports = mCM.getNetworkCapabilities(underlyingNetwork).getTransportTypes();
+ assertVpnTransportContains(transports);
+ }
+
+ private void assertVpnTransportContains(int... transports) {
+ NetworkCapabilities vpnCaps = mCM.getNetworkCapabilities(mNetwork);
+ for (int transport : transports) {
+ assertTrue(vpnCaps.hasTransport(transport));
+ }
+ }
+
private void assertDefaultProxy(ProxyInfo expected) {
assertEquals("Incorrect proxy config.", expected, mCM.getDefaultProxy());
String expectedHost = expected == null ? null : expected.getHost();
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 5f5ea43..4598c39 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -81,6 +81,11 @@
"testGetRestrictBackgroundStatus_requiredWhitelistedPackages");
}
+ public void testDataSaverMode_broadcastNotSentOnUnsupportedDevices() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testBroadcastNotSentOnUnsupportedDevices");
+ }
+
/*****************************
* Battery Saver Mode tests. *
*****************************/
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
index e34ee89..6e37a24 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -64,4 +64,31 @@
public void testBindToNetworkWithProxy() throws Exception {
runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testBindToNetworkWithProxy");
}
+
+ public void testVpnMeterednessWithNoUnderlyingNetwork() throws Exception {
+ runDeviceTests(
+ TEST_PKG, TEST_PKG + ".VpnTest", "testVpnMeterednessWithNoUnderlyingNetwork");
+ }
+
+ public void testVpnMeterednessWithNullUnderlyingNetwork() throws Exception {
+ runDeviceTests(
+ TEST_PKG, TEST_PKG + ".VpnTest", "testVpnMeterednessWithNullUnderlyingNetwork");
+ }
+
+ public void testVpnMeterednessWithNonNullUnderlyingNetwork() throws Exception {
+ runDeviceTests(
+ TEST_PKG, TEST_PKG + ".VpnTest", "testVpnMeterednessWithNonNullUnderlyingNetwork");
+ }
+
+ public void testAlwaysMeteredVpnWithNullUnderlyingNetwork() throws Exception {
+ runDeviceTests(
+ TEST_PKG, TEST_PKG + ".VpnTest", "testAlwaysMeteredVpnWithNullUnderlyingNetwork");
+ }
+
+ public void testAlwaysMeteredVpnWithNonNullUnderlyingNetwork() throws Exception {
+ runDeviceTests(
+ TEST_PKG,
+ TEST_PKG + ".VpnTest",
+ "testAlwaysMeteredVpnWithNonNullUnderlyingNetwork");
+ }
}
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 ca14c27..bc2ee2c 100644
--- a/tests/cts/hostside/src/com/android/cts/net/NetPolicyTestsPreparer.java
+++ b/tests/cts/hostside/src/com/android/cts/net/NetPolicyTestsPreparer.java
@@ -48,7 +48,7 @@
}
private void setAppIdleConstants(String appIdleConstants) throws DeviceNotAvailableException {
- executeCmd("settings put global app_idle_constants " + appIdleConstants);
+ executeCmd("settings put global app_idle_constants \"" + appIdleConstants + "\"");
}
private String getAppIdleConstants() throws DeviceNotAvailableException {
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index dbd8fef..44a00ef 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -16,7 +16,8 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.net.cts">
+ package="android.net.cts"
+ android:targetSandboxVersion="2">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -36,12 +37,6 @@
<application android:usesCleartextTraffic="true">
<uses-library android:name="android.test.runner" />
<uses-library android:name="org.apache.http.legacy" android:required="false" />
-
- <receiver android:name=".ConnectivityReceiver">
- <intent-filter>
- <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
- </intent-filter>
- </receiver>
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/tests/cts/net/AndroidTest.xml b/tests/cts/net/AndroidTest.xml
index ff93afc..1807a6b 100644
--- a/tests/cts/net/AndroidTest.xml
+++ b/tests/cts/net/AndroidTest.xml
@@ -16,11 +16,12 @@
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="networking" />
<option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
+ <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="not-shardable" value="true" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsNetTestCases.apk" />
- <option name="test-file-name" value="CtsNetTestAppForApi23.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.net.cts" />
diff --git a/tests/cts/net/api23Test/Android.bp b/tests/cts/net/api23Test/Android.bp
new file mode 100644
index 0000000..48161cf
--- /dev/null
+++ b/tests/cts/net/api23Test/Android.bp
@@ -0,0 +1,51 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_test {
+ name: "CtsNetApi23TestCases",
+ defaults: ["cts_defaults"],
+
+ // Include both the 32 and 64 bit versions
+ compile_multilib: "both",
+
+ libs: [
+ "android.test.base.stubs",
+ ],
+
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "core-tests-support",
+ "compatibility-device-util-axt",
+ "ctstestrunner-axt",
+ "ctstestserver",
+ "mockwebserver",
+ "junit",
+ "junit-params",
+ "truth-prebuilt",
+ ],
+
+ platform_apis: true,
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "vts",
+ "general-tests",
+ ],
+
+}
diff --git a/tests/cts/net/api23Test/AndroidManifest.xml b/tests/cts/net/api23Test/AndroidManifest.xml
new file mode 100644
index 0000000..8af87f6
--- /dev/null
+++ b/tests/cts/net/api23Test/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.net.cts.api23test">
+
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application android:usesCleartextTraffic="true">
+ <uses-library android:name="android.test.runner" />
+
+ <receiver android:name=".ConnectivityReceiver">
+ <intent-filter>
+ <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+ </intent-filter>
+ </receiver>
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.net.cts.api23test"
+ android:label="CTS tests of android.net">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+</manifest>
+
diff --git a/tests/cts/net/api23Test/AndroidTest.xml b/tests/cts/net/api23Test/AndroidTest.xml
new file mode 100644
index 0000000..21f28fc
--- /dev/null
+++ b/tests/cts/net/api23Test/AndroidTest.xml
@@ -0,0 +1,30 @@
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Net API23 test cases">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="networking" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="not-shardable" value="true" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsNetApi23TestCases.apk" />
+ <option name="test-file-name" value="CtsNetTestAppForApi23.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.net.cts.api23test" />
+ <option name="hidden-api-checks" value="false" />
+ </test>
+</configuration>
diff --git a/tests/cts/net/api23Test/src/android/net/cts/api23test/ConnectivityManagerApi23Test.java b/tests/cts/net/api23Test/src/android/net/cts/api23test/ConnectivityManagerApi23Test.java
new file mode 100644
index 0000000..f38490e
--- /dev/null
+++ b/tests/cts/net/api23Test/src/android/net/cts/api23test/ConnectivityManagerApi23Test.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts.api23test;
+
+import static android.content.pm.PackageManager.FEATURE_WIFI;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiManager;
+import android.os.Looper;
+import android.system.Os;
+import android.system.OsConstants;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class ConnectivityManagerApi23Test extends AndroidTestCase {
+ private static final String TAG = ConnectivityManagerApi23Test.class.getSimpleName();
+
+ 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 HTTP_PORT = 80;
+ // Intent string to get the number of wifi CONNECTIVITY_ACTION callbacks the test app has seen
+ public static final String GET_WIFI_CONNECTIVITY_ACTION_COUNT =
+ "android.net.cts.appForApi23.getWifiConnectivityActionCount";
+ // Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent.
+ private static final String NETWORK_CALLBACK_ACTION =
+ "ConnectivityManagerTest.NetworkCallbackAction";
+ private static final String HTTP_REQUEST =
+ "GET /generate_204 HTTP/1.0\r\n" +
+ "Host: " + TEST_HOST + "\r\n" +
+ "Connection: keep-alive\r\n\r\n";
+
+ private Context mContext;
+ private ConnectivityManager mCm;
+ private WifiManager mWifiManager;
+ private PackageManager mPackageManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Looper.prepare();
+ mContext = getContext();
+ mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mPackageManager = mContext.getPackageManager();
+ }
+
+ /**
+ * Tests reporting of connectivity changed.
+ */
+ public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
+ ConnectivityReceiver.prepare();
+
+ toggleWifi();
+
+ // The connectivity broadcast has been sent; push through a terminal broadcast
+ // to wait for in the receive to confirm it didn't see the connectivity change.
+ Intent finalIntent = new Intent(ConnectivityReceiver.FINAL_ACTION);
+ finalIntent.setClass(mContext, ConnectivityReceiver.class);
+ mContext.sendBroadcast(finalIntent);
+ assertFalse(ConnectivityReceiver.waitForBroadcast());
+ }
+
+ public void testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent()
+ throws InterruptedException {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot"
+ + "execute unless device supports WiFi");
+ return;
+ }
+ 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();
+
+ Intent getConnectivityCount = new Intent(GET_WIFI_CONNECTIVITY_ACTION_COUNT);
+ assertEquals(2, sendOrderedBroadcastAndReturnResultCode(
+ getConnectivityCount, SEND_BROADCAST_TIMEOUT));
+ }
+
+ public void testConnectivityChanged_whenRegistered_shouldReceiveIntent() {
+ if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_whenRegistered_shouldReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
+ ConnectivityReceiver.prepare();
+ ConnectivityReceiver receiver = new ConnectivityReceiver();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ mContext.registerReceiver(receiver, filter);
+
+ toggleWifi();
+ Intent finalIntent = new Intent(ConnectivityReceiver.FINAL_ACTION);
+ finalIntent.setClass(mContext, ConnectivityReceiver.class);
+ mContext.sendBroadcast(finalIntent);
+
+ assertTrue(ConnectivityReceiver.waitForBroadcast());
+ }
+
+ // Toggle WiFi twice, leaving it in the state it started in
+ private void toggleWifi() {
+ if (mWifiManager.isWifiEnabled()) {
+ Network wifiNetwork = getWifiNetwork();
+ disconnectFromWifi(wifiNetwork);
+ connectToWifi();
+ } else {
+ connectToWifi();
+ Network wifiNetwork = getWifiNetwork();
+ disconnectFromWifi(wifiNetwork);
+ }
+ }
+
+ private int sendOrderedBroadcastAndReturnResultCode(
+ Intent intent, int timeoutMs) throws InterruptedException {
+ final LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
+ mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ result.offer(getResultCode());
+ }
+ }, null, 0, null, null);
+
+ Integer resultCode = result.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ assertNotNull("Timed out (more than " + timeoutMs +
+ " milliseconds) waiting for result code for broadcast", resultCode);
+ return resultCode;
+ }
+
+ private Network getWifiNetwork() {
+ TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+ Network network = null;
+ try {
+ network = callback.waitForAvailable();
+ } catch (InterruptedException e) {
+ fail("NetworkCallback wait was interrupted.");
+ } finally {
+ mCm.unregisterNetworkCallback(callback);
+ }
+ assertNotNull("Cannot find Network for wifi. Is wifi connected?", network);
+ return network;
+ }
+
+ /** Disable WiFi and wait for it to become disconnected from the network. */
+ private void disconnectFromWifi(Network wifiNetworkToCheck) {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+ Network lostWifiNetwork = null;
+
+ ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
+ ConnectivityManager.TYPE_WIFI, NetworkInfo.State.DISCONNECTED);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ mContext.registerReceiver(receiver, filter);
+
+ // Assert that we can establish a TCP connection on wifi.
+ Socket wifiBoundSocket = null;
+ if (wifiNetworkToCheck != null) {
+ try {
+ wifiBoundSocket = getBoundSocket(wifiNetworkToCheck, TEST_HOST, HTTP_PORT);
+ testHttpRequest(wifiBoundSocket);
+ } catch (IOException e) {
+ fail("HTTP request before wifi disconnected failed with: " + e);
+ }
+ }
+
+ boolean disconnected = false;
+ try {
+ assertTrue(mWifiManager.setWifiEnabled(false));
+ // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
+ lostWifiNetwork = callback.waitForLost();
+ assertNotNull(lostWifiNetwork);
+ disconnected = receiver.waitForState();
+ } catch (InterruptedException ex) {
+ fail("disconnectFromWifi was interrupted");
+ } finally {
+ mCm.unregisterNetworkCallback(callback);
+ mContext.unregisterReceiver(receiver);
+ }
+
+ assertTrue("Wifi failed to reach DISCONNECTED state.", disconnected);
+
+ // Check that the socket is closed when wifi disconnects.
+ if (wifiBoundSocket != null) {
+ try {
+ testHttpRequest(wifiBoundSocket);
+ fail("HTTP request should not succeed after wifi disconnects");
+ } catch (IOException expected) {
+ assertEquals(Os.strerror(OsConstants.ECONNABORTED), expected.getMessage());
+ }
+ }
+ }
+
+ /** Enable WiFi and wait for it to become connected to a network. */
+ private Network connectToWifi() {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+ Network wifiNetwork = null;
+
+ ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
+ ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+ mContext.registerReceiver(receiver, filter);
+
+ boolean connected = false;
+ try {
+ assertTrue(mWifiManager.setWifiEnabled(true));
+ // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION.
+ wifiNetwork = callback.waitForAvailable();
+ assertNotNull(wifiNetwork);
+ connected = receiver.waitForState();
+ } catch (InterruptedException ex) {
+ fail("connectToWifi was interrupted");
+ } finally {
+ mCm.unregisterNetworkCallback(callback);
+ mContext.unregisterReceiver(receiver);
+ }
+
+ assertTrue("Wifi must be configured to connect to an access point for this test.",
+ connected);
+ return wifiNetwork;
+ }
+
+ private NetworkRequest makeWifiNetworkRequest() {
+ return new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .build();
+ }
+
+ private void testHttpRequest(Socket s) throws IOException {
+ OutputStream out = s.getOutputStream();
+ InputStream in = s.getInputStream();
+
+ final byte[] requestBytes = HTTP_REQUEST.getBytes("UTF-8");
+ byte[] responseBytes = new byte[4096];
+ out.write(requestBytes);
+ in.read(responseBytes);
+ assertTrue(new String(responseBytes, "UTF-8").startsWith("HTTP/1.0 204 No Content\r\n"));
+ }
+
+ private Socket getBoundSocket(Network network, String host, int port) throws IOException {
+ InetSocketAddress addr = new InetSocketAddress(host, port);
+ Socket s = network.getSocketFactory().createSocket();
+ try {
+ s.setSoTimeout(SOCKET_TIMEOUT_MS);
+ s.connect(addr, SOCKET_TIMEOUT_MS);
+ } catch (IOException e) {
+ s.close();
+ throw e;
+ }
+ return s;
+ }
+
+ /**
+ * Receiver that captures the last connectivity change's network type and state. Recognizes
+ * both {@code CONNECTIVITY_ACTION} and {@code NETWORK_CALLBACK_ACTION} intents.
+ */
+ private class ConnectivityActionReceiver extends BroadcastReceiver {
+
+ private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
+
+ private final int mNetworkType;
+ private final NetworkInfo.State mNetState;
+
+ ConnectivityActionReceiver(int networkType, NetworkInfo.State netState) {
+ mNetworkType = networkType;
+ mNetState = netState;
+ }
+
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ NetworkInfo networkInfo = null;
+
+ // When receiving ConnectivityManager.CONNECTIVITY_ACTION, the NetworkInfo parcelable
+ // is stored in EXTRA_NETWORK_INFO. With a NETWORK_CALLBACK_ACTION, the Network is
+ // sent in EXTRA_NETWORK and we need to ask the ConnectivityManager for the NetworkInfo.
+ if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
+ networkInfo = intent.getExtras()
+ .getParcelable(ConnectivityManager.EXTRA_NETWORK_INFO);
+ assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK_INFO", networkInfo);
+ } else if (NETWORK_CALLBACK_ACTION.equals(action)) {
+ Network network = intent.getExtras()
+ .getParcelable(ConnectivityManager.EXTRA_NETWORK);
+ assertNotNull("ConnectivityActionReceiver expected EXTRA_NETWORK", network);
+ networkInfo = mCm.getNetworkInfo(network);
+ if (networkInfo == null) {
+ // When disconnecting, it seems like we get an intent sent with an invalid
+ // Network; that is, by the time we call ConnectivityManager.getNetworkInfo(),
+ // it is invalid. Ignore these.
+ Log.i(TAG, "ConnectivityActionReceiver NETWORK_CALLBACK_ACTION ignoring "
+ + "invalid network");
+ return;
+ }
+ } else {
+ fail("ConnectivityActionReceiver received unxpected intent action: " + action);
+ }
+
+ assertNotNull("ConnectivityActionReceiver didn't find NetworkInfo", networkInfo);
+ int networkType = networkInfo.getType();
+ State networkState = networkInfo.getState();
+ Log.i(TAG, "Network type: " + networkType + " state: " + networkState);
+ if (networkType == mNetworkType && networkInfo.getState() == mNetState) {
+ mReceiveLatch.countDown();
+ }
+ }
+
+ public boolean waitForState() throws InterruptedException {
+ return mReceiveLatch.await(30, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Callback used in testRegisterNetworkCallback that allows caller to block on
+ * {@code onAvailable}.
+ */
+ private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
+ private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
+ private final CountDownLatch mLostLatch = new CountDownLatch(1);
+ private final CountDownLatch mUnavailableLatch = new CountDownLatch(1);
+
+ public Network currentNetwork;
+ public Network lastLostNetwork;
+
+ public Network waitForAvailable() throws InterruptedException {
+ return mAvailableLatch.await(30, TimeUnit.SECONDS) ? currentNetwork : null;
+ }
+
+ public Network waitForLost() throws InterruptedException {
+ return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null;
+ }
+
+ public boolean waitForUnavailable() throws InterruptedException {
+ return mUnavailableLatch.await(2, TimeUnit.SECONDS);
+ }
+
+
+ @Override
+ public void onAvailable(Network network) {
+ currentNetwork = network;
+ mAvailableLatch.countDown();
+ }
+
+ @Override
+ public void onLost(Network network) {
+ lastLostNetwork = network;
+ if (network.equals(currentNetwork)) {
+ currentNetwork = null;
+ }
+ mLostLatch.countDown();
+ }
+
+ @Override
+ public void onUnavailable() {
+ mUnavailableLatch.countDown();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityReceiver.java b/tests/cts/net/api23Test/src/android/net/cts/api23test/ConnectivityReceiver.java
similarity index 98%
rename from tests/cts/net/src/android/net/cts/ConnectivityReceiver.java
rename to tests/cts/net/api23Test/src/android/net/cts/api23test/ConnectivityReceiver.java
index 6a7b4a0..9d2b8ad 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityReceiver.java
+++ b/tests/cts/net/api23Test/src/android/net/cts/api23test/ConnectivityReceiver.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.cts;
+package android.net.cts.api23test;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/tests/cts/net/src/android/net/cts/AirplaneModeTest.java b/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
index 0a3146c..524e549 100644
--- a/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
+++ b/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
@@ -18,13 +18,14 @@
import android.content.ContentResolver;
import android.content.Context;
-import android.content.pm.PackageManager;
+import android.platform.test.annotations.AppModeFull;
import android.provider.Settings;
import android.test.AndroidTestCase;
import android.util.Log;
import java.lang.Thread;
+@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
public class AirplaneModeTest extends AndroidTestCase {
private static final String TAG = "AirplaneModeTest";
private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index cb1aa09..c444445 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -35,7 +35,6 @@
import android.app.PendingIntent;
import android.app.UiAutomation;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -43,6 +42,8 @@
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.IpSecManager;
+import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -52,11 +53,13 @@
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
import android.net.SocketKeepalive;
+import android.net.util.KeepaliveUtils;
import android.net.wifi.WifiManager;
import android.os.Looper;
import android.os.MessageQueue;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.platform.test.annotations.AppModeFull;
import android.provider.Settings;
import android.system.Os;
import android.system.OsConstants;
@@ -86,6 +89,7 @@
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
@@ -99,8 +103,6 @@
private static final String TAG = ConnectivityManagerTest.class.getSimpleName();
- private static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";
-
public static final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE;
public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI;
@@ -110,7 +112,6 @@
private static final int CONNECT_TIMEOUT_MS = 2000;
private static final int KEEPALIVE_CALLBACK_TIMEOUT_MS = 2000;
private static final int KEEPALIVE_SOCKET_TIMEOUT_MS = 5000;
- private static final int SEND_BROADCAST_TIMEOUT = 30000;
private static final int MIN_KEEPALIVE_INTERVAL = 10;
private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 5000;
private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
@@ -125,10 +126,6 @@
private static final String NETWORK_CALLBACK_ACTION =
"ConnectivityManagerTest.NetworkCallbackAction";
- // Intent string to get the number of wifi CONNECTIVITY_ACTION callbacks the test app has seen
- public static final String GET_WIFI_CONNECTIVITY_ACTION_COUNT =
- "android.net.cts.appForApi23.getWifiConnectivityActionCount";
-
// device could have only one interface: data, wifi.
private static final int MIN_NUM_NETWORK_TYPES = 1;
@@ -294,6 +291,7 @@
* Tests that connections can be opened on WiFi and cellphone networks,
* and that they are made from different IP addresses.
*/
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public void testOpenConnection() throws Exception {
boolean canRunTest = mPackageManager.hasSystemFeature(FEATURE_WIFI)
&& mPackageManager.hasSystemFeature(FEATURE_TELEPHONY);
@@ -456,6 +454,7 @@
* WiFi. We could add a version that uses the telephony data connection but it's not clear
* that it would increase test coverage by much (how many devices have 3G radio but not Wifi?).
*/
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public void testRegisterNetworkCallback() {
if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
@@ -496,6 +495,7 @@
* {@link #testRegisterNetworkCallback} except that a {@code PendingIntent} is used instead
* of a {@code NetworkCallback}.
*/
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public void testRegisterNetworkCallback_withPendingIntent() {
if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
Log.i(TAG, "testRegisterNetworkCallback cannot execute unless device supports WiFi");
@@ -540,6 +540,7 @@
* Exercises the requestNetwork with NetworkCallback API. This checks to
* see if we get a callback for an INTERNET request.
*/
+ @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
public void testRequestNetworkCallback() {
final TestNetworkCallback callback = new TestNetworkCallback();
mCm.requestNetwork(new NetworkRequest.Builder()
@@ -562,6 +563,7 @@
* Exercises the requestNetwork with NetworkCallback API with timeout - expected to
* fail. Use WIFI and switch Wi-Fi off.
*/
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public void testRequestNetworkCallback_onUnavailable() {
final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
if (previousWifiEnabledState) {
@@ -587,93 +589,6 @@
}
}
- /**
- * Tests reporting of connectivity changed.
- */
- public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() {
- if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
- Log.i(TAG, "testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent cannot execute unless device supports WiFi");
- return;
- }
- ConnectivityReceiver.prepare();
-
- toggleWifi();
-
- // The connectivity broadcast has been sent; push through a terminal broadcast
- // to wait for in the receive to confirm it didn't see the connectivity change.
- Intent finalIntent = new Intent(ConnectivityReceiver.FINAL_ACTION);
- finalIntent.setClass(mContext, ConnectivityReceiver.class);
- mContext.sendBroadcast(finalIntent);
- assertFalse(ConnectivityReceiver.waitForBroadcast());
- }
-
- public void testConnectivityChanged_whenRegistered_shouldReceiveIntent() {
- if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
- Log.i(TAG, "testConnectivityChanged_whenRegistered_shouldReceiveIntent cannot execute unless device supports WiFi");
- return;
- }
- ConnectivityReceiver.prepare();
- ConnectivityReceiver receiver = new ConnectivityReceiver();
- IntentFilter filter = new IntentFilter();
- filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- mContext.registerReceiver(receiver, filter);
-
- toggleWifi();
- Intent finalIntent = new Intent(ConnectivityReceiver.FINAL_ACTION);
- finalIntent.setClass(mContext, ConnectivityReceiver.class);
- mContext.sendBroadcast(finalIntent);
-
- assertTrue(ConnectivityReceiver.waitForBroadcast());
- }
-
- public void testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent()
- throws InterruptedException {
- if (!mPackageManager.hasSystemFeature(FEATURE_WIFI)) {
- Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
- return;
- }
- 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();
-
- Intent getConnectivityCount = new Intent(GET_WIFI_CONNECTIVITY_ACTION_COUNT);
- assertEquals(2, sendOrderedBroadcastAndReturnResultCode(
- getConnectivityCount, SEND_BROADCAST_TIMEOUT));
- }
-
- private int sendOrderedBroadcastAndReturnResultCode(
- Intent intent, int timeoutMs) throws InterruptedException {
- final LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
- mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- result.offer(getResultCode());
- }
- }, null, 0, null, null);
-
- Integer resultCode = result.poll(timeoutMs, TimeUnit.MILLISECONDS);
- assertNotNull("Timed out (more than " + timeoutMs +
- " milliseconds) waiting for result code for broadcast", resultCode);
- return resultCode;
- }
-
- // Toggle WiFi twice, leaving it in the state it started in
- private void toggleWifi() {
- if (mWifiManager.isWifiEnabled()) {
- Network wifiNetwork = getWifiNetwork();
- disconnectFromWifi(wifiNetwork);
- connectToWifi();
- } else {
- connectToWifi();
- Network wifiNetwork = getWifiNetwork();
- disconnectFromWifi(wifiNetwork);
- }
- }
-
/** Enable WiFi and wait for it to become connected to a network. */
private Network connectToWifi() {
final TestNetworkCallback callback = new TestNetworkCallback();
@@ -705,6 +620,16 @@
return wifiNetwork;
}
+ private InetAddress getFirstV4Address(Network network) {
+ LinkProperties linkProperties = mCm.getLinkProperties(network);
+ for (InetAddress address : linkProperties.getAddresses()) {
+ if (address instanceof Inet4Address) {
+ return address;
+ }
+ }
+ return null;
+ }
+
private Socket getBoundSocket(Network network, String host, int port) throws IOException {
InetSocketAddress addr = new InetSocketAddress(host, port);
Socket s = network.getSocketFactory().createSocket();
@@ -899,6 +824,7 @@
}
/** Verify restricted networks cannot be requested. */
+ @AppModeFull(reason = "CHANGE_NETWORK_STATE permission can't be granted to instant apps")
public void testRestrictedNetworks() {
// Verify we can request unrestricted networks:
NetworkRequest request = new NetworkRequest.Builder()
@@ -1016,6 +942,7 @@
* Verify that getMultipathPreference does return appropriate values
* for metered and unmetered networks.
*/
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public void testGetMultipathPreference() throws Exception {
final ContentResolver resolver = mContext.getContentResolver();
final Network network = ensureWifiConnected();
@@ -1171,6 +1098,16 @@
return s;
}
+ private int getSupportedKeepalivesFromRes() throws Exception {
+ final Network network = ensureWifiConnected();
+ final NetworkCapabilities nc = mCm.getNetworkCapabilities(network);
+
+ // Get number of supported concurrent keepalives for testing network.
+ final int[] keepalivesPerTransport = KeepaliveUtils.getSupportedKeepalives(mContext);
+ return KeepaliveUtils.getSupportedKeepalivesForNetworkCapabilities(
+ keepalivesPerTransport, nc);
+ }
+
private boolean isKeepaliveSupported() throws Exception {
final Network network = ensureWifiConnected();
final Executor executor = mContext.getMainExecutor();
@@ -1207,6 +1144,7 @@
}
}
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public void testCreateTcpKeepalive() throws Exception {
adoptShellPermissionIdentity();
@@ -1278,4 +1216,150 @@
}
}
+
+ private int createConcurrentSocketKeepalives(int nattCount, int tcpCount) throws Exception {
+ // Use customization value in resource to prevent the need of privilege.
+ if (getSupportedKeepalivesFromRes() == 0) return 0;
+
+ final Network network = ensureWifiConnected();
+
+ final ArrayList<SocketKeepalive> kalist = new ArrayList<>();
+ final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
+ final Executor executor = mContext.getMainExecutor();
+
+ // Create concurrent TCP keepalives.
+ for (int i = 0; i < tcpCount; i++) {
+ // Assert that TCP connections can be established on wifi. The file descriptor of tcp
+ // sockets will be duplicated and kept valid in service side if the keepalives are
+ // successfully started.
+ try (Socket tcpSocket = getConnectedSocket(network, TEST_HOST, HTTP_PORT,
+ 0 /* Unused */, AF_INET)) {
+ final SocketKeepalive ka = mCm.createSocketKeepalive(network, tcpSocket, executor,
+ callback);
+ ka.start(MIN_KEEPALIVE_INTERVAL);
+ TestSocketKeepaliveCallback.CallbackValue cv = callback.pollCallback();
+ assertNotNull(cv);
+ if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_ERROR
+ && cv.error == SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES) {
+ // Limit reached.
+ break;
+ }
+ if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_STARTED) {
+ kalist.add(ka);
+ } else {
+ fail("Unexpected error when creating " + (i + 1) + " TCP keepalives: " + cv);
+ }
+ }
+ }
+
+ // Assert that a Nat-T socket can be created.
+ final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
+ final UdpEncapsulationSocket nattSocket = mIpSec.openUdpEncapsulationSocket();
+
+ final InetAddress srcAddr = getFirstV4Address(network);
+ final InetAddress dstAddr = getAddrByName(TEST_HOST, AF_INET);
+ assertNotNull(srcAddr);
+ assertNotNull(dstAddr);
+
+ // Test concurrent Nat-T keepalives.
+ for (int i = 0; i < nattCount; i++) {
+ final SocketKeepalive ka = mCm.createSocketKeepalive(network, nattSocket,
+ srcAddr, dstAddr, executor, callback);
+ ka.start(MIN_KEEPALIVE_INTERVAL);
+ TestSocketKeepaliveCallback.CallbackValue cv = callback.pollCallback();
+ assertNotNull(cv);
+ if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_ERROR
+ && cv.error == SocketKeepalive.ERROR_INSUFFICIENT_RESOURCES) {
+ // Limit reached.
+ break;
+ }
+ if (cv.callbackType == TestSocketKeepaliveCallback.CallbackType.ON_STARTED) {
+ kalist.add(ka);
+ } else {
+ fail("Unexpected error when creating " + (i + 1) + " Nat-T keepalives: " + cv);
+ }
+ }
+
+ final int ret = kalist.size();
+
+ // Clean up.
+ for (final SocketKeepalive ka : kalist) {
+ ka.stop();
+ callback.expectStopped();
+ }
+ kalist.clear();
+ nattSocket.close();
+
+ return ret;
+ }
+
+ /**
+ * Verifies that the concurrent keepalive slots meet the minimum requirement, and don't
+ * get leaked after iterations.
+ */
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ public void testSocketKeepaliveLimit() throws Exception {
+ adoptShellPermissionIdentity();
+
+ final int supported = getSupportedKeepalivesFromRes();
+
+ if (!isKeepaliveSupported()) {
+ // Sanity check.
+ assertEquals(0, supported);
+ return;
+ }
+
+ // Verifies that the supported keepalive slots meet MIN_SUPPORTED_KEEPALIVE_COUNT.
+ assertGreaterOrEqual(supported, KeepaliveUtils.MIN_SUPPORTED_KEEPALIVE_COUNT);
+
+ // Verifies that different types of keepalives can be established.
+ assertEquals(supported, createConcurrentSocketKeepalives(supported + 1, 0));
+ assertEquals(supported, createConcurrentSocketKeepalives(0, supported + 1));
+
+ // Verifies that different types can be established at the same time.
+ assertEquals(supported, createConcurrentSocketKeepalives(
+ supported / 2, supported - supported / 2));
+
+ // Verifies that keepalives don't get leaked in second round.
+ assertEquals(supported, createConcurrentSocketKeepalives(supported + 1, 0));
+ assertEquals(supported, createConcurrentSocketKeepalives(0, supported + 1));
+ assertEquals(supported, createConcurrentSocketKeepalives(
+ supported / 2, supported - supported / 2));
+
+ dropShellPermissionIdentity();
+ }
+
+ /**
+ * Verifies that the keepalive slots are limited as customized for unprivileged requests.
+ */
+ @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+ public void testSocketKeepaliveUnprivileged() throws Exception {
+ final int supported = getSupportedKeepalivesFromRes();
+
+ adoptShellPermissionIdentity();
+ if (!isKeepaliveSupported()) {
+ // Sanity check.
+ assertEquals(0, supported);
+ return;
+ }
+ dropShellPermissionIdentity();
+
+ final int allowedUnprivilegedPerUid = mContext.getResources().getInteger(
+ R.integer.config_allowedUnprivilegedKeepalivePerUid);
+ final int reservedPrivilegedSlots = mContext.getResources().getInteger(
+ R.integer.config_reservedPrivilegedKeepaliveSlots);
+ // Verifies that unprivileged request per uid cannot exceed the limit customized in the
+ // resource. Currently, unprivileged keepalive slots are limited to Nat-T only, this test
+ // does not apply to TCP.
+ assertGreaterOrEqual(supported, reservedPrivilegedSlots);
+ assertGreaterOrEqual(supported, allowedUnprivilegedPerUid);
+ final int expectedUnprivileged =
+ Math.min(allowedUnprivilegedPerUid, supported - reservedPrivilegedSlots);
+ assertEquals(expectedUnprivileged, createConcurrentSocketKeepalives(supported + 1, 0));
+ }
+
+ private static void assertGreaterOrEqual(long greater, long lesser) {
+ assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
+ greater >= lesser);
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
index 35d0f48..26049cc 100644
--- a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
@@ -23,16 +23,18 @@
import android.net.IpSecAlgorithm;
import android.net.IpSecManager;
import android.net.IpSecTransform;
+import android.platform.test.annotations.AppModeFull;
import android.system.Os;
import android.system.OsConstants;
-import android.test.AndroidTestCase;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
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;
@@ -42,7 +44,12 @@
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;
-public class IpSecBaseTest extends AndroidTestCase {
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class IpSecBaseTest {
private static final String TAG = IpSecBaseTest.class.getSimpleName();
@@ -69,11 +76,19 @@
protected ConnectivityManager mCM;
protected IpSecManager mISM;
+ protected Context mContext;
- protected void setUp() throws Exception {
- super.setUp();
- mISM = (IpSecManager) getContext().getSystemService(Context.IPSEC_SERVICE);
- mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mISM =
+ (IpSecManager)
+ InstrumentationRegistry.getContext()
+ .getSystemService(Context.IPSEC_SERVICE);
+ mCM =
+ (ConnectivityManager)
+ InstrumentationRegistry.getContext()
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
}
protected static byte[] getKey(int bitLength) {
@@ -195,6 +210,17 @@
public static class JavaUdpSocket implements GenericUdpSocket {
public final DatagramSocket mSocket;
+ public JavaUdpSocket(InetAddress localAddr, int port) {
+ try {
+ mSocket = new DatagramSocket(port, 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);
+ }
+ }
+
public JavaUdpSocket(InetAddress localAddr) {
try {
mSocket = new DatagramSocket(0, localAddr);
@@ -425,26 +451,25 @@
}
protected static IpSecTransform buildIpSecTransform(
- Context mContext,
+ Context context,
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));
+ new IpSecTransform.Builder(context)
+ .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);
+ return builder.buildTransportModeTransform(remoteAddr, spi);
}
private IpSecTransform buildDefaultTransform(InetAddress localAddr) throws Exception {
@@ -454,6 +479,8 @@
}
}
+ @Test
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testJavaTcpSocketPair() throws Exception {
for (String addr : LOOPBACK_ADDRS) {
InetAddress local = InetAddress.getByName(addr);
@@ -464,6 +491,8 @@
}
}
+ @Test
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testJavaUdpSocketPair() throws Exception {
for (String addr : LOOPBACK_ADDRS) {
InetAddress local = InetAddress.getByName(addr);
@@ -475,6 +504,8 @@
}
}
+ @Test
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testJavaUdpSocketPairUnconnected() throws Exception {
for (String addr : LOOPBACK_ADDRS) {
InetAddress local = InetAddress.getByName(addr);
@@ -486,6 +517,8 @@
}
}
+ @Test
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testNativeTcpSocketPair() throws Exception {
for (String addr : LOOPBACK_ADDRS) {
InetAddress local = InetAddress.getByName(addr);
@@ -497,6 +530,8 @@
}
}
+ @Test
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testNativeUdpSocketPair() throws Exception {
for (String addr : LOOPBACK_ADDRS) {
InetAddress local = InetAddress.getByName(addr);
@@ -508,6 +543,8 @@
}
}
+ @Test
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testNativeUdpSocketPairUnconnected() throws Exception {
for (String addr : LOOPBACK_ADDRS) {
InetAddress local = InetAddress.getByName(addr);
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
index 3387064..1241785 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -16,19 +16,33 @@
package android.net.cts;
+import static android.net.cts.PacketUtils.AES_CBC_BLK_SIZE;
+import static android.net.cts.PacketUtils.AES_CBC_IV_LEN;
+import static android.net.cts.PacketUtils.AES_GCM_BLK_SIZE;
+import static android.net.cts.PacketUtils.AES_GCM_IV_LEN;
+import static android.net.cts.PacketUtils.IP4_HDRLEN;
+import static android.net.cts.PacketUtils.IP6_HDRLEN;
+import static android.net.cts.PacketUtils.TCP_HDRLEN_WITH_TIMESTAMP_OPT;
+import static android.net.cts.PacketUtils.UDP_HDRLEN;
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.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import android.net.IpSecAlgorithm;
import android.net.IpSecManager;
import android.net.IpSecTransform;
import android.net.TrafficStats;
+import android.platform.test.annotations.AppModeFull;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import androidx.test.runner.AndroidJUnit4;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -37,6 +51,12 @@
import java.net.InetAddress;
import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@AppModeFull(reason = "Socket cannot bind in instant app mode")
public class IpSecManagerTest extends IpSecBaseTest {
private static final String TAG = IpSecManagerTest.class.getSimpleName();
@@ -53,18 +73,9 @@
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 {
+ @Before
+ @Override
+ public void setUp() throws Exception {
super.setUp();
}
@@ -74,6 +85,7 @@
* Realloc the same SPI that was specifically created (expect SpiUnavailable)
* Close SPIs
*/
+ @Test
public void testAllocSpi() throws Exception {
for (InetAddress addr : GOOGLE_DNS_LIST) {
IpSecManager.SecurityParameterIndex randomSpi = null, droidSpi = null;
@@ -225,6 +237,7 @@
* release transform
* send data (expect exception)
*/
+ @Test
public void testCreateTransform() throws Exception {
InetAddress localAddr = InetAddress.getByName(IPV4_LOOPBACK);
IpSecManager.SecurityParameterIndex spi =
@@ -432,19 +445,6 @@
}
}
- /** 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,
@@ -485,7 +485,7 @@
try (IpSecTransform transform =
transformBuilder.buildTransportModeTransform(local, spi)) {
if (protocol == IPPROTO_TCP) {
- transportHdrLen = TCP_HDRLEN_WITH_OPTIONS;
+ transportHdrLen = TCP_HDRLEN_WITH_TIMESTAMP_OPT;
checkTcp(transform, local, sendCount, useJavaSockets);
} else if (protocol == IPPROTO_UDP) {
transportHdrLen = UDP_HDRLEN;
@@ -522,7 +522,7 @@
int innerPacketSize = TEST_DATA.length + transportHdrLen + ipHdrLen;
int outerPacketSize =
- calculateEspPacketSize(
+ PacketUtils.calculateEspPacketSize(
TEST_DATA.length + transportHdrLen, ivLen, blkSize, truncLenBits)
+ udpEncapLen
+ ipHdrLen;
@@ -540,13 +540,13 @@
// Add TCP ACKs for data packets
if (protocol == IPPROTO_TCP) {
int encryptedTcpPktSize =
- calculateEspPacketSize(TCP_HDRLEN_WITH_OPTIONS, ivLen, blkSize, truncLenBits);
+ PacketUtils.calculateEspPacketSize(
+ TCP_HDRLEN_WITH_TIMESTAMP_OPT, ivLen, blkSize, truncLenBits);
-
- // Add data packet ACKs
- expectedOuterBytes += (encryptedTcpPktSize + udpEncapLen + ipHdrLen) * (sendCount);
- expectedInnerBytes += (TCP_HDRLEN_WITH_OPTIONS + ipHdrLen) * (sendCount);
- expectedPackets += sendCount;
+ // Add data packet ACKs
+ expectedOuterBytes += (encryptedTcpPktSize + udpEncapLen + ipHdrLen) * (sendCount);
+ expectedInnerBytes += (TCP_HDRLEN_WITH_TIMESTAMP_OPT + ipHdrLen) * (sendCount);
+ expectedPackets += sendCount;
}
StatsChecker.waitForNumPackets(expectedPackets);
@@ -607,6 +607,7 @@
}
}
+ @Test
public void testIkeOverUdpEncapSocket() throws Exception {
// IPv6 not supported for UDP-encap-ESP
InetAddress local = InetAddress.getByName(IPV4_LOOPBACK);
@@ -665,24 +666,28 @@
// checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, true, 1000);
// }
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
@@ -690,6 +695,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -697,6 +703,7 @@
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -704,6 +711,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -711,6 +719,7 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -718,6 +727,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -725,6 +735,7 @@
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -732,6 +743,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -739,6 +751,7 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -746,6 +759,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -753,6 +767,7 @@
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -760,6 +775,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -767,6 +783,7 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -774,6 +791,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -781,6 +799,7 @@
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -788,6 +807,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -795,6 +815,7 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -802,6 +823,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -809,6 +831,7 @@
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -816,6 +839,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
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);
@@ -823,6 +847,7 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, crypt, auth, null, false, 1, true);
}
+ @Test
public void testAesGcm64Tcp4() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
@@ -830,6 +855,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm64Tcp6() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
@@ -837,6 +863,7 @@
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm64Udp4() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
@@ -844,6 +871,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm64Udp6() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
@@ -851,6 +879,7 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm96Tcp4() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
@@ -858,6 +887,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm96Tcp6() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
@@ -865,6 +895,7 @@
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm96Udp4() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
@@ -872,6 +903,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm96Udp6() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
@@ -879,6 +911,7 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm128Tcp4() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
@@ -886,6 +919,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm128Tcp6() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
@@ -893,6 +927,7 @@
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm128Udp4() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
@@ -900,6 +935,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
public void testAesGcm128Udp6() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
@@ -907,6 +943,7 @@
checkTransform(IPPROTO_UDP, IPV6_LOOPBACK, null, null, authCrypt, false, 1, true);
}
+ @Test
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);
@@ -914,6 +951,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
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);
@@ -921,6 +959,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
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);
@@ -928,6 +967,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
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);
@@ -935,6 +975,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
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);
@@ -942,6 +983,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
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);
@@ -949,6 +991,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
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);
@@ -956,6 +999,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
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);
@@ -963,6 +1007,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
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);
@@ -970,6 +1015,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
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);
@@ -977,6 +1023,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, crypt, auth, null, true, 1, true);
}
+ @Test
public void testAesGcm64Tcp4UdpEncap() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
@@ -984,6 +1031,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
}
+ @Test
public void testAesGcm64Udp4UdpEncap() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
@@ -991,6 +1039,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
}
+ @Test
public void testAesGcm96Tcp4UdpEncap() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
@@ -998,6 +1047,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
}
+ @Test
public void testAesGcm96Udp4UdpEncap() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
@@ -1005,6 +1055,7 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
}
+ @Test
public void testAesGcm128Tcp4UdpEncap() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
@@ -1012,6 +1063,7 @@
checkTransform(IPPROTO_TCP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
}
+ @Test
public void testAesGcm128Udp4UdpEncap() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
@@ -1019,78 +1071,91 @@
checkTransform(IPPROTO_UDP, IPV4_LOOPBACK, null, null, authCrypt, true, 1, true);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
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);
}
+ @Test
public void testOpenUdpEncapSocketSpecificPort() throws Exception {
IpSecManager.UdpEncapsulationSocket encapSocket = null;
int port = -1;
@@ -1119,6 +1184,7 @@
assertTrue("Returned invalid port", encapSocket.getPort() == port);
}
+ @Test
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/IpSecManagerTunnelTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
index 5dc9b63..93638ac 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
@@ -16,161 +16,729 @@
package android.net.cts;
+import static android.app.AppOpsManager.OP_MANAGE_IPSEC_TUNNELS;
+import static android.net.IpSecManager.UdpEncapsulationSocket;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+import static android.net.cts.PacketUtils.AES_CBC_BLK_SIZE;
+import static android.net.cts.PacketUtils.AES_CBC_IV_LEN;
+import static android.net.cts.PacketUtils.BytePayload;
+import static android.net.cts.PacketUtils.EspHeader;
+import static android.net.cts.PacketUtils.IP4_HDRLEN;
+import static android.net.cts.PacketUtils.IP6_HDRLEN;
+import static android.net.cts.PacketUtils.Ip4Header;
+import static android.net.cts.PacketUtils.Ip6Header;
+import static android.net.cts.PacketUtils.IpHeader;
+import static android.net.cts.PacketUtils.UDP_HDRLEN;
+import static android.net.cts.PacketUtils.UdpHeader;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
import android.net.IpSecAlgorithm;
import android.net.IpSecManager;
import android.net.IpSecTransform;
+import android.net.LinkAddress;
import android.net.Network;
+import android.net.NetworkRequest;
+import android.net.TestNetworkInterface;
+import android.net.TestNetworkManager;
+import android.net.cts.PacketUtils.Payload;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemProperties;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.SystemUtil;
+import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
-import java.net.InterfaceAddress;
import java.net.NetworkInterface;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
public class IpSecManagerTunnelTest extends IpSecBaseTest {
-
private static final String TAG = IpSecManagerTunnelTest.class.getSimpleName();
- private static final int IP4_PREFIX_LEN = 24;
- private static final int IP6_PREFIX_LEN = 48;
- private static final InetAddress OUTER_ADDR4 = InetAddress.parseNumericAddress("192.0.2.0");
- private static final InetAddress OUTER_ADDR6 =
- InetAddress.parseNumericAddress("2001:db8:f00d::1");
- private static final InetAddress INNER_ADDR4 = InetAddress.parseNumericAddress("10.0.0.1");
- private static final InetAddress INNER_ADDR6 =
- InetAddress.parseNumericAddress("2001:db8:d00d::1");
- private Network mUnderlyingNetwork;
- private Network mIpSecNetwork;
+ private static final InetAddress LOCAL_OUTER_4 = InetAddress.parseNumericAddress("192.0.2.1");
+ private static final InetAddress REMOTE_OUTER_4 = InetAddress.parseNumericAddress("192.0.2.2");
+ private static final InetAddress LOCAL_OUTER_6 =
+ InetAddress.parseNumericAddress("2001:db8:1::1");
+ private static final InetAddress REMOTE_OUTER_6 =
+ InetAddress.parseNumericAddress("2001:db8:1::2");
- protected void setUp() throws Exception {
+ private static final InetAddress LOCAL_INNER_4 =
+ InetAddress.parseNumericAddress("198.51.100.1");
+ private static final InetAddress REMOTE_INNER_4 =
+ InetAddress.parseNumericAddress("198.51.100.2");
+ private static final InetAddress LOCAL_INNER_6 =
+ InetAddress.parseNumericAddress("2001:db8:2::1");
+ private static final InetAddress REMOTE_INNER_6 =
+ InetAddress.parseNumericAddress("2001:db8:2::2");
+
+ private static final int IP4_PREFIX_LEN = 32;
+ private static final int IP6_PREFIX_LEN = 128;
+
+ private static final int TIMEOUT_MS = 500;
+
+ // Static state to reduce setup/teardown
+ private static ConnectivityManager sCM;
+ private static TestNetworkManager sTNM;
+ private static ParcelFileDescriptor sTunFd;
+ private static TestNetworkCallback sTunNetworkCallback;
+ private static Network sTunNetwork;
+ private static TunUtils sTunUtils;
+
+ private static Context sContext = InstrumentationRegistry.getContext();
+ private static IBinder sBinder = new Binder();
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .adoptShellPermissionIdentity();
+ sCM = (ConnectivityManager) sContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ sTNM = (TestNetworkManager) sContext.getSystemService(Context.TEST_NETWORK_SERVICE);
+
+ // Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted, and
+ // a standard permission is insufficient. So we shell out the appop, to give us the
+ // right appop permissions.
+ setAppop(OP_MANAGE_IPSEC_TUNNELS, true);
+
+ TestNetworkInterface testIntf =
+ sTNM.createTunInterface(
+ new LinkAddress[] {
+ new LinkAddress(LOCAL_OUTER_4, IP4_PREFIX_LEN),
+ new LinkAddress(LOCAL_OUTER_6, IP6_PREFIX_LEN)
+ });
+
+ sTunFd = testIntf.getFileDescriptor();
+ sTunNetworkCallback = setupAndGetTestNetwork(testIntf.getInterfaceName());
+ sTunNetwork = sTunNetworkCallback.getNetworkBlocking();
+
+ sTunUtils = new TunUtils(sTunFd);
+ }
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
super.setUp();
+
+ // Set to true before every run; some tests flip this.
+ setAppop(OP_MANAGE_IPSEC_TUNNELS, true);
+
+ // Clear sTunUtils state
+ sTunUtils.reset();
}
- protected void tearDown() {
- setAppop(false);
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ setAppop(OP_MANAGE_IPSEC_TUNNELS, false);
+
+ sCM.unregisterNetworkCallback(sTunNetworkCallback);
+
+ sTNM.teardownTestNetwork(sTunNetwork);
+ sTunFd.close();
+
+ InstrumentationRegistry.getInstrumentation()
+ .getUiAutomation()
+ .dropShellPermissionIdentity();
}
- private void setAppop(boolean allow) {
- // Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted by the
- // telephony framework, and the only permission that is sufficient is NETWORK_STACK. So we
- // shell out the appop manager, to give us the right appop permissions.
- String cmd =
- "appops set "
- + mContext.getPackageName()
- + " MANAGE_IPSEC_TUNNELS "
- + (allow ? "allow" : "deny");
- SystemUtil.runShellCommand(cmd);
+ private static boolean hasTunnelsFeature() {
+ return sContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
+ || SystemProperties.getInt("ro.product.first_api_level", 0)
+ >= Build.VERSION_CODES.Q;
}
- public void testSecurityExceptionsCreateTunnelInterface() throws Exception {
+ private static void setAppop(int appop, boolean allow) {
+ String opName = AppOpsManager.opToName(appop);
+ for (String pkg : new String[] {"com.android.shell", sContext.getPackageName()}) {
+ String cmd =
+ String.format(
+ "appops set %s %s %s",
+ pkg, // Package name
+ opName, // Appop
+ (allow ? "allow" : "deny")); // Action
+ SystemUtil.runShellCommand(cmd);
+ }
+ }
+
+ private static TestNetworkCallback setupAndGetTestNetwork(String ifname) throws Exception {
+ // Build a network request
+ NetworkRequest nr =
+ new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_TEST)
+ .removeCapability(NET_CAPABILITY_TRUSTED)
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .setNetworkSpecifier(ifname)
+ .build();
+
+ TestNetworkCallback cb = new TestNetworkCallback();
+ sCM.requestNetwork(nr, cb);
+
+ // Setup the test network after network request is filed to prevent Network from being
+ // reaped due to no requests matching it.
+ sTNM.setupTestNetwork(ifname, sBinder);
+
+ return cb;
+ }
+
+ @Test
+ public void testSecurityExceptionCreateTunnelInterfaceWithoutAppop() throws Exception {
+ if (!hasTunnelsFeature()) return;
+
// Ensure we don't have the appop. Permission is not requested in the Manifest
- setAppop(false);
+ setAppop(OP_MANAGE_IPSEC_TUNNELS, false);
// Security exceptions are thrown regardless of IPv4/IPv6. Just test one
try {
- mISM.createIpSecTunnelInterface(OUTER_ADDR6, OUTER_ADDR6, mUnderlyingNetwork);
+ mISM.createIpSecTunnelInterface(LOCAL_INNER_6, REMOTE_INNER_6, sTunNetwork);
fail("Did not throw SecurityException for Tunnel creation without appop");
} catch (SecurityException expected) {
}
}
- public void testSecurityExceptionsBuildTunnelTransform() throws Exception {
+ @Test
+ public void testSecurityExceptionBuildTunnelTransformWithoutAppop() throws Exception {
+ if (!hasTunnelsFeature()) return;
+
// Ensure we don't have the appop. Permission is not requested in the Manifest
- setAppop(false);
+ setAppop(OP_MANAGE_IPSEC_TUNNELS, false);
// Security exceptions are thrown regardless of IPv4/IPv6. Just test one
try (IpSecManager.SecurityParameterIndex spi =
- mISM.allocateSecurityParameterIndex(OUTER_ADDR4);
+ mISM.allocateSecurityParameterIndex(LOCAL_INNER_4);
IpSecTransform transform =
- new IpSecTransform.Builder(mContext)
- .buildTunnelModeTransform(OUTER_ADDR4, spi)) {
+ new IpSecTransform.Builder(sContext)
+ .buildTunnelModeTransform(REMOTE_INNER_4, spi)) {
fail("Did not throw SecurityException for Transform creation without appop");
} catch (SecurityException expected) {
}
}
- private void checkTunnel(InetAddress inner, InetAddress outer, boolean useEncap)
+ /* Test runnables for callbacks after IPsec tunnels are set up. */
+ private interface TestRunnable {
+ void run(Network ipsecNetwork) throws Exception;
+ }
+
+ private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
+ private final CompletableFuture<Network> futureNetwork = new CompletableFuture<>();
+
+ @Override
+ public void onAvailable(Network network) {
+ futureNetwork.complete(network);
+ }
+
+ public Network getNetworkBlocking() throws Exception {
+ return futureNetwork.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private int getPacketSize(
+ int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode) {
+ int expectedPacketSize = TEST_DATA.length + UDP_HDRLEN;
+
+ // Inner Transport mode packet size
+ if (transportInTunnelMode) {
+ expectedPacketSize =
+ PacketUtils.calculateEspPacketSize(
+ expectedPacketSize,
+ AES_CBC_IV_LEN,
+ AES_CBC_BLK_SIZE,
+ AUTH_KEY.length * 4);
+ }
+
+ // Inner IP Header
+ expectedPacketSize += innerFamily == AF_INET ? IP4_HDRLEN : IP6_HDRLEN;
+
+ // Tunnel mode transform size
+ expectedPacketSize =
+ PacketUtils.calculateEspPacketSize(
+ expectedPacketSize, AES_CBC_IV_LEN, AES_CBC_BLK_SIZE, AUTH_KEY.length * 4);
+
+ // UDP encap size
+ expectedPacketSize += useEncap ? UDP_HDRLEN : 0;
+
+ // Outer IP Header
+ expectedPacketSize += outerFamily == AF_INET ? IP4_HDRLEN : IP6_HDRLEN;
+
+ return expectedPacketSize;
+ }
+
+ private interface TestRunnableFactory {
+ TestRunnable getTestRunnable(
+ boolean transportInTunnelMode,
+ int spi,
+ InetAddress localInner,
+ InetAddress remoteInner,
+ InetAddress localOuter,
+ InetAddress remoteOuter,
+ IpSecTransform inTransportTransform,
+ IpSecTransform outTransportTransform,
+ int encapPort,
+ int expectedPacketSize)
+ throws Exception;
+ }
+
+ private class OutputTestRunnableFactory implements TestRunnableFactory {
+ public TestRunnable getTestRunnable(
+ boolean transportInTunnelMode,
+ int spi,
+ InetAddress localInner,
+ InetAddress remoteInner,
+ InetAddress localOuter,
+ InetAddress remoteOuter,
+ IpSecTransform inTransportTransform,
+ IpSecTransform outTransportTransform,
+ int encapPort,
+ int expectedPacketSize) {
+ return new TestRunnable() {
+ @Override
+ public void run(Network ipsecNetwork) throws Exception {
+ // Build a socket and send traffic
+ JavaUdpSocket socket = new JavaUdpSocket(localInner);
+ ipsecNetwork.bindSocket(socket.mSocket);
+
+ // For Transport-In-Tunnel mode, apply transform to socket
+ if (transportInTunnelMode) {
+ mISM.applyTransportModeTransform(
+ socket.mSocket, IpSecManager.DIRECTION_IN, inTransportTransform);
+ mISM.applyTransportModeTransform(
+ socket.mSocket, IpSecManager.DIRECTION_OUT, outTransportTransform);
+ }
+
+ socket.sendTo(TEST_DATA, remoteInner, socket.getPort());
+
+ // Verify that an encrypted packet is sent. As of right now, checking encrypted
+ // body is not possible, due to our not knowing some of the fields of the
+ // inner IP header (flow label, flags, etc)
+ sTunUtils.awaitEspPacketNoPlaintext(
+ spi, TEST_DATA, encapPort != 0, expectedPacketSize);
+
+ socket.close();
+ }
+ };
+ }
+ }
+
+ private class InputPacketGeneratorTestRunnableFactory implements TestRunnableFactory {
+ public TestRunnable getTestRunnable(
+ boolean transportInTunnelMode,
+ int spi,
+ InetAddress localInner,
+ InetAddress remoteInner,
+ InetAddress localOuter,
+ InetAddress remoteOuter,
+ IpSecTransform inTransportTransform,
+ IpSecTransform outTransportTransform,
+ int encapPort,
+ int expectedPacketSize)
+ throws Exception {
+ return new TestRunnable() {
+ @Override
+ public void run(Network ipsecNetwork) throws Exception {
+ // Build a socket and receive traffic
+ JavaUdpSocket socket = new JavaUdpSocket(localInner);
+ // JavaUdpSocket socket = new JavaUdpSocket(localInner, socketPort.get());
+ ipsecNetwork.bindSocket(socket.mSocket);
+
+ // For Transport-In-Tunnel mode, apply transform to socket
+ if (transportInTunnelMode) {
+ mISM.applyTransportModeTransform(
+ socket.mSocket, IpSecManager.DIRECTION_IN, outTransportTransform);
+ mISM.applyTransportModeTransform(
+ socket.mSocket, IpSecManager.DIRECTION_OUT, inTransportTransform);
+ }
+
+ byte[] pkt;
+ if (transportInTunnelMode) {
+ pkt =
+ getTransportInTunnelModePacket(
+ spi,
+ spi,
+ remoteInner,
+ localInner,
+ remoteOuter,
+ localOuter,
+ socket.getPort(),
+ encapPort);
+ } else {
+ pkt =
+ getTunnelModePacket(
+ spi,
+ remoteInner,
+ localInner,
+ remoteOuter,
+ localOuter,
+ socket.getPort(),
+ encapPort);
+ }
+ sTunUtils.injectPacket(pkt);
+
+ // Receive packet from socket, and validate
+ receiveAndValidatePacket(socket);
+
+ socket.close();
+ }
+ };
+ }
+ }
+
+ private void checkTunnelOutput(
+ int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
throws Exception {
- setAppop(true);
- int innerPrefixLen = inner instanceof Inet6Address ? IP6_PREFIX_LEN : IP4_PREFIX_LEN;
+ checkTunnel(
+ innerFamily,
+ outerFamily,
+ useEncap,
+ transportInTunnelMode,
+ new OutputTestRunnableFactory());
+ }
- try (IpSecManager.SecurityParameterIndex spi = mISM.allocateSecurityParameterIndex(outer);
+ private void checkTunnelInput(
+ int innerFamily, int outerFamily, boolean useEncap, boolean transportInTunnelMode)
+ throws Exception {
+ checkTunnel(
+ innerFamily,
+ outerFamily,
+ useEncap,
+ transportInTunnelMode,
+ new InputPacketGeneratorTestRunnableFactory());
+ }
+
+ public void checkTunnel(
+ int innerFamily,
+ int outerFamily,
+ boolean useEncap,
+ boolean transportInTunnelMode,
+ TestRunnableFactory factory)
+ throws Exception {
+ if (!hasTunnelsFeature()) return;
+
+ InetAddress localInner = innerFamily == AF_INET ? LOCAL_INNER_4 : LOCAL_INNER_6;
+ InetAddress remoteInner = innerFamily == AF_INET ? REMOTE_INNER_4 : REMOTE_INNER_6;
+
+ InetAddress localOuter = outerFamily == AF_INET ? LOCAL_OUTER_4 : LOCAL_OUTER_6;
+ InetAddress remoteOuter = outerFamily == AF_INET ? REMOTE_OUTER_4 : REMOTE_OUTER_6;
+
+ // Preselect both SPI and encap port, to be used for both inbound and outbound tunnels.
+ // Re-uses the same SPI to ensure that even in cases of symmetric SPIs shared across tunnel
+ // and transport mode, packets are encrypted/decrypted properly based on the src/dst.
+ int spi = getRandomSpi(localOuter, remoteOuter);
+ int expectedPacketSize =
+ getPacketSize(innerFamily, outerFamily, useEncap, transportInTunnelMode);
+
+ try (IpSecManager.SecurityParameterIndex inTransportSpi =
+ mISM.allocateSecurityParameterIndex(localInner, spi);
+ IpSecManager.SecurityParameterIndex outTransportSpi =
+ mISM.allocateSecurityParameterIndex(remoteInner, spi);
+ IpSecTransform inTransportTransform =
+ buildIpSecTransform(sContext, inTransportSpi, null, remoteInner);
+ IpSecTransform outTransportTransform =
+ buildIpSecTransform(sContext, outTransportSpi, null, localInner);
+ UdpEncapsulationSocket encapSocket = mISM.openUdpEncapsulationSocket()) {
+
+ buildTunnelAndNetwork(
+ localInner,
+ remoteInner,
+ localOuter,
+ remoteOuter,
+ spi,
+ useEncap ? encapSocket : null,
+ factory.getTestRunnable(
+ transportInTunnelMode,
+ spi,
+ localInner,
+ remoteInner,
+ localOuter,
+ remoteOuter,
+ inTransportTransform,
+ outTransportTransform,
+ useEncap ? encapSocket.getPort() : 0,
+ expectedPacketSize));
+ }
+ }
+
+ private void buildTunnelAndNetwork(
+ InetAddress localInner,
+ InetAddress remoteInner,
+ InetAddress localOuter,
+ InetAddress remoteOuter,
+ int spi,
+ UdpEncapsulationSocket encapSocket,
+ TestRunnable test)
+ throws Exception {
+ int innerPrefixLen = localInner instanceof Inet6Address ? IP6_PREFIX_LEN : IP4_PREFIX_LEN;
+ TestNetworkCallback testNetworkCb = null;
+
+ try (IpSecManager.SecurityParameterIndex inSpi =
+ mISM.allocateSecurityParameterIndex(localOuter, spi);
+ IpSecManager.SecurityParameterIndex outSpi =
+ mISM.allocateSecurityParameterIndex(remoteOuter, spi);
IpSecManager.IpSecTunnelInterface tunnelIntf =
- mISM.createIpSecTunnelInterface(outer, outer, mCM.getActiveNetwork());
- IpSecManager.UdpEncapsulationSocket encapSocket =
- mISM.openUdpEncapsulationSocket()) {
+ mISM.createIpSecTunnelInterface(localOuter, remoteOuter, sTunNetwork)) {
+ // Build the test network
+ tunnelIntf.addAddress(localInner, innerPrefixLen);
+ testNetworkCb = setupAndGetTestNetwork(tunnelIntf.getInterfaceName());
+ Network testNetwork = testNetworkCb.getNetworkBlocking();
- IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(mContext);
+ // Check interface was created
+ NetworkInterface netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
+ assertNotNull(netIntf);
+
+ // Verify address was added
+ netIntf = NetworkInterface.getByInetAddress(localInner);
+ assertNotNull(netIntf);
+ assertEquals(tunnelIntf.getInterfaceName(), netIntf.getDisplayName());
+
+ // Configure Transform parameters
+ IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(sContext);
transformBuilder.setEncryption(
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
transformBuilder.setAuthentication(
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4));
- if (useEncap) {
+ if (encapSocket != null) {
transformBuilder.setIpv4Encapsulation(encapSocket, encapSocket.getPort());
}
- // Check transform application
- try (IpSecTransform transform = transformBuilder.buildTunnelModeTransform(outer, spi)) {
- mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_IN, transform);
- mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_OUT, transform);
+ // Apply transform and check that traffic is properly encrypted
+ try (IpSecTransform inTransform =
+ transformBuilder.buildTunnelModeTransform(remoteOuter, inSpi);
+ IpSecTransform outTransform =
+ transformBuilder.buildTunnelModeTransform(localOuter, outSpi)) {
+ mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_IN, inTransform);
+ mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_OUT, outTransform);
- // TODO: Test to ensure that send/receive works with these transforms.
+ test.run(testNetwork);
}
- // Check interface was created
- NetworkInterface netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
- assertNotNull(netIntf);
+ // Teardown the test network
+ sTNM.teardownTestNetwork(testNetwork);
- // Add addresses and check
- tunnelIntf.addAddress(inner, innerPrefixLen);
- for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) {
- assertEquals(intfAddr.getAddress(), inner);
- assertEquals(intfAddr.getNetworkPrefixLength(), innerPrefixLen);
- }
-
- // Remove addresses and check
- tunnelIntf.removeAddress(inner, innerPrefixLen);
- assertTrue(netIntf.getInterfaceAddresses().isEmpty());
+ // Remove addresses and check that interface is still present, but fails lookup-by-addr
+ tunnelIntf.removeAddress(localInner, innerPrefixLen);
+ assertNotNull(NetworkInterface.getByName(tunnelIntf.getInterfaceName()));
+ assertNull(NetworkInterface.getByInetAddress(localInner));
// Check interface was cleaned up
tunnelIntf.close();
- netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
- assertNull(netIntf);
+ assertNull(NetworkInterface.getByName(tunnelIntf.getInterfaceName()));
+ } finally {
+ if (testNetworkCb != null) {
+ sCM.unregisterNetworkCallback(testNetworkCb);
+ }
}
}
- /*
- * Create, add and remove addresses, then teardown tunnel
- */
+ private static void receiveAndValidatePacket(JavaUdpSocket socket) throws Exception {
+ byte[] socketResponseBytes = socket.receive();
+ assertArrayEquals(TEST_DATA, socketResponseBytes);
+ }
+
+ private int getRandomSpi(InetAddress localOuter, InetAddress remoteOuter) throws Exception {
+ // Try to allocate both in and out SPIs using the same requested SPI value.
+ try (IpSecManager.SecurityParameterIndex inSpi =
+ mISM.allocateSecurityParameterIndex(localOuter);
+ IpSecManager.SecurityParameterIndex outSpi =
+ mISM.allocateSecurityParameterIndex(remoteOuter, inSpi.getSpi()); ) {
+ return inSpi.getSpi();
+ }
+ }
+
+ private IpHeader getIpHeader(int protocol, InetAddress src, InetAddress dst, Payload payload) {
+ if ((src instanceof Inet6Address) != (dst instanceof Inet6Address)) {
+ throw new IllegalArgumentException("Invalid src/dst address combination");
+ }
+
+ if (src instanceof Inet6Address) {
+ return new Ip6Header(protocol, (Inet6Address) src, (Inet6Address) dst, payload);
+ } else {
+ return new Ip4Header(protocol, (Inet4Address) src, (Inet4Address) dst, payload);
+ }
+ }
+
+ private EspHeader buildTransportModeEspPacket(
+ int spi, InetAddress src, InetAddress dst, int port, Payload payload) throws Exception {
+ IpHeader preEspIpHeader = getIpHeader(payload.getProtocolId(), src, dst, payload);
+
+ return new EspHeader(
+ payload.getProtocolId(),
+ spi,
+ 1, // sequence number
+ CRYPT_KEY, // Same key for auth and crypt
+ payload.getPacketBytes(preEspIpHeader));
+ }
+
+ private EspHeader buildTunnelModeEspPacket(
+ int spi,
+ InetAddress srcInner,
+ InetAddress dstInner,
+ InetAddress srcOuter,
+ InetAddress dstOuter,
+ int port,
+ int encapPort,
+ Payload payload)
+ throws Exception {
+ IpHeader innerIp = getIpHeader(payload.getProtocolId(), srcInner, dstInner, payload);
+ return new EspHeader(
+ innerIp.getProtocolId(),
+ spi,
+ 1, // sequence number
+ CRYPT_KEY, // Same key for auth and crypt
+ innerIp.getPacketBytes());
+ }
+
+ private IpHeader maybeEncapPacket(
+ InetAddress src, InetAddress dst, int encapPort, EspHeader espPayload)
+ throws Exception {
+
+ Payload payload = espPayload;
+ if (encapPort != 0) {
+ payload = new UdpHeader(encapPort, encapPort, espPayload);
+ }
+
+ return getIpHeader(payload.getProtocolId(), src, dst, payload);
+ }
+
+ private byte[] getTunnelModePacket(
+ int spi,
+ InetAddress srcInner,
+ InetAddress dstInner,
+ InetAddress srcOuter,
+ InetAddress dstOuter,
+ int port,
+ int encapPort)
+ throws Exception {
+ UdpHeader udp = new UdpHeader(port, port, new BytePayload(TEST_DATA));
+
+ EspHeader espPayload =
+ buildTunnelModeEspPacket(
+ spi, srcInner, dstInner, srcOuter, dstOuter, port, encapPort, udp);
+ return maybeEncapPacket(srcOuter, dstOuter, encapPort, espPayload).getPacketBytes();
+ }
+
+ private byte[] getTransportInTunnelModePacket(
+ int spiInner,
+ int spiOuter,
+ InetAddress srcInner,
+ InetAddress dstInner,
+ InetAddress srcOuter,
+ InetAddress dstOuter,
+ int port,
+ int encapPort)
+ throws Exception {
+ UdpHeader udp = new UdpHeader(port, port, new BytePayload(TEST_DATA));
+
+ EspHeader espPayload = buildTransportModeEspPacket(spiInner, srcInner, dstInner, port, udp);
+ espPayload =
+ buildTunnelModeEspPacket(
+ spiOuter,
+ srcInner,
+ dstInner,
+ srcOuter,
+ dstOuter,
+ port,
+ encapPort,
+ espPayload);
+ return maybeEncapPacket(srcOuter, dstOuter, encapPort, espPayload).getPacketBytes();
+ }
+
+ // Transport-in-Tunnel mode tests
+ @Test
+ public void testTransportInTunnelModeV4InV4() throws Exception {
+ checkTunnelOutput(AF_INET, AF_INET, false, true);
+ checkTunnelInput(AF_INET, AF_INET, false, true);
+ }
+
+ @Test
+ public void testTransportInTunnelModeV4InV4UdpEncap() throws Exception {
+ checkTunnelOutput(AF_INET, AF_INET, true, true);
+ checkTunnelInput(AF_INET, AF_INET, true, true);
+ }
+
+ @Test
+ public void testTransportInTunnelModeV4InV6() throws Exception {
+ checkTunnelOutput(AF_INET, AF_INET6, false, true);
+ checkTunnelInput(AF_INET, AF_INET6, false, true);
+ }
+
+ @Test
+ public void testTransportInTunnelModeV6InV4() throws Exception {
+ checkTunnelOutput(AF_INET6, AF_INET, false, true);
+ checkTunnelInput(AF_INET6, AF_INET, false, true);
+ }
+
+ @Test
+ public void testTransportInTunnelModeV6InV4UdpEncap() throws Exception {
+ checkTunnelOutput(AF_INET6, AF_INET, true, true);
+ checkTunnelInput(AF_INET6, AF_INET, true, true);
+ }
+
+ @Test
+ public void testTransportInTunnelModeV6InV6() throws Exception {
+ checkTunnelOutput(AF_INET, AF_INET6, false, true);
+ checkTunnelInput(AF_INET, AF_INET6, false, true);
+ }
+
+ // Tunnel mode tests
+ @Test
public void testTunnelV4InV4() throws Exception {
- checkTunnel(INNER_ADDR4, OUTER_ADDR4, false);
+ checkTunnelOutput(AF_INET, AF_INET, false, false);
+ checkTunnelInput(AF_INET, AF_INET, false, false);
}
+ @Test
public void testTunnelV4InV4UdpEncap() throws Exception {
- checkTunnel(INNER_ADDR4, OUTER_ADDR4, true);
+ checkTunnelOutput(AF_INET, AF_INET, true, false);
+ checkTunnelInput(AF_INET, AF_INET, true, false);
}
+ @Test
public void testTunnelV4InV6() throws Exception {
- checkTunnel(INNER_ADDR4, OUTER_ADDR6, false);
+ checkTunnelOutput(AF_INET, AF_INET6, false, false);
+ checkTunnelInput(AF_INET, AF_INET6, false, false);
}
+ @Test
public void testTunnelV6InV4() throws Exception {
- checkTunnel(INNER_ADDR6, OUTER_ADDR4, false);
+ checkTunnelOutput(AF_INET6, AF_INET, false, false);
+ checkTunnelInput(AF_INET6, AF_INET, false, false);
}
+ @Test
public void testTunnelV6InV4UdpEncap() throws Exception {
- checkTunnel(INNER_ADDR6, OUTER_ADDR4, true);
+ checkTunnelOutput(AF_INET6, AF_INET, true, false);
+ checkTunnelInput(AF_INET6, AF_INET, true, false);
}
+ @Test
public void testTunnelV6InV6() throws Exception {
- checkTunnel(INNER_ADDR6, OUTER_ADDR6, false);
+ checkTunnelOutput(AF_INET6, AF_INET6, false, false);
+ checkTunnelInput(AF_INET6, AF_INET6, false, false);
}
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java b/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java
index e0c03a1..cb0cda8 100644
--- a/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkWatchlistTest.java
@@ -23,6 +23,7 @@
import android.content.Context;
import android.net.ConnectivityManager;
+import android.platform.test.annotations.AppModeFull;
import android.os.FileUtils;
import androidx.test.InstrumentationRegistry;
@@ -99,6 +100,7 @@
* returns the hash of config we set.
*/
@Test
+ @AppModeFull(reason = "Cannot access resource file in instant app mode")
public void testGetWatchlistConfigHash() throws Exception {
// Set watchlist config file for test
setWatchlistConfig(TEST_WATCHLIST_XML);
diff --git a/tests/cts/net/src/android/net/cts/PacketUtils.java b/tests/cts/net/src/android/net/cts/PacketUtils.java
new file mode 100644
index 0000000..6177827
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/PacketUtils.java
@@ -0,0 +1,460 @@
+/*
+ * 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.system.OsConstants.IPPROTO_IPV6;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+public class PacketUtils {
+ private static final String TAG = PacketUtils.class.getSimpleName();
+
+ private static final int DATA_BUFFER_LEN = 4096;
+
+ static final int IP4_HDRLEN = 20;
+ static final int IP6_HDRLEN = 40;
+ static final int UDP_HDRLEN = 8;
+ static final int TCP_HDRLEN = 20;
+ static final int TCP_HDRLEN_WITH_TIMESTAMP_OPT = TCP_HDRLEN + 12;
+
+ // Not defined in OsConstants
+ static final int IPPROTO_IPV4 = 4;
+ static final int IPPROTO_ESP = 50;
+
+ // Encryption parameters
+ static final int AES_GCM_IV_LEN = 8;
+ static final int AES_CBC_IV_LEN = 16;
+ static final int AES_GCM_BLK_SIZE = 4;
+ static final int AES_CBC_BLK_SIZE = 16;
+
+ // Encryption algorithms
+ static final String AES = "AES";
+ static final String AES_CBC = "AES/CBC/NoPadding";
+ static final String HMAC_SHA_256 = "HmacSHA256";
+
+ public interface Payload {
+ byte[] getPacketBytes(IpHeader header) throws Exception;
+
+ void addPacketBytes(IpHeader header, ByteBuffer resultBuffer) throws Exception;
+
+ short length();
+
+ int getProtocolId();
+ }
+
+ public abstract static class IpHeader {
+
+ public final byte proto;
+ public final InetAddress srcAddr;
+ public final InetAddress dstAddr;
+ public final Payload payload;
+
+ public IpHeader(int proto, InetAddress src, InetAddress dst, Payload payload) {
+ this.proto = (byte) proto;
+ this.srcAddr = src;
+ this.dstAddr = dst;
+ this.payload = payload;
+ }
+
+ public abstract byte[] getPacketBytes() throws Exception;
+
+ public abstract int getProtocolId();
+ }
+
+ public static class Ip4Header extends IpHeader {
+ private short checksum;
+
+ public Ip4Header(int proto, Inet4Address src, Inet4Address dst, Payload payload) {
+ super(proto, src, dst, payload);
+ }
+
+ public byte[] getPacketBytes() throws Exception {
+ ByteBuffer resultBuffer = buildHeader();
+ payload.addPacketBytes(this, resultBuffer);
+
+ return getByteArrayFromBuffer(resultBuffer);
+ }
+
+ public ByteBuffer buildHeader() {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ // Version, IHL
+ bb.put((byte) (0x45));
+
+ // DCSP, ECN
+ bb.put((byte) 0);
+
+ // Total Length
+ bb.putShort((short) (IP4_HDRLEN + payload.length()));
+
+ // Empty for Identification, Flags and Fragment Offset
+ bb.putShort((short) 0);
+ bb.put((byte) 0x40);
+ bb.put((byte) 0x00);
+
+ // TTL
+ bb.put((byte) 64);
+
+ // Protocol
+ bb.put(proto);
+
+ // Header Checksum
+ final int ipChecksumOffset = bb.position();
+ bb.putShort((short) 0);
+
+ // Src/Dst addresses
+ bb.put(srcAddr.getAddress());
+ bb.put(dstAddr.getAddress());
+
+ bb.putShort(ipChecksumOffset, calculateChecksum(bb));
+
+ return bb;
+ }
+
+ private short calculateChecksum(ByteBuffer bb) {
+ int checksum = 0;
+
+ // Calculate sum of 16-bit values, excluding checksum. IPv4 headers are always 32-bit
+ // aligned, so no special cases needed for unaligned values.
+ ShortBuffer shortBuffer = ByteBuffer.wrap(getByteArrayFromBuffer(bb)).asShortBuffer();
+ while (shortBuffer.hasRemaining()) {
+ short val = shortBuffer.get();
+
+ // Wrap as needed
+ checksum = addAndWrapForChecksum(checksum, val);
+ }
+
+ return onesComplement(checksum);
+ }
+
+ public int getProtocolId() {
+ return IPPROTO_IPV4;
+ }
+ }
+
+ public static class Ip6Header extends IpHeader {
+ public Ip6Header(int nextHeader, Inet6Address src, Inet6Address dst, Payload payload) {
+ super(nextHeader, src, dst, payload);
+ }
+
+ public byte[] getPacketBytes() throws Exception {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ // Version | Traffic Class (First 4 bits)
+ bb.put((byte) 0x60);
+
+ // Traffic class (Last 4 bits), Flow Label
+ bb.put((byte) 0);
+ bb.put((byte) 0);
+ bb.put((byte) 0);
+
+ // Payload Length
+ bb.putShort((short) payload.length());
+
+ // Next Header
+ bb.put(proto);
+
+ // Hop Limit
+ bb.put((byte) 64);
+
+ // Src/Dst addresses
+ bb.put(srcAddr.getAddress());
+ bb.put(dstAddr.getAddress());
+
+ // Payload
+ payload.addPacketBytes(this, bb);
+
+ return getByteArrayFromBuffer(bb);
+ }
+
+ public int getProtocolId() {
+ return IPPROTO_IPV6;
+ }
+ }
+
+ public static class BytePayload implements Payload {
+ public final byte[] payload;
+
+ public BytePayload(byte[] payload) {
+ this.payload = payload;
+ }
+
+ public int getProtocolId() {
+ return -1;
+ }
+
+ public byte[] getPacketBytes(IpHeader header) {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ addPacketBytes(header, bb);
+ return getByteArrayFromBuffer(bb);
+ }
+
+ public void addPacketBytes(IpHeader header, ByteBuffer resultBuffer) {
+ resultBuffer.put(payload);
+ }
+
+ public short length() {
+ return (short) payload.length;
+ }
+ }
+
+ public static class UdpHeader implements Payload {
+
+ public final short srcPort;
+ public final short dstPort;
+ public final Payload payload;
+
+ public UdpHeader(int srcPort, int dstPort, Payload payload) {
+ this.srcPort = (short) srcPort;
+ this.dstPort = (short) dstPort;
+ this.payload = payload;
+ }
+
+ public int getProtocolId() {
+ return IPPROTO_UDP;
+ }
+
+ public short length() {
+ return (short) (payload.length() + 8);
+ }
+
+ public byte[] getPacketBytes(IpHeader header) throws Exception {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ addPacketBytes(header, bb);
+ return getByteArrayFromBuffer(bb);
+ }
+
+ public void addPacketBytes(IpHeader header, ByteBuffer resultBuffer) throws Exception {
+ // Source, Destination port
+ resultBuffer.putShort(srcPort);
+ resultBuffer.putShort(dstPort);
+
+ // Payload Length
+ resultBuffer.putShort(length());
+
+ // Get payload bytes for checksum + payload
+ ByteBuffer payloadBuffer = ByteBuffer.allocate(DATA_BUFFER_LEN);
+ payload.addPacketBytes(header, payloadBuffer);
+ byte[] payloadBytes = getByteArrayFromBuffer(payloadBuffer);
+
+ // Checksum
+ resultBuffer.putShort(calculateChecksum(header, payloadBytes));
+
+ // Payload
+ resultBuffer.put(payloadBytes);
+ }
+
+ private short calculateChecksum(IpHeader header, byte[] payloadBytes) throws Exception {
+ int newChecksum = 0;
+ ShortBuffer srcBuffer = ByteBuffer.wrap(header.srcAddr.getAddress()).asShortBuffer();
+ ShortBuffer dstBuffer = ByteBuffer.wrap(header.dstAddr.getAddress()).asShortBuffer();
+
+ while (srcBuffer.hasRemaining() || dstBuffer.hasRemaining()) {
+ short val = srcBuffer.hasRemaining() ? srcBuffer.get() : dstBuffer.get();
+
+ // Wrap as needed
+ newChecksum = addAndWrapForChecksum(newChecksum, val);
+ }
+
+ // Add pseudo-header values. Proto is 0-padded, so just use the byte.
+ newChecksum = addAndWrapForChecksum(newChecksum, header.proto);
+ newChecksum = addAndWrapForChecksum(newChecksum, length());
+ newChecksum = addAndWrapForChecksum(newChecksum, srcPort);
+ newChecksum = addAndWrapForChecksum(newChecksum, dstPort);
+ newChecksum = addAndWrapForChecksum(newChecksum, length());
+
+ ShortBuffer payloadShortBuffer = ByteBuffer.wrap(payloadBytes).asShortBuffer();
+ while (payloadShortBuffer.hasRemaining()) {
+ newChecksum = addAndWrapForChecksum(newChecksum, payloadShortBuffer.get());
+ }
+ if (payload.length() % 2 != 0) {
+ newChecksum =
+ addAndWrapForChecksum(
+ newChecksum, (payloadBytes[payloadBytes.length - 1] << 8));
+ }
+
+ return onesComplement(newChecksum);
+ }
+ }
+
+ public static class EspHeader implements Payload {
+ public final int nextHeader;
+ public final int spi;
+ public final int seqNum;
+ public final byte[] key;
+ public final byte[] payload;
+
+ /**
+ * Generic constructor for ESP headers.
+ *
+ * <p>For Tunnel mode, payload will be a full IP header + attached payloads
+ *
+ * <p>For Transport mode, payload will be only the attached payloads, but with the checksum
+ * calculated using the pre-encryption IP header
+ */
+ public EspHeader(int nextHeader, int spi, int seqNum, byte[] key, byte[] payload) {
+ this.nextHeader = nextHeader;
+ this.spi = spi;
+ this.seqNum = seqNum;
+ this.key = key;
+ this.payload = payload;
+ }
+
+ public int getProtocolId() {
+ return IPPROTO_ESP;
+ }
+
+ public short length() {
+ // ALWAYS uses AES-CBC, HMAC-SHA256 (128b trunc len)
+ return (short)
+ calculateEspPacketSize(payload.length, AES_CBC_IV_LEN, AES_CBC_BLK_SIZE, 128);
+ }
+
+ public byte[] getPacketBytes(IpHeader header) throws Exception {
+ ByteBuffer bb = ByteBuffer.allocate(DATA_BUFFER_LEN);
+
+ addPacketBytes(header, bb);
+ return getByteArrayFromBuffer(bb);
+ }
+
+ public void addPacketBytes(IpHeader header, ByteBuffer resultBuffer) throws Exception {
+ ByteBuffer espPayloadBuffer = ByteBuffer.allocate(DATA_BUFFER_LEN);
+ espPayloadBuffer.putInt(spi);
+ espPayloadBuffer.putInt(seqNum);
+ espPayloadBuffer.put(getCiphertext(key));
+
+ espPayloadBuffer.put(getIcv(getByteArrayFromBuffer(espPayloadBuffer)), 0, 16);
+ resultBuffer.put(getByteArrayFromBuffer(espPayloadBuffer));
+ }
+
+ private byte[] getIcv(byte[] authenticatedSection) throws GeneralSecurityException {
+ Mac sha256HMAC = Mac.getInstance(HMAC_SHA_256);
+ SecretKeySpec authKey = new SecretKeySpec(key, HMAC_SHA_256);
+ sha256HMAC.init(authKey);
+
+ return sha256HMAC.doFinal(authenticatedSection);
+ }
+
+ /**
+ * Encrypts and builds ciphertext block. Includes the IV, Padding and Next-Header blocks
+ *
+ * <p>The ciphertext does NOT include the SPI/Sequence numbers, or the ICV.
+ */
+ private byte[] getCiphertext(byte[] key) throws GeneralSecurityException {
+ int paddedLen = calculateEspEncryptedLength(payload.length, AES_CBC_BLK_SIZE);
+ ByteBuffer paddedPayload = ByteBuffer.allocate(paddedLen);
+ paddedPayload.put(payload);
+
+ // Add padding - consecutive integers from 0x01
+ int pad = 1;
+ while (paddedPayload.position() < paddedPayload.limit()) {
+ paddedPayload.put((byte) pad++);
+ }
+
+ paddedPayload.position(paddedPayload.limit() - 2);
+ paddedPayload.put((byte) (paddedLen - 2 - payload.length)); // Pad length
+ paddedPayload.put((byte) nextHeader);
+
+ // Generate Initialization Vector
+ byte[] iv = new byte[AES_CBC_IV_LEN];
+ new SecureRandom().nextBytes(iv);
+ IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
+ SecretKeySpec secretKeySpec = new SecretKeySpec(key, AES);
+
+ // Encrypt payload
+ Cipher cipher = Cipher.getInstance(AES_CBC);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
+ byte[] encrypted = cipher.doFinal(getByteArrayFromBuffer(paddedPayload));
+
+ // Build ciphertext
+ ByteBuffer cipherText = ByteBuffer.allocate(AES_CBC_IV_LEN + encrypted.length);
+ cipherText.put(iv);
+ cipherText.put(encrypted);
+
+ return getByteArrayFromBuffer(cipherText);
+ }
+ }
+
+ private static int addAndWrapForChecksum(int currentChecksum, int value) {
+ currentChecksum += value & 0x0000ffff;
+
+ // Wrap anything beyond the first 16 bits, and add to lower order bits
+ return (currentChecksum >>> 16) + (currentChecksum & 0x0000ffff);
+ }
+
+ private static short onesComplement(int val) {
+ val = (val >>> 16) + (val & 0xffff);
+
+ if (val == 0) return 0;
+ return (short) ((~val) & 0xffff);
+ }
+
+ public static int calculateEspPacketSize(
+ int payloadLen, int cryptIvLength, int cryptBlockSize, int authTruncLen) {
+ final int ESP_HDRLEN = 4 + 4; // SPI + Seq#
+ final int ICV_LEN = authTruncLen / 8; // Auth trailer; based on truncation length
+ payloadLen += cryptIvLength; // Initialization Vector
+
+ // Align to block size of encryption algorithm
+ payloadLen = calculateEspEncryptedLength(payloadLen, cryptBlockSize);
+ return payloadLen + ESP_HDRLEN + ICV_LEN;
+ }
+
+ private static int calculateEspEncryptedLength(int payloadLen, int cryptBlockSize) {
+ payloadLen += 2; // ESP trailer
+
+ // Align to block size of encryption algorithm
+ return payloadLen + calculateEspPadLen(payloadLen, cryptBlockSize);
+ }
+
+ private static int calculateEspPadLen(int payloadLen, int cryptBlockSize) {
+ return (cryptBlockSize - (payloadLen % cryptBlockSize)) % cryptBlockSize;
+ }
+
+ private static byte[] getByteArrayFromBuffer(ByteBuffer buffer) {
+ return Arrays.copyOfRange(buffer.array(), 0, buffer.position());
+ }
+
+ /*
+ * Debug printing
+ */
+ private static final char[] hexArray = "0123456789ABCDEF".toCharArray();
+
+ public static String bytesToHex(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(hexArray[b >>> 4]);
+ sb.append(hexArray[b & 0x0F]);
+ sb.append(' ');
+ }
+ return sb.toString();
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java b/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
index 60ac226..5250450 100644
--- a/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
+++ b/tests/cts/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
@@ -24,6 +24,7 @@
import javax.net.ssl.SSLPeerUnverifiedException;
import android.net.SSLCertificateSocketFactory;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import libcore.javax.net.ssl.SSLConfigurationAsserts;
@@ -101,6 +102,7 @@
*
* NOTE: Test will fail if external server is not available.
*/
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void test_createSocket_simple() throws Exception {
try {
mFactory.createSocket(TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT);
@@ -117,6 +119,7 @@
*
* NOTE: Test will fail if external server is not available.
*/
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void test_createSocket_wrapping() throws Exception {
try {
Socket underlying = new Socket(TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT);
@@ -135,6 +138,7 @@
*
* NOTE: Test will fail if external server is not available.
*/
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void test_createSocket_bind() throws Exception {
try {
mFactory.createSocket(TEST_CREATE_SOCKET_HOST, TEST_CREATE_SOCKET_PORT, null, 0);
diff --git a/tests/cts/net/src/android/net/cts/TheaterModeTest.java b/tests/cts/net/src/android/net/cts/TheaterModeTest.java
index 10fca6f..d1ddeaa 100644
--- a/tests/cts/net/src/android/net/cts/TheaterModeTest.java
+++ b/tests/cts/net/src/android/net/cts/TheaterModeTest.java
@@ -18,12 +18,11 @@
import android.content.ContentResolver;
import android.content.Context;
+import android.platform.test.annotations.AppModeFull;
import android.provider.Settings;
import android.test.AndroidTestCase;
import android.util.Log;
-import java.lang.Thread;
-
public class TheaterModeTest extends AndroidTestCase {
private static final String TAG = "TheaterModeTest";
private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
@@ -40,6 +39,7 @@
|| mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI));
}
+ @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
public void testTheaterMode() {
setup();
if (!mHasFeature) {
diff --git a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
index 503ba51..5bd1e20 100755
--- a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
+++ b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
@@ -21,6 +21,7 @@
import android.net.TrafficStats;
import android.os.Process;
import android.os.SystemProperties;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -81,6 +82,7 @@
return packetCount * (20 + 32 + bytes);
}
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testTrafficStatsForLocalhost() throws IOException {
final long mobileTxPacketsBefore = TrafficStats.getMobileTxPackets();
final long mobileRxPacketsBefore = TrafficStats.getMobileRxPackets();
diff --git a/tests/cts/net/src/android/net/cts/TunUtils.java b/tests/cts/net/src/android/net/cts/TunUtils.java
new file mode 100644
index 0000000..a030713
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/TunUtils.java
@@ -0,0 +1,257 @@
+/*
+ * 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.cts.PacketUtils.IP4_HDRLEN;
+import static android.net.cts.PacketUtils.IP6_HDRLEN;
+import static android.net.cts.PacketUtils.IPPROTO_ESP;
+import static android.net.cts.PacketUtils.UDP_HDRLEN;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import android.os.ParcelFileDescriptor;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.function.Predicate;
+
+public class TunUtils {
+ private static final String TAG = TunUtils.class.getSimpleName();
+
+ private static final int DATA_BUFFER_LEN = 4096;
+ private static final int TIMEOUT = 100;
+
+ private static final int IP4_PROTO_OFFSET = 9;
+ private static final int IP6_PROTO_OFFSET = 6;
+
+ private static final int IP4_ADDR_OFFSET = 12;
+ private static final int IP4_ADDR_LEN = 4;
+ private static final int IP6_ADDR_OFFSET = 8;
+ private static final int IP6_ADDR_LEN = 16;
+
+ private final ParcelFileDescriptor mTunFd;
+ private final List<byte[]> mPackets = new ArrayList<>();
+ private final Thread mReaderThread;
+
+ public TunUtils(ParcelFileDescriptor tunFd) {
+ mTunFd = tunFd;
+
+ // Start background reader thread
+ mReaderThread =
+ new Thread(
+ () -> {
+ try {
+ // Loop will exit and thread will quit when tunFd is closed.
+ // Receiving either EOF or an exception will exit this reader loop.
+ // FileInputStream in uninterruptable, so there's no good way to
+ // ensure that this thread shuts down except upon FD closure.
+ while (true) {
+ byte[] intercepted = receiveFromTun();
+ if (intercepted == null) {
+ // Exit once we've hit EOF
+ return;
+ } else if (intercepted.length > 0) {
+ // Only save packet if we've received any bytes.
+ synchronized (mPackets) {
+ mPackets.add(intercepted);
+ mPackets.notifyAll();
+ }
+ }
+ }
+ } catch (IOException ignored) {
+ // Simply exit this reader thread
+ return;
+ }
+ });
+ mReaderThread.start();
+ }
+
+ private byte[] receiveFromTun() throws IOException {
+ FileInputStream in = new FileInputStream(mTunFd.getFileDescriptor());
+ byte[] inBytes = new byte[DATA_BUFFER_LEN];
+ int bytesRead = in.read(inBytes);
+
+ if (bytesRead < 0) {
+ return null; // return null for EOF
+ } else if (bytesRead >= DATA_BUFFER_LEN) {
+ throw new IllegalStateException("Too big packet. Fragmentation unsupported");
+ }
+ return Arrays.copyOf(inBytes, bytesRead);
+ }
+
+ private byte[] getFirstMatchingPacket(Predicate<byte[]> verifier, int startIndex) {
+ synchronized (mPackets) {
+ for (int i = startIndex; i < mPackets.size(); i++) {
+ byte[] pkt = mPackets.get(i);
+ if (verifier.test(pkt)) {
+ return pkt;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks if the specified bytes were ever sent in plaintext.
+ *
+ * <p>Only checks for known plaintext bytes to prevent triggering on ICMP/RA packets or the like
+ *
+ * @param plaintext the plaintext bytes to check for
+ * @param startIndex the index in the list to check for
+ */
+ public boolean hasPlaintextPacket(byte[] plaintext, int startIndex) {
+ Predicate<byte[]> verifier =
+ (pkt) -> {
+ return Collections.indexOfSubList(Arrays.asList(pkt), Arrays.asList(plaintext))
+ != -1;
+ };
+ return getFirstMatchingPacket(verifier, startIndex) != null;
+ }
+
+ public byte[] getEspPacket(int spi, boolean encap, int startIndex) {
+ return getFirstMatchingPacket(
+ (pkt) -> {
+ return isEsp(pkt, spi, encap);
+ },
+ startIndex);
+ }
+
+ public byte[] awaitEspPacketNoPlaintext(
+ int spi, byte[] plaintext, boolean useEncap, int expectedPacketSize) throws Exception {
+ long endTime = System.currentTimeMillis() + TIMEOUT;
+ int startIndex = 0;
+
+ synchronized (mPackets) {
+ while (System.currentTimeMillis() < endTime) {
+ byte[] espPkt = getEspPacket(spi, useEncap, startIndex);
+ if (espPkt != null) {
+ // Validate packet size
+ assertEquals(expectedPacketSize, espPkt.length);
+
+ // Always check plaintext from start
+ assertFalse(hasPlaintextPacket(plaintext, 0));
+ return espPkt; // We've found the packet we're looking for.
+ }
+
+ startIndex = mPackets.size();
+
+ // Try to prevent waiting too long. If waitTimeout <= 0, we've already hit timeout
+ long waitTimeout = endTime - System.currentTimeMillis();
+ if (waitTimeout > 0) {
+ mPackets.wait(waitTimeout);
+ }
+ }
+
+ fail("No such ESP packet found with SPI " + spi);
+ }
+ return null;
+ }
+
+ private static boolean isSpiEqual(byte[] pkt, int espOffset, int spi) {
+ // Check SPI byte by byte.
+ return pkt[espOffset] == (byte) ((spi >>> 24) & 0xff)
+ && pkt[espOffset + 1] == (byte) ((spi >>> 16) & 0xff)
+ && pkt[espOffset + 2] == (byte) ((spi >>> 8) & 0xff)
+ && pkt[espOffset + 3] == (byte) (spi & 0xff);
+ }
+
+ private static boolean isEsp(byte[] pkt, int spi, boolean encap) {
+ if (isIpv6(pkt)) {
+ // IPv6 UDP encap not supported by kernels; assume non-encap.
+ return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP6_HDRLEN, spi);
+ } else {
+ // Use default IPv4 header length (assuming no options)
+ if (encap) {
+ return pkt[IP4_PROTO_OFFSET] == IPPROTO_UDP
+ && isSpiEqual(pkt, IP4_HDRLEN + UDP_HDRLEN, spi);
+ } else {
+ return pkt[IP4_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP4_HDRLEN, spi);
+ }
+ }
+ }
+
+ private static boolean isIpv6(byte[] pkt) {
+ // First nibble shows IP version. 0x60 for IPv6
+ return (pkt[0] & (byte) 0xF0) == (byte) 0x60;
+ }
+
+ private static byte[] getReflectedPacket(byte[] pkt) {
+ byte[] reflected = Arrays.copyOf(pkt, pkt.length);
+
+ if (isIpv6(pkt)) {
+ // Set reflected packet's dst to that of the original's src
+ System.arraycopy(
+ pkt, // src
+ IP6_ADDR_OFFSET + IP6_ADDR_LEN, // src offset
+ reflected, // dst
+ IP6_ADDR_OFFSET, // dst offset
+ IP6_ADDR_LEN); // len
+ // Set reflected packet's src IP to that of the original's dst IP
+ System.arraycopy(
+ pkt, // src
+ IP6_ADDR_OFFSET, // src offset
+ reflected, // dst
+ IP6_ADDR_OFFSET + IP6_ADDR_LEN, // dst offset
+ IP6_ADDR_LEN); // len
+ } else {
+ // Set reflected packet's dst to that of the original's src
+ System.arraycopy(
+ pkt, // src
+ IP4_ADDR_OFFSET + IP4_ADDR_LEN, // src offset
+ reflected, // dst
+ IP4_ADDR_OFFSET, // dst offset
+ IP4_ADDR_LEN); // len
+ // Set reflected packet's src IP to that of the original's dst IP
+ System.arraycopy(
+ pkt, // src
+ IP4_ADDR_OFFSET, // src offset
+ reflected, // dst
+ IP4_ADDR_OFFSET + IP4_ADDR_LEN, // dst offset
+ IP4_ADDR_LEN); // len
+ }
+ return reflected;
+ }
+
+ /** Takes all captured packets, flips the src/dst, and re-injects them. */
+ public void reflectPackets() throws IOException {
+ synchronized (mPackets) {
+ for (byte[] pkt : mPackets) {
+ injectPacket(getReflectedPacket(pkt));
+ }
+ }
+ }
+
+ public void injectPacket(byte[] pkt) throws IOException {
+ FileOutputStream out = new FileOutputStream(mTunFd.getFileDescriptor());
+ out.write(pkt);
+ out.flush();
+ }
+
+ /** Resets the intercepted packets. */
+ public void reset() throws IOException {
+ synchronized (mPackets) {
+ mPackets.clear();
+ }
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/UrlQuerySanitizerTest.java b/tests/cts/net/src/android/net/cts/UrlQuerySanitizerTest.java
index 7076ea2..2d615bb 100644
--- a/tests/cts/net/src/android/net/cts/UrlQuerySanitizerTest.java
+++ b/tests/cts/net/src/android/net/cts/UrlQuerySanitizerTest.java
@@ -170,6 +170,12 @@
String initialPercentSign = "title=%B5";
assertEquals(expectedPlus, uqs.unescape(initialPlus));
assertEquals(expectedPercentSignHex, uqs.unescape(initialPercentSign));
+ String expectedPlusThenPercentSign = "Joe Random, User";
+ String plusThenPercentSign = "Joe+Random%2C%20User";
+ assertEquals(expectedPlusThenPercentSign, uqs.unescape(plusThenPercentSign));
+ String expectedPercentSignThenPlus = "Joe, Random User";
+ String percentSignThenPlus = "Joe%2C+Random+User";
+ assertEquals(expectedPercentSignThenPlus, uqs.unescape(percentSignThenPlus));
assertTrue(uqs.decodeHexDigit('0') >= 0);
assertTrue(uqs.decodeHexDigit('b') >= 0);
diff --git a/tests/cts/net/src/android/net/cts/VpnServiceTest.java b/tests/cts/net/src/android/net/cts/VpnServiceTest.java
index 8bdd7b0..15af23c 100644
--- a/tests/cts/net/src/android/net/cts/VpnServiceTest.java
+++ b/tests/cts/net/src/android/net/cts/VpnServiceTest.java
@@ -18,6 +18,7 @@
import android.content.Intent;
import android.net.VpnService;
import android.os.ParcelFileDescriptor;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import java.io.File;
@@ -35,6 +36,7 @@
private VpnService mVpnService = new VpnService();
+ @AppModeFull(reason = "PackageManager#queryIntentActivities cannot access in instant app mode")
public void testPrepare() throws Exception {
// Should never return null since we are not prepared.
Intent intent = VpnService.prepare(mContext);
@@ -60,6 +62,7 @@
}
}
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testProtect_DatagramSocket() throws Exception {
DatagramSocket socket = new DatagramSocket();
try {
@@ -88,6 +91,7 @@
}
}
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testProtect_int() throws Exception {
DatagramSocket socket = new DatagramSocket();
ParcelFileDescriptor descriptor = ParcelFileDescriptor.fromDatagramSocket(socket);
diff --git a/tests/cts/net/src/android/net/http/cts/ApacheHttpClientTest.java b/tests/cts/net/src/android/net/http/cts/ApacheHttpClientTest.java
index 2e5306d..8d7dff0 100644
--- a/tests/cts/net/src/android/net/http/cts/ApacheHttpClientTest.java
+++ b/tests/cts/net/src/android/net/http/cts/ApacheHttpClientTest.java
@@ -22,6 +22,7 @@
import org.apache.http.impl.client.DefaultHttpClient;
import android.net.Uri;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import android.webkit.cts.CtsTestServer;
@@ -30,6 +31,7 @@
import java.util.ArrayList;
import java.util.List;
+@AppModeFull(reason = "Socket cannot bind in instant app mode")
public class ApacheHttpClientTest extends AndroidTestCase {
private static final int NUM_DOWNLOADS = 20;
diff --git a/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java b/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java
index 198f973..354954e 100644
--- a/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java
+++ b/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java
@@ -22,6 +22,7 @@
import junit.framework.TestCase;
import android.net.http.HttpResponseCache;
+import android.platform.test.annotations.AppModeFull;
import com.android.compatibility.common.util.FileUtils;
@@ -126,6 +127,7 @@
* Make sure that statistics tracking are wired all the way through the
* wrapper class. http://code.google.com/p/android/issues/detail?id=25418
*/
+ @AppModeFull(reason = "Socket cannot bind in instant app mode")
public void testStatisticsTracking() throws Exception {
HttpResponseCache cache = HttpResponseCache.install(cacheDir, 10 * 1024 * 1024);
diff --git a/tests/cts/net/src/android/net/rtp/cts/AudioGroupTest.java b/tests/cts/net/src/android/net/rtp/cts/AudioGroupTest.java
index f06d7e9..1bd7fad 100644
--- a/tests/cts/net/src/android/net/rtp/cts/AudioGroupTest.java
+++ b/tests/cts/net/src/android/net/rtp/cts/AudioGroupTest.java
@@ -21,13 +21,14 @@
import android.net.rtp.AudioGroup;
import android.net.rtp.AudioStream;
import android.net.rtp.RtpStream;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
-import android.util.Log;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
+@AppModeFull(reason = "RtpStream cannot create in instant app mode")
public class AudioGroupTest extends AndroidTestCase {
private static final String TAG = AudioGroupTest.class.getSimpleName();
diff --git a/tests/cts/net/src/android/net/rtp/cts/AudioStreamTest.java b/tests/cts/net/src/android/net/rtp/cts/AudioStreamTest.java
index 323b022..f2db6ee 100644
--- a/tests/cts/net/src/android/net/rtp/cts/AudioStreamTest.java
+++ b/tests/cts/net/src/android/net/rtp/cts/AudioStreamTest.java
@@ -17,10 +17,12 @@
import android.net.rtp.AudioCodec;
import android.net.rtp.AudioStream;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import java.net.InetAddress;
+@AppModeFull(reason = "RtpStream cannot create in instant app mode")
public class AudioStreamTest extends AndroidTestCase {
private void testRtpStream(InetAddress address) throws Exception {
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 389621e..1901f02 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
@@ -40,6 +40,7 @@
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import com.android.compatibility.common.util.SystemUtil;
@@ -56,6 +57,7 @@
* Wi-Fi Aware CTS test suite: single device testing. Performs tests on a single
* device to validate Wi-Fi Aware.
*/
+@AppModeFull(reason = "Cannot get WifiAwareManager in instant app mode")
public class SingleDeviceTest extends AndroidTestCase {
private static final String TAG = "WifiAwareCtsTests";
diff --git a/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
index c80e372..628571c 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -32,6 +32,7 @@
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.WifiP2pManager;
import android.provider.Settings;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -45,6 +46,7 @@
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public class ConcurrencyTest extends AndroidTestCase {
private class MySync {
static final int WIFI_STATE = 0;
diff --git a/tests/cts/net/src/android/net/wifi/cts/MulticastLockTest.java b/tests/cts/net/src/android/net/wifi/cts/MulticastLockTest.java
index 54fe9c7..71f04a3 100644
--- a/tests/cts/net/src/android/net/wifi/cts/MulticastLockTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/MulticastLockTest.java
@@ -19,8 +19,10 @@
import android.content.Context;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.MulticastLock;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public class MulticastLockTest extends AndroidTestCase {
private static final String WIFI_TAG = "MulticastLockTest";
diff --git a/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java b/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java
index 2e2e75b..f2a2b48 100644
--- a/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -29,6 +30,7 @@
import java.util.List;
import java.util.ArrayList;
+@AppModeFull(reason = "Socket cannot bind in instant app mode")
public class NsdManagerTest extends AndroidTestCase {
private static final String TAG = "NsdManagerTest";
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 836df61..ccf5fe2 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
@@ -25,11 +25,13 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import android.util.Log;
import com.android.compatibility.common.util.SystemUtil;
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public class ScanResultTest extends AndroidTestCase {
private static class MySync {
int expectedState = STATE_NULL;
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiConfigurationTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiConfigurationTest.java
index 4480a24..a59c85e 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiConfigurationTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiConfigurationTest.java
@@ -21,8 +21,10 @@
import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public class WifiConfigurationTest extends AndroidTestCase {
private WifiManager mWifiManager;
@Override
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
index 0fce1ee..b592c10 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -22,6 +22,7 @@
import android.net.wifi.WifiEnterpriseConfig.Eap;
import android.net.wifi.WifiEnterpriseConfig.Phase2;
import android.net.wifi.WifiManager;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import com.android.compatibility.common.util.SystemUtil;
@@ -33,6 +34,7 @@
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public class WifiEnterpriseConfigTest extends AndroidTestCase {
private WifiManager mWifiManager;
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
index 77598ed..5367722 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
@@ -26,6 +26,7 @@
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
import android.net.wifi.WifiSsid;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
import com.android.compatibility.common.util.PollingCheck;
@@ -33,6 +34,7 @@
import java.util.concurrent.Callable;
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public class WifiInfoTest extends AndroidTestCase {
private static class MySync {
int expectedState = STATE_NULL;
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java
index 0703e60..6ac92d4 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java
@@ -19,8 +19,10 @@
import android.content.Context;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
+import android.platform.test.annotations.AppModeFull;
import android.test.AndroidTestCase;
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public class WifiLockTest extends AndroidTestCase {
private static final String WIFI_TAG = "WifiLockTest";
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 1d66682..d8c7dc8 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -36,9 +36,11 @@
import android.net.wifi.hotspot2.pps.HomeSp;
import android.os.Process;
import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
import android.provider.Settings;
import android.support.test.uiautomator.UiDevice;
import android.test.AndroidTestCase;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -56,6 +58,7 @@
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
public class WifiManagerTest extends AndroidTestCase {
private static class MySync {
int expectedState = STATE_NULL;
@@ -744,21 +747,16 @@
allowedUIDs.add(Process.SHELL_UID);
allowedUIDs.add(Process.PHONE_UID);
allowedUIDs.add(Process.NETWORK_STACK_UID);
+ allowedUIDs.add(Process.NFC_UID);
// only quick settings is allowed to bind to the BIND_QUICK_SETTINGS_TILE permission, using
- // this fact to determined allowed package name for sysui
- String validPkg = "";
- final List<PackageInfo> sysuiPackage = pm.getPackagesHoldingPermissions(new String[] {
+ // this fact to determined allowed package name for sysui. This is a signature permission,
+ // so allow any package with this permission.
+ final List<PackageInfo> sysuiPackages = pm.getPackagesHoldingPermissions(new String[] {
android.Manifest.permission.BIND_QUICK_SETTINGS_TILE
}, PackageManager.MATCH_UNINSTALLED_PACKAGES);
-
- if (sysuiPackage.size() > 1) {
- fail("The BIND_QUICK_SETTINGS_TILE permission must only be held by one package");
- }
-
- if (sysuiPackage.size() == 1) {
- validPkg = sysuiPackage.get(0).packageName;
- allowedPackages.add(validPkg);
+ for (PackageInfo info : sysuiPackages) {
+ allowedPackages.add(info.packageName);
}
// the captive portal flow also currently holds the NETWORK_SETTINGS permission
@@ -801,6 +799,8 @@
* functionality. The permission is intended to be granted to only the device setup wizard.
*/
public void testNetworkSetupWizardPermission() {
+ final ArraySet<String> allowedPackages = new ArraySet();
+
final PackageManager pm = getContext().getPackageManager();
final Intent intent = new Intent(Intent.ACTION_MAIN);
@@ -808,16 +808,39 @@
final ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DISABLED_COMPONENTS);
String validPkg = "";
if (ri != null) {
+ allowedPackages.add(ri.activityInfo.packageName);
validPkg = ri.activityInfo.packageName;
}
- final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
- android.Manifest.permission.NETWORK_SETUP_WIZARD
+ final Intent preIntent = new Intent("com.android.setupwizard.OEM_PRE_SETUP");
+ preIntent.addCategory(Intent.CATEGORY_DEFAULT);
+ final ResolveInfo preRi = pm
+ .resolveActivity(preIntent, PackageManager.MATCH_DISABLED_COMPONENTS);
+ String prePackageName = "";
+ if (null != preRi) {
+ prePackageName = preRi.activityInfo.packageName;
+ }
+
+ final Intent postIntent = new Intent("com.android.setupwizard.OEM_POST_SETUP");
+ postIntent.addCategory(Intent.CATEGORY_DEFAULT);
+ final ResolveInfo postRi = pm
+ .resolveActivity(postIntent, PackageManager.MATCH_DISABLED_COMPONENTS);
+ String postPackageName = "";
+ if (null != postRi) {
+ postPackageName = postRi.activityInfo.packageName;
+ }
+ if (!TextUtils.isEmpty(prePackageName) && !TextUtils.isEmpty(postPackageName)
+ && prePackageName.equals(postPackageName)) {
+ allowedPackages.add(prePackageName);
+ }
+
+ final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[]{
+ android.Manifest.permission.NETWORK_SETUP_WIZARD
}, PackageManager.MATCH_UNINSTALLED_PACKAGES);
for (PackageInfo pi : holding) {
- if (!Objects.equals(pi.packageName, validPkg)) {
+ if (!allowedPackages.contains(pi.packageName)) {
fail("The NETWORK_SETUP_WIZARD permission must not be held by " + pi.packageName
- + " and must be revoked for security reasons [" + validPkg +"]");
+ + " and must be revoked for security reasons [" + validPkg + "]");
}
}
}
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
index 20791c0..f71cadb 100644
--- a/tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java
+++ b/tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java
@@ -19,6 +19,7 @@
import android.net.wifi.ScanResult;
import android.net.wifi.rtt.RangingRequest;
import android.net.wifi.rtt.RangingResult;
+import android.platform.test.annotations.AppModeFull;
import com.android.compatibility.common.util.DeviceReportLog;
import com.android.compatibility.common.util.ResultType;
@@ -31,6 +32,7 @@
/**
* Wi-Fi RTT CTS test: range to all available Access Points which support IEEE 802.11mc.
*/
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
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;