Merge changes from topic "asb-2018-04-m" into marshmallow-cts-dev
am: 7f0c5e7da8
Change-Id: I1db217fe93ba600f5a732aa8418705f2133af020
diff --git a/tests/cts/hostside/Android.mk b/tests/cts/hostside/Android.mk
index 6637d61..ad97ecd 100644
--- a/tests/cts/hostside/Android.mk
+++ b/tests/cts/hostside/Android.mk
@@ -23,8 +23,13 @@
LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES := cts-migration-lib
+
LOCAL_CTS_TEST_PACKAGE := android.net.hostsidenetwork
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
include $(BUILD_CTS_HOST_JAVA_LIBRARY)
# Build the test APKs using their own makefiles
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
new file mode 100644
index 0000000..9945805
--- /dev/null
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 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 host test cases">
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="CtsHostsideNetworkTests.jar" />
+ <option name="runtime-hint" value="3m56s" />
+ </test>
+</configuration>
diff --git a/tests/cts/hostside/aidl/Android.mk b/tests/cts/hostside/aidl/Android.mk
new file mode 100644
index 0000000..a7ec6ef
--- /dev/null
+++ b/tests/cts/hostside/aidl/Android.mk
@@ -0,0 +1,22 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := com/android/cts/net/hostside/IRemoteSocketFactory.aidl
+LOCAL_MODULE := CtsHostsideNetworkTestsAidl
+include $(BUILD_JAVA_LIBRARY)
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl
new file mode 100644
index 0000000..68176ad
--- /dev/null
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.os.ParcelFileDescriptor;
+
+interface IRemoteSocketFactory {
+ ParcelFileDescriptor openSocketFd(String host, int port, int timeoutMs);
+ String getPackageName();
+ int getUid();
+}
diff --git a/tests/cts/hostside/app/Android.mk b/tests/cts/hostside/app/Android.mk
index 055287a..9519ec5 100644
--- a/tests/cts/hostside/app/Android.mk
+++ b/tests/cts/hostside/app/Android.mk
@@ -20,7 +20,8 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ub-uiautomator \
+ CtsHostsideNetworkTestsAidl
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -29,4 +30,7 @@
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/cts/hostside/app/AndroidManifest.xml b/tests/cts/hostside/app/AndroidManifest.xml
index cdde7dc..0598a3b 100644
--- a/tests/cts/hostside/app/AndroidManifest.xml
+++ b/tests/cts/hostside/app/AndroidManifest.xml
@@ -17,8 +17,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.net.hostside">
- <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.INTERNET"/>
<application>
<uses-library android:name="android.test.runner" />
@@ -29,6 +31,14 @@
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
+ <service
+ android:name=".MyNotificationListenerService"
+ android:label="MyNotificationListenerService"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService" />
+ </intent-filter>
+ </service>
</application>
<instrumentation
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
new file mode 100644
index 0000000..fb773cb
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.util.Log;
+
+/**
+ * Base class for metered and non-metered tests on idle apps.
+ */
+abstract class AbstractAppIdleTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+ @Override
+ protected final void setUp() throws Exception {
+ super.setUp();
+
+ if (!isSupported()) return;
+
+ // Set initial state.
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ setAppIdle(false);
+ turnBatteryOff();
+
+ registerBroadcastReceiver();
+ }
+
+ @Override
+ protected final void tearDown() throws Exception {
+ super.tearDown();
+
+ if (!isSupported()) return;
+
+ try {
+ tearDownMeteredNetwork();
+ } finally {
+ turnBatteryOn();
+ setAppIdle(false);
+ }
+ }
+
+ @Override
+ protected boolean isSupported() throws Exception {
+ boolean supported = isDozeModeEnabled();
+ if (!supported) {
+ Log.i(TAG, "Skipping " + getClass() + "." + getName()
+ + "() because device does not support Doze Mode");
+ }
+ return supported;
+ }
+
+ /**
+ * Resets the (non) metered network state.
+ *
+ * <p>By default is empty - it's up to subclasses to override.
+ */
+ protected void tearDownMeteredNetwork() throws Exception {
+ }
+
+ public void testBackgroundNetworkAccess_enabled() throws Exception {
+ if (!isSupported()) return;
+
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ // Make sure foreground app doesn't lose access upon enabling it.
+ setAppIdle(true);
+ launchActivity();
+ assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
+ assertForegroundNetworkAccess();
+ finishActivity();
+ assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
+ assertBackgroundNetworkAccess(true);
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ // Same for foreground service.
+ setAppIdle(true);
+ startForegroundService();
+ assertAppIdle(true); // Sanity check - still idle
+ assertForegroundServiceNetworkAccess();
+ stopForegroundService();
+ assertAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+ }
+
+ public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+ if (!isSupported()) return;
+
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertAppIdle(false); // Sanity check - not idle anymore, since whitelisted
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertAppIdle(true); // Sanity check - idle again, once whitelisted was removed
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+
+ // Sanity check - no whitelist, no access!
+ setAppIdle(true);
+ assertBackgroundNetworkAccess(false);
+ }
+
+ public void testBackgroundNetworkAccess_disabled() throws Exception {
+ if (!isSupported()) return;
+
+ assertBackgroundNetworkAccess(true);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
new file mode 100644
index 0000000..ed738a6
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.util.Log;
+
+/**
+ * Base class for metered and non-metered Battery Saver Mode tests.
+ */
+abstract class AbstractBatterySaverModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+ @Override
+ protected final void setUp() throws Exception {
+ super.setUp();
+
+ if (!isSupported()) return;
+
+ // Set initial state.
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ setBatterySaverMode(false);
+
+ registerBroadcastReceiver();
+ }
+
+ @Override
+ protected final void tearDown() throws Exception {
+ super.tearDown();
+
+ if (!isSupported()) return;
+
+ try {
+ tearDownMeteredNetwork();
+ } finally {
+ setBatterySaverMode(false);
+ }
+ }
+
+ @Override
+ protected boolean isSupported() throws Exception {
+ boolean supported = isDozeModeEnabled();
+ if (!supported) {
+ Log.i(TAG, "Skipping " + getClass() + "." + getName()
+ + "() because device does not support Doze Mode");
+ }
+ return supported;
+ }
+
+ /**
+ * Sets the initial (non) metered network state.
+ *
+ * <p>By default is empty - it's up to subclasses to override.
+ */
+ protected void setUpMeteredNetwork() throws Exception {
+ }
+
+ /**
+ * Resets the (non) metered network state.
+ *
+ * <p>By default is empty - it's up to subclasses to override.
+ */
+ protected void tearDownMeteredNetwork() throws Exception {
+ }
+
+ public void testBackgroundNetworkAccess_enabled() throws Exception {
+ if (!isSupported()) return;
+
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ // Make sure foreground app doesn't lose access upon enabling it.
+ setBatterySaverMode(false);
+ launchActivity();
+ assertForegroundNetworkAccess();
+ setBatterySaverMode(true);
+ assertForegroundNetworkAccess();
+ finishActivity();
+ assertBackgroundNetworkAccess(false);
+
+ // Same for foreground service.
+ setBatterySaverMode(false);
+ startForegroundService();
+ assertForegroundNetworkAccess();
+ setBatterySaverMode(true);
+ assertForegroundNetworkAccess();
+ stopForegroundService();
+ assertBackgroundNetworkAccess(false);
+ }
+
+ public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+ if (!isSupported()) return;
+
+ setBatterySaverMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ }
+
+ public void testBackgroundNetworkAccess_disabled() throws Exception {
+ if (!isSupported()) return;
+
+ assertBackgroundNetworkAccess(true);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
new file mode 100644
index 0000000..cc05b04
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+/**
+ * Base class for metered and non-metered Doze Mode tests.
+ */
+abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+ @Override
+ protected final void setUp() throws Exception {
+ super.setUp();
+
+ if (!isSupported()) return;
+
+ // Set initial state.
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ setDozeMode(false);
+
+ registerBroadcastReceiver();
+ }
+
+ @Override
+ protected final void tearDown() throws Exception {
+ super.tearDown();
+
+ if (!isSupported()) return;
+
+ try {
+ tearDownMeteredNetwork();
+ } finally {
+ setDozeMode(false);
+ }
+ }
+
+ @Override
+ protected boolean isSupported() throws Exception {
+ boolean supported = isDozeModeEnabled();
+ if (!supported) {
+ Log.i(TAG, "Skipping " + getClass() + "." + getName()
+ + "() because device does not support Doze Mode");
+ }
+ return supported;
+ }
+
+ /**
+ * Sets the initial (non) metered network state.
+ *
+ * <p>By default is empty - it's up to subclasses to override.
+ */
+ protected void setUpMeteredNetwork() throws Exception {
+ }
+
+ /**
+ * Resets the (non) metered network state.
+ *
+ * <p>By default is empty - it's up to subclasses to override.
+ */
+ protected void tearDownMeteredNetwork() throws Exception {
+ }
+
+ public void testBackgroundNetworkAccess_enabled() throws Exception {
+ if (!isSupported()) return;
+
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ // Make sure foreground service doesn't lose network access upon enabling doze.
+ setDozeMode(false);
+ startForegroundService();
+ assertForegroundNetworkAccess();
+ setDozeMode(true);
+ assertForegroundNetworkAccess();
+ stopForegroundService();
+ assertBackgroundState();
+ assertBackgroundNetworkAccess(false);
+ }
+
+ public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+ if (!isSupported()) return;
+
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ }
+
+ public void testBackgroundNetworkAccess_disabled() throws Exception {
+ if (!isSupported()) return;
+
+ assertBackgroundNetworkAccess(true);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ }
+
+ public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
+ throws Exception {
+ if (!isSupported()) return;
+
+ setPendingIntentWhitelistDuration(NETWORK_TIMEOUT_MS);
+ try {
+ registerNotificationListenerService();
+ setDozeMode(true);
+ assertBackgroundNetworkAccess(false);
+
+ sendNotification(42);
+ assertBackgroundNetworkAccess(true);
+ // Make sure access is disabled after it expires
+ SystemClock.sleep(NETWORK_TIMEOUT_MS);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ resetDeviceIdleSettings();
+ }
+ }
+
+ // Must override so it only tests foreground service - once an app goes to foreground, device
+ // leaves Doze Mode.
+ @Override
+ protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception {
+ startForegroundService();
+ assertForegroundServiceNetworkAccess();
+ stopForegroundService();
+ assertBackgroundState();
+ }
+}
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
new file mode 100644
index 0000000..aad7561
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static android.cts.util.SystemUtil.runShellCommand;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import android.app.Instrumentation;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
+import android.net.wifi.WifiManager;
+import android.os.SystemClock;
+import android.service.notification.NotificationListenerService;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+/**
+ * Superclass for tests related to background network restrictions.
+ */
+abstract class AbstractRestrictBackgroundNetworkTestCase extends InstrumentationTestCase {
+ protected static final String TAG = "RestrictBackgroundNetworkTests";
+
+ protected static final String TEST_PKG = "com.android.cts.net.hostside";
+ protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
+
+ private static final int SLEEP_TIME_SEC = 1;
+ private static final boolean DEBUG = true;
+
+ // Constants below must match values defined on app2's Common.java
+ private static final String MANIFEST_RECEIVER = "ManifestReceiver";
+ private static final String DYNAMIC_RECEIVER = "DynamicReceiver";
+ private static final String ACTION_GET_COUNTERS =
+ "com.android.cts.net.hostside.app2.action.GET_COUNTERS";
+ private static final String ACTION_GET_RESTRICT_BACKGROUND_STATUS =
+ "com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS";
+ private static final String ACTION_CHECK_NETWORK =
+ "com.android.cts.net.hostside.app2.action.CHECK_NETWORK";
+ private static final String ACTION_RECEIVER_READY =
+ "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
+ static final String ACTION_SEND_NOTIFICATION =
+ "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+ private static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
+ private static final String EXTRA_RECEIVER_NAME =
+ "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
+ private static final String EXTRA_NOTIFICATION_ID =
+ "com.android.cts.net.hostside.app2.extra.NOTIFICATION_ID";
+ private static final String NETWORK_STATUS_SEPARATOR = "\\|";
+ private static final int SECOND_IN_MS = 1000;
+ static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
+ private static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
+ private static final int PROCESS_STATE_TOP = 2;
+
+
+ // Must be higher than NETWORK_TIMEOUT_MS
+ private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4;
+
+ protected Context mContext;
+ protected Instrumentation mInstrumentation;
+ protected ConnectivityManager mCm;
+ protected WifiManager mWfm;
+ protected int mUid;
+ private int mMyUid;
+ private String mMeteredWifi;
+ private boolean mSupported;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mInstrumentation = getInstrumentation();
+ mContext = mInstrumentation.getContext();
+ mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mWfm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mUid = getUid(TEST_APP2_PKG);
+ mMyUid = getUid(mContext.getPackageName());
+ mSupported = setUpActiveNetworkMeteringState();
+
+ Log.i(TAG, "Apps status on " + getName() + ":\n"
+ + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n"
+ + "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
+ }
+
+ protected int getUid(String packageName) throws Exception {
+ return mContext.getPackageManager().getPackageUid(packageName, 0);
+ }
+
+ protected void assertRestrictBackgroundChangedReceived(int expectedCount) throws Exception {
+ assertRestrictBackgroundChangedReceived(DYNAMIC_RECEIVER, expectedCount);
+ assertRestrictBackgroundChangedReceived(MANIFEST_RECEIVER, 0);
+ }
+
+ protected void assertRestrictBackgroundChangedReceived(String receiverName, int expectedCount)
+ throws Exception {
+ int attempts = 0;
+ int count = 0;
+ final int maxAttempts = 5;
+ do {
+ attempts++;
+ count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED);
+ if (count == expectedCount) {
+ break;
+ }
+ Log.d(TAG, "Expecting count " + expectedCount + " but actual is " + count + " after "
+ + attempts + " attempts; sleeping "
+ + SLEEP_TIME_SEC + " seconds before trying again");
+ SystemClock.sleep(SLEEP_TIME_SEC * SECOND_IN_MS);
+ } while (attempts <= maxAttempts);
+ assertEquals("Number of expected broadcasts for " + receiverName + " not reached after "
+ + maxAttempts * SLEEP_TIME_SEC + " seconds", expectedCount, count);
+ }
+
+ protected String sendOrderedBroadcast(Intent intent) throws Exception {
+ return sendOrderedBroadcast(intent, ORDERED_BROADCAST_TIMEOUT_MS);
+ }
+
+ protected String sendOrderedBroadcast(Intent intent, int timeoutMs) throws Exception {
+ final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
+ Log.d(TAG, "Sending ordered broadcast: " + intent);
+ mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String resultData = getResultData();
+ if (resultData == null) {
+ Log.e(TAG, "Received null data from ordered intent");
+ return;
+ }
+ result.offer(resultData);
+ }
+ }, null, 0, null, null);
+
+ final String resultData = result.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ Log.d(TAG, "Ordered broadcast response after " + timeoutMs + "ms: " + resultData );
+ return resultData;
+ }
+
+ protected int getNumberBroadcastsReceived(String receiverName, String action) throws Exception {
+ final Intent intent = new Intent(ACTION_GET_COUNTERS);
+ intent.putExtra(EXTRA_ACTION, ACTION_RESTRICT_BACKGROUND_CHANGED);
+ intent.putExtra(EXTRA_RECEIVER_NAME, receiverName);
+ final String resultData = sendOrderedBroadcast(intent);
+ assertNotNull("timeout waiting for ordered broadcast result", resultData);
+ return Integer.valueOf(resultData);
+ }
+
+ protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception {
+ final Intent intent = new Intent(ACTION_GET_RESTRICT_BACKGROUND_STATUS);
+ final String resultData = sendOrderedBroadcast(intent);
+ assertNotNull("timeout waiting for ordered broadcast result", resultData);
+ final String actualStatus = toString(Integer.parseInt(resultData));
+ assertEquals("wrong status", toString(expectedStatus), actualStatus);
+ }
+
+ protected void assertMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
+ final int actualStatus = mCm.getRestrictBackgroundStatus();
+ assertEquals("Wrong status", toString(expectedStatus), toString(actualStatus));
+ }
+
+ protected boolean isMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
+ final int actualStatus = mCm.getRestrictBackgroundStatus();
+ if (expectedStatus != actualStatus) {
+ Log.d(TAG, "Expected: " + toString(expectedStatus)
+ + " but actual: " + toString(actualStatus));
+ return false;
+ }
+ return true;
+ }
+
+ protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
+ assertBackgroundState(); // Sanity check.
+ assertNetworkAccess(expectAllowed);
+ }
+
+ protected void assertForegroundNetworkAccess() throws Exception {
+ assertForegroundState(); // Sanity check.
+ assertNetworkAccess(true);
+ }
+
+ protected void assertForegroundServiceNetworkAccess() throws Exception {
+ assertForegroundServiceState(); // Sanity check.
+ assertNetworkAccess(true);
+ }
+
+ /**
+ * Whether this device suport this type of test.
+ *
+ * <p>Should be overridden when necessary (but always calling
+ * {@code super.isSupported()} first), and explicitly used before each test
+ * Example:
+ *
+ * <pre><code>
+ * public void testSomething() {
+ * if (!isSupported()) return;
+ * </code></pre>
+ *
+ * @return {@code true} by default.
+ */
+ protected boolean isSupported() throws Exception {
+ return mSupported;
+ }
+
+ /**
+ * Asserts that an app always have access while on foreground or running a foreground service.
+ *
+ * <p>This method will launch an activity and a foreground service to make the assertion, but
+ * will finish the activity / stop the service afterwards.
+ */
+ protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception{
+ // Checks foreground first.
+ launchActivity();
+ assertForegroundNetworkAccess();
+ finishActivity();
+
+ // Then foreground service
+ startForegroundService();
+ assertForegroundServiceNetworkAccess();
+ stopForegroundService();
+ }
+
+ protected final void assertBackgroundState() throws Exception {
+ final int maxTries = 30;
+ ProcessState state = null;
+ for (int i = 1; i <= maxTries; i++) {
+ state = getProcessStateByUid(mUid);
+ Log.v(TAG, "assertBackgroundState(): status for app2 (" + mUid + ") on attempt #" + i
+ + ": " + state);
+ if (isBackground(state.state)) {
+ return;
+ }
+ Log.d(TAG, "App not on background state on attempt #" + i
+ + "; sleeping 1s before trying again");
+ SystemClock.sleep(SECOND_IN_MS);
+ }
+ fail("App2 is not on background state after " + maxTries + " attempts: " + state );
+ }
+
+ protected final void assertForegroundState() throws Exception {
+ final int maxTries = 30;
+ ProcessState state = null;
+ for (int i = 1; i <= maxTries; i++) {
+ state = getProcessStateByUid(mUid);
+ Log.v(TAG, "assertForegroundState(): status for app2 (" + mUid + ") on attempt #" + i
+ + ": " + state);
+ if (!isBackground(state.state)) {
+ return;
+ }
+ Log.d(TAG, "App not on foreground state on attempt #" + i
+ + "; sleeping 1s before trying again");
+ SystemClock.sleep(SECOND_IN_MS);
+ }
+ fail("App2 is not on foreground state after " + maxTries + " attempts: " + state );
+ }
+
+ protected final void assertForegroundServiceState() throws Exception {
+ final int maxTries = 30;
+ ProcessState state = null;
+ for (int i = 1; i <= maxTries; i++) {
+ state = getProcessStateByUid(mUid);
+ Log.v(TAG, "assertForegroundServiceState(): status for app2 (" + mUid + ") on attempt #"
+ + i + ": " + state);
+ if (state.state == PROCESS_STATE_FOREGROUND_SERVICE) {
+ return;
+ }
+ Log.d(TAG, "App not on foreground service state on attempt #" + i
+ + "; sleeping 1s before trying again");
+ SystemClock.sleep(SECOND_IN_MS);
+ }
+ fail("App2 is not on foreground service state after " + maxTries + " attempts: " + state );
+ }
+
+ /**
+ * Returns whether an app state should be considered "background" for restriction purposes.
+ */
+ protected boolean isBackground(int state) {
+ return state > PROCESS_STATE_FOREGROUND_SERVICE;
+ }
+
+ /**
+ * Asserts whether the active network is available or not.
+ */
+ private void assertNetworkAccess(boolean expectAvailable) throws Exception {
+ final Intent intent = new Intent(ACTION_CHECK_NETWORK);
+
+ final int maxTries = 5;
+ String resultData = null;
+ for (int i = 1; i <= maxTries; i++) {
+ resultData = sendOrderedBroadcast(intent);
+ assertNotNull("timeout waiting for ordered broadcast", resultData);
+
+ // Network status format is described on MyBroadcastReceiver.checkNetworkStatus()
+ final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR);
+ assertEquals("Wrong network status: " + resultData, 5, parts.length); // Sanity check
+ final State state = State.valueOf(parts[0]);
+ final DetailedState detailedState = DetailedState.valueOf(parts[1]);
+ final boolean connected = Boolean.valueOf(parts[2]);
+ final String connectionCheckDetails = parts[3];
+ final String networkInfo = parts[4];
+
+ if (expectAvailable) {
+ if (!connected) {
+ // Since it's establishing a connection to an external site, it could be flaky.
+ Log.w(TAG, "Failed to connect to an external site on attempt #" + i +
+ " (error: " + connectionCheckDetails + ", NetworkInfo: " + networkInfo
+ + "); sleeping " + NETWORK_TIMEOUT_MS + "ms before trying again");
+ SystemClock.sleep(NETWORK_TIMEOUT_MS);
+ continue;
+ }
+ if (state != State.CONNECTED) {
+ Log.d(TAG, "State (" + state + ") not set to CONNECTED on attempt #" + i
+ + "; sleeping 1s before trying again");
+ SystemClock.sleep(SECOND_IN_MS);
+ } else {
+ assertEquals("wrong detailed state for " + networkInfo,
+ DetailedState.CONNECTED, detailedState);
+ return;
+ }
+ return;
+ } else {
+ assertFalse("should not be connected: " + connectionCheckDetails
+ + " (network info: " + networkInfo + ")", connected);
+ if (state != State.DISCONNECTED) {
+ // When the network info state change, it's possible the app still get the
+ // previous value, so we need to retry a couple times.
+ Log.d(TAG, "State (" + state + ") not set to DISCONNECTED on attempt #" + i
+ + "; sleeping 1s before trying again");
+ SystemClock.sleep(SECOND_IN_MS);
+ } else {
+ assertEquals("wrong detailed state for " + networkInfo,
+ DetailedState.BLOCKED, detailedState);
+ return;
+ }
+ }
+ }
+ fail("Invalid state for expectAvailable=" + expectAvailable + " after " + maxTries
+ + " attempts. Last data: " + resultData);
+ }
+
+ protected String executeShellCommand(String command) throws Exception {
+ final String result = runShellCommand(mInstrumentation, command).trim();
+ if (DEBUG) Log.d(TAG, "Command '" + command + "' returned '" + result + "'");
+ return result;
+ }
+
+ /**
+ * Runs a Shell command which is not expected to generate output.
+ */
+ protected void executeSilentShellCommand(String command) throws Exception {
+ final String result = executeShellCommand(command);
+ assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty());
+ }
+
+ /**
+ * Asserts the result of a command, wait and re-running it a couple times if necessary.
+ */
+ protected void assertDelayedShellCommand(String command, final String expectedResult)
+ throws Exception {
+ assertDelayedShellCommand(command, 5, 1, expectedResult);
+ }
+
+ protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
+ final String expectedResult) throws Exception {
+ assertDelayedShellCommand(command, maxTries, napTimeSeconds, new ExpectResultChecker() {
+
+ @Override
+ public boolean isExpected(String result) {
+ return expectedResult.equals(result);
+ }
+
+ @Override
+ public String getExpected() {
+ return expectedResult;
+ }
+ });
+ }
+
+ protected void assertDelayedShellCommand(String command, ExpectResultChecker checker)
+ throws Exception {
+ assertDelayedShellCommand(command, 5, 1, checker);
+ }
+ protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
+ ExpectResultChecker checker) throws Exception {
+ String result = "";
+ for (int i = 1; i <= maxTries; i++) {
+ result = executeShellCommand(command).trim();
+ if (checker.isExpected(result)) return;
+ Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '"
+ + checker.getExpected() + "' on attempt #" + i
+ + "; sleeping " + napTimeSeconds + "s before trying again");
+ SystemClock.sleep(napTimeSeconds * SECOND_IN_MS);
+ }
+ fail("Command '" + command + "' did not return '" + checker.getExpected() + "' after "
+ + maxTries
+ + " attempts. Last result: '" + result + "'");
+ }
+
+ /**
+ * Sets the initial metering state for the active network.
+ *
+ * <p>It's called on setup and by default does nothing - it's up to the
+ * subclasses to override.
+ *
+ * @return whether the tests in the subclass are supported on this device.
+ */
+ protected boolean setUpActiveNetworkMeteringState() throws Exception {
+ return true;
+ }
+
+ /**
+ * Makes sure the active network is not metered.
+ *
+ * <p>If the device does not supoprt un-metered networks (for example if it
+ * only has cellular data but not wi-fi), it should return {@code false};
+ * otherwise, it should return {@code true} (or fail if the un-metered
+ * network could not be set).
+ *
+ * @return {@code true} if the network is now unmetered.
+ */
+ protected boolean setUnmeteredNetwork() throws Exception {
+ final NetworkInfo info = mCm.getActiveNetworkInfo();
+ assertNotNull("Could not get active network", info);
+ if (!mCm.isActiveNetworkMetered()) {
+ Log.d(TAG, "Active network is not metered: " + info);
+ } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {
+ Log.i(TAG, "Setting active WI-FI network as not metered: " + info );
+ setWifiMeteredStatus(false);
+ } else {
+ Log.d(TAG, "Active network cannot be set to un-metered: " + info);
+ return false;
+ }
+ assertActiveNetworkMetered(false); // Sanity check.
+ return true;
+ }
+
+ /**
+ * Enables metering on the active network if supported.
+ *
+ * <p>If the device does not support metered networks it should return
+ * {@code false}; otherwise, it should return {@code true} (or fail if the
+ * metered network could not be set).
+ *
+ * @return {@code true} if the network is now metered.
+ */
+ protected boolean setMeteredNetwork() throws Exception {
+ final NetworkInfo info = mCm.getActiveNetworkInfo();
+ final boolean metered = mCm.isActiveNetworkMetered();
+ if (metered) {
+ Log.d(TAG, "Active network already metered: " + info);
+ return true;
+ } else if (info.getType() != ConnectivityManager.TYPE_WIFI) {
+ Log.w(TAG, "Active network does not support metering: " + info);
+ return false;
+ } else {
+ Log.w(TAG, "Active network not metered: " + info);
+ }
+ final String netId = setWifiMeteredStatus(true);
+
+ // Set flag so status is reverted on resetMeteredNetwork();
+ mMeteredWifi = netId;
+ // Sanity check.
+ assertWifiMeteredStatus(netId, true);
+ assertActiveNetworkMetered(true);
+ return true;
+ }
+
+ /**
+ * Resets the device metering state to what it was before the test started.
+ *
+ * <p>This reverts any metering changes made by {@code setMeteredNetwork}.
+ */
+ protected void resetMeteredNetwork() throws Exception {
+ if (mMeteredWifi != null) {
+ Log.i(TAG, "resetMeteredNetwork(): SID '" + mMeteredWifi
+ + "' was set as metered by test case; resetting it");
+ setWifiMeteredStatus(mMeteredWifi, false);
+ assertActiveNetworkMetered(false); // Sanity check.
+ }
+ }
+
+ private void assertActiveNetworkMetered(boolean expected) throws Exception {
+ final int maxTries = 5;
+ NetworkInfo info = null;
+ for (int i = 1; i <= maxTries; i++) {
+ info = mCm.getActiveNetworkInfo();
+ if (info != null) {
+ break;
+ }
+ Log.v(TAG, "No active network info on attempt #" + i
+ + "; sleeping 1s before polling again");
+ Thread.sleep(SECOND_IN_MS);
+ }
+ assertNotNull("No active network after " + maxTries + " attempts", info);
+ assertEquals("Wrong metered status for active network " + info, expected,
+ mCm.isActiveNetworkMetered());
+ }
+
+ private String setWifiMeteredStatus(boolean metered) throws Exception {
+ // We could call setWifiEnabled() here, but it might take sometime to be in a consistent
+ // state (for example, if one of the saved network is not properly authenticated), so it's
+ // better to let the hostside test take care of that.
+ assertTrue("wi-fi is disabled", mWfm.isWifiEnabled());
+ // TODO: if it's not guaranteed the device has wi-fi, we need to change the tests
+ // to make the actual verification of restrictions optional.
+ final String ssid = mWfm.getConnectionInfo().getSSID();
+ return setWifiMeteredStatus(ssid, metered);
+ }
+
+ private String setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
+ assertNotNull("null SSID", ssid);
+ final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any.
+ assertFalse("empty SSID", ssid.isEmpty());
+
+ Log.i(TAG, "Setting wi-fi network " + netId + " metered status to " + metered);
+ final String setCommand = "cmd netpolicy set metered-network " + netId + " " + metered;
+ assertDelayedShellCommand(setCommand, "");
+
+ return netId;
+ }
+
+ private void assertWifiMeteredStatus(String netId, boolean status) throws Exception {
+ final String command = "cmd netpolicy list wifi-networks";
+ final String expectedLine = netId + ";" + status;
+ assertDelayedShellCommand(command, new ExpectResultChecker() {
+
+ @Override
+ public boolean isExpected(String result) {
+ return result.contains(expectedLine);
+ }
+
+ @Override
+ public String getExpected() {
+ return "line containing " + expectedLine;
+ }
+ });
+ }
+
+ protected void setRestrictBackground(boolean enabled) throws Exception {
+ executeShellCommand("cmd netpolicy set restrict-background " + enabled);
+ final String output = executeShellCommand("cmd netpolicy get restrict-background ");
+ final String expectedSuffix = enabled ? "enabled" : "disabled";
+ // TODO: use MoreAsserts?
+ assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
+ output.endsWith(expectedSuffix));
+ }
+
+ protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
+ assertRestrictBackgroundWhitelist(uid, true);
+ }
+
+ protected void removeRestrictBackgroundWhitelist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy remove restrict-background-whitelist " + uid);
+ assertRestrictBackgroundWhitelist(uid, false);
+ }
+
+ protected void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception {
+ assertRestrictBackground("restrict-background-whitelist", uid, expected);
+ }
+
+ protected void addRestrictBackgroundBlacklist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy add restrict-background-blacklist " + uid);
+ assertRestrictBackgroundBlacklist(uid, true);
+ }
+
+ protected void removeRestrictBackgroundBlacklist(int uid) throws Exception {
+ executeShellCommand("cmd netpolicy remove restrict-background-blacklist " + uid);
+ assertRestrictBackgroundBlacklist(uid, false);
+ }
+
+ protected void assertRestrictBackgroundBlacklist(int uid, boolean expected) throws Exception {
+ assertRestrictBackground("restrict-background-blacklist", uid, expected);
+ }
+
+ private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception {
+ final int maxTries = 5;
+ boolean actual = false;
+ final String expectedUid = Integer.toString(uid);
+ String uids = "";
+ for (int i = 1; i <= maxTries; i++) {
+ final String output =
+ executeShellCommand("cmd netpolicy list " + list);
+ uids = output.split(":")[1];
+ for (String candidate : uids.split(" ")) {
+ actual = candidate.trim().equals(expectedUid);
+ if (expected == actual) {
+ return;
+ }
+ }
+ Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected "
+ + expected + ", got " + actual + "); sleeping 1s before polling again");
+ SystemClock.sleep(SECOND_IN_MS);
+ }
+ fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual
+ + ". Full list: " + uids);
+ }
+
+ protected void assertPowerSaveModeWhitelist(String packageName, boolean expected)
+ throws Exception {
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ assertDelayedShellCommand("dumpsys deviceidle whitelist =" + packageName,
+ Boolean.toString(expected));
+ }
+
+ protected void addPowerSaveModeWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Adding package " + packageName + " to power-save-mode whitelist");
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ executeShellCommand("dumpsys deviceidle whitelist +" + packageName);
+ assertPowerSaveModeWhitelist(packageName, true); // Sanity check
+ }
+
+ protected void removePowerSaveModeWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Removing package " + packageName + " from power-save-mode whitelist");
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ executeShellCommand("dumpsys deviceidle whitelist -" + packageName);
+ assertPowerSaveModeWhitelist(packageName, false); // Sanity check
+ }
+
+ protected void turnBatteryOff() throws Exception {
+ executeSilentShellCommand("cmd battery unplug");
+ }
+
+ protected void turnBatteryOn() throws Exception {
+ executeSilentShellCommand("cmd battery reset");
+ }
+
+ protected void turnScreenOff() throws Exception {
+ executeSilentShellCommand("input keyevent KEYCODE_SLEEP");
+ }
+
+ protected void turnScreenOn() throws Exception {
+ executeSilentShellCommand("input keyevent KEYCODE_WAKEUP");
+ executeSilentShellCommand("wm dismiss-keyguard");
+ }
+
+ protected void setBatterySaverMode(boolean enabled) throws Exception {
+ Log.i(TAG, "Setting Battery Saver Mode to " + enabled);
+ if (enabled) {
+ turnBatteryOff();
+ executeSilentShellCommand("cmd battery unplug");
+ executeSilentShellCommand("settings put global low_power 1");
+ } else {
+ executeSilentShellCommand("settings put global low_power 0");
+ turnBatteryOn();
+ }
+ }
+
+ protected void setDozeMode(boolean enabled) throws Exception {
+ // Sanity check, since tests should check beforehand....
+ assertTrue("Device does not support Doze Mode", isDozeModeEnabled());
+
+ Log.i(TAG, "Setting Doze Mode to " + enabled);
+ if (enabled) {
+ turnBatteryOff();
+ turnScreenOff();
+ executeShellCommand("dumpsys deviceidle force-idle deep");
+ } else {
+ turnScreenOn();
+ turnBatteryOn();
+ executeShellCommand("dumpsys deviceidle unforce");
+ }
+ // Sanity check.
+ assertDozeMode(enabled);
+ }
+
+ protected void assertDozeMode(boolean enabled) throws Exception {
+ assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
+ }
+
+ protected boolean isDozeModeEnabled() throws Exception {
+ final String result = executeShellCommand("cmd deviceidle enabled deep").trim();
+ return result.equals("1");
+ }
+
+ protected void setAppIdle(boolean enabled) throws Exception {
+ Log.i(TAG, "Setting app idle to " + enabled);
+ executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled );
+ assertAppIdle(enabled); // Sanity check
+ }
+
+ protected void assertAppIdle(boolean enabled) throws Exception {
+ assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 10, 2, "Idle=" + enabled);
+ }
+
+ /**
+ * Starts a service that will register a broadcast receiver to receive
+ * {@code RESTRICT_BACKGROUND_CHANGE} intents.
+ * <p>
+ * The service must run in a separate app because otherwise it would be killed every time
+ * {@link #runDeviceTests(String, String)} is executed.
+ */
+ protected void registerBroadcastReceiver() throws Exception {
+ executeShellCommand("am startservice com.android.cts.net.hostside.app2/.MyService");
+ // Wait until receiver is ready.
+ final int maxTries = 10;
+ for (int i = 1; i <= maxTries; i++) {
+ final String message =
+ sendOrderedBroadcast(new Intent(ACTION_RECEIVER_READY), 4 * SECOND_IN_MS);
+ Log.d(TAG, "app2 receiver acked: " + message);
+ if (message != null) {
+ return;
+ }
+ Log.v(TAG, "app2 receiver is not ready yet; sleeping 1s before polling again");
+ SystemClock.sleep(SECOND_IN_MS);
+ }
+ fail("app2 receiver is not ready");
+ }
+
+ /**
+ * Registers a {@link NotificationListenerService} implementation that will execute the
+ * notification actions right after the notification is sent.
+ */
+ protected void registerNotificationListenerService() throws Exception {
+ final StringBuilder listeners = new StringBuilder(getNotificationListenerServices());
+ if (listeners.length() > 0) {
+ listeners.append(":");
+ }
+ listeners.append(MyNotificationListenerService.getId());
+ executeShellCommand("settings put secure enabled_notification_listeners " + listeners);
+ final String newListeners = getNotificationListenerServices();
+ assertEquals("Failed to set 'enabled_notification_listeners'",
+ listeners.toString(), newListeners);
+ }
+
+ private String getNotificationListenerServices() throws Exception {
+ return executeShellCommand("settings get secure enabled_notification_listeners");
+ }
+
+ protected void setPendingIntentWhitelistDuration(int durationMs) throws Exception {
+ final String command = String.format(
+ "settings put global device_idle_constants %s=%d",
+ "notification_whitelist_duration", durationMs);
+ executeSilentShellCommand(command);
+ }
+
+ protected void resetDeviceIdleSettings() throws Exception {
+ executeShellCommand("settings delete global device_idle_constants");
+ }
+
+ protected void startForegroundService() throws Exception {
+ executeShellCommand(
+ "am startservice -f 1 com.android.cts.net.hostside.app2/.MyForegroundService");
+ assertForegroundServiceState();
+ }
+
+ protected void stopForegroundService() throws Exception {
+ executeShellCommand(
+ "am startservice -f 2 com.android.cts.net.hostside.app2/.MyForegroundService");
+ // NOTE: cannot assert state because it depends on whether activity was on top before.
+ }
+
+ /**
+ * Launches an activity on app2 so its process is elevated to foreground status.
+ */
+ protected void launchActivity() throws Exception {
+ turnScreenOn();
+ executeShellCommand("am start com.android.cts.net.hostside.app2/.MyActivity");
+ final int maxTries = 30;
+ ProcessState state = null;
+ for (int i = 1; i <= maxTries; i++) {
+ state = getProcessStateByUid(mUid);
+ if (state.state == PROCESS_STATE_TOP) return;
+ Log.w(TAG, "launchActivity(): uid " + mUid + " not on TOP state on attempt #" + i
+ + "; turning screen on and sleeping 1s before checking again");
+ turnScreenOn();
+ SystemClock.sleep(SECOND_IN_MS);
+ }
+ fail("App2 is not on foreground state after " + maxTries + " attempts: " + state);
+ }
+
+ /**
+ * Finishes an activity on app2 so its process is demoted fromforeground status.
+ */
+ protected void finishActivity() throws Exception {
+ executeShellCommand("am broadcast -a "
+ + " com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY "
+ + "--receiver-foreground --receiver-registered-only");
+ }
+
+ protected void sendNotification(int notificationId) {
+ final Intent intent = new Intent(ACTION_SEND_NOTIFICATION);
+ intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
+ Log.d(TAG, "Sending broadcast: " + intent);
+ mContext.sendBroadcast(intent);
+ }
+
+ private String toString(int status) {
+ switch (status) {
+ case RESTRICT_BACKGROUND_STATUS_DISABLED:
+ return "DISABLED";
+ case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+ return "WHITELISTED";
+ case RESTRICT_BACKGROUND_STATUS_ENABLED:
+ return "ENABLED";
+ default:
+ return "UNKNOWN_STATUS_" + status;
+ }
+ }
+
+ private ProcessState getProcessStateByUid(int uid) throws Exception {
+ return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid));
+ }
+
+ private static class ProcessState {
+ private final String fullState;
+ final int state;
+
+ ProcessState(String fullState) {
+ this.fullState = fullState;
+ try {
+ this.state = Integer.parseInt(fullState.split(" ")[0]);
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Could not parse " + fullState);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return fullState;
+ }
+ }
+
+ /**
+ * Helper class used to assert the result of a Shell command.
+ */
+ protected static interface ExpectResultChecker {
+ /**
+ * Checkes whether the result of the command matched the expectation.
+ */
+ boolean isExpected(String result);
+ /**
+ * Gets the expected result so it's displayed on log and failure messages.
+ */
+ String getExpected();
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
new file mode 100644
index 0000000..622d993
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
+
+ @Override
+ protected boolean setUpActiveNetworkMeteringState() throws Exception {
+ return setMeteredNetwork();
+ }
+
+ @Override
+ protected void tearDownMeteredNetwork() throws Exception {
+ resetMeteredNetwork();
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
new file mode 100644
index 0000000..bde71f9
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
+ @Override
+ protected boolean setUpActiveNetworkMeteringState() throws Exception {
+ return setUnmeteredNetwork();
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
new file mode 100644
index 0000000..3071cfe
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
+
+ @Override
+ protected boolean setUpActiveNetworkMeteringState() throws Exception {
+ return setMeteredNetwork();
+ }
+
+ @Override
+ protected void tearDownMeteredNetwork() throws Exception {
+ resetMeteredNetwork();
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
new file mode 100644
index 0000000..6d3076f
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
+
+ @Override
+ protected boolean setUpActiveNetworkMeteringState() throws Exception {
+ return setUnmeteredNetwork();
+ }
+}
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
new file mode 100644
index 0000000..d5e236f
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+
+import android.util.Log;
+
+public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+ private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
+ "com.android.providers.downloads"
+ };
+
+ private boolean mIsDataSaverSupported;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mIsDataSaverSupported = isDataSaverSupported();
+ if (!isSupported()) return;
+
+ // Set initial state.
+ setRestrictBackground(false);
+ removeRestrictBackgroundWhitelist(mUid);
+ removeRestrictBackgroundBlacklist(mUid);
+
+ registerBroadcastReceiver();
+ assertRestrictBackgroundChangedReceived(0);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ if (!isSupported()) return;
+
+ try {
+ resetMeteredNetwork();
+ } finally {
+ setRestrictBackground(false);
+ }
+ }
+
+ @Override
+ protected boolean setUpActiveNetworkMeteringState() throws Exception {
+ return setMeteredNetwork();
+ }
+
+ @Override
+ protected boolean isSupported() throws Exception {
+ if (!mIsDataSaverSupported) {
+ Log.i(TAG, "Skipping " + getClass() + "." + getName()
+ + "() because device does not support Data Saver Mode");
+ }
+ return mIsDataSaverSupported && super.isSupported();
+ }
+
+ /**
+ * As per CDD requirements, if the device doesn't support data saver mode then
+ * ConnectivityManager.getRestrictBackgroundStatus() will always return
+ * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
+ * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
+ * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
+ */
+ private boolean isDataSaverSupported() throws Exception {
+ assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+ try {
+ setRestrictBackground(true);
+ return !isMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+ } finally {
+ setRestrictBackground(false);
+ }
+ }
+
+ public void testGetRestrictBackgroundStatus_disabled() throws Exception {
+ if (!isSupported()) return;
+
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+
+ // Sanity check: make sure status is always disabled, never whitelisted
+ addRestrictBackgroundWhitelist(mUid);
+ assertRestrictBackgroundChangedReceived(0);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+ }
+
+ public void testGetRestrictBackgroundStatus_whitelisted() throws Exception {
+ if (!isSupported()) return;
+
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(1);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ addRestrictBackgroundWhitelist(mUid);
+ assertRestrictBackgroundChangedReceived(2);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+
+ removeRestrictBackgroundWhitelist(mUid);
+ assertRestrictBackgroundChangedReceived(3);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+ }
+
+ public void testGetRestrictBackgroundStatus_enabled() throws Exception {
+ if (!isSupported()) return;
+
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(1);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ // Make sure foreground app doesn't lose access upon enabling it.
+ setRestrictBackground(false);
+ launchActivity();
+ assertForegroundNetworkAccess();
+ setRestrictBackground(true);
+ assertForegroundNetworkAccess();
+ finishActivity();
+ assertBackgroundNetworkAccess(false);
+
+ // Same for foreground service.
+ setRestrictBackground(false);
+ startForegroundService();
+ assertForegroundNetworkAccess();
+ setRestrictBackground(true);
+ assertForegroundNetworkAccess();
+ stopForegroundService();
+ assertBackgroundNetworkAccess(false);
+ }
+
+ public void testGetRestrictBackgroundStatus_blacklisted() throws Exception {
+ if (!isSupported()) return;
+
+ addRestrictBackgroundBlacklist(mUid);
+ assertRestrictBackgroundChangedReceived(1);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ // Make sure blacklist prevails over whitelist.
+ setRestrictBackground(true);
+ assertRestrictBackgroundChangedReceived(2);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+ addRestrictBackgroundWhitelist(mUid);
+ assertRestrictBackgroundChangedReceived(3);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+ // Check status after removing blacklist.
+ removeRestrictBackgroundBlacklist(mUid);
+ assertRestrictBackgroundChangedReceived(4);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+ setRestrictBackground(false);
+ assertRestrictBackgroundChangedReceived(5);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+ }
+
+ public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
+ if (!isSupported()) return;
+
+ final StringBuilder error = new StringBuilder();
+ for (String packageName : REQUIRED_WHITELISTED_PACKAGES) {
+ int uid = -1;
+ try {
+ uid = getUid(packageName);
+ assertRestrictBackgroundWhitelist(uid, true);
+ } catch (Throwable t) {
+ error.append("\nFailed for '").append(packageName).append("'");
+ if (uid > 0) {
+ error.append(" (uid ").append(uid).append(")");
+ }
+ error.append(": ").append(t).append("\n");
+ }
+ }
+ if (error.length() > 0) {
+ fail(error.toString());
+ }
+ }
+
+ 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/DozeModeMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
new file mode 100644
index 0000000..e4189af
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
+
+ @Override
+ protected boolean setUpActiveNetworkMeteringState() throws Exception {
+ return setMeteredNetwork();
+ }
+
+ @Override
+ protected void tearDownMeteredNetwork() throws Exception {
+ resetMeteredNetwork();
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
new file mode 100644
index 0000000..edbbb9e
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
+
+ @Override
+ protected boolean setUpActiveNetworkMeteringState() throws Exception {
+ return setUnmeteredNetwork();
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
new file mode 100644
index 0000000..ec49eee
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside;
+
+import android.util.Log;
+
+/**
+ * Test cases for the more complex scenarios where multiple restrictions (like Battery Saver Mode
+ * and Data Saver Mode) are applied simultaneously.
+ * <p>
+ * <strong>NOTE: </strong>it might sound like the test methods on this class are testing too much,
+ * which would make it harder to diagnose individual failures, but the assumption is that such
+ * failure most likely will happen when the restriction is tested individually as well.
+ */
+public class MixedModesTest extends AbstractRestrictBackgroundNetworkTestCase {
+ private static final String TAG = "MixedModesTest";
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ if (!isSupported()) return;
+
+ // Set initial state.
+ removeRestrictBackgroundWhitelist(mUid);
+ removeRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+ registerBroadcastReceiver();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ if (!isSupported()) return;
+
+ try {
+ setRestrictBackground(false);
+ } finally {
+ setBatterySaverMode(false);
+ }
+ }
+
+ /**
+ * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on metered networks.
+ */
+ public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
+ if (!isSupported()) return;
+
+ if (!isDozeModeEnabled()) {
+ Log.w(TAG, "testDataAndBatterySaverModes_meteredNetwork() skipped because "
+ + "device does not support Doze Mode");
+ return;
+ }
+
+ Log.i(TAG, "testDataAndBatterySaverModes_meteredNetwork() tests");
+ if (!setMeteredNetwork()) {
+ Log.w(TAG, "testDataAndBatterySaverModes_meteredNetwork() skipped because "
+ + "device cannot use a metered network");
+ return;
+ }
+
+ try {
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+
+ Log.v(TAG, "Not whitelisted for any.");
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+ addRestrictBackgroundWhitelist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ removeRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+ Log.v(TAG, "Whitelisted for both.");
+ addRestrictBackgroundWhitelist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundBlacklist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ } finally {
+ resetMeteredNetwork();
+ }
+ }
+
+ /**
+ * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on non-metered
+ * networks.
+ */
+ public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
+ if (!isSupported()) return;
+
+ if (!isDozeModeEnabled()) {
+ Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because "
+ + "device does not support Doze Mode");
+ return;
+ }
+
+ if (!setUnmeteredNetwork()) {
+ Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because network"
+ + " is metered");
+ return;
+ }
+ Log.i(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() tests");
+ setRestrictBackground(true);
+ setBatterySaverMode(true);
+
+ Log.v(TAG, "Not whitelisted for any.");
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+
+ Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+ addRestrictBackgroundWhitelist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ removeRestrictBackgroundWhitelist(mUid);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+ Log.v(TAG, "Whitelisted for both.");
+ addRestrictBackgroundWhitelist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundWhitelist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(false);
+ removeRestrictBackgroundBlacklist(mUid);
+
+ Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+ addRestrictBackgroundBlacklist(mUid);
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+ assertsForegroundAlwaysHasNetworkAccess();
+ assertBackgroundNetworkAccess(true);
+ removeRestrictBackgroundBlacklist(mUid);
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java
index 375c852..0d0bc58 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java
@@ -18,13 +18,9 @@
import android.app.Activity;
import android.content.Intent;
-import android.net.VpnService;
import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
import android.view.WindowManager;
-import java.util.Arrays;
-import java.util.ArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java
new file mode 100644
index 0000000..b9c3031
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside;
+
+import android.app.Notification;
+import android.app.PendingIntent.CanceledException;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+/**
+ * NotificationListenerService implementation that executes the notification actions once they're
+ * created.
+ */
+public class MyNotificationListenerService extends NotificationListenerService {
+ private static final String TAG = "MyNotificationListenerService";
+
+ @Override
+ public void onListenerConnected() {
+ Log.d(TAG, "onListenerConnected()");
+ }
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ Log.d(TAG, "onNotificationPosted(): " + sbn);
+ if (!sbn.getPackageName().startsWith(getPackageName())) {
+ Log.v(TAG, "ignoring notification from a different package");
+ return;
+ }
+ final Notification notification = sbn.getNotification();
+ if (notification.actions == null) {
+ Log.w(TAG, "ignoring notification without an action");
+ }
+ for (Notification.Action action : notification.actions) {
+ Log.i(TAG, "Sending pending intent " + action.actionIntent);
+ try {
+ action.actionIntent.send();
+ } catch (CanceledException e) {
+ Log.w(TAG, "Pending Intent canceled");
+ }
+ }
+ }
+
+ static String getId() {
+ return String.format("%s/%s", MyNotificationListenerService.class.getPackage().getName(),
+ MyNotificationListenerService.class.getName());
+ }
+}
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 a3f400c..90a3ce4 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
@@ -26,7 +26,6 @@
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
-import java.util.ArrayList;
public class MyVpnService extends VpnService {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java
new file mode 100644
index 0000000..799fe50
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.cts.net.hostside.IRemoteSocketFactory;
+
+import java.io.FileDescriptor;
+
+public class RemoteSocketFactoryClient {
+ private static final int TIMEOUT_MS = 5000;
+ private static final String PACKAGE = RemoteSocketFactoryClient.class.getPackage().getName();
+ private static final String APP2_PACKAGE = PACKAGE + ".app2";
+ private static final String SERVICE_NAME = APP2_PACKAGE + ".RemoteSocketFactoryService";
+
+ private Context mContext;
+ private ServiceConnection mServiceConnection;
+ private IRemoteSocketFactory mService;
+
+ public RemoteSocketFactoryClient(Context context) {
+ mContext = context;
+ }
+
+ public void bind() {
+ if (mService != null) {
+ throw new IllegalStateException("Already bound");
+ }
+
+ final ConditionVariable cv = new ConditionVariable();
+ mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mService = IRemoteSocketFactory.Stub.asInterface(service);
+ cv.open();
+ }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+ };
+
+ final Intent intent = new Intent();
+ intent.setComponent(new ComponentName(APP2_PACKAGE, SERVICE_NAME));
+ mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+ cv.block(TIMEOUT_MS);
+ if (mService == null) {
+ throw new IllegalStateException(
+ "Could not bind to RemoteSocketFactory service after " + TIMEOUT_MS + "ms");
+ }
+ }
+
+ public void unbind() {
+ if (mService != null) {
+ mContext.unbindService(mServiceConnection);
+ }
+ }
+
+ public FileDescriptor openSocketFd(
+ String host, int port, int timeoutMs) throws RemoteException {
+ return mService.openSocketFd(host, port, timeoutMs).getFileDescriptor();
+ }
+
+ public String getPackageName() throws RemoteException {
+ return mService.getPackageName();
+ }
+
+ public int getUid() throws RemoteException {
+ return mService.getUid();
+ }
+}
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 5045cc2..12fe625 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
@@ -27,6 +27,8 @@
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.VpnService;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -40,11 +42,18 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.cts.net.hostside.IRemoteSocketFactory;
+
+import java.io.BufferedReader;
import java.io.Closeable;
import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.io.PrintWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet6Address;
@@ -52,6 +61,8 @@
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
+import java.net.SocketException;
+import java.nio.charset.StandardCharsets;
import java.util.Random;
/**
@@ -79,11 +90,14 @@
public static String TAG = "VpnTest";
public static int TIMEOUT_MS = 3 * 1000;
public static int SOCKET_TIMEOUT_MS = 100;
+ public static String TEST_HOST = "connectivitycheck.gstatic.com";
private UiDevice mDevice;
private MyActivity mActivity;
private String mPackageName;
private ConnectivityManager mCM;
+ private RemoteSocketFactoryClient mRemoteSocketFactoryClient;
+
Network mNetwork;
NetworkCallback mCallback;
final Object mLock = new Object();
@@ -107,11 +121,14 @@
MyActivity.class, null);
mPackageName = mActivity.getPackageName();
mCM = (ConnectivityManager) mActivity.getSystemService(mActivity.CONNECTIVITY_SERVICE);
+ mRemoteSocketFactoryClient = new RemoteSocketFactoryClient(mActivity);
+ mRemoteSocketFactoryClient.bind();
mDevice.waitForIdle();
}
@Override
public void tearDown() throws Exception {
+ mRemoteSocketFactoryClient.unbind();
if (mCallback != null) {
mCM.unregisterNetworkCallback(mCallback);
}
@@ -441,7 +458,7 @@
}
}
- private void checkTrafficOnVpn() throws IOException, ErrnoException {
+ private void checkTrafficOnVpn() throws Exception {
checkUdpEcho("192.0.2.251", "192.0.2.2");
checkUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
checkPing("2001:db8:dead:beef::f00");
@@ -449,29 +466,88 @@
checkTcpReflection("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
}
- private void checkNoTrafficOnVpn() throws IOException, ErrnoException {
+ private void checkNoTrafficOnVpn() throws Exception {
checkUdpEcho("192.0.2.251", null);
checkUdpEcho("2001:db8:dead:beef::f00", null);
checkTcpReflection("192.0.2.252", null);
checkTcpReflection("2001:db8:dead:beef::f00", null);
}
+ private FileDescriptor openSocketFd(String host, int port, int timeoutMs) throws Exception {
+ Socket s = new Socket(host, port);
+ s.setSoTimeout(timeoutMs);
+ return ParcelFileDescriptor.fromSocket(s).getFileDescriptor();
+ }
+
+ private FileDescriptor openSocketFdInOtherApp(
+ String host, int port, int timeoutMs) throws Exception {
+ Log.d(TAG, String.format("Creating test socket in UID=%d, my UID=%d",
+ mRemoteSocketFactoryClient.getUid(), Os.getuid()));
+ FileDescriptor fd = mRemoteSocketFactoryClient.openSocketFd(host, port, TIMEOUT_MS);
+ return fd;
+ }
+
+ private void sendRequest(FileDescriptor fd, String host) throws Exception {
+ String request = "GET /generate_204 HTTP/1.1\r\n" +
+ "Host: " + host + "\r\n" +
+ "Connection: keep-alive\r\n\r\n";
+ byte[] requestBytes = request.getBytes(StandardCharsets.UTF_8);
+ int ret = Os.write(fd, requestBytes, 0, requestBytes.length);
+ Log.d(TAG, "Wrote " + ret + "bytes");
+
+ String expected = "HTTP/1.1 204 No Content\r\n";
+ byte[] response = new byte[expected.length()];
+ Os.read(fd, response, 0, response.length);
+
+ String actual = new String(response, StandardCharsets.UTF_8);
+ assertEquals(expected, actual);
+ Log.d(TAG, "Got response: " + actual);
+ }
+
+ private void assertSocketStillOpen(FileDescriptor fd, String host) throws Exception {
+ try {
+ sendRequest(fd, host);
+ } finally {
+ Os.close(fd);
+ }
+ }
+
+ private void assertSocketClosed(FileDescriptor fd, String host) throws Exception {
+ try {
+ sendRequest(fd, host);
+ fail("Socket opened before VPN connects should be closed when VPN connects");
+ } catch (ErrnoException expected) {
+ assertEquals(ECONNABORTED, expected.errno);
+ } finally {
+ Os.close(fd);
+ }
+ }
+
public void testDefault() throws Exception {
if (!supportedHardware()) return;
+ FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
+
startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
new String[] {"0.0.0.0/0", "::/0"},
"", "");
+ assertSocketClosed(fd, TEST_HOST);
+
checkTrafficOnVpn();
}
public void testAppAllowed() throws Exception {
if (!supportedHardware()) return;
+ FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
+
+ 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"},
- mPackageName, "");
+ allowedApps, "");
+
+ assertSocketClosed(fd, TEST_HOST);
checkTrafficOnVpn();
}
@@ -479,9 +555,16 @@
public void testAppDisallowed() throws Exception {
if (!supportedHardware()) return;
+ FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
+ FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
+
+ String disallowedApps = 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"},
- "", mPackageName);
+ "", disallowedApps);
+
+ assertSocketStillOpen(localFd, TEST_HOST);
+ assertSocketStillOpen(remoteFd, TEST_HOST);
checkNoTrafficOnVpn();
}
diff --git a/tests/cts/hostside/app2/Android.mk b/tests/cts/hostside/app2/Android.mk
new file mode 100644
index 0000000..706455d
--- /dev/null
+++ b/tests/cts/hostside/app2/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2016 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := CtsHostsideNetworkTestsAidl
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsHostsideNetworkTestsApp2
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/cts/hostside/app2/AndroidManifest.xml b/tests/cts/hostside/app2/AndroidManifest.xml
new file mode 100644
index 0000000..1fa49ba
--- /dev/null
+++ b/tests/cts/hostside/app2/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.net.hostside.app2" >
+
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <!--
+ This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
+ them in a shared preferences which is then read by the test app. These broadcasts are
+ handled by 2 listeners, one defined the manifest and another dynamically registered by
+ a service.
+
+ The manifest-defined listener also handles ordered broadcasts used to share data with the
+ test app.
+
+ This application also provides a service, RemoteSocketFactoryService, that the test app can
+ use to open sockets to remote hosts as a different user ID.
+ -->
+ <application>
+ <activity android:name=".MyActivity" android:exported="true"/>
+ <service android:name=".MyService" android:exported="true"/>
+ <service android:name=".MyForegroundService" android:exported="true"/>
+ <service android:name=".RemoteSocketFactoryService" android:exported="true"/>
+
+ <receiver android:name=".MyBroadcastReceiver" >
+ <intent-filter>
+ <action android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED" />
+ <action android:name="com.android.cts.net.hostside.app2.action.GET_COUNTERS" />
+ <action android:name="com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS" />
+ <action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK" />
+ <action android:name="com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION" />
+ </intent-filter>
+ </receiver>
+ </application>
+
+</manifest>
diff --git a/tests/cts/hostside/app2/res/drawable/ic_notification.png b/tests/cts/hostside/app2/res/drawable/ic_notification.png
new file mode 100644
index 0000000..6ae570b
--- /dev/null
+++ b/tests/cts/hostside/app2/res/drawable/ic_notification.png
Binary files differ
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
new file mode 100644
index 0000000..f02f651
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+public final class Common {
+
+ static final String TAG = "CtsNetApp2";
+
+ // Constants below must match values defined on app's
+ // AbstractRestrictBackgroundNetworkTestCase.java
+ static final String MANIFEST_RECEIVER = "ManifestReceiver";
+ static final String DYNAMIC_RECEIVER = "DynamicReceiver";
+ static final String ACTION_GET_COUNTERS =
+ "com.android.cts.net.hostside.app2.action.GET_COUNTERS";
+ static final String ACTION_GET_RESTRICT_BACKGROUND_STATUS =
+ "com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS";
+ static final String ACTION_CHECK_NETWORK =
+ "com.android.cts.net.hostside.app2.action.CHECK_NETWORK";
+ static final String ACTION_RECEIVER_READY =
+ "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
+ static final String ACTION_FINISH_ACTIVITY =
+ "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
+ static final String ACTION_SEND_NOTIFICATION =
+ "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+ static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
+ static final String EXTRA_RECEIVER_NAME =
+ "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
+ static final String EXTRA_NOTIFICATION_ID =
+ "com.android.cts.net.hostside.app2.extra.NOTIFICATION_ID";
+
+ static int getUid(Context context) {
+ final String packageName = context.getPackageName();
+ try {
+ return context.getPackageManager().getPackageUid(packageName, 0);
+ } catch (NameNotFoundException e) {
+ throw new IllegalStateException("Could not get UID for " + packageName, e);
+ }
+ }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
new file mode 100644
index 0000000..444b696
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
+import static com.android.cts.net.hostside.app2.Common.TAG;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Activity used to bring process to foreground.
+ */
+public class MyActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ registerReceiver(new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "Finishing MyActivity");
+ MyActivity.this.finish();
+ }}, new IntentFilter(ACTION_FINISH_ACTIVITY));
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "MyActivity.onStart()");
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.d(TAG, "MyActivity.onDestroy()");
+ super.onDestroy();
+ }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
new file mode 100644
index 0000000..60e5de1
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside.app2;
+
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+
+import static com.android.cts.net.hostside.app2.Common.ACTION_CHECK_NETWORK;
+import static com.android.cts.net.hostside.app2.Common.ACTION_GET_COUNTERS;
+import static com.android.cts.net.hostside.app2.Common.ACTION_GET_RESTRICT_BACKGROUND_STATUS;
+import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.net.hostside.app2.Common.ACTION_SEND_NOTIFICATION;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_ACTION;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_ID;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_RECEIVER_NAME;
+import static com.android.cts.net.hostside.app2.Common.MANIFEST_RECEIVER;
+import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.net.hostside.app2.Common.getUid;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Receiver used to:
+ * <ol>
+ * <li>Stored received RESTRICT_BACKGROUND_CHANGED broadcasts in a shared preference.
+ * <li>Returned the number of RESTRICT_BACKGROUND_CHANGED broadcasts in an ordered broadcast.
+ * </ol>
+ */
+public class MyBroadcastReceiver extends BroadcastReceiver {
+
+ private static final int NETWORK_TIMEOUT_MS = 15 * 1000;
+
+ private final String mName;
+
+ public MyBroadcastReceiver() {
+ this(MANIFEST_RECEIVER);
+ }
+
+ MyBroadcastReceiver(String name) {
+ Log.d(TAG, "Constructing MyBroadcastReceiver named " + name);
+ mName = name;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.d(TAG, "onReceive() for " + mName + ": " + intent);
+ final String action = intent.getAction();
+ switch (action) {
+ case ACTION_RESTRICT_BACKGROUND_CHANGED:
+ increaseCounter(context, action);
+ break;
+ case ACTION_GET_COUNTERS:
+ setResultDataFromCounter(context, intent);
+ break;
+ case ACTION_GET_RESTRICT_BACKGROUND_STATUS:
+ getRestrictBackgroundStatus(context, intent);
+ break;
+ case ACTION_CHECK_NETWORK:
+ checkNetwork(context, intent);
+ break;
+ case ACTION_RECEIVER_READY:
+ final String message = mName + " is ready to rumble";
+ Log.d(TAG, message);
+ setResultData(message);
+ break;
+ case ACTION_SEND_NOTIFICATION:
+ sendNotification(context, intent);
+ break;
+ default:
+ Log.e(TAG, "received unexpected action: " + action);
+ }
+ }
+
+ private void increaseCounter(Context context, String action) {
+ final SharedPreferences prefs = context.getSharedPreferences(mName, Context.MODE_PRIVATE);
+ final int value = prefs.getInt(action, 0) + 1;
+ Log.d(TAG, "increaseCounter('" + action + "'): setting '" + mName + "' to " + value);
+ prefs.edit().putInt(action, value).apply();
+ }
+
+ private int getCounter(Context context, String action, String receiverName) {
+ final SharedPreferences prefs = context.getSharedPreferences(receiverName,
+ Context.MODE_PRIVATE);
+ final int value = prefs.getInt(action, 0);
+ Log.d(TAG, "getCounter('" + action + "', '" + receiverName + "'): " + value);
+ return value;
+ }
+
+ private void getRestrictBackgroundStatus(Context context, Intent intent) {
+ final ConnectivityManager cm = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+ final int apiStatus = cm.getRestrictBackgroundStatus();
+ Log.d(TAG, "getRestrictBackgroundStatus: returning " + apiStatus);
+ setResultData(Integer.toString(apiStatus));
+ }
+
+ private void checkNetwork(final Context context, Intent intent) {
+ final ConnectivityManager cm = (ConnectivityManager) context
+ .getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ String netStatus = null;
+ try {
+ netStatus = checkNetworkStatus(context, cm);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Timeout checking network status");
+ }
+ Log.d(TAG, "checkNetwork(): returning " + netStatus);
+ setResultData(netStatus);
+ }
+
+
+ private static final String NETWORK_STATUS_TEMPLATE = "%s|%s|%s|%s|%s";
+ /**
+ * Checks whether the network is available and return a string which can then be send as a
+ * result data for the ordered broadcast.
+ *
+ * <p>
+ * The string has the following format:
+ *
+ * <p><pre><code>
+ * NetinfoState|NetinfoDetailedState|RealConnectionCheck|RealConnectionCheckDetails|Netinfo
+ * </code></pre>
+ *
+ * <p>Where:
+ *
+ * <ul>
+ * <li>{@code NetinfoState}: enum value of {@link NetworkInfo.State}.
+ * <li>{@code NetinfoDetailedState}: enum value of {@link NetworkInfo.DetailedState}.
+ * <li>{@code RealConnectionCheck}: boolean value of a real connection check (i.e., an attempt
+ * to access an external website.
+ * <li>{@code RealConnectionCheckDetails}: if HTTP output core or exception string of the real
+ * connection attempt
+ * <li>{@code Netinfo}: string representation of the {@link NetworkInfo}.
+ * </ul>
+ *
+ * For example, if the connection was established fine, the result would be something like:
+ * <p><pre><code>
+ * CONNECTED|CONNECTED|true|200|[type: WIFI[], state: CONNECTED/CONNECTED, reason: ...]
+ * </code></pre>
+ *
+ */
+ private String checkNetworkStatus(final Context context, final ConnectivityManager cm)
+ throws InterruptedException {
+ final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
+ new Thread(new Runnable() {
+
+ @Override
+ public void run() {
+ // TODO: connect to a hostside server instead
+ final String address = "http://example.com";
+ final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+ Log.d(TAG, "Running checkNetworkStatus() on thread "
+ + Thread.currentThread().getName() + " for UID " + getUid(context)
+ + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address);
+ boolean checkStatus = false;
+ String checkDetails = "N/A";
+ try {
+ final URL url = new URL(address);
+ final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setReadTimeout(NETWORK_TIMEOUT_MS);
+ conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2);
+ conn.setRequestMethod("GET");
+ conn.setDoInput(true);
+ conn.connect();
+ final int response = conn.getResponseCode();
+ checkStatus = true;
+ checkDetails = "HTTP response for " + address + ": " + response;
+ } catch (Exception e) {
+ checkStatus = false;
+ checkDetails = "Exception getting " + address + ": " + e;
+ }
+ Log.d(TAG, checkDetails);
+ final String status = String.format(NETWORK_STATUS_TEMPLATE,
+ networkInfo.getState().name(), networkInfo.getDetailedState().name(),
+ Boolean.toString(checkStatus), checkDetails, networkInfo);
+ Log.d(TAG, "Offering " + status);
+ result.offer(status);
+ }
+ }, mName).start();
+ return result.poll(NETWORK_TIMEOUT_MS * 2, TimeUnit.MILLISECONDS);
+ }
+
+ private void setResultDataFromCounter(Context context, Intent intent) {
+ final String action = intent.getStringExtra(EXTRA_ACTION);
+ if (action == null) {
+ Log.e(TAG, "Missing extra '" + EXTRA_ACTION + "' on " + intent);
+ return;
+ }
+ final String receiverName = intent.getStringExtra(EXTRA_RECEIVER_NAME);
+ if (receiverName == null) {
+ Log.e(TAG, "Missing extra '" + EXTRA_RECEIVER_NAME + "' on " + intent);
+ return;
+ }
+ final int counter = getCounter(context, action, receiverName);
+ setResultData(String.valueOf(counter));
+ }
+
+ /**
+ * Sends a system notification containing actions with pending intents to launch the app's
+ * main activitiy or service.
+ */
+ private void sendNotification(Context context, Intent intent) {
+ final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
+ final Intent serviceIntent = new Intent(context, MyService.class);
+ final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent, 0);
+ final Bundle badBundle = new Bundle();
+ badBundle.putCharSequence("parcelable", "I am not");
+ final Action action = new Action.Builder(
+ R.drawable.ic_notification, "ACTION", pendingIntent)
+ .addExtras(badBundle)
+ .build();
+
+ final Notification notification = new Notification.Builder(context)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setContentTitle("Light, Cameras...")
+ .setContentIntent(pendingIntent)
+ .addAction(action)
+ .build();
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+ .notify(notificationId, notification);
+ }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
new file mode 100644
index 0000000..b88c45d
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import static com.android.cts.net.hostside.app2.Common.TAG;
+import android.R;
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * Service used to change app state to FOREGROUND_SERVICE.
+ */
+public class MyForegroundService extends Service {
+
+ private static final int FLAG_START_FOREGROUND = 1;
+ private static final int FLAG_STOP_FOREGROUND = 2;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.v(TAG, "MyForegroundService.onStartCommand(): " + intent);
+ switch (intent.getFlags()) {
+ case FLAG_START_FOREGROUND:
+ Log.d(TAG, "Starting foreground");
+ startForeground(42, new Notification.Builder(this)
+ .setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
+ .build());
+ break;
+ case FLAG_STOP_FOREGROUND:
+ Log.d(TAG, "Stopping foreground");
+ stopForeground(true);
+ break;
+ default:
+ Log.wtf(TAG, "Invalid flag on intent " + intent);
+ }
+ return START_STICKY;
+ }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
new file mode 100644
index 0000000..e6454c7
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.net.hostside.app2.Common.DYNAMIC_RECEIVER;
+import static com.android.cts.net.hostside.app2.Common.TAG;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * Service used to dynamically register a broadcast receiver.
+ */
+public class MyService extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.d(TAG, "MyService.onStartCommand: " + intent);
+ final Context context = getApplicationContext();
+ final MyBroadcastReceiver myReceiver = new MyBroadcastReceiver(DYNAMIC_RECEIVER);
+ context.registerReceiver(myReceiver, new IntentFilter(ACTION_RECEIVER_READY));
+ context.registerReceiver(myReceiver, new IntentFilter(ACTION_RESTRICT_BACKGROUND_CHANGED));
+ Log.d(TAG, "receiver registered");
+ return START_STICKY;
+ }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java
new file mode 100644
index 0000000..b1b7d77
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside.app2;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.cts.net.hostside.IRemoteSocketFactory;
+
+import java.net.Socket;
+
+
+public class RemoteSocketFactoryService extends Service {
+
+ private static final String TAG = RemoteSocketFactoryService.class.getSimpleName();
+
+ private IRemoteSocketFactory.Stub mBinder = new IRemoteSocketFactory.Stub() {
+ @Override
+ public ParcelFileDescriptor openSocketFd(String host, int port, int timeoutMs) {
+ try {
+ Socket s = new Socket(host, port);
+ s.setSoTimeout(timeoutMs);
+ return ParcelFileDescriptor.fromSocket(s);
+ } catch (Exception e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public String getPackageName() {
+ return RemoteSocketFactoryService.this.getPackageName();
+ }
+
+ @Override
+ public int getUid() {
+ return Process.myUid();
+ }
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
new file mode 100644
index 0000000..e96537c
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+import com.android.cts.migration.MigrationHelper;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.io.FileNotFoundException;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+abstract class HostsideNetworkTestCase extends DeviceTestCase implements IAbiReceiver,
+ IBuildReceiver {
+ protected static final boolean DEBUG = false;
+ protected static final String TAG = "HostsideNetworkTests";
+ protected static final String TEST_PKG = "com.android.cts.net.hostside";
+ protected static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
+ protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
+ protected static final String TEST_APP2_APK = "CtsHostsideNetworkTestsApp2.apk";
+
+ private IAbi mAbi;
+ private IBuildInfo mCtsBuild;
+
+ @Override
+ public void setAbi(IAbi abi) {
+ mAbi = abi;
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = buildInfo;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ assertNotNull(mAbi);
+ assertNotNull(mCtsBuild);
+
+ uninstallPackage(TEST_PKG, false);
+ installPackage(TEST_APK);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ uninstallPackage(TEST_PKG, true);
+ }
+
+ protected void installPackage(String apk) throws FileNotFoundException,
+ DeviceNotAvailableException {
+ assertNull(getDevice().installPackage(MigrationHelper.getTestFile(mCtsBuild, apk), false));
+ }
+
+ protected void uninstallPackage(String packageName, boolean shouldSucceed)
+ throws DeviceNotAvailableException {
+ final String result = getDevice().uninstallPackage(packageName);
+ if (shouldSucceed) {
+ assertNull("uninstallPackage(" + packageName + ") failed: " + result, result);
+ }
+ }
+
+ protected void assertPackageUninstalled(String packageName) throws DeviceNotAvailableException,
+ InterruptedException {
+ final String command = "cmd package list packages " + packageName;
+ final int max_tries = 5;
+ for (int i = 1; i <= max_tries; i++) {
+ final String result = runCommand(command);
+ if (result.trim().isEmpty()) {
+ return;
+ }
+ // 'list packages' filters by substring, so we need to iterate with the results
+ // and check one by one, otherwise 'com.android.cts.net.hostside' could return
+ // 'com.android.cts.net.hostside.app2'
+ boolean found = false;
+ for (String line : result.split("[\\r\\n]+")) {
+ if (line.endsWith(packageName)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return;
+ }
+ i++;
+ Log.v(TAG, "Package " + packageName + " not uninstalled yet (" + result
+ + "); sleeping 1s before polling again");
+ Thread.sleep(1000);
+ }
+ fail("Package '" + packageName + "' not uinstalled after " + max_tries + " seconds");
+ }
+
+ protected void runDeviceTests(String packageName, String testClassName)
+ throws DeviceNotAvailableException {
+ runDeviceTests(packageName, testClassName, null);
+ }
+
+ protected void runDeviceTests(String packageName, String testClassName, String methodName)
+ throws DeviceNotAvailableException {
+ RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
+ "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
+
+ if (testClassName != null) {
+ if (methodName != null) {
+ testRunner.setMethodName(testClassName, methodName);
+ } else {
+ testRunner.setClassName(testClassName);
+ }
+ }
+
+ final CollectingTestListener listener = new CollectingTestListener();
+ getDevice().runInstrumentationTests(testRunner, listener);
+
+ final TestRunResult result = listener.getCurrentRunResults();
+ if (result.isRunFailure()) {
+ throw new AssertionError("Failed to successfully run device tests for "
+ + result.getName() + ": " + result.getRunFailureMessage());
+ }
+
+ if (result.hasFailedTests()) {
+ // build a meaningful error message
+ StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+ for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+ result.getTestResults().entrySet()) {
+ if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+ errorBuilder.append(resultEntry.getKey().toString());
+ errorBuilder.append(":\n");
+ errorBuilder.append(resultEntry.getValue().getStackTrace());
+ }
+ }
+ throw new AssertionError(errorBuilder.toString());
+ }
+ }
+
+ private static final Pattern UID_PATTERN =
+ Pattern.compile(".*userId=([0-9]+)$", Pattern.MULTILINE);
+
+ protected int getUid(String packageName) throws DeviceNotAvailableException {
+ final String output = runCommand("dumpsys package " + packageName);
+ final Matcher matcher = UID_PATTERN.matcher(output);
+ while (matcher.find()) {
+ final String match = matcher.group(1);
+ return Integer.parseInt(match);
+ }
+ throw new RuntimeException("Did not find regexp '" + UID_PATTERN + "' on adb output\n"
+ + output);
+ }
+
+ protected String runCommand(String command) throws DeviceNotAvailableException {
+ Log.d(TAG, "Command: '" + command + "'");
+ final String output = getDevice().executeShellCommand(command);
+ if (DEBUG) Log.v(TAG, "Output: " + output.trim());
+ return output;
+ }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTests.java
deleted file mode 100644
index a7698f3..0000000
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTests.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.net;
-
-import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.ddmlib.testrunner.TestResult;
-import com.android.ddmlib.testrunner.TestResult.TestStatus;
-import com.android.ddmlib.testrunner.TestRunResult;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.result.CollectingTestListener;
-
-import java.util.Map;
-
-public class HostsideNetworkTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
- private static final String TEST_PKG = "com.android.cts.net.hostside";
- private static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
-
- private IAbi mAbi;
- private CtsBuildHelper mCtsBuild;
-
- @Override
- public void setAbi(IAbi abi) {
- mAbi = abi;
- }
-
- @Override
- public void setBuild(IBuildInfo buildInfo) {
- mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
-
- assertNotNull(mAbi);
- assertNotNull(mCtsBuild);
-
- getDevice().uninstallPackage(TEST_PKG);
-
- assertNull(getDevice().installPackage(mCtsBuild.getTestApp(TEST_APK), false));
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
-
- getDevice().uninstallPackage(TEST_PKG);
- }
-
- public void testVpn() throws Exception {
- runDeviceTests(TEST_PKG, ".VpnTest");
- }
-
- public void runDeviceTests(String packageName, String testClassName)
- throws DeviceNotAvailableException {
- RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
- "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
-
- final CollectingTestListener listener = new CollectingTestListener();
- getDevice().runInstrumentationTests(testRunner, listener);
-
- final TestRunResult result = listener.getCurrentRunResults();
- if (result.isRunFailure()) {
- throw new AssertionError("Failed to successfully run device tests for "
- + result.getName() + ": " + result.getRunFailureMessage());
- }
-
- if (result.hasFailedTests()) {
- // build a meaningful error message
- StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
- for (Map.Entry<TestIdentifier, TestResult> resultEntry :
- result.getTestResults().entrySet()) {
- if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
- errorBuilder.append(resultEntry.getKey().toString());
- errorBuilder.append(":\n");
- errorBuilder.append(resultEntry.getValue().getStackTrace());
- }
- }
- throw new AssertionError(errorBuilder.toString());
- }
- }
-}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
new file mode 100644
index 0000000..7d5f817
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+public class HostsideRestrictBackgroundNetworkTests extends HostsideNetworkTestCase {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ uninstallPackage(TEST_APP2_PKG, false);
+ installPackage(TEST_APP2_APK);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ uninstallPackage(TEST_APP2_PKG, true);
+ }
+
+ /**************************
+ * Data Saver Mode tests. *
+ **************************/
+
+ public void testDataSaverMode_disabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_disabled");
+ }
+
+ public void testDataSaverMode_whitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_whitelisted");
+ }
+
+ public void testDataSaverMode_enabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_enabled");
+ }
+
+ public void testDataSaverMode_blacklisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_blacklisted");
+ }
+
+ public void testDataSaverMode_reinstall() throws Exception {
+ final int oldUid = getUid(TEST_APP2_PKG);
+
+ // Make sure whitelist is revoked when package is removed
+ addRestrictBackgroundWhitelist(oldUid);
+
+ uninstallPackage(TEST_APP2_PKG, true);
+ assertPackageUninstalled(TEST_APP2_PKG);
+ assertRestrictBackgroundWhitelist(oldUid, false);
+
+ installPackage(TEST_APP2_APK);
+ final int newUid = getUid(TEST_APP2_PKG);
+ assertRestrictBackgroundWhitelist(oldUid, false);
+ assertRestrictBackgroundWhitelist(newUid, false);
+ }
+
+ public void testDataSaverMode_requiredWhitelistedPackages() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+ "testGetRestrictBackgroundStatus_requiredWhitelistedPackages");
+ }
+
+ /*****************************
+ * Battery Saver Mode tests. *
+ *****************************/
+
+ public void testBatterySaverModeMetered_disabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ public void testBatterySaverModeMetered_whitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ public void testBatterySaverModeMetered_enabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ public void testBatterySaverMode_reinstall() throws Exception {
+ if (!isDozeModeEnabled()) {
+ Log.w(TAG, "testBatterySaverMode_reinstall() skipped because device does not support "
+ + "Doze Mode");
+ return;
+ }
+
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+
+ uninstallPackage(TEST_APP2_PKG, true);
+ assertPackageUninstalled(TEST_APP2_PKG);
+ assertPowerSaveModeWhitelist(TEST_APP2_PKG, false);
+
+ installPackage(TEST_APP2_APK);
+ assertPowerSaveModeWhitelist(TEST_APP2_PKG, false);
+ }
+
+ public void testBatterySaverModeNonMetered_disabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ public void testBatterySaverModeNonMetered_whitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ public void testBatterySaverModeNonMetered_enabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ /*******************
+ * App idle tests. *
+ *******************/
+
+ public void testAppIdleMetered_disabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ public void testAppIdleMetered_whitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ public void testAppIdleMetered_enabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
+ // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
+ // public void testAppIdle_reinstall() throws Exception {
+ // }
+
+ public void testAppIdleNonMetered_disabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ public void testAppIdleNonMetered_whitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ public void testAppIdleNonMetered_enabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ /********************
+ * Doze Mode tests. *
+ ********************/
+
+ public void testDozeModeMetered_disabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ public void testDozeModeMetered_whitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ public void testDozeModeMetered_enabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ public void testDozeModeMetered_enabledButWhitelistedOnNotificationAction() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+ "testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
+ }
+
+ // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
+ // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
+ // public void testDozeMode_reinstall() throws Exception {
+ // }
+
+ public void testDozeModeNonMetered_disabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ "testBackgroundNetworkAccess_disabled");
+ }
+
+ public void testDozeModeNonMetered_whitelisted() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ "testBackgroundNetworkAccess_whitelisted");
+ }
+
+ public void testDozeModeNonMetered_enabled() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ "testBackgroundNetworkAccess_enabled");
+ }
+
+ public void testDozeModeNonMetered_enabledButWhitelistedOnNotificationAction()
+ throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+ "testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
+ }
+
+ /**********************
+ * Mixed modes tests. *
+ **********************/
+
+ public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDataAndBatterySaverModes_meteredNetwork");
+ }
+
+ public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDataAndBatterySaverModes_nonMeteredNetwork");
+ }
+
+ /*******************
+ * Helper methods. *
+ *******************/
+
+ private void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception {
+ final int max_tries = 5;
+ boolean actual = false;
+ for (int i = 1; i <= max_tries; i++) {
+ final String output = runCommand("cmd netpolicy list restrict-background-whitelist ");
+ actual = output.contains(Integer.toString(uid));
+ if (expected == actual) {
+ return;
+ }
+ Log.v(TAG, "whitelist check for uid " + uid + " doesn't match yet (expected "
+ + expected + ", got " + actual + "); sleeping 1s before polling again");
+ Thread.sleep(1000);
+ }
+ fail("whitelist check for uid " + uid + " failed: expected "
+ + expected + ", got " + actual);
+ }
+
+ private void assertPowerSaveModeWhitelist(String packageName, boolean expected)
+ throws Exception {
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ assertDelayedCommand("dumpsys deviceidle whitelist =" + packageName,
+ Boolean.toString(expected));
+ }
+
+ /**
+ * Asserts the result of a command, wait and re-running it a couple times if necessary.
+ */
+ private void assertDelayedCommand(String command, String expectedResult)
+ throws InterruptedException, DeviceNotAvailableException {
+ final int maxTries = 5;
+ for (int i = 1; i <= maxTries; i++) {
+ final String result = runCommand(command).trim();
+ if (result.equals(expectedResult)) return;
+ Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '"
+ + expectedResult + "' on attempt #; sleeping 1s before polling again");
+ Thread.sleep(1000);
+ }
+ fail("Command '" + command + "' did not return '" + expectedResult + "' after " + maxTries
+ + " attempts");
+ }
+
+ protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
+ runCommand("cmd netpolicy add restrict-background-whitelist " + uid);
+ assertRestrictBackgroundWhitelist(uid, true);
+ }
+
+ private void addPowerSaveModeWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Adding package " + packageName + " to power-save-mode whitelist");
+ // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+ // need to use netpolicy for whitelisting
+ runCommand("dumpsys deviceidle whitelist +" + packageName);
+ assertPowerSaveModeWhitelist(packageName, true); // Sanity check
+ }
+
+ protected boolean isDozeModeEnabled() throws Exception {
+ final String result = runCommand("cmd deviceidle enabled deep").trim();
+ return result.equals("1");
+ }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
new file mode 100644
index 0000000..69b07af
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+public class HostsideVpnTests extends HostsideNetworkTestCase {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ uninstallPackage(TEST_APP2_PKG, false);
+ installPackage(TEST_APP2_APK);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ uninstallPackage(TEST_APP2_PKG, true);
+ }
+
+ public void testDefault() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testDefault");
+ }
+
+ public void testAppAllowed() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testAppAllowed");
+ }
+
+ public void testAppDisallowed() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testAppDisallowed");
+ }
+}
diff --git a/tests/cts/net/Android.mk b/tests/cts/net/Android.mk
index 6524871..c553a9b 100644
--- a/tests/cts/net/Android.mk
+++ b/tests/cts/net/Android.mk
@@ -27,7 +27,7 @@
LOCAL_JAVA_LIBRARIES := voip-common conscrypt org.apache.http.legacy
LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libnativedns_jni \
- libnativemultinetwork_jni
+ libnativemultinetwork_jni libnativehelper_compat_libc++
# include CtsTestServer as a temporary hack to free net.cts from cts.stub.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -40,6 +40,9 @@
# uncomment when b/13249961 is fixed
#LOCAL_SDK_VERSION := current
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
include $(BUILD_CTS_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index 001e294..dd310a1 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -16,7 +16,7 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.cts.net">
+ package="android.net.cts">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -28,14 +28,21 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application>
<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="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.cts.net"
+ android:targetPackage="android.net.cts"
android:label="CTS tests of android.net">
<meta-data android:name="listener"
android:value="com.android.cts.runner.CtsTestRunListener" />
diff --git a/tests/cts/net/AndroidTest.xml b/tests/cts/net/AndroidTest.xml
new file mode 100644
index 0000000..389b926
--- /dev/null
+++ b/tests/cts/net/AndroidTest.xml
@@ -0,0 +1,25 @@
+<!-- Copyright (C) 2015 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 test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+ <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" />
+ <option name="runtime-hint" value="9m4s" />
+ </test>
+</configuration>
diff --git a/tests/cts/net/appForApi23/Android.mk b/tests/cts/net/appForApi23/Android.mk
new file mode 100644
index 0000000..f0d3535
--- /dev/null
+++ b/tests/cts/net/appForApi23/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2016 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Include both the 32 and 64 bit versions
+LOCAL_MULTILIB := both
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsNetTestAppForApi23
+
+LOCAL_SDK_VERSION := 23
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/cts/net/appForApi23/AndroidManifest.xml b/tests/cts/net/appForApi23/AndroidManifest.xml
new file mode 100644
index 0000000..ed4cedb
--- /dev/null
+++ b/tests/cts/net/appForApi23/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2016 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.appForApi23">
+
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
+ <application>
+ <receiver android:name=".ConnectivityReceiver">
+ <intent-filter>
+ <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.net.cts.appForApi23.getWifiConnectivityActionCount" />
+ </intent-filter>
+ </receiver>
+
+ <activity android:name=".ConnectivityListeningActivity"
+ android:label="ConnectivityListeningActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ </application>
+
+</manifest>
+
diff --git a/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityListeningActivity.java b/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityListeningActivity.java
new file mode 100644
index 0000000..24fb68e8
--- /dev/null
+++ b/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityListeningActivity.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2016 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.appForApi23;
+
+import android.app.Activity;
+
+// Stub activity used to start the app
+public class ConnectivityListeningActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityReceiver.java b/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityReceiver.java
new file mode 100644
index 0000000..8039a4f
--- /dev/null
+++ b/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityReceiver.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2016 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.appForApi23;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+
+public class ConnectivityReceiver extends BroadcastReceiver {
+ public static String GET_WIFI_CONNECTIVITY_ACTION_COUNT =
+ "android.net.cts.appForApi23.getWifiConnectivityActionCount";
+
+ private static int sWifiConnectivityActionCount = 0;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+ int networkType = intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 0);
+ if (networkType == ConnectivityManager.TYPE_WIFI) {
+ sWifiConnectivityActionCount++;
+ }
+ }
+ if (GET_WIFI_CONNECTIVITY_ACTION_COUNT.equals(intent.getAction())) {
+ setResultCode(sWifiConnectivityActionCount);
+ }
+ }
+}
diff --git a/tests/cts/net/jni/Android.mk b/tests/cts/net/jni/Android.mk
index ca82b30..0ec8d28 100644
--- a/tests/cts/net/jni/Android.mk
+++ b/tests/cts/net/jni/Android.mk
@@ -25,8 +25,8 @@
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_SHARED_LIBRARIES := libnativehelper liblog
-
+LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog
+LOCAL_CXX_STL := libc++_static
include $(BUILD_SHARED_LIBRARY)
include $(CLEAR_VARS)
@@ -35,5 +35,6 @@
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := NativeMultinetworkJni.c
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_SHARED_LIBRARIES := libandroid libnativehelper liblog
+LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog
+LOCAL_CXX_STL := libc++_static
include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/cts/net/native/qtaguid/Android.mk b/tests/cts/net/native/qtaguid/Android.mk
index 4a09906..4f5bf9f 100644
--- a/tests/cts/net/native/qtaguid/Android.mk
+++ b/tests/cts/net/native/qtaguid/Android.mk
@@ -16,7 +16,7 @@
LOCAL_PATH:= $(call my-dir)
-test_executable := NativeNetTest
+test_executable := CtsNativeNetTestCases
list_executable := $(test_executable)_list
include $(CLEAR_VARS)
@@ -46,6 +46,9 @@
LOCAL_CTS_TEST_PACKAGE := android.net.native
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
LOCAL_CFLAGS := -Werror -Wall
include $(BUILD_CTS_EXECUTABLE)
diff --git a/tests/cts/net/native/qtaguid/AndroidTest.xml b/tests/cts/net/native/qtaguid/AndroidTest.xml
new file mode 100644
index 0000000..6f4e8f5
--- /dev/null
+++ b/tests/cts/net/native/qtaguid/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS Native Network xt_qtaguid test cases">
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="push" value="CtsNativeNetTestCases->/data/local/tmp/CtsNativeNetTestCases" />
+ <option name="append-bitness" value="true" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="CtsNativeNetTestCases" />
+ <option name="runtime-hint" value="1m" />
+ </test>
+</configuration>
diff --git a/tests/cts/net/src/android/net/cts/AirplaneModeTest.java b/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
new file mode 100644
index 0000000..0a3146c
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.lang.Thread;
+
+public class AirplaneModeTest extends AndroidTestCase {
+ private static final String TAG = "AirplaneModeTest";
+ private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
+ private static final String FEATURE_WIFI = "android.hardware.wifi";
+ private static final int TIMEOUT_MS = 10 * 1000;
+ private boolean mHasFeature;
+ private Context mContext;
+ private ContentResolver resolver;
+
+ public void setup() {
+ mContext= getContext();
+ resolver = mContext.getContentResolver();
+ mHasFeature = (mContext.getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH)
+ || mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI));
+ }
+
+ public void testAirplaneMode() {
+ setup();
+ if (!mHasFeature) {
+ Log.i(TAG, "The device doesn't support network bluetooth or wifi feature");
+ return;
+ }
+
+ for (int testCount = 0; testCount < 2; testCount++) {
+ if (!doOneTest()) {
+ fail("Airplane mode failed to change in " + TIMEOUT_MS + "msec");
+ return;
+ }
+ }
+ }
+
+ private boolean doOneTest() {
+ boolean airplaneModeOn = isAirplaneModeOn();
+ setAirplaneModeOn(!airplaneModeOn);
+
+ try {
+ Thread.sleep(TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Sleep time interrupted.", e);
+ }
+
+ if (airplaneModeOn == isAirplaneModeOn()) {
+ return false;
+ }
+ return true;
+ }
+
+ private void setAirplaneModeOn(boolean enabling) {
+ // Change the system setting for airplane mode
+ Settings.Global.putInt(resolver, Settings.Global.AIRPLANE_MODE_ON, enabling ? 1 : 0);
+ }
+
+ private boolean isAirplaneModeOn() {
+ // Read the system setting for airplane mode
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 9a99c22..185ebfa 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -21,6 +21,7 @@
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -35,18 +36,22 @@
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
import android.net.wifi.WifiManager;
+import android.os.SystemProperties;
+import android.system.Os;
+import android.system.OsConstants;
import android.test.AndroidTestCase;
import android.util.Log;
-import android.os.SystemProperties;
import com.android.internal.telephony.PhoneConstants;
-import java.util.ArrayList;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.InetSocketAddress;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class ConnectivityManagerTest extends AndroidTestCase {
@@ -57,15 +62,29 @@
public static final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE;
public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI;
+
private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1
+ 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;
+ 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";
// Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent.
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;
+ private Context mContext;
private ConnectivityManager mCm;
private WifiManager mWifiManager;
private PackageManager mPackageManager;
@@ -75,13 +94,14 @@
@Override
protected void setUp() throws Exception {
super.setUp();
- mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
- mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
- mPackageManager = getContext().getPackageManager();
+ mContext = getContext();
+ mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mPackageManager = mContext.getPackageManager();
// Get com.android.internal.R.array.networkAttributes
- int resId = getContext().getResources().getIdentifier("networkAttributes", "array", "android");
- String[] naStrings = getContext().getResources().getStringArray(resId);
+ int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
+ String[] naStrings = mContext.getResources().getStringArray(resId);
//TODO: What is the "correct" way to determine if this is a wifi only device?
boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false);
for (String naString : naStrings) {
@@ -249,6 +269,12 @@
mCm.getBackgroundDataSetting();
}
+ private NetworkRequest makeWifiNetworkRequest() {
+ return new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .build();
+ }
+
/**
* Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to
* see if we get a callback for the TRANSPORT_WIFI transport type being available.
@@ -265,13 +291,14 @@
}
// We will register for a WIFI network being available or lost.
- NetworkRequest request = new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .build();
- TestNetworkCallback callback = new TestNetworkCallback();
- mCm.registerNetworkCallback(request, callback);
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
- boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+ final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultTrackingCallback);
+
+ final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+ Network wifiNetwork = null;
try {
// Make sure WiFi is connected to an access point to start with.
@@ -282,16 +309,21 @@
// Now we should expect to get a network callback about availability of the wifi
// network even if it was already connected as a state-based action when the callback
// is registered.
- assertTrue("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI",
- callback.waitForAvailable());
+ wifiNetwork = callback.waitForAvailable();
+ assertNotNull("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI",
+ wifiNetwork);
+
+ assertNotNull("Did not receive NetworkCallback.onAvailable for any default network",
+ defaultTrackingCallback.waitForAvailable());
} catch (InterruptedException e) {
fail("Broadcast receiver or NetworkCallback wait was interrupted.");
} finally {
mCm.unregisterNetworkCallback(callback);
+ mCm.unregisterNetworkCallback(defaultTrackingCallback);
- // Return WiFI to its original enabled/disabled state.
+ // Return WiFi to its original enabled/disabled state.
if (!previousWifiEnabledState) {
- disconnectFromWifi();
+ disconnectFromWifi(wifiNetwork);
}
}
}
@@ -322,12 +354,9 @@
mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
// We will register for a WIFI network being available or lost.
- NetworkRequest request = new NetworkRequest.Builder()
- .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
- .build();
- mCm.registerNetworkCallback(request, pendingIntent);
+ mCm.registerNetworkCallback(makeWifiNetworkRequest(), pendingIntent);
- boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+ final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
try {
// Make sure WiFi is connected to an access point to start with.
@@ -347,15 +376,105 @@
pendingIntent.cancel();
mContext.unregisterReceiver(receiver);
- // Return WiFI to its original enabled/disabled state.
+ // Return WiFi to its original enabled/disabled state.
if (!previousWifiEnabledState) {
- disconnectFromWifi();
+ disconnectFromWifi(null);
}
}
}
+ /**
+ * Tests reporting of connectivity changed.
+ */
+ public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() {
+ if (!mPackageManager.hasSystemFeature(PackageManager.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(PackageManager.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(PackageManager.FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
+ Intent startIntent = new Intent();
+ startIntent.setComponent(new ComponentName("android.net.cts.appForApi23",
+ "android.net.cts.appForApi23.ConnectivityListeningActivity"));
+ mContext.startActivity(startIntent);
+
+ 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 void connectToWifi() {
+ 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();
@@ -365,36 +484,94 @@
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 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;
+ }
+
+ 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"));
}
/** Disable WiFi and wait for it to become disconnected from the network. */
- private void disconnectFromWifi() {
+ 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());
+ }
+ }
}
/**
@@ -461,15 +638,48 @@
*/
private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
+ private final CountDownLatch mLostLatch = new CountDownLatch(1);
- public boolean waitForAvailable() throws InterruptedException {
- return mAvailableLatch.await(30, TimeUnit.SECONDS);
+ 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;
}
@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();
+ }
+ }
+
+ 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;
}
/** Verify restricted networks cannot be requested. */
@@ -486,8 +696,6 @@
try {
mCm.requestNetwork(request, callback);
fail("No exception thrown when restricted network requested.");
- } catch (SecurityException e) {
- // Expected.
- }
+ } catch (SecurityException expected) {}
}
}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityReceiver.java b/tests/cts/net/src/android/net/cts/ConnectivityReceiver.java
new file mode 100644
index 0000000..6a7b4a0
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/ConnectivityReceiver.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ConnectivityReceiver extends BroadcastReceiver {
+ static boolean sReceivedConnectivity;
+ static boolean sReceivedFinal;
+ static CountDownLatch sLatch;
+
+ static void prepare() {
+ synchronized (ConnectivityReceiver.class) {
+ sReceivedConnectivity = sReceivedFinal = false;
+ sLatch = new CountDownLatch(1);
+ }
+ }
+
+ static boolean waitForBroadcast() {
+ try {
+ sLatch.await(30, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new IllegalStateException(e);
+ }
+ synchronized (ConnectivityReceiver.class) {
+ sLatch = null;
+ if (!sReceivedFinal) {
+ throw new IllegalStateException("Never received final broadcast");
+ }
+ return sReceivedConnectivity;
+ }
+ }
+
+ static final String FINAL_ACTION = "android.net.cts.action.FINAL";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i("ConnectivityReceiver", "Received: " + intent.getAction());
+ if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+ sReceivedConnectivity = true;
+ } else if (FINAL_ACTION.equals(intent.getAction())) {
+ sReceivedFinal = true;
+ if (sLatch != null) {
+ sLatch.countDown();
+ }
+ }
+ }
+}
diff --git a/tests/cts/net/src/android/net/cts/DnsTest.java b/tests/cts/net/src/android/net/cts/DnsTest.java
index fed0a8d..8575c33 100644
--- a/tests/cts/net/src/android/net/cts/DnsTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsTest.java
@@ -62,8 +62,8 @@
try {
addrs = InetAddress.getAllByName("www.google.com");
} catch (UnknownHostException e) {}
- assertTrue("[RERUN] DNS could not resolve www.gooogle.com. Check internet connection",
- addrs.length != 0);
+ assertTrue("[RERUN] DNS could not resolve www.google.com. Check internet connection",
+ addrs.length != 0);
boolean foundV4 = false, foundV6 = false;
for (InetAddress addr : addrs) {
if (addr instanceof Inet4Address) foundV4 = true;
@@ -71,11 +71,8 @@
if (DBG) Log.e(TAG, "www.google.com gave " + addr.toString());
}
- // assertTrue(foundV4);
- // assertTrue(foundV6);
-
// We should have at least one of the addresses to connect!
- assertTrue(foundV4 || foundV6);
+ assertTrue("www.google.com must have IPv4 and/or IPv6 address", foundV4 || foundV6);
// Skip the rest of the test if the active network for watch is PROXY.
// TODO: Check NetworkInfo type in addition to type name once ag/601257 is merged.
@@ -85,21 +82,22 @@
return;
}
+ // Clear test state so we don't get confused with the previous results.
+ addrs = new InetAddress[0];
+ foundV4 = foundV6 = false;
try {
addrs = InetAddress.getAllByName("ipv6.google.com");
} catch (UnknownHostException e) {}
- assertTrue(addrs.length != 0);
- foundV4 = false;
- foundV6 = false;
+ assertTrue("[RERUN] DNS could not resolve ipv6.google.com, check the network supports IPv6",
+ addrs.length != 0);
for (InetAddress addr : addrs) {
- if (addr instanceof Inet4Address) foundV4 = true;
- else if (addr instanceof Inet6Address) foundV6 = true;
+ assertFalse ("[RERUN] ipv6.google.com returned IPv4 address: " + addr.getHostAddress() +
+ ", check your network's DNS server", addr instanceof Inet4Address);
+ foundV6 |= (addr instanceof Inet6Address);
if (DBG) Log.e(TAG, "ipv6.google.com gave " + addr.toString());
}
- assertTrue("[RERUN] ipv6.google.com returned an ipv4 address, check your network's DNS connection.",
- foundV4 == false);
- assertTrue(foundV6 == true);
+ assertTrue(foundV6);
assertTrue(testNativeDns());
}
diff --git a/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java b/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java
index dc7be1f..103d1da 100644
--- a/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java
+++ b/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java
@@ -15,28 +15,27 @@
*/
package android.net.cts;
+import junit.framework.TestCase;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
-import android.test.AndroidTestCase;
-public class LocalServerSocketTest extends AndroidTestCase {
+public class LocalServerSocketTest extends TestCase {
public void testLocalServerSocket() throws IOException {
- LocalServerSocket localServerSocket = new LocalServerSocket(LocalSocketTest.mSockAddr);
+ String address = "com.android.net.LocalServerSocketTest_testLocalServerSocket";
+ LocalServerSocket localServerSocket = new LocalServerSocket(address);
assertNotNull(localServerSocket.getLocalSocketAddress());
- commonFunctions(localServerSocket);
- }
- public void commonFunctions(LocalServerSocket localServerSocket) throws IOException {
// create client socket
LocalSocket clientSocket = new LocalSocket();
// establish connection between client and server
- clientSocket.connect(new LocalSocketAddress(LocalSocketTest.mSockAddr));
+ clientSocket.connect(new LocalSocketAddress(address));
LocalSocket serverSocket = localServerSocket.accept();
// send data from client to server
diff --git a/tests/cts/net/src/android/net/cts/LocalSocketTest.java b/tests/cts/net/src/android/net/cts/LocalSocketTest.java
index 865ec21..77f0a44 100644
--- a/tests/cts/net/src/android/net/cts/LocalSocketTest.java
+++ b/tests/cts/net/src/android/net/cts/LocalSocketTest.java
@@ -16,26 +16,31 @@
package android.net.cts;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import junit.framework.TestCase;
+
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
-import android.test.AndroidTestCase;
-public class LocalSocketTest extends AndroidTestCase{
- public final static String mSockAddr = "com.android.net.LocalSocketTest";
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
- public void testLocalConnections() throws IOException{
+public class LocalSocketTest extends TestCase {
+ private final static String ADDRESS_PREFIX = "com.android.net.LocalSocketTest";
+
+ public void testLocalConnections() throws IOException {
+ String address = ADDRESS_PREFIX + "_testLocalConnections";
// create client and server socket
- LocalServerSocket localServerSocket = new LocalServerSocket(mSockAddr);
+ LocalServerSocket localServerSocket = new LocalServerSocket(address);
LocalSocket clientSocket = new LocalSocket();
// establish connection between client and server
- LocalSocketAddress locSockAddr = new LocalSocketAddress(mSockAddr);
+ LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
assertFalse(clientSocket.isConnected());
clientSocket.connect(locSockAddr);
assertTrue(clientSocket.isConnected());
@@ -111,9 +116,10 @@
}
}
- public void testAccessors() throws IOException{
+ public void testAccessors() throws IOException {
+ String address = ADDRESS_PREFIX + "_testAccessors";
LocalSocket socket = new LocalSocket();
- LocalSocketAddress addr = new LocalSocketAddress("secondary");
+ LocalSocketAddress addr = new LocalSocketAddress(address);
assertFalse(socket.isBound());
socket.bind(addr);
@@ -129,9 +135,9 @@
socket.setSendBufferSize(3998);
assertEquals(3998 << 1, socket.getSendBufferSize());
- // Timeout is not support at present, so set is ignored
- socket.setSoTimeout(1996);
assertEquals(0, socket.getSoTimeout());
+ socket.setSoTimeout(1996);
+ assertTrue(socket.getSoTimeout() > 0);
try {
socket.getRemoteSocketAddress();
@@ -167,5 +173,127 @@
} catch (UnsupportedOperationException e) {
// expected
}
+
+ socket.close();
+ }
+
+ public void testAvailable() throws Exception {
+ String address = ADDRESS_PREFIX + "_testAvailable";
+ LocalServerSocket localServerSocket = new LocalServerSocket(address);
+ LocalSocket clientSocket = new LocalSocket();
+
+ // establish connection between client and server
+ LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
+ clientSocket.connect(locSockAddr);
+ assertTrue(clientSocket.isConnected());
+ LocalSocket serverSocket = localServerSocket.accept();
+
+ OutputStream clientOutputStream = clientSocket.getOutputStream();
+ InputStream serverInputStream = serverSocket.getInputStream();
+ assertEquals(0, serverInputStream.available());
+
+ byte[] buffer = new byte[50];
+ clientOutputStream.write(buffer);
+ assertEquals(50, serverInputStream.available());
+
+ InputStream clientInputStream = clientSocket.getInputStream();
+ OutputStream serverOutputStream = serverSocket.getOutputStream();
+ assertEquals(0, clientInputStream.available());
+ serverOutputStream.write(buffer);
+ assertEquals(50, serverInputStream.available());
+
+ clientSocket.close();
+ serverSocket.close();
+ localServerSocket.close();
+ }
+
+ public void testFlush() throws Exception {
+ String address = ADDRESS_PREFIX + "_testFlush";
+ LocalServerSocket localServerSocket = new LocalServerSocket(address);
+ LocalSocket clientSocket = new LocalSocket();
+
+ // establish connection between client and server
+ LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
+ clientSocket.connect(locSockAddr);
+ assertTrue(clientSocket.isConnected());
+ LocalSocket serverSocket = localServerSocket.accept();
+
+ OutputStream clientOutputStream = clientSocket.getOutputStream();
+ InputStream serverInputStream = serverSocket.getInputStream();
+ testFlushWorks(clientOutputStream, serverInputStream);
+
+ OutputStream serverOutputStream = serverSocket.getOutputStream();
+ InputStream clientInputStream = clientSocket.getInputStream();
+ testFlushWorks(serverOutputStream, clientInputStream);
+
+ clientSocket.close();
+ serverSocket.close();
+ localServerSocket.close();
+ }
+
+ private void testFlushWorks(OutputStream outputStream, InputStream inputStream)
+ throws Exception {
+ final int bytesToTransfer = 50;
+ StreamReader inputStreamReader = new StreamReader(inputStream, bytesToTransfer);
+
+ byte[] buffer = new byte[bytesToTransfer];
+ outputStream.write(buffer);
+ assertEquals(bytesToTransfer, inputStream.available());
+
+ // Start consuming the data.
+ inputStreamReader.start();
+
+ // This doesn't actually flush any buffers, it just polls until the reader has read all the
+ // bytes.
+ outputStream.flush();
+
+ inputStreamReader.waitForCompletion(5000);
+ inputStreamReader.assertBytesRead(bytesToTransfer);
+ assertEquals(0, inputStream.available());
+ }
+
+ private static class StreamReader extends Thread {
+ private final InputStream is;
+ private final int expectedByteCount;
+ private final CountDownLatch completeLatch = new CountDownLatch(1);
+
+ private volatile Exception exception;
+ private int bytesRead;
+
+ private StreamReader(InputStream is, int expectedByteCount) {
+ this.is = is;
+ this.expectedByteCount = expectedByteCount;
+ }
+
+ @Override
+ public void run() {
+ try {
+ byte[] buffer = new byte[10];
+ int readCount;
+ while ((readCount = is.read(buffer)) >= 0) {
+ bytesRead += readCount;
+ if (bytesRead >= expectedByteCount) {
+ break;
+ }
+ }
+ } catch (IOException e) {
+ exception = e;
+ } finally {
+ completeLatch.countDown();
+ }
+ }
+
+ public void waitForCompletion(long waitMillis) throws Exception {
+ if (!completeLatch.await(waitMillis, TimeUnit.MILLISECONDS)) {
+ fail("Timeout waiting for completion");
+ }
+ if (exception != null) {
+ throw new Exception("Read failed", exception);
+ }
+ }
+
+ public void assertBytesRead(int expected) {
+ assertEquals(expected, bytesRead);
+ }
}
}
diff --git a/tests/cts/net/src/android/net/cts/TheaterModeTest.java b/tests/cts/net/src/android/net/cts/TheaterModeTest.java
new file mode 100644
index 0000000..10fca6f
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/TheaterModeTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import android.content.ContentResolver;
+import android.content.Context;
+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";
+ private static final String FEATURE_WIFI = "android.hardware.wifi";
+ private static final int TIMEOUT_MS = 10 * 1000;
+ private boolean mHasFeature;
+ private Context mContext;
+ private ContentResolver resolver;
+
+ public void setup() {
+ mContext= getContext();
+ resolver = mContext.getContentResolver();
+ mHasFeature = (mContext.getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH)
+ || mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI));
+ }
+
+ public void testTheaterMode() {
+ setup();
+ if (!mHasFeature) {
+ Log.i(TAG, "The device doesn't support network bluetooth or wifi feature");
+ return;
+ }
+
+ for (int testCount = 0; testCount < 2; testCount++) {
+ if (!doOneTest()) {
+ fail("Theater mode failed to change in " + TIMEOUT_MS + "msec");
+ return;
+ }
+ }
+ }
+
+ private boolean doOneTest() {
+ boolean theaterModeOn = isTheaterModeOn();
+
+ setTheaterModeOn(!theaterModeOn);
+ try {
+ Thread.sleep(TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Sleep time interrupted.", e);
+ }
+
+ if (theaterModeOn == isTheaterModeOn()) {
+ return false;
+ }
+ return true;
+ }
+
+ private void setTheaterModeOn(boolean enabling) {
+ // Change the system setting for theater mode
+ Settings.Global.putInt(resolver, Settings.Global.THEATER_MODE_ON, enabling ? 1 : 0);
+ }
+
+ private boolean isTheaterModeOn() {
+ // Read the system setting for theater mode
+ return Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.THEATER_MODE_ON, 0) != 0;
+ }
+}
diff --git a/tests/cts/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java b/tests/cts/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
index 9c0d774..99de614 100644
--- a/tests/cts/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
+++ b/tests/cts/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
@@ -17,61 +17,39 @@
package android.net.http.cts;
import android.net.http.X509TrustManagerExtensions;
-import android.util.Base64;
-
-import java.io.File;
-import java.io.ByteArrayInputStream;
import java.security.KeyStore;
-import java.security.cert.CertificateFactory;
+import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
import junit.framework.TestCase;
-import com.android.org.conscrypt.TrustedCertificateStore;
-import com.android.org.conscrypt.TrustManagerImpl;
-
public class X509TrustManagerExtensionsTest extends TestCase {
- public void testIsUserAddedCert() throws Exception {
- final String testCert =
- "MIICfjCCAeegAwIBAgIJAMefIzKHY5H4MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV" +
- "BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEPMA0G" +
- "A1UECgwGR2V3Z3VsMRMwEQYDVQQDDApnZXdndWwuY29tMB4XDTEzMTEwNTAwNDE0" +
- "MFoXDTEzMTIwNTAwNDE0MFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYw" +
- "FAYDVQQHDA1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQKDAZHZXdndWwxEzARBgNVBAMM" +
- "Cmdld2d1bC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKpc/I0Ss4sm" +
- "yV2iX5xRMM7+XXAhiWrceGair4MpvDrGIa1kFj2phtx4IqTfDnNU7AhRJYkDYmJQ" +
- "fUJ8i6F+I08uNiGVO4DtPJbZcBXg9ME9EMaJCslm995ueeNWSw1Ky8zM0tt4p+94" +
- "BcXJ7PC3N2WgkvtE8xwNbaeUfhGPzJKXAgMBAAGjUDBOMB0GA1UdDgQWBBQQ/iW7" +
- "JCkSI2sbn4nTBiZ9PSiO8zAfBgNVHSMEGDAWgBQQ/iW7JCkSI2sbn4nTBiZ9PSiO" +
- "8zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBABQBrUOWTCSIl3vkRR3w" +
- "3bPzh3BpqDmxH9xe4rZr+MVKKjpGjY1z2m2EEtyNz3tbgVQym5+si00DUHFL0IP1" +
- "SuRULmPyEpTBVbV+PA5Kc967ZcDgYt4JtdMcCeKbIFaU6r8oEYEL2PTlNZmgbunM" +
- "pXktkhVvNxZeSa8yM9bPhXkN";
+ private static X509TrustManager getFirstX509TrustManager(TrustManagerFactory tmf)
+ throws Exception {
+ for (TrustManager trustManager : tmf.getTrustManagers()) {
+ if (trustManager instanceof X509TrustManager) {
+ return (X509TrustManager) trustManager;
+ }
+ }
+ fail("Unable to find X509TrustManager");
+ return null;
+ }
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- X509Certificate cert = (X509Certificate)cf.generateCertificate(
- new ByteArrayInputStream(Base64.decode(testCert, Base64.DEFAULT)));
-
- // Test without adding cert to keystore.
- KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
- X509TrustManagerExtensions tmeNegative =
- new X509TrustManagerExtensions(new TrustManagerImpl(keyStore));
- assertEquals(false, tmeNegative.isUserAddedCertificate(cert));
-
- // Test with cert added to keystore.
- final File DIR_TEMP = new File(System.getProperty("java.io.tmpdir"));
- final File DIR_TEST = new File(DIR_TEMP, "test");
- final File system = new File(DIR_TEST, "system-test");
- final File added = new File(DIR_TEST, "added-test");
- final File deleted = new File(DIR_TEST, "deleted-test");
-
- TrustedCertificateStore tcs = new TrustedCertificateStore(system, added, deleted);
- added.mkdirs();
- tcs.installCertificate(cert);
- X509TrustManagerExtensions tmePositive =
- new X509TrustManagerExtensions(new TrustManagerImpl(keyStore, null, tcs));
- assertEquals(true, tmePositive.isUserAddedCertificate(cert));
+ public void testIsUserAddedCertificateDefaults() throws Exception {
+ final TrustManagerFactory tmf =
+ TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ tmf.init((KeyStore) null);
+ X509TrustManager tm = getFirstX509TrustManager(tmf);
+ X509TrustManagerExtensions xtm = new X509TrustManagerExtensions(tm);
+ // Verify that all the default system provided CAs are not marked as user added.
+ for (Certificate cert : tm.getAcceptedIssuers()) {
+ assertFalse(xtm.isUserAddedCertificate((X509Certificate) cert));
+ }
}
}
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 5accd77..a066ba8 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -20,12 +20,20 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.WifiP2pManager;
import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_STATE_DISABLED;
import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_STATE_ENABLED;
import android.test.AndroidTestCase;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
public class ConcurrencyTest extends AndroidTestCase {
private class MySync {
int expectedWifiState;
@@ -94,10 +102,7 @@
}
mContext.unregisterReceiver(mReceiver);
- if (mWifiManager.isWifiEnabled()) {
- assertTrue(mWifiManager.setWifiEnabled(false));
- Thread.sleep(DURATION);
- }
+ enableWifi();
super.tearDown();
}
@@ -114,6 +119,33 @@
}
}
+ /*
+ * Enables Wifi and block until connection is established.
+ */
+ private void enableWifi() throws InterruptedException {
+ if (!mWifiManager.isWifiEnabled()) {
+ assertTrue(mWifiManager.setWifiEnabled(true));
+ }
+
+ ConnectivityManager cm =
+ (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkRequest request =
+ new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ final CountDownLatch latch = new CountDownLatch(1);
+ NetworkCallback networkCallback = new NetworkCallback() {
+ @Override
+ public void onAvailable(Network network) {
+ latch.countDown();
+ }
+ };
+ cm.registerNetworkCallback(request, networkCallback);
+ latch.await(DURATION, TimeUnit.MILLISECONDS);
+
+ cm.unregisterNetworkCallback(networkCallback);
+ }
+
public void testConcurrency() {
// Cannot support p2p alone
if (!WifiFeature.isWifiSupported(getContext())) {
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 e132cce..2e2e75b 100644
--- a/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java
@@ -24,6 +24,7 @@
import java.io.IOException;
import java.net.ServerSocket;
+import java.util.Arrays;
import java.util.Random;
import java.util.List;
import java.util.ArrayList;
@@ -41,6 +42,7 @@
NsdManager.RegistrationListener mRegistrationListener;
NsdManager.DiscoveryListener mDiscoveryListener;
NsdManager.ResolveListener mResolveListener;
+ private NsdServiceInfo mResolvedService;
public NsdManagerTest() {
initRegistrationListener();
@@ -119,6 +121,7 @@
@Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
+ mResolvedService = serviceInfo;
setEvent("onServiceResolved", serviceInfo);
}
};
@@ -254,14 +257,87 @@
if (DBG) Log.d(TAG, "Tear down test ...");
}
- public void runTest() throws Exception {
+ public void testNDSManager() throws Exception {
+ EventData lastEvent = null;
+
+ if (DBG) Log.d(TAG, "Starting test ...");
+
NsdServiceInfo si = new NsdServiceInfo();
si.setServiceType(SERVICE_TYPE);
si.setServiceName(mServiceName);
- EventData lastEvent = null;
+ byte testByteArray[] = new byte[] {-128, 127, 2, 1, 0, 1, 2};
+ String String256 = "1_________2_________3_________4_________5_________6_________" +
+ "7_________8_________9_________10________11________12________13________" +
+ "14________15________16________17________18________19________20________" +
+ "21________22________23________24________25________123456";
- if (DBG) Log.d(TAG, "Starting test ...");
+ // Illegal attributes
+ try {
+ si.setAttribute(null, (String) null);
+ fail("Could set null key");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ si.setAttribute("", (String) null);
+ fail("Could set empty key");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ si.setAttribute(String256, (String) null);
+ fail("Could set key with 255 characters");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ si.setAttribute("key", String256.substring(3));
+ fail("Could set key+value combination with more than 255 characters");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ si.setAttribute("key", String256.substring(4));
+ fail("Could set key+value combination with 255 characters");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ si.setAttribute(new String(new byte[]{0x19}), (String) null);
+ fail("Could set key with invalid character");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ si.setAttribute("=", (String) null);
+ fail("Could set key with invalid character");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ si.setAttribute(new String(new byte[]{0x7F}), (String) null);
+ fail("Could set key with invalid character");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // Allowed attributes
+ si.setAttribute("booleanAttr", (String) null);
+ si.setAttribute("keyValueAttr", "value");
+ si.setAttribute("keyEqualsAttr", "=");
+ si.setAttribute(" whiteSpaceKeyValueAttr ", " value ");
+ si.setAttribute("binaryDataAttr", testByteArray);
+ si.setAttribute("nullBinaryDataAttr", (byte[]) null);
+ si.setAttribute("emptyBinaryDataAttr", new byte[]{});
+ si.setAttribute("longkey", String256.substring(9));
ServerSocket socket;
int localPort;
@@ -347,6 +423,25 @@
mNsdManager.resolveService(si, mResolveListener);
lastEvent = waitForCallback("onServiceResolved"); // id = 4
+ assertNotNull(mResolvedService);
+
+ // Check Txt attributes
+ assertEquals(8, mResolvedService.getAttributes().size());
+ assertTrue(mResolvedService.getAttributes().containsKey("booleanAttr"));
+ assertNull(mResolvedService.getAttributes().get("booleanAttr"));
+ assertEquals("value", new String(mResolvedService.getAttributes().get("keyValueAttr")));
+ assertEquals("=", new String(mResolvedService.getAttributes().get("keyEqualsAttr")));
+ assertEquals(" value ", new String(mResolvedService.getAttributes()
+ .get(" whiteSpaceKeyValueAttr ")));
+ assertEquals(String256.substring(9), new String(mResolvedService.getAttributes()
+ .get("longkey")));
+ assertTrue(Arrays.equals(testByteArray,
+ mResolvedService.getAttributes().get("binaryDataAttr")));
+ assertTrue(mResolvedService.getAttributes().containsKey("nullBinaryDataAttr"));
+ assertNull(mResolvedService.getAttributes().get("nullBinaryDataAttr"));
+ assertTrue(mResolvedService.getAttributes().containsKey("emptyBinaryDataAttr"));
+ assertNull(mResolvedService.getAttributes().get("emptyBinaryDataAttr"));
+
assertTrue(lastEvent != null);
assertTrue(lastEvent.mSucceeded);
@@ -394,7 +489,41 @@
registeredName = lastEvent.mInfo.getServiceName();
// Expect a record to be discovered
- lastEvent = waitForCallback("onServiceFound"); // id = 8
+ // Expect a service record to be discovered (and filter the ones
+ // that are unrelated to this test)
+ found = false;
+ for (int i = 0; i < 32; i++) {
+
+ lastEvent = waitForCallback("onServiceFound"); // id = 8
+ if (lastEvent == null) {
+ // no more onServiceFound events are being reported!
+ break;
+ }
+
+ assertTrue(lastEvent.mSucceeded);
+
+ if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " +
+ lastEvent.mInfo.getServiceName());
+
+ if (lastEvent.mInfo.getServiceName().equals(registeredName)) {
+ // Save it, as it will get overwritten with new serviceFound events
+ si = lastEvent.mInfo;
+ found = true;
+ }
+
+ // Remove this event from the event cache, so it won't be found by subsequent
+ // calls to waitForCallback
+ synchronized (mEventCache) {
+ mEventCache.remove(lastEvent);
+ }
+ }
+
+ assertTrue(found);
+
+ // Resolve the service
+ clearEventCache();
+ mNsdManager.resolveService(si, mResolveListener);
+ lastEvent = waitForCallback("onServiceResolved"); // id = 9
assertTrue(lastEvent != null);
assertTrue(lastEvent.mSucceeded);
@@ -404,11 +533,16 @@
assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
+ assertNotNull(mResolvedService);
+
+ // Check that we don't have any TXT records
+ assertEquals(0, mResolvedService.getAttributes().size());
+
checkForAdditionalEvents();
clearEventCache();
mNsdManager.stopServiceDiscovery(mDiscoveryListener);
- lastEvent = waitForCallback("onDiscoveryStopped"); // id = 9
+ lastEvent = waitForCallback("onDiscoveryStopped"); // id = 10
assertTrue(lastEvent != null);
assertTrue(lastEvent.mSucceeded);
assertTrue(checkCacheSize(1));
@@ -418,7 +552,7 @@
mNsdManager.unregisterService(mRegistrationListener);
- lastEvent = waitForCallback("onServiceUnregistered"); // id = 10
+ lastEvent = waitForCallback("onServiceUnregistered"); // id = 11
assertTrue(lastEvent != null);
assertTrue(lastEvent.mSucceeded);
assertTrue(checkCacheSize(1));
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 2cc5951..f3eb4e9 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -25,6 +25,10 @@
import android.net.wifi.WifiManager;
import android.test.AndroidTestCase;
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
public class WifiEnterpriseConfigTest extends AndroidTestCase {
private WifiManager mWifiManager;
@@ -39,6 +43,396 @@
private static final String ANON_IDENTITY = "anonidentity";
private static final int ENABLE_DELAY = 10000;
+ /*
+ * The keys and certificates below are generated with:
+ *
+ * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+ * openssl ecparam -name prime256v1 -out ecparam.pem
+ * openssl req -newkey ec:ecparam.pem -keyout userkey.pem -nodes -days 3650 -out userkey.req
+ * mkdir -p demoCA/newcerts
+ * touch demoCA/index.txt
+ * echo "01" > demoCA/serial
+ * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+ */
+
+ /**
+ * Generated from above and converted with:
+ *
+ * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+
+ private static final byte[] FAKE_EC_1 = {
+ (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0x2f, (byte) 0x30, (byte) 0x82,
+ (byte) 0x03, (byte) 0x17, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xa7, (byte) 0xe4,
+ (byte) 0x70, (byte) 0x50, (byte) 0x9b, (byte) 0xd2, (byte) 0x68, (byte) 0x68,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0xad,
+ (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+ (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a,
+ (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+ (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x12,
+ (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x07, (byte) 0x0c, (byte) 0x09, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+ (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79,
+ (byte) 0x31, (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x53,
+ (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x6f,
+ (byte) 0x6d, (byte) 0x70, (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x31,
+ (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x07, (byte) 0x53, (byte) 0x65,
+ (byte) 0x63, (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x31,
+ (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x18, (byte) 0x57, (byte) 0x69,
+ (byte) 0x66, (byte) 0x69, (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
+ (byte) 0x72, (byte) 0x70, (byte) 0x72, (byte) 0x69, (byte) 0x73, (byte) 0x65,
+ (byte) 0x43, (byte) 0x6f, (byte) 0x6e, (byte) 0x66, (byte) 0x69, (byte) 0x67,
+ (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x31, (byte) 0x29,
+ (byte) 0x30, (byte) 0x27, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x09,
+ (byte) 0x01, (byte) 0x16, (byte) 0x1a, (byte) 0x61, (byte) 0x6e, (byte) 0x2d,
+ (byte) 0x65, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6c, (byte) 0x2d,
+ (byte) 0x61, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73,
+ (byte) 0x40, (byte) 0x64, (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69,
+ (byte) 0x6e, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30,
+ (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x36, (byte) 0x30,
+ (byte) 0x31, (byte) 0x31, (byte) 0x35, (byte) 0x31, (byte) 0x31, (byte) 0x31,
+ (byte) 0x38, (byte) 0x35, (byte) 0x31, (byte) 0x5a, (byte) 0x17, (byte) 0x0d,
+ (byte) 0x32, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x31, (byte) 0x32,
+ (byte) 0x31, (byte) 0x31, (byte) 0x31, (byte) 0x38, (byte) 0x35, (byte) 0x31,
+ (byte) 0x5a, (byte) 0x30, (byte) 0x81, (byte) 0xad, (byte) 0x31, (byte) 0x0b,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31,
+ (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f,
+ (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61,
+ (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x12, (byte) 0x30, (byte) 0x10,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0c,
+ (byte) 0x09, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d,
+ (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79, (byte) 0x31, (byte) 0x15,
+ (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+ (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x6f, (byte) 0x6d, (byte) 0x70,
+ (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x31, (byte) 0x10, (byte) 0x30,
+ (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b,
+ (byte) 0x0c, (byte) 0x07, (byte) 0x53, (byte) 0x65, (byte) 0x63, (byte) 0x74,
+ (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x31, (byte) 0x21, (byte) 0x30,
+ (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03,
+ (byte) 0x0c, (byte) 0x18, (byte) 0x57, (byte) 0x69, (byte) 0x66, (byte) 0x69,
+ (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x70,
+ (byte) 0x72, (byte) 0x69, (byte) 0x73, (byte) 0x65, (byte) 0x43, (byte) 0x6f,
+ (byte) 0x6e, (byte) 0x66, (byte) 0x69, (byte) 0x67, (byte) 0x54, (byte) 0x65,
+ (byte) 0x73, (byte) 0x74, (byte) 0x31, (byte) 0x29, (byte) 0x30, (byte) 0x27,
+ (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+ (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x09, (byte) 0x01, (byte) 0x16,
+ (byte) 0x1a, (byte) 0x61, (byte) 0x6e, (byte) 0x2d, (byte) 0x65, (byte) 0x6d,
+ (byte) 0x61, (byte) 0x69, (byte) 0x6c, (byte) 0x2d, (byte) 0x61, (byte) 0x64,
+ (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x40, (byte) 0x64,
+ (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x2e,
+ (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x82, (byte) 0x01,
+ (byte) 0x22, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+ (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+ (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82,
+ (byte) 0x01, (byte) 0x0f, (byte) 0x00, (byte) 0x30, (byte) 0x82, (byte) 0x01,
+ (byte) 0x0a, (byte) 0x02, (byte) 0x82, (byte) 0x01, (byte) 0x01, (byte) 0x00,
+ (byte) 0xb4, (byte) 0x6e, (byte) 0x66, (byte) 0x24, (byte) 0xe7, (byte) 0x5c,
+ (byte) 0xd8, (byte) 0x6f, (byte) 0x08, (byte) 0xd3, (byte) 0x80, (byte) 0xa3,
+ (byte) 0xb9, (byte) 0xaf, (byte) 0x90, (byte) 0xef, (byte) 0x1c, (byte) 0x2a,
+ (byte) 0x5f, (byte) 0x39, (byte) 0x0b, (byte) 0xbd, (byte) 0x75, (byte) 0x0d,
+ (byte) 0x3e, (byte) 0x19, (byte) 0x2e, (byte) 0x47, (byte) 0x1e, (byte) 0x14,
+ (byte) 0xc2, (byte) 0x1a, (byte) 0x59, (byte) 0xcc, (byte) 0x1b, (byte) 0xb6,
+ (byte) 0x9b, (byte) 0x46, (byte) 0x1f, (byte) 0x7f, (byte) 0x71, (byte) 0xdd,
+ (byte) 0x38, (byte) 0xbe, (byte) 0x89, (byte) 0x30, (byte) 0xba, (byte) 0x88,
+ (byte) 0xfb, (byte) 0x3f, (byte) 0x57, (byte) 0x35, (byte) 0xe7, (byte) 0xa7,
+ (byte) 0x2f, (byte) 0x2c, (byte) 0x8d, (byte) 0x7c, (byte) 0xe2, (byte) 0xd8,
+ (byte) 0x0c, (byte) 0x0a, (byte) 0xe6, (byte) 0x62, (byte) 0x46, (byte) 0x8c,
+ (byte) 0xf4, (byte) 0x51, (byte) 0xfc, (byte) 0x6a, (byte) 0x79, (byte) 0xdd,
+ (byte) 0x0a, (byte) 0x41, (byte) 0x23, (byte) 0xd3, (byte) 0xe9, (byte) 0x5e,
+ (byte) 0x91, (byte) 0xcd, (byte) 0xbd, (byte) 0x55, (byte) 0x28, (byte) 0x71,
+ (byte) 0xec, (byte) 0x52, (byte) 0x19, (byte) 0x85, (byte) 0x0c, (byte) 0x1b,
+ (byte) 0xfa, (byte) 0xbf, (byte) 0xfe, (byte) 0xae, (byte) 0x5c, (byte) 0x3b,
+ (byte) 0x99, (byte) 0x42, (byte) 0xd4, (byte) 0xe7, (byte) 0x17, (byte) 0xec,
+ (byte) 0x41, (byte) 0x22, (byte) 0x2c, (byte) 0x1e, (byte) 0x7b, (byte) 0x53,
+ (byte) 0xad, (byte) 0x02, (byte) 0xfd, (byte) 0xf6, (byte) 0x4a, (byte) 0xb1,
+ (byte) 0x6e, (byte) 0x6c, (byte) 0x87, (byte) 0xf5, (byte) 0x7d, (byte) 0x9b,
+ (byte) 0x34, (byte) 0x0e, (byte) 0x3b, (byte) 0x0e, (byte) 0xaa, (byte) 0xc5,
+ (byte) 0xc4, (byte) 0xef, (byte) 0xf2, (byte) 0x5a, (byte) 0xa9, (byte) 0xac,
+ (byte) 0x19, (byte) 0xce, (byte) 0x5f, (byte) 0xc5, (byte) 0xcc, (byte) 0x0d,
+ (byte) 0xee, (byte) 0x7f, (byte) 0x32, (byte) 0xb4, (byte) 0xfe, (byte) 0xc1,
+ (byte) 0xca, (byte) 0x9b, (byte) 0x3f, (byte) 0xad, (byte) 0x2c, (byte) 0x7a,
+ (byte) 0xc5, (byte) 0x8d, (byte) 0x48, (byte) 0xa1, (byte) 0xc9, (byte) 0x74,
+ (byte) 0xfe, (byte) 0x8a, (byte) 0xe3, (byte) 0xb0, (byte) 0x92, (byte) 0xee,
+ (byte) 0x73, (byte) 0x09, (byte) 0x0a, (byte) 0xbc, (byte) 0xc8, (byte) 0x63,
+ (byte) 0xba, (byte) 0x0e, (byte) 0x26, (byte) 0xab, (byte) 0x1e, (byte) 0xff,
+ (byte) 0xbc, (byte) 0x24, (byte) 0x12, (byte) 0x26, (byte) 0x11, (byte) 0xe0,
+ (byte) 0x04, (byte) 0xcb, (byte) 0x96, (byte) 0x7d, (byte) 0x41, (byte) 0xf7,
+ (byte) 0x79, (byte) 0x32, (byte) 0x05, (byte) 0x33, (byte) 0x19, (byte) 0x6e,
+ (byte) 0xb9, (byte) 0x75, (byte) 0xf3, (byte) 0x50, (byte) 0xa4, (byte) 0xc3,
+ (byte) 0x55, (byte) 0x9d, (byte) 0x8f, (byte) 0xb6, (byte) 0xab, (byte) 0x97,
+ (byte) 0xe7, (byte) 0xe2, (byte) 0xe8, (byte) 0x15, (byte) 0xfc, (byte) 0x35,
+ (byte) 0xbd, (byte) 0xce, (byte) 0x17, (byte) 0xbe, (byte) 0xe3, (byte) 0x73,
+ (byte) 0xd4, (byte) 0x88, (byte) 0x39, (byte) 0x27, (byte) 0x7e, (byte) 0x6d,
+ (byte) 0xa2, (byte) 0x27, (byte) 0xfa, (byte) 0x96, (byte) 0xe3, (byte) 0x38,
+ (byte) 0xc0, (byte) 0xa1, (byte) 0x55, (byte) 0xc6, (byte) 0xf3, (byte) 0x20,
+ (byte) 0xea, (byte) 0x50, (byte) 0x8d, (byte) 0x6c, (byte) 0x94, (byte) 0x9a,
+ (byte) 0x43, (byte) 0x74, (byte) 0xc0, (byte) 0xfa, (byte) 0xef, (byte) 0xe0,
+ (byte) 0xb1, (byte) 0x1c, (byte) 0x6d, (byte) 0x5e, (byte) 0x44, (byte) 0x08,
+ (byte) 0xef, (byte) 0xd5, (byte) 0x80, (byte) 0xad, (byte) 0x02, (byte) 0x03,
+ (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3, (byte) 0x50, (byte) 0x30,
+ (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14,
+ (byte) 0xe9, (byte) 0xd0, (byte) 0x9e, (byte) 0x0e, (byte) 0x62, (byte) 0x31,
+ (byte) 0x02, (byte) 0x9a, (byte) 0x33, (byte) 0xd7, (byte) 0x4a, (byte) 0x93,
+ (byte) 0x0d, (byte) 0xf3, (byte) 0xd6, (byte) 0x74, (byte) 0xce, (byte) 0x69,
+ (byte) 0xe1, (byte) 0xef, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04, (byte) 0x18, (byte) 0x30,
+ (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0xe9, (byte) 0xd0, (byte) 0x9e,
+ (byte) 0x0e, (byte) 0x62, (byte) 0x31, (byte) 0x02, (byte) 0x9a, (byte) 0x33,
+ (byte) 0xd7, (byte) 0x4a, (byte) 0x93, (byte) 0x0d, (byte) 0xf3, (byte) 0xd6,
+ (byte) 0x74, (byte) 0xce, (byte) 0x69, (byte) 0xe1, (byte) 0xef, (byte) 0x30,
+ (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13,
+ (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01,
+ (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+ (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+ (byte) 0x01, (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82,
+ (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x52, (byte) 0x70, (byte) 0xb6,
+ (byte) 0x10, (byte) 0x7f, (byte) 0xaa, (byte) 0x86, (byte) 0x8f, (byte) 0x02,
+ (byte) 0xb0, (byte) 0x97, (byte) 0x89, (byte) 0xb9, (byte) 0x04, (byte) 0x1d,
+ (byte) 0x79, (byte) 0xa3, (byte) 0x74, (byte) 0x7c, (byte) 0xdf, (byte) 0xad,
+ (byte) 0x87, (byte) 0xe4, (byte) 0x00, (byte) 0xd3, (byte) 0x3a, (byte) 0x5c,
+ (byte) 0x48, (byte) 0x3b, (byte) 0xfe, (byte) 0x77, (byte) 0xfd, (byte) 0xbe,
+ (byte) 0xce, (byte) 0x5b, (byte) 0xd2, (byte) 0xea, (byte) 0x3e, (byte) 0x7f,
+ (byte) 0xef, (byte) 0x20, (byte) 0x0d, (byte) 0x0b, (byte) 0xc7, (byte) 0xc4,
+ (byte) 0x25, (byte) 0x20, (byte) 0xe1, (byte) 0x8f, (byte) 0xc5, (byte) 0x19,
+ (byte) 0x37, (byte) 0x9c, (byte) 0xa0, (byte) 0x9d, (byte) 0x02, (byte) 0x30,
+ (byte) 0x5f, (byte) 0x49, (byte) 0x4e, (byte) 0x56, (byte) 0xc4, (byte) 0xab,
+ (byte) 0xcb, (byte) 0x5c, (byte) 0xe6, (byte) 0x40, (byte) 0x93, (byte) 0x92,
+ (byte) 0xee, (byte) 0xa1, (byte) 0x69, (byte) 0x7d, (byte) 0x10, (byte) 0x6b,
+ (byte) 0xd4, (byte) 0xf7, (byte) 0xec, (byte) 0xd9, (byte) 0xa5, (byte) 0x29,
+ (byte) 0x63, (byte) 0x29, (byte) 0xd9, (byte) 0x27, (byte) 0x2d, (byte) 0x5e,
+ (byte) 0x34, (byte) 0x37, (byte) 0xa9, (byte) 0xba, (byte) 0x0a, (byte) 0x7b,
+ (byte) 0x99, (byte) 0x1a, (byte) 0x7d, (byte) 0xa7, (byte) 0xa7, (byte) 0xf0,
+ (byte) 0xbf, (byte) 0x40, (byte) 0x29, (byte) 0x5d, (byte) 0x2f, (byte) 0x2e,
+ (byte) 0x0f, (byte) 0x35, (byte) 0x90, (byte) 0xb5, (byte) 0xc3, (byte) 0xfd,
+ (byte) 0x1e, (byte) 0xe2, (byte) 0xb3, (byte) 0xae, (byte) 0xf9, (byte) 0xde,
+ (byte) 0x9d, (byte) 0x76, (byte) 0xe1, (byte) 0x20, (byte) 0xf5, (byte) 0x1c,
+ (byte) 0x30, (byte) 0x42, (byte) 0x80, (byte) 0x2a, (byte) 0x4f, (byte) 0x85,
+ (byte) 0x5c, (byte) 0xb4, (byte) 0x49, (byte) 0x68, (byte) 0x6c, (byte) 0x7c,
+ (byte) 0x2a, (byte) 0xc8, (byte) 0xbc, (byte) 0x15, (byte) 0xed, (byte) 0x88,
+ (byte) 0xfd, (byte) 0x8a, (byte) 0x63, (byte) 0xe0, (byte) 0x93, (byte) 0xfd,
+ (byte) 0x86, (byte) 0xab, (byte) 0xa9, (byte) 0xf6, (byte) 0x63, (byte) 0xa5,
+ (byte) 0x29, (byte) 0xaf, (byte) 0xdc, (byte) 0x8f, (byte) 0xca, (byte) 0xc2,
+ (byte) 0x28, (byte) 0xe7, (byte) 0x26, (byte) 0x89, (byte) 0x75, (byte) 0xf1,
+ (byte) 0x3e, (byte) 0x2e, (byte) 0x86, (byte) 0x11, (byte) 0x8b, (byte) 0xfa,
+ (byte) 0xf5, (byte) 0xb4, (byte) 0xb4, (byte) 0x04, (byte) 0x02, (byte) 0xa3,
+ (byte) 0x85, (byte) 0x81, (byte) 0xad, (byte) 0xb3, (byte) 0xec, (byte) 0x2d,
+ (byte) 0x4b, (byte) 0x40, (byte) 0x59, (byte) 0x61, (byte) 0x0d, (byte) 0x59,
+ (byte) 0x09, (byte) 0x09, (byte) 0xee, (byte) 0xc7, (byte) 0x51, (byte) 0xef,
+ (byte) 0x6f, (byte) 0xd6, (byte) 0x9a, (byte) 0xa5, (byte) 0x45, (byte) 0xa2,
+ (byte) 0x89, (byte) 0xc2, (byte) 0x97, (byte) 0x93, (byte) 0xbc, (byte) 0x5b,
+ (byte) 0x37, (byte) 0x55, (byte) 0x73, (byte) 0x55, (byte) 0x0c, (byte) 0x9c,
+ (byte) 0xcb, (byte) 0x10, (byte) 0xec, (byte) 0x76, (byte) 0xfe, (byte) 0xa7,
+ (byte) 0x70, (byte) 0x4e, (byte) 0x9a, (byte) 0xa2, (byte) 0xf9, (byte) 0x40,
+ (byte) 0xdd, (byte) 0x96, (byte) 0x7d, (byte) 0x67, (byte) 0x5c, (byte) 0x8e,
+ (byte) 0x43, (byte) 0x1a, (byte) 0x26, (byte) 0xaa, (byte) 0xee, (byte) 0x38,
+ (byte) 0x11, (byte) 0x26, (byte) 0x3d, (byte) 0x69, (byte) 0xc7, (byte) 0x6a,
+ (byte) 0xe7, (byte) 0xbd, (byte) 0x67, (byte) 0x70, (byte) 0x35, (byte) 0xff,
+ (byte) 0x72, (byte) 0x2c, (byte) 0x87, (byte) 0x82, (byte) 0x68, (byte) 0x3f,
+ (byte) 0x8d
+ };
+
+ private static final byte[] FAKE_EC_2 = {
+ (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0x4f, (byte) 0x30, (byte) 0x82,
+ (byte) 0x03, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xd9, (byte) 0xc4,
+ (byte) 0xe1, (byte) 0xfc, (byte) 0x3d, (byte) 0x02, (byte) 0x21, (byte) 0x1f,
+ (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+ (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0xbd,
+ (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+ (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a,
+ (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+ (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x12,
+ (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x07, (byte) 0x0c, (byte) 0x09, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+ (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79,
+ (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x12, (byte) 0x53,
+ (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x4f, (byte) 0x74,
+ (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x2d, (byte) 0x43, (byte) 0x6f,
+ (byte) 0x6d, (byte) 0x70, (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x31,
+ (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x0c, (byte) 0x53, (byte) 0x6f,
+ (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x65, (byte) 0x63,
+ (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x31, (byte) 0x21,
+ (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x03, (byte) 0x0c, (byte) 0x18, (byte) 0x57, (byte) 0x69, (byte) 0x66,
+ (byte) 0x69, (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72,
+ (byte) 0x70, (byte) 0x72, (byte) 0x69, (byte) 0x73, (byte) 0x65, (byte) 0x43,
+ (byte) 0x6f, (byte) 0x6e, (byte) 0x66, (byte) 0x69, (byte) 0x67, (byte) 0x54,
+ (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x31, (byte) 0x2e, (byte) 0x30,
+ (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+ (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x09, (byte) 0x01,
+ (byte) 0x16, (byte) 0x1f, (byte) 0x61, (byte) 0x6e, (byte) 0x2d, (byte) 0x65,
+ (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6c, (byte) 0x2d, (byte) 0x61,
+ (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x40,
+ (byte) 0x73, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x64,
+ (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x2e,
+ (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x1e, (byte) 0x17,
+ (byte) 0x0d, (byte) 0x31, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x31,
+ (byte) 0x35, (byte) 0x31, (byte) 0x31, (byte) 0x33, (byte) 0x32, (byte) 0x34,
+ (byte) 0x36, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x36,
+ (byte) 0x30, (byte) 0x31, (byte) 0x31, (byte) 0x32, (byte) 0x31, (byte) 0x31,
+ (byte) 0x33, (byte) 0x32, (byte) 0x34, (byte) 0x36, (byte) 0x5a, (byte) 0x30,
+ (byte) 0x81, (byte) 0xbd, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13,
+ (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30,
+ (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08,
+ (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65,
+ (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65,
+ (byte) 0x31, (byte) 0x12, (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0c, (byte) 0x09, (byte) 0x53,
+ (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x69,
+ (byte) 0x74, (byte) 0x79, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c,
+ (byte) 0x12, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d,
+ (byte) 0x4f, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x2d,
+ (byte) 0x43, (byte) 0x6f, (byte) 0x6d, (byte) 0x70, (byte) 0x61, (byte) 0x6e,
+ (byte) 0x79, (byte) 0x31, (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x0c,
+ (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+ (byte) 0x65, (byte) 0x63, (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e,
+ (byte) 0x31, (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x18, (byte) 0x57,
+ (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x45, (byte) 0x6e, (byte) 0x74,
+ (byte) 0x65, (byte) 0x72, (byte) 0x70, (byte) 0x72, (byte) 0x69, (byte) 0x73,
+ (byte) 0x65, (byte) 0x43, (byte) 0x6f, (byte) 0x6e, (byte) 0x66, (byte) 0x69,
+ (byte) 0x67, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x31,
+ (byte) 0x2e, (byte) 0x30, (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+ (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+ (byte) 0x09, (byte) 0x01, (byte) 0x16, (byte) 0x1f, (byte) 0x61, (byte) 0x6e,
+ (byte) 0x2d, (byte) 0x65, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6c,
+ (byte) 0x2d, (byte) 0x61, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73,
+ (byte) 0x73, (byte) 0x40, (byte) 0x73, (byte) 0x6f, (byte) 0x6d, (byte) 0x65,
+ (byte) 0x2d, (byte) 0x64, (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69,
+ (byte) 0x6e, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30,
+ (byte) 0x82, (byte) 0x01, (byte) 0x22, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
+ (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+ (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00,
+ (byte) 0x03, (byte) 0x82, (byte) 0x01, (byte) 0x0f, (byte) 0x00, (byte) 0x30,
+ (byte) 0x82, (byte) 0x01, (byte) 0x0a, (byte) 0x02, (byte) 0x82, (byte) 0x01,
+ (byte) 0x01, (byte) 0x00, (byte) 0xa9, (byte) 0xa3, (byte) 0x21, (byte) 0xfd,
+ (byte) 0xa6, (byte) 0xc1, (byte) 0x04, (byte) 0x48, (byte) 0xc2, (byte) 0xc8,
+ (byte) 0x44, (byte) 0x50, (byte) 0xc4, (byte) 0x6d, (byte) 0x35, (byte) 0x24,
+ (byte) 0xf0, (byte) 0x6d, (byte) 0x69, (byte) 0xfb, (byte) 0xd1, (byte) 0xfc,
+ (byte) 0xde, (byte) 0xe9, (byte) 0xdb, (byte) 0xca, (byte) 0xee, (byte) 0x24,
+ (byte) 0x3d, (byte) 0x85, (byte) 0x8d, (byte) 0x84, (byte) 0xb4, (byte) 0x73,
+ (byte) 0xd1, (byte) 0x09, (byte) 0x37, (byte) 0x16, (byte) 0x80, (byte) 0x70,
+ (byte) 0x6b, (byte) 0x61, (byte) 0xcc, (byte) 0xf2, (byte) 0x98, (byte) 0xbd,
+ (byte) 0x53, (byte) 0x3a, (byte) 0x68, (byte) 0x60, (byte) 0x02, (byte) 0xba,
+ (byte) 0x0c, (byte) 0x53, (byte) 0x96, (byte) 0xfb, (byte) 0x80, (byte) 0xd1,
+ (byte) 0x5b, (byte) 0xc3, (byte) 0xcb, (byte) 0x7a, (byte) 0x81, (byte) 0x00,
+ (byte) 0x5d, (byte) 0x20, (byte) 0x72, (byte) 0xc0, (byte) 0xe4, (byte) 0x48,
+ (byte) 0x0e, (byte) 0xa2, (byte) 0xcd, (byte) 0xa2, (byte) 0x63, (byte) 0x8c,
+ (byte) 0x05, (byte) 0x7c, (byte) 0x63, (byte) 0x5b, (byte) 0xda, (byte) 0x0e,
+ (byte) 0xa7, (byte) 0x05, (byte) 0x09, (byte) 0x6d, (byte) 0xd5, (byte) 0xe4,
+ (byte) 0x3a, (byte) 0x4e, (byte) 0xa1, (byte) 0xf5, (byte) 0xfd, (byte) 0x47,
+ (byte) 0xee, (byte) 0x7b, (byte) 0xa3, (byte) 0x4c, (byte) 0x8c, (byte) 0xd3,
+ (byte) 0xbb, (byte) 0x58, (byte) 0x0f, (byte) 0x1c, (byte) 0x56, (byte) 0x80,
+ (byte) 0x80, (byte) 0xb5, (byte) 0xf9, (byte) 0x80, (byte) 0xc2, (byte) 0xd1,
+ (byte) 0x1d, (byte) 0x3f, (byte) 0xe8, (byte) 0x2a, (byte) 0x63, (byte) 0x0b,
+ (byte) 0x54, (byte) 0x5f, (byte) 0xd4, (byte) 0xcb, (byte) 0xb7, (byte) 0x94,
+ (byte) 0xe2, (byte) 0x35, (byte) 0x65, (byte) 0x59, (byte) 0xd1, (byte) 0x72,
+ (byte) 0xa4, (byte) 0xb8, (byte) 0xee, (byte) 0x82, (byte) 0x11, (byte) 0x7a,
+ (byte) 0x4c, (byte) 0x26, (byte) 0x66, (byte) 0x9b, (byte) 0x27, (byte) 0x3d,
+ (byte) 0x14, (byte) 0x4b, (byte) 0x4b, (byte) 0xc8, (byte) 0xf0, (byte) 0x6e,
+ (byte) 0x43, (byte) 0x8f, (byte) 0xee, (byte) 0x1f, (byte) 0xeb, (byte) 0x20,
+ (byte) 0xe2, (byte) 0x4c, (byte) 0x79, (byte) 0xbf, (byte) 0x21, (byte) 0x0d,
+ (byte) 0x36, (byte) 0xed, (byte) 0x5f, (byte) 0xcc, (byte) 0x70, (byte) 0x68,
+ (byte) 0x8a, (byte) 0x05, (byte) 0x7c, (byte) 0x2f, (byte) 0x1b, (byte) 0xe9,
+ (byte) 0xec, (byte) 0x83, (byte) 0x6e, (byte) 0x9a, (byte) 0x78, (byte) 0x31,
+ (byte) 0x3d, (byte) 0xf4, (byte) 0xde, (byte) 0x1b, (byte) 0xd2, (byte) 0x76,
+ (byte) 0x32, (byte) 0x6c, (byte) 0x1e, (byte) 0xc9, (byte) 0x90, (byte) 0x7f,
+ (byte) 0xc4, (byte) 0x30, (byte) 0xc0, (byte) 0xae, (byte) 0xab, (byte) 0x70,
+ (byte) 0x08, (byte) 0x78, (byte) 0xbf, (byte) 0x2e, (byte) 0x8b, (byte) 0x07,
+ (byte) 0xab, (byte) 0x8f, (byte) 0x03, (byte) 0xc5, (byte) 0xd3, (byte) 0xeb,
+ (byte) 0x98, (byte) 0x19, (byte) 0x50, (byte) 0x83, (byte) 0x52, (byte) 0xf7,
+ (byte) 0xff, (byte) 0xf5, (byte) 0x89, (byte) 0xe6, (byte) 0xe7, (byte) 0xa7,
+ (byte) 0xcb, (byte) 0xdf, (byte) 0x96, (byte) 0x9d, (byte) 0x14, (byte) 0x04,
+ (byte) 0x5e, (byte) 0x45, (byte) 0x82, (byte) 0xf7, (byte) 0x23, (byte) 0x1a,
+ (byte) 0xb6, (byte) 0x64, (byte) 0x57, (byte) 0xe8, (byte) 0x7e, (byte) 0xa1,
+ (byte) 0xaf, (byte) 0x58, (byte) 0x68, (byte) 0x70, (byte) 0xc5, (byte) 0x0f,
+ (byte) 0x8d, (byte) 0x54, (byte) 0xf3, (byte) 0x49, (byte) 0xa3, (byte) 0x97,
+ (byte) 0x32, (byte) 0xa7, (byte) 0x2a, (byte) 0x79, (byte) 0xbe, (byte) 0xcd,
+ (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
+ (byte) 0x50, (byte) 0x30, (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16,
+ (byte) 0x04, (byte) 0x14, (byte) 0xac, (byte) 0xf3, (byte) 0x73, (byte) 0x9a,
+ (byte) 0x25, (byte) 0x08, (byte) 0x01, (byte) 0x07, (byte) 0x86, (byte) 0x8b,
+ (byte) 0xc4, (byte) 0xed, (byte) 0xb1, (byte) 0x6b, (byte) 0x53, (byte) 0xa3,
+ (byte) 0x21, (byte) 0xb4, (byte) 0xb4, (byte) 0x46, (byte) 0x30, (byte) 0x1f,
+ (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04,
+ (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0xac,
+ (byte) 0xf3, (byte) 0x73, (byte) 0x9a, (byte) 0x25, (byte) 0x08, (byte) 0x01,
+ (byte) 0x07, (byte) 0x86, (byte) 0x8b, (byte) 0xc4, (byte) 0xed, (byte) 0xb1,
+ (byte) 0x6b, (byte) 0x53, (byte) 0xa3, (byte) 0x21, (byte) 0xb4, (byte) 0xb4,
+ (byte) 0x46, (byte) 0x30, (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03,
+ (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
+ (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+ (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x0b, (byte) 0x05, (byte) 0x00,
+ (byte) 0x03, (byte) 0x82, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x16,
+ (byte) 0xf6, (byte) 0xd0, (byte) 0xe1, (byte) 0x14, (byte) 0x2d, (byte) 0x52,
+ (byte) 0x47, (byte) 0xa2, (byte) 0x89, (byte) 0xe6, (byte) 0x7f, (byte) 0xac,
+ (byte) 0x88, (byte) 0x04, (byte) 0x15, (byte) 0x21, (byte) 0x00, (byte) 0x72,
+ (byte) 0xf9, (byte) 0xee, (byte) 0xb2, (byte) 0x1b, (byte) 0x8e, (byte) 0x46,
+ (byte) 0x8b, (byte) 0x90, (byte) 0x20, (byte) 0x4f, (byte) 0xa7, (byte) 0xae,
+ (byte) 0x30, (byte) 0xb6, (byte) 0x24, (byte) 0xc5, (byte) 0x54, (byte) 0xaf,
+ (byte) 0x6c, (byte) 0x1e, (byte) 0xd6, (byte) 0x73, (byte) 0x22, (byte) 0x48,
+ (byte) 0x07, (byte) 0xb5, (byte) 0x13, (byte) 0x35, (byte) 0xbb, (byte) 0x9e,
+ (byte) 0xd9, (byte) 0x19, (byte) 0x79, (byte) 0xda, (byte) 0x76, (byte) 0x7f,
+ (byte) 0xf7, (byte) 0x87, (byte) 0xc9, (byte) 0xc3, (byte) 0x0b, (byte) 0x38,
+ (byte) 0x20, (byte) 0x26, (byte) 0xfc, (byte) 0x7f, (byte) 0x32, (byte) 0x2a,
+ (byte) 0xd5, (byte) 0x09, (byte) 0x87, (byte) 0xda, (byte) 0x23, (byte) 0x1f,
+ (byte) 0x71, (byte) 0x83, (byte) 0x00, (byte) 0x17, (byte) 0xf6, (byte) 0xb9,
+ (byte) 0x57, (byte) 0x21, (byte) 0xdf, (byte) 0x29, (byte) 0xcc, (byte) 0xdb,
+ (byte) 0xe9, (byte) 0x2c, (byte) 0xba, (byte) 0x86, (byte) 0x34, (byte) 0x53,
+ (byte) 0x29, (byte) 0x09, (byte) 0xc7, (byte) 0x3c, (byte) 0x8e, (byte) 0xa3,
+ (byte) 0x86, (byte) 0x81, (byte) 0x26, (byte) 0x7b, (byte) 0xa1, (byte) 0xbe,
+ (byte) 0xbc, (byte) 0xc9, (byte) 0x83, (byte) 0xb5, (byte) 0x36, (byte) 0x65,
+ (byte) 0x51, (byte) 0xb4, (byte) 0x41, (byte) 0xf0, (byte) 0x05, (byte) 0x78,
+ (byte) 0x3a, (byte) 0xa6, (byte) 0xad, (byte) 0x4b, (byte) 0x08, (byte) 0xd1,
+ (byte) 0xe4, (byte) 0xf1, (byte) 0x2e, (byte) 0xc7, (byte) 0x23, (byte) 0x6d,
+ (byte) 0xf0, (byte) 0x9d, (byte) 0x60, (byte) 0x6d, (byte) 0xe7, (byte) 0x11,
+ (byte) 0xaf, (byte) 0x41, (byte) 0x68, (byte) 0xee, (byte) 0x06, (byte) 0x76,
+ (byte) 0x82, (byte) 0x48, (byte) 0xee, (byte) 0x41, (byte) 0xc4, (byte) 0xf8,
+ (byte) 0xe1, (byte) 0x83, (byte) 0xbc, (byte) 0xa8, (byte) 0xbd, (byte) 0x9c,
+ (byte) 0x17, (byte) 0x45, (byte) 0xf4, (byte) 0x36, (byte) 0x67, (byte) 0x47,
+ (byte) 0x0e, (byte) 0x32, (byte) 0x13, (byte) 0x6e, (byte) 0xc1, (byte) 0x1e,
+ (byte) 0x08, (byte) 0xef, (byte) 0x10, (byte) 0xdf, (byte) 0x45, (byte) 0xbf,
+ (byte) 0x5a, (byte) 0xc4, (byte) 0x44, (byte) 0x4c, (byte) 0xd0, (byte) 0xd5,
+ (byte) 0x23, (byte) 0xde, (byte) 0xd7, (byte) 0x83, (byte) 0x1e, (byte) 0xb0,
+ (byte) 0x27, (byte) 0x4d, (byte) 0x57, (byte) 0xa3, (byte) 0xe8, (byte) 0x36,
+ (byte) 0x52, (byte) 0x1c, (byte) 0x48, (byte) 0x0a, (byte) 0xc4, (byte) 0xd8,
+ (byte) 0x32, (byte) 0xfc, (byte) 0xd0, (byte) 0x26, (byte) 0x6f, (byte) 0xa4,
+ (byte) 0x61, (byte) 0x2c, (byte) 0x3a, (byte) 0xa9, (byte) 0xfe, (byte) 0xa4,
+ (byte) 0x7a, (byte) 0x58, (byte) 0x54, (byte) 0x58, (byte) 0x96, (byte) 0x2b,
+ (byte) 0x6e, (byte) 0x9c, (byte) 0xc9, (byte) 0x00, (byte) 0xda, (byte) 0xc6,
+ (byte) 0xbb, (byte) 0x97, (byte) 0xc4, (byte) 0x95, (byte) 0x32, (byte) 0x6b,
+ (byte) 0x03, (byte) 0x6f, (byte) 0x33, (byte) 0x59, (byte) 0xd4, (byte) 0xa4,
+ (byte) 0x4a, (byte) 0x29, (byte) 0x29, (byte) 0x9a, (byte) 0xf4, (byte) 0x87,
+ (byte) 0x26, (byte) 0xe6, (byte) 0xee, (byte) 0x5c, (byte) 0x0b, (byte) 0xe9,
+ (byte) 0x98, (byte) 0x5d, (byte) 0xab, (byte) 0x31, (byte) 0xa1, (byte) 0x63,
+ (byte) 0xaa, (byte) 0x1a, (byte) 0xea, (byte) 0x61, (byte) 0x27, (byte) 0x5e,
+ (byte) 0x9e, (byte) 0x34, (byte) 0x73
+ };
+
+
private boolean hasWifi() {
return getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI);
@@ -57,7 +451,7 @@
}
}
- public void testSettersAndGetters() {
+ public void testSettersAndGetters() throws Exception {
if (!hasWifi()) {
return;
}
@@ -87,7 +481,17 @@
assertTrue(config.getAnonymousIdentity().equals(ANON_IDENTITY));
config.setPassword(PASSWORD);
assertTrue(config.getPassword().equals(PASSWORD));
- config.setCaCertificate(null);
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ X509Certificate cert1 = (X509Certificate) factory.generateCertificate(
+ new ByteArrayInputStream(FAKE_EC_1));
+ X509Certificate cert2 = (X509Certificate) factory.generateCertificate(
+ new ByteArrayInputStream(FAKE_EC_2));
+ config.setCaCertificate(cert1);
+ assertTrue(config.getCaCertificate().getSerialNumber().equals(cert1.getSerialNumber()));
+ config.setCaCertificates(new X509Certificate[]{cert1, cert2});
+ X509Certificate[] certs = config.getCaCertificates();
+ assertTrue(cert1.getSerialNumber().equals(certs[0].getSerialNumber()));
+ assertTrue(cert2.getSerialNumber().equals(certs[1].getSerialNumber()));
config.setClientKeyEntry(null, null);
config.setSubjectMatch(SUBJECT_MATCH);
assertTrue(config.getSubjectMatch().equals(SUBJECT_MATCH));
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 4478bd4..897e5cf 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -296,6 +296,8 @@
for (int i = 0; i < WIFI_SCAN_TEST_ITERATIONS; ++i) {
startScan();
// Make sure at least one AP is found.
+ assertTrue("mScanResult should not be null. This may be due to a scan timeout",
+ mScanResults != null);
assertFalse("empty scan results!", mScanResults.isEmpty());
long nowMillis = SystemClock.elapsedRealtime();
// Keep track of how many APs are fresh in one scan.
@@ -373,7 +375,7 @@
assertTrue(notExist != pos);
// Enable & disable network
- boolean disableOthers = false;
+ boolean disableOthers = true;
assertTrue(mWifiManager.enableNetwork(netId, disableOthers));
wifiConfiguration = mWifiManager.getConfiguredNetworks().get(pos);
assertDisableOthers(wifiConfiguration, disableOthers);
@@ -484,6 +486,9 @@
}
assertTrue(mWifiManager.isWifiEnabled());
+ // This will generate a distinct stack trace if the initial connection fails.
+ connectWifi();
+
int i = 0;
for (; i < 15; i++) {
// Wait for a WiFi connection
diff --git a/tests/cts/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java b/tests/cts/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java
new file mode 100644
index 0000000..5e2a55e
--- /dev/null
+++ b/tests/cts/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2010 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 org.apache.http.conn.ssl.cts;
+
+import javax.security.auth.x500.X500Principal;
+import junit.framework.TestCase;
+
+import org.apache.http.conn.ssl.AbstractVerifier;
+
+import java.lang.Override;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * See also {@link libcore.javax.security.auth.x500.X500PrincipalTest} as it shows some cases
+ * we are not checking as they are not allowed by the X500 principal in the first place.
+ */
+public final class AbstractVerifierTest extends TestCase {
+
+ public void testGetCns() {
+ assertCns("");
+ assertCns("ou=xxx");
+ assertCns("ou=xxx,cn=xxx", "xxx");
+ assertCns("ou=xxx+cn=yyy,cn=zzz+cn=abc", "yyy", "zzz", "abc");
+ assertCns("cn=a,cn=b", "a", "b");
+ assertCns("cn=a c,cn=b", "a c", "b");
+ assertCns("cn=a ,cn=b", "a", "b");
+ assertCns("cn=Cc,cn=Bb,cn=Aa", "Cc", "Bb", "Aa");
+ assertCns("cn=imap.gmail.com", "imap.gmail.com");
+ assertCns("l=\"abcn=a,b\", cn=c", "c");
+ assertCns("l=\"abcn=a,b\", cn=c", "c");
+ assertCns("l=\"abcn=a,b\", cn= c", "c");
+ assertCns("cn=<", "<");
+ assertCns("cn=>", ">");
+ assertCns("cn= >", ">");
+ assertCns("cn=a b", "a b");
+ assertCns("cn =a b", "a b");
+ assertCns("Cn=a b", "a b");
+ assertCns("cN=a b", "a b");
+ assertCns("CN=a b", "a b");
+ assertCns("cn=a#b", "a#b");
+ assertCns("cn=#130161", "a");
+ assertCns("l=q\t+cn=p", "p");
+ assertCns("l=q\n+cn=p", "p");
+ assertCns("l=q\n,cn=p", "p");
+ assertCns("l=,cn=p", "p");
+ assertCns("l=\tq\n,cn=\tp", "\tp");
+ }
+
+ /** A cn=, generates an empty value, unless it's at the very end */
+ public void testEmptyValues() {
+ assertCns("l=,cn=+cn=q", "", "q");
+ assertCns("l=,cn=,cn=q", "", "q");
+ assertCns("l=,cn=");
+ assertCns("l=,cn=q,cn= ", "q");
+ assertCns("l=,cn=q ,cn= ", "q");
+ assertCns("l=,cn=\"\"");
+ assertCns("l=,cn=\" \",cn=\" \"", " ", " ");
+ assertCns("l=,cn= ,cn= ","");
+ assertCns("l=,cn=,cn= ,cn= ", "", "");
+ }
+
+
+ public void testGetCns_escapedChars() {
+ assertCns("cn=\\,", ",");
+ assertCns("cn=\\#", "#");
+ assertCns("cn=\\+", "+");
+ assertCns("cn=\\\"", "\"");
+ assertCns("cn=\\\\", "\\");
+ assertCns("cn=\\<", "<");
+ assertCns("cn=\\>", ">");
+ assertCns("cn=\\;", ";");
+ assertCns("cn=\\+", "+");
+ assertCns("cn=\"\\+\"", "+");
+ assertCns("cn=\"\\,\"", ",");
+ assertCns("cn= a =", "a =");
+ assertCns("cn==", "=");
+ }
+
+ public void testGetCns_whitespace() {
+ assertCns("cn= p", "p");
+ assertCns("cn=\np", "p");
+ assertCns("cn=\tp", "\tp");
+ }
+
+ public void testGetCnsWithOid() {
+ assertCns("2.5.4.3=a,ou=xxx", "a");
+ assertCns("2.5.4.3=\" a \",ou=xxx", " a ");
+ assertCns("2.5.5.3=a,ou=xxx,cn=b", "b");
+ }
+
+ public void testGetCnsWithQuotedStrings() {
+ assertCns("cn=\"\\\" a ,=<>#;\"", "\" a ,=<>#;");
+ assertCns("cn=abc\\,def", "abc,def");
+ assertCns("cn=\"\\\" a ,\\=<>\\#;\"", "\" a ,=<>#;");
+ }
+
+ public void testGetCnsWithUtf8() {
+ assertCns("cn=\"Lu\\C4\\8Di\\C4\\87\"", "\u004c\u0075\u010d\u0069\u0107");
+ assertCns("cn=Lu\\C4\\8Di\\C4\\87", "\u004c\u0075\u010d\u0069\u0107");
+ assertCns("cn=Lu\\C4\\8di\\c4\\87", "\u004c\u0075\u010d\u0069\u0107");
+ assertCns("cn=\"Lu\\C4\\8di\\c4\\87\"", "\u004c\u0075\u010d\u0069\u0107");
+ assertCns("cn=\u004c\u0075\u010d\u0069\u0107", "\u004c\u0075\u010d\u0069\u0107");
+ // \63=c
+ assertExceptionInPrincipal("\\63n=ab");
+ assertExceptionInPrincipal("cn=\\a");
+ }
+
+ public void testGetCnsWithWhitespace() {
+ assertCns("ou=a, cn= a b ,o=x", "a b");
+ assertCns("cn=\" a b \" ,o=x", " a b ");
+ }
+
+ private static void assertCns(String dn, String... expected) {
+ String[] result = AbstractVerifier.getCNs(createStubCertificate(dn));
+ if (expected.length == 0) {
+ assertNull(result);
+ } else {
+ assertNotNull(dn, result);
+ assertEquals(dn, Arrays.asList(expected), Arrays.asList(result));
+ }
+ }
+
+ private static void assertExceptionInPrincipal(String dn) {
+ try {
+ X500Principal principal = new X500Principal(dn);
+ fail("Expected " + IllegalArgumentException.class.getName()
+ + " because of incorrect input name");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+ }
+
+ private static X509Certificate createStubCertificate(final String subjectName) {
+ return new X509Certificate() {
+ @Override
+ public X500Principal getSubjectX500Principal() {
+ return new X500Principal(subjectName);
+ }
+
+ @Override
+ public Set<String> getCriticalExtensionOIDs() {
+ return null;
+ }
+
+ @Override
+ public byte[] getExtensionValue(String oid) {
+ return new byte[0];
+ }
+
+ @Override
+ public Set<String> getNonCriticalExtensionOIDs() {
+ return null;
+ }
+
+ @Override
+ public boolean hasUnsupportedCriticalExtension() {
+ return false;
+ }
+
+ @Override
+ public byte[] getEncoded() throws CertificateEncodingException {
+ return new byte[0];
+ }
+
+ @Override
+ public void verify(PublicKey key)
+ throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+ NoSuchProviderException, SignatureException {
+
+ }
+
+ @Override
+ public void verify(PublicKey key, String sigProvider)
+ throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+ NoSuchProviderException, SignatureException {
+
+ }
+
+ @Override
+ public String toString() {
+ return null;
+ }
+
+ @Override
+ public PublicKey getPublicKey() {
+ return null;
+ }
+
+ @Override
+ public void checkValidity()
+ throws CertificateExpiredException, CertificateNotYetValidException {
+
+ }
+
+ @Override
+ public void checkValidity(Date date)
+ throws CertificateExpiredException, CertificateNotYetValidException {
+
+ }
+
+ @Override
+ public int getVersion() {
+ return 0;
+ }
+
+ @Override
+ public BigInteger getSerialNumber() {
+ return null;
+ }
+
+ @Override
+ public Principal getIssuerDN() {
+ return null;
+ }
+
+ @Override
+ public Principal getSubjectDN() {
+ return null;
+ }
+
+ @Override
+ public Date getNotBefore() {
+ return null;
+ }
+
+ @Override
+ public Date getNotAfter() {
+ return null;
+ }
+
+ @Override
+ public byte[] getTBSCertificate() throws CertificateEncodingException {
+ return new byte[0];
+ }
+
+ @Override
+ public byte[] getSignature() {
+ return new byte[0];
+ }
+
+ @Override
+ public String getSigAlgName() {
+ return null;
+ }
+
+ @Override
+ public String getSigAlgOID() {
+ return null;
+ }
+
+ @Override
+ public byte[] getSigAlgParams() {
+ return new byte[0];
+ }
+
+ @Override
+ public boolean[] getIssuerUniqueID() {
+ return new boolean[0];
+ }
+
+ @Override
+ public boolean[] getSubjectUniqueID() {
+ return new boolean[0];
+ }
+
+ @Override
+ public boolean[] getKeyUsage() {
+ return new boolean[0];
+ }
+
+ @Override
+ public int getBasicConstraints() {
+ return 0;
+ }
+ };
+ }
+}
+