Merge "Merge "Fix expected reverse lookup of Google DNS IP addresses" into nougat-cts-dev am: 3b416dd354" into nougat-mr1-cts-dev
am: 50491fb1cc
Change-Id: Ie5459302cb2aa040ed44747f189434091e1c4786
diff --git a/tests/cts/hostside/Android.mk b/tests/cts/hostside/Android.mk
index ad97ecd..1c3f053 100644
--- a/tests/cts/hostside/Android.mk
+++ b/tests/cts/hostside/Android.mk
@@ -21,9 +21,7 @@
LOCAL_MODULE := CtsHostsideNetworkTests
-LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
-
-LOCAL_STATIC_JAVA_LIBRARIES := cts-migration-lib
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed
LOCAL_CTS_TEST_PACKAGE := android.net.hostsidenetwork
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index 9945805..ad7f81b 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Config for CTS net host test cases">
+ <option name="config-descriptor:metadata" key="component" value="networking" />
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="CtsHostsideNetworkTests.jar" />
<option name="runtime-hint" value="3m56s" />
diff --git a/tests/cts/hostside/aidl/Android.mk b/tests/cts/hostside/aidl/Android.mk
index a7ec6ef..85f71c3 100644
--- a/tests/cts/hostside/aidl/Android.mk
+++ b/tests/cts/hostside/aidl/Android.mk
@@ -17,6 +17,9 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := com/android/cts/net/hostside/IRemoteSocketFactory.aidl
+LOCAL_SRC_FILES := \
+ com/android/cts/net/hostside/IMyService.aidl \
+ com/android/cts/net/hostside/INetworkStateObserver.aidl \
+ com/android/cts/net/hostside/IRemoteSocketFactory.aidl
LOCAL_MODULE := CtsHostsideNetworkTestsAidl
-include $(BUILD_JAVA_LIBRARY)
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
new file mode 100644
index 0000000..72d1059
--- /dev/null
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.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;
+
+interface IMyService {
+ void registerBroadcastReceiver();
+ int getCounters(String receiverName, String action);
+ String checkNetworkStatus();
+ String getRestrictBackgroundStatus();
+ void sendNotification(int notificationId, String notificationType);
+}
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
new file mode 100644
index 0000000..165f530
--- /dev/null
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkStateObserver.aidl
@@ -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 com.android.cts.net.hostside;
+
+interface INetworkStateObserver {
+ boolean isForeground();
+ void onNetworkStateChecked(String resultData);
+}
\ No newline at end of file
diff --git a/tests/cts/hostside/app/Android.mk b/tests/cts/hostside/app/Android.mk
index 9519ec5..1c1a798 100644
--- a/tests/cts/hostside/app/Android.mk
+++ b/tests/cts/hostside/app/Android.mk
@@ -20,7 +20,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ub-uiautomator \
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner ub-uiautomator \
CtsHostsideNetworkTestsAidl
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index 831b387..d2c0873 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -32,6 +32,7 @@
// Set initial state.
removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
setAppIdle(false);
turnBatteryOff();
@@ -82,9 +83,7 @@
// 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();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
finishActivity();
assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
assertBackgroundNetworkAccess(true);
@@ -93,9 +92,7 @@
// Same for foreground service.
setAppIdle(true);
- startForegroundService();
- assertAppIdle(true); // Sanity check - still idle
- assertForegroundServiceNetworkAccess();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
stopForegroundService();
assertAppIdle(true);
assertBackgroundNetworkAccess(false);
@@ -115,6 +112,14 @@
assertAppIdle(true); // Sanity check - idle again, once whitelisted was removed
assertBackgroundNetworkAccess(false);
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertAppIdle(false); // Sanity check - not idle anymore, since whitelisted
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertAppIdle(true); // Sanity check - idle again, once whitelisted was removed
+ assertBackgroundNetworkAccess(false);
+
assertsForegroundAlwaysHasNetworkAccess();
// Sanity check - no whitelist, no access!
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
index b5637be..28175b8 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -31,6 +31,7 @@
// Set initial state.
removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
setBatterySaverMode(false);
registerBroadcastReceiver();
@@ -86,8 +87,7 @@
// Make sure foreground app doesn't lose access upon Battery Saver.
setBatterySaverMode(false);
- launchActivity();
- assertForegroundNetworkAccess();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
setBatterySaverMode(true);
assertForegroundNetworkAccess();
@@ -103,8 +103,7 @@
// Make sure foreground service doesn't lose access upon enabling Battery Saver.
setBatterySaverMode(false);
- startForegroundService();
- assertForegroundNetworkAccess();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
setBatterySaverMode(true);
assertForegroundNetworkAccess();
stopForegroundService();
@@ -123,6 +122,12 @@
removePowerSaveModeWhitelist(TEST_APP2_PKG);
assertBackgroundNetworkAccess(false);
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
assertsForegroundAlwaysHasNetworkAccess();
assertBackgroundNetworkAccess(false);
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
index fc674a7..c2dce38 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -32,6 +32,7 @@
// Set initial state.
removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
setDozeMode(false);
registerBroadcastReceiver();
@@ -87,8 +88,7 @@
// Make sure foreground service doesn't lose network access upon enabling doze.
setDozeMode(false);
- startForegroundService();
- assertForegroundNetworkAccess();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
setDozeMode(true);
assertForegroundNetworkAccess();
stopForegroundService();
@@ -108,6 +108,12 @@
removePowerSaveModeWhitelist(TEST_APP2_PKG);
assertBackgroundNetworkAccess(false);
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
assertsForegroundAlwaysHasNetworkAccess();
assertBackgroundNetworkAccess(false);
}
@@ -158,8 +164,7 @@
// leaves Doze Mode.
@Override
protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception {
- startForegroundService();
- assertForegroundServiceNetworkAccess();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
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
index 8efa633..f3d5d2c 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -16,30 +16,39 @@
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 static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import android.app.Instrumentation;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
import android.net.wifi.WifiManager;
+import android.os.BatteryManager;
+import android.os.Binder;
+import android.os.Bundle;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
import android.util.Log;
+import com.android.cts.net.hostside.INetworkStateObserver;
+
/**
* Superclass for tests related to background network restrictions.
*/
@@ -49,31 +58,20 @@
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 String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity";
+ private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService";
+
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";
static final String ACTION_SHOW_TOAST =
"com.android.cts.net.hostside.app2.action.SHOW_TOAST";
- 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 EXTRA_NOTIFICATION_TYPE =
- "com.android.cts.net.hostside.app2.extra.NOTIFICATION_TYPE";
protected static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
protected static final String NOTIFICATION_TYPE_DELETE = "DELETE";
@@ -90,10 +88,24 @@
private static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
private static final int PROCESS_STATE_TOP = 2;
+ private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+
+ protected static final int TYPE_COMPONENT_ACTIVTIY = 0;
+ protected static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1;
+
+ private static final int BATTERY_STATE_TIMEOUT_MS = 5000;
+ private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 500;
+
+ private static final int FOREGROUND_PROC_NETWORK_TIMEOUT_MS = 6000;
// Must be higher than NETWORK_TIMEOUT_MS
private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4;
+ private static final IntentFilter BATTERY_CHANGED_FILTER =
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+
+ private static final String APP_NOT_FOREGROUND_ERROR = "app_not_fg";
+
protected Context mContext;
protected Instrumentation mInstrumentation;
protected ConnectivityManager mCm;
@@ -101,7 +113,7 @@
protected int mUid;
private int mMyUid;
private String mMeteredWifi;
- private boolean mHasWatch;
+ private MyServiceClient mServiceClient;
private String mDeviceIdleConstantsSetting;
private boolean mSupported;
@@ -115,13 +127,9 @@
mWfm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mUid = getUid(TEST_APP2_PKG);
mMyUid = getUid(mContext.getPackageName());
- mHasWatch = mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WATCH);
- if (mHasWatch) {
- mDeviceIdleConstantsSetting = "device_idle_constants_watch";
- } else {
- mDeviceIdleConstantsSetting = "device_idle_constants";
- }
+ mServiceClient = new MyServiceClient(mContext);
+ mServiceClient.bind();
+ mDeviceIdleConstantsSetting = "device_idle_constants";
mSupported = setUpActiveNetworkMeteringState();
Log.i(TAG, "Apps status on " + getName() + ":\n"
@@ -129,6 +137,13 @@
+ "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
}
+ @Override
+ protected void tearDown() throws Exception {
+ mServiceClient.unbind();
+
+ super.tearDown();
+ }
+
protected int getUid(String packageName) throws Exception {
return mContext.getPackageManager().getPackageUid(packageName, 0);
}
@@ -184,19 +199,13 @@
}
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);
+ return mServiceClient.getCounters(receiverName, action);
}
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));
+ final String status = mServiceClient.getRestrictBackgroundStatus();
+ assertNotNull("didn't get API status from app2", status);
+ final String actualStatus = toString(Integer.parseInt(status));
assertEquals("wrong status", toString(expectedStatus), actualStatus);
}
@@ -256,13 +265,11 @@
*/
protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception{
// Checks foreground first.
- launchActivity();
- assertForegroundNetworkAccess();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
finishActivity();
// Then foreground service
- startForegroundService();
- assertForegroundServiceNetworkAccess();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
stopForegroundService();
}
@@ -295,6 +302,7 @@
}
Log.d(TAG, "App not on foreground state on attempt #" + i
+ "; sleeping 1s before trying again");
+ turnScreenOn();
SystemClock.sleep(SECOND_IN_MS);
}
fail("App2 is not on foreground state after " + maxTries + " attempts: " + state );
@@ -359,15 +367,20 @@
* @return error message with the mismatch (or empty if assertion passed).
*/
private String checkNetworkAccess(boolean expectAvailable) throws Exception {
- String resultData = sendOrderedBroadcast(new Intent(ACTION_CHECK_NETWORK));
+ final String resultData = mServiceClient.checkNetworkStatus();
+ return checkForAvailabilityInResultData(resultData, expectAvailable);
+ }
+
+ private String checkForAvailabilityInResultData(String resultData, boolean expectAvailable) {
if (resultData == null) {
- return "timeout waiting for ordered broadcast";
+ assertNotNull("Network status from app2 is null", 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 State state = parts[0].equals("null") ? null : State.valueOf(parts[0]);
+ final DetailedState detailedState = parts[1].equals("null")
+ ? null : DetailedState.valueOf(parts[1]);
final boolean connected = Boolean.valueOf(parts[2]);
final String connectionCheckDetails = parts[3];
final String networkInfo = parts[4];
@@ -609,6 +622,9 @@
protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
assertRestrictBackgroundWhitelist(uid, true);
+ // UID policies live by the Highlander rule: "There can be only one".
+ // Hence, if app is whitelisted, it should not be blacklisted.
+ assertRestrictBackgroundBlacklist(uid, false);
}
protected void removeRestrictBackgroundWhitelist(int uid) throws Exception {
@@ -623,6 +639,9 @@
protected void addRestrictBackgroundBlacklist(int uid) throws Exception {
executeShellCommand("cmd netpolicy add restrict-background-blacklist " + uid);
assertRestrictBackgroundBlacklist(uid, true);
+ // UID policies live by the Highlander rule: "There can be only one".
+ // Hence, if app is blacklisted, it should not be whitelisted.
+ assertRestrictBackgroundWhitelist(uid, false);
}
protected void removeRestrictBackgroundBlacklist(int uid) throws Exception {
@@ -681,12 +700,56 @@
assertPowerSaveModeWhitelist(packageName, false); // Sanity check
}
+ protected void assertPowerSaveModeExceptIdleWhitelist(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 except-idle-whitelist =" + packageName,
+ Boolean.toString(expected));
+ }
+
+ protected void addPowerSaveModeExceptIdleWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Adding package " + packageName + " to power-save-mode-except-idle 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 except-idle-whitelist +" + packageName);
+ assertPowerSaveModeExceptIdleWhitelist(packageName, true); // Sanity check
+ }
+
+ protected void removePowerSaveModeExceptIdleWhitelist(String packageName) throws Exception {
+ Log.i(TAG, "Removing package " + packageName
+ + " from power-save-mode-except-idle 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 except-idle-whitelist reset");
+ assertPowerSaveModeExceptIdleWhitelist(packageName, false); // Sanity check
+ }
+
protected void turnBatteryOff() throws Exception {
executeSilentShellCommand("cmd battery unplug");
+ assertBatteryState(false);
}
protected void turnBatteryOn() throws Exception {
executeSilentShellCommand("cmd battery reset");
+ assertBatteryState(true);
+
+ }
+
+ private void assertBatteryState(boolean pluggedIn) throws Exception {
+ final long endTime = SystemClock.elapsedRealtime() + BATTERY_STATE_TIMEOUT_MS;
+ while (isDevicePluggedIn() != pluggedIn && SystemClock.elapsedRealtime() <= endTime) {
+ Thread.sleep(BATTERY_STATE_CHECK_INTERVAL_MS);
+ }
+ if (isDevicePluggedIn() != pluggedIn) {
+ fail("Timed out waiting for the plugged-in state to change,"
+ + " expected pluggedIn: " + pluggedIn);
+ }
+ }
+
+ private boolean isDevicePluggedIn() {
+ final Intent batteryIntent = mContext.registerReceiver(null, BATTERY_CHANGED_FILTER);
+ return batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) > 0;
}
protected void turnScreenOff() throws Exception {
@@ -702,10 +765,9 @@
Log.i(TAG, "Setting Battery Saver Mode to " + enabled);
if (enabled) {
turnBatteryOff();
- executeSilentShellCommand("cmd battery unplug");
- executeSilentShellCommand("settings put global low_power 1");
+ executeSilentShellCommand("cmd power set-mode 1");
} else {
- executeSilentShellCommand("settings put global low_power 0");
+ executeSilentShellCommand("cmd power set-mode 0");
turnBatteryOn();
}
}
@@ -739,12 +801,40 @@
protected void setAppIdle(boolean enabled) throws Exception {
Log.i(TAG, "Setting app idle to " + enabled);
+ final String beforeStats = getUsageStatsDump();
executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled );
- assertAppIdle(enabled); // Sanity check
+ try {
+ assertAppIdle(enabled); // Sanity check
+ } catch (Throwable e) {
+ final String afterStats = getUsageStatsDump();
+ Log.d(TAG, "UsageStats before:\n" + beforeStats);
+ Log.d(TAG, "UsageStats after:\n" + afterStats);
+ throw e;
+ }
+ }
+
+ private String getUsageStatsDump() throws Exception {
+ final String output = runShellCommand(mInstrumentation, "dumpsys usagestats").trim();
+ final StringBuilder sb = new StringBuilder();
+ final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n');
+ splitter.setString(output);
+ String str;
+ while (splitter.hasNext()) {
+ str = splitter.next();
+ if (str.contains("package=")
+ && !str.contains(TEST_PKG) && !str.contains(TEST_APP2_PKG)) {
+ continue;
+ }
+ if (str.contains("config=")) {
+ continue;
+ }
+ sb.append(str).append('\n');
+ }
+ return sb.toString();
}
protected void assertAppIdle(boolean enabled) throws Exception {
- assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 10, 2, "Idle=" + enabled);
+ assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 15, 2, "Idle=" + enabled);
}
/**
@@ -755,12 +845,14 @@
* {@link #runDeviceTests(String, String)} is executed.
*/
protected void registerBroadcastReceiver() throws Exception {
- executeShellCommand("am startservice com.android.cts.net.hostside.app2/.MyService");
+ mServiceClient.registerBroadcastReceiver();
+
+ final Intent intent = new Intent(ACTION_RECEIVER_READY)
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
// 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);
+ final String message = sendOrderedBroadcast(intent, SECOND_IN_MS * 4);
Log.d(TAG, "app2 receiver acked: " + message);
if (message != null) {
return;
@@ -802,35 +894,86 @@
mDeviceIdleConstantsSetting));
}
- protected void startForegroundService() throws Exception {
- executeShellCommand(
- "am startservice -f 1 com.android.cts.net.hostside.app2/.MyForegroundService");
+ protected void launchComponentAndAssertNetworkAccess(int type) throws Exception {
+ if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
+ startForegroundService();
+ assertForegroundServiceNetworkAccess();
+ return;
+ } else if (type == TYPE_COMPONENT_ACTIVTIY) {
+ turnScreenOn();
+ // Wait for screen-on state to propagate through the system.
+ SystemClock.sleep(2000);
+ final CountDownLatch latch = new CountDownLatch(1);
+ final Intent launchIntent = getIntentForComponent(type);
+ final Bundle extras = new Bundle();
+ final String[] errors = new String[]{null};
+ extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, errors));
+ launchIntent.putExtras(extras);
+ mContext.startActivity(launchIntent);
+ if (latch.await(FOREGROUND_PROC_NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ if (!errors[0].isEmpty()) {
+ if (errors[0] == APP_NOT_FOREGROUND_ERROR) {
+ // App didn't come to foreground when the activity is started, so try again.
+ assertForegroundNetworkAccess();
+ } else {
+ fail("Network is not available for app2 (" + mUid + "): " + errors[0]);
+ }
+ }
+ } else {
+ fail("Timed out waiting for network availability status from app2 (" + mUid + ")");
+ }
+ } else {
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ }
+
+ private void startForegroundService() throws Exception {
+ final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_FOREGROUND_SERVICE);
+ mContext.startForegroundService(launchIntent);
assertForegroundServiceState();
}
+ private Intent getIntentForComponent(int type) {
+ final Intent intent = new Intent();
+ if (type == TYPE_COMPONENT_ACTIVTIY) {
+ intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS));
+ } else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) {
+ intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS))
+ .setFlags(1);
+ } else {
+ fail("Unknown type: " + type);
+ }
+ return intent;
+ }
+
protected void stopForegroundService() throws Exception {
- executeShellCommand(
- "am startservice -f 2 com.android.cts.net.hostside.app2/.MyForegroundService");
+ executeShellCommand(String.format("am startservice -f 2 %s/%s",
+ TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS));
// 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);
+ private Binder getNewNetworkStateObserver(final CountDownLatch latch,
+ final String[] errors) {
+ return new INetworkStateObserver.Stub() {
+ @Override
+ public boolean isForeground() {
+ try {
+ final ProcessState state = getProcessStateByUid(mUid);
+ return !isBackground(state.state);
+ } catch (Exception e) {
+ Log.d(TAG, "Error while reading the proc state for " + mUid + ": " + e);
+ return false;
+ }
+ }
+
+ @Override
+ public void onNetworkStateChecked(String resultData) {
+ errors[0] = resultData == null
+ ? APP_NOT_FOREGROUND_ERROR
+ : checkForAvailabilityInResultData(resultData, true);
+ latch.countDown();
+ }
+ };
}
/**
@@ -842,13 +985,10 @@
+ "--receiver-foreground --receiver-registered-only");
}
- protected void sendNotification(int notificationId, String notificationType) {
- final Intent intent = new Intent(ACTION_SEND_NOTIFICATION);
- intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
- intent.putExtra(EXTRA_NOTIFICATION_TYPE, notificationType);
- Log.d(TAG, "Sending notification broadcast (id=" + notificationId + ", type="
- + notificationType + ": " + intent);
- mContext.sendBroadcast(intent);
+ protected void sendNotification(int notificationId, String notificationType) throws Exception {
+ Log.d(TAG, "Sending notification broadcast (id=" + notificationId
+ + ", type=" + notificationType);
+ mServiceClient.sendNotification(notificationId, notificationType);
}
protected String showToast() {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
index 72d0be9..599a31c 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -135,8 +135,7 @@
// Make sure foreground app doesn't lose access upon enabling Data Saver.
setRestrictBackground(false);
- launchActivity();
- assertForegroundNetworkAccess();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY);
setRestrictBackground(true);
assertForegroundNetworkAccess();
@@ -152,8 +151,7 @@
// Make sure foreground service doesn't lose access upon enabling Data Saver.
setRestrictBackground(false);
- startForegroundService();
- assertForegroundNetworkAccess();
+ launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE);
setRestrictBackground(true);
assertForegroundNetworkAccess();
stopForegroundService();
@@ -168,26 +166,34 @@
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
assertsForegroundAlwaysHasNetworkAccess();
+ assertRestrictBackgroundChangedReceived(1);
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
- // Make sure blacklist prevails over whitelist.
+ // UID policies live by the Highlander rule: "There can be only one".
+ // Hence, if app is whitelisted, it should not be blacklisted anymore.
setRestrictBackground(true);
assertRestrictBackgroundChangedReceived(2);
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
addRestrictBackgroundWhitelist(mUid);
assertRestrictBackgroundChangedReceived(3);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
// Check status after removing blacklist.
+ // ...re-enables first
+ addRestrictBackgroundBlacklist(mUid);
+ assertRestrictBackgroundChangedReceived(4);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+ assertsForegroundAlwaysHasNetworkAccess();
+ // ... remove blacklist - access's still rejected because Data Saver is on
removeRestrictBackgroundBlacklist(mUid);
assertRestrictBackgroundChangedReceived(4);
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+ assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+ assertsForegroundAlwaysHasNetworkAccess();
+ // ... finally, disable Data Saver
setRestrictBackground(false);
assertRestrictBackgroundChangedReceived(5);
assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
-
assertsForegroundAlwaysHasNetworkAccess();
- assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
}
public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
index ec49eee..5248255 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -38,6 +38,7 @@
removeRestrictBackgroundWhitelist(mUid);
removeRestrictBackgroundBlacklist(mUid);
removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
registerBroadcastReceiver();
}
@@ -200,4 +201,74 @@
removeRestrictBackgroundBlacklist(mUid);
removePowerSaveModeWhitelist(TEST_APP2_PKG);
}
+
+ /**
+ * Tests that powersave whitelists works as expected when doze and battery saver modes
+ * are enabled.
+ */
+ public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
+ if (!isSupported()) {
+ return;
+ }
+ if (!isDozeModeEnabled()) {
+ Log.i(TAG, "Skipping " + getClass() + "." + getName()
+ + "() because device does not support Doze Mode");
+ return;
+ }
+
+ setBatterySaverMode(true);
+ setDozeMode(true);
+
+ try {
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setBatterySaverMode(false);
+ setDozeMode(false);
+ }
+ }
+
+ /**
+ * Tests that powersave whitelists works as expected when doze and appIdle modes
+ * are enabled.
+ */
+ public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
+ if (!isSupported()) {
+ return;
+ }
+ if (!isDozeModeEnabled()) {
+ Log.i(TAG, "Skipping " + getClass() + "." + getName()
+ + "() because device does not support Doze Mode");
+ return;
+ }
+
+ setDozeMode(true);
+ setAppIdle(true);
+
+ try {
+ addPowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(true);
+
+ removePowerSaveModeWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ addPowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+
+ removePowerSaveModeExceptIdleWhitelist(TEST_APP2_PKG);
+ assertBackgroundNetworkAccess(false);
+ } finally {
+ setAppIdle(false);
+ setDozeMode(false);
+ }
+ }
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
new file mode 100644
index 0000000..ff05d8c
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -0,0 +1,101 @@
+/*
+ * 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.IMyService;
+
+import java.io.FileDescriptor;
+
+public class MyServiceClient {
+ private static final int TIMEOUT_MS = 5000;
+ private static final String PACKAGE = MyServiceClient.class.getPackage().getName();
+ private static final String APP2_PACKAGE = PACKAGE + ".app2";
+ private static final String SERVICE_NAME = APP2_PACKAGE + ".MyService";
+
+ private Context mContext;
+ private ServiceConnection mServiceConnection;
+ private IMyService mService;
+
+ public MyServiceClient(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 = IMyService.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));
+ // Needs to use BIND_ALLOW_OOM_MANAGEMENT and BIND_NOT_FOREGROUND so app2 does not run in
+ // the same process state as app
+ mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE
+ | Context.BIND_ALLOW_OOM_MANAGEMENT | Context.BIND_NOT_FOREGROUND);
+ cv.block(TIMEOUT_MS);
+ if (mService == null) {
+ throw new IllegalStateException(
+ "Could not bind to MyService service after " + TIMEOUT_MS + "ms");
+ }
+ }
+
+ public void unbind() {
+ if (mService != null) {
+ mContext.unbindService(mServiceConnection);
+ }
+ }
+
+ public void registerBroadcastReceiver() throws RemoteException {
+ mService.registerBroadcastReceiver();
+ }
+
+ public int getCounters(String receiverName, String action) throws RemoteException {
+ return mService.getCounters(receiverName, action);
+ }
+
+ public String checkNetworkStatus() throws RemoteException {
+ return mService.checkNetworkStatus();
+ }
+
+ public String getRestrictBackgroundStatus() throws RemoteException {
+ return mService.getRestrictBackgroundStatus();
+ }
+
+ public void sendNotification(int notificationId, String notificationType) throws RemoteException {
+ mService.sendNotification(notificationId, notificationType);
+ }
+}
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 075fce6..bc982ce 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
@@ -84,6 +84,11 @@
* https://android-review.googlesource.com/#/c/99225/
* https://android-review.googlesource.com/#/c/100557/
*
+ * To ensure that the kernel has the required commits, run the kernel unit
+ * tests described at:
+ *
+ * https://source.android.com/devices/tech/config/kernel_network_tests.html
+ *
*/
public class VpnTest extends InstrumentationTestCase {
@@ -105,8 +110,7 @@
private boolean supportedHardware() {
final PackageManager pm = getInstrumentation().getContext().getPackageManager();
- return !pm.hasSystemFeature("android.hardware.type.television") &&
- !pm.hasSystemFeature("android.hardware.type.watch");
+ return !pm.hasSystemFeature("android.hardware.type.watch");
}
@Override
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
index e07c0f5..351733e 100644
--- 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
@@ -16,7 +16,14 @@
package com.android.cts.net.hostside.app2;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.cts.net.hostside.INetworkStateObserver;
public final class Common {
@@ -26,27 +33,13 @@
// 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 ACTION_SHOW_TOAST =
"com.android.cts.net.hostside.app2.action.SHOW_TOAST";
- 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 final String EXTRA_NOTIFICATION_TYPE =
- "com.android.cts.net.hostside.app2.extra.NOTIFICATION_TYPE";
static final String NOTIFICATION_TYPE_CONTENT = "CONTENT";
static final String NOTIFICATION_TYPE_DELETE = "DELETE";
@@ -56,6 +49,9 @@
static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE";
static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT";
+ static final String TEST_PKG = "com.android.cts.net.hostside";
+ static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
+
static int getUid(Context context) {
final String packageName = context.getPackageName();
try {
@@ -64,4 +60,35 @@
throw new IllegalStateException("Could not get UID for " + packageName, e);
}
}
+
+ static void notifyNetworkStateObserver(Context context, Intent intent) {
+ if (intent == null) {
+ return;
+ }
+ final Bundle extras = intent.getExtras();
+ if (extras == null) {
+ return;
+ }
+ final INetworkStateObserver observer = INetworkStateObserver.Stub.asInterface(
+ extras.getBinder(KEY_NETWORK_STATE_OBSERVER));
+ if (observer != null) {
+ try {
+ if (!observer.isForeground()) {
+ Log.e(TAG, "App didn't come to foreground");
+ observer.onNetworkStateChecked(null);
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error occurred while reading the proc state: " + e);
+ }
+ AsyncTask.execute(() -> {
+ try {
+ observer.onNetworkStateChecked(
+ MyBroadcastReceiver.checkNetworkStatus(context));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error occurred while notifying the observer: " + 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
index 444b696..286cc2f 100644
--- a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
@@ -17,30 +17,48 @@
import static com.android.cts.net.hostside.app2.Common.ACTION_FINISH_ACTIVITY;
import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.RemoteException;
import android.util.Log;
+import com.android.cts.net.hostside.INetworkStateObserver;
+
/**
* Activity used to bring process to foreground.
*/
public class MyActivity extends Activity {
+ private BroadcastReceiver finishCommandReceiver = null;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- registerReceiver(new BroadcastReceiver() {
-
+ Log.d(TAG, "MyActivity.onCreate()");
+ Common.notifyNetworkStateObserver(this, getIntent());
+ finishCommandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Finishing MyActivity");
MyActivity.this.finish();
- }}, new IntentFilter(ACTION_FINISH_ACTIVITY));
+ }
+ };
+ registerReceiver(finishCommandReceiver, new IntentFilter(ACTION_FINISH_ACTIVITY));
+ }
+
+ @Override
+ public void finish() {
+ if (finishCommandReceiver != null) {
+ unregisterReceiver(finishCommandReceiver);
+ }
+ super.finish();
}
@Override
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
index 733c3aa..aa54075 100644
--- 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
@@ -18,16 +18,8 @@
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.ACTION_SHOW_TOAST;
-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_NOTIFICATION_TYPE;
-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.NOTIFICATION_TYPE_ACTION;
import static com.android.cts.net.hostside.app2.Common.NOTIFICATION_TYPE_ACTION_BUNDLE;
@@ -56,14 +48,12 @@
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.
+ * <li>Count number of {@code RESTRICT_BACKGROUND_CHANGED} broadcasts received.
+ * <li>Show a toast.
* </ol>
*/
public class MyBroadcastReceiver extends BroadcastReceiver {
@@ -89,23 +79,11 @@
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;
case ACTION_SHOW_TOAST:
showToast(context);
break;
@@ -114,14 +92,20 @@
}
}
+ @Override
+ public String toString() {
+ return "[MyBroadcastReceiver: mName=" + mName + "]";
+ }
+
private void increaseCounter(Context context, String action) {
- final SharedPreferences prefs = context.getSharedPreferences(mName, Context.MODE_PRIVATE);
+ final SharedPreferences prefs = context.getApplicationContext()
+ .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) {
+ static int getCounter(Context context, String action, String receiverName) {
final SharedPreferences prefs = context.getSharedPreferences(receiverName,
Context.MODE_PRIVATE);
final int value = prefs.getInt(action, 0);
@@ -129,29 +113,14 @@
return value;
}
- private void getRestrictBackgroundStatus(Context context, Intent intent) {
+ static String getRestrictBackgroundStatus(Context context) {
final ConnectivityManager cm = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
final int apiStatus = cm.getRestrictBackgroundStatus();
Log.d(TAG, "getRestrictBackgroundStatus: returning " + apiStatus);
- setResultData(Integer.toString(apiStatus));
+ return String.valueOf(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
@@ -182,78 +151,61 @@
* </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;
+ // TODO: now that it uses Binder, it counl return a Bundle with the data parts instead...
+ static String checkNetworkStatus(Context context) {
+ final ConnectivityManager cm =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ // 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;
}
- final String receiverName = intent.getStringExtra(EXTRA_RECEIVER_NAME);
- if (receiverName == null) {
- Log.e(TAG, "Missing extra '" + EXTRA_RECEIVER_NAME + "' on " + intent);
- return;
+ Log.d(TAG, checkDetails);
+ final String state, detailedState;
+ if (networkInfo != null) {
+ state = networkInfo.getState().name();
+ detailedState = networkInfo.getDetailedState().name();
+ } else {
+ state = detailedState = "null";
}
- final int counter = getCounter(context, action, receiverName);
- setResultData(String.valueOf(counter));
+ final String status = String.format(NETWORK_STATUS_TEMPLATE, state, detailedState,
+ Boolean.valueOf(checkStatus), checkDetails, networkInfo);
+ Log.d(TAG, "Offering " + status);
+ return status;
}
/**
* 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 String notificationType = intent.getStringExtra(EXTRA_NOTIFICATION_TYPE);
- Log.d(TAG, "sendNotification: id=" + notificationId + ", type=" + notificationType
- + ", intent=" + intent);
+ static void sendNotification(Context context, String channelId, int notificationId,
+ String notificationType ) {
+ Log.d(TAG, "sendNotification: id=" + notificationId + ", type=" + notificationType);
final Intent serviceIntent = new Intent(context, MyService.class);
final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent,
notificationId);
final Bundle bundle = new Bundle();
bundle.putCharSequence("parcelable", "I am not");
- final Notification.Builder builder = new Notification.Builder(context)
+ final Notification.Builder builder = new Notification.Builder(context, channelId)
.setSmallIcon(R.drawable.ic_notification);
Action action = null;
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
index b88c45d..ff4ba65 100644
--- 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
@@ -16,18 +16,27 @@
package com.android.cts.net.hostside.app2;
import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.net.hostside.app2.Common.TEST_PKG;
+
import android.R;
import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
+import com.android.cts.net.hostside.INetworkStateObserver;
+
/**
* Service used to change app state to FOREGROUND_SERVICE.
*/
public class MyForegroundService extends Service {
-
+ private static final String NOTIFICATION_CHANNEL_ID = "cts/MyForegroundService";
private static final int FLAG_START_FOREGROUND = 1;
private static final int FLAG_STOP_FOREGROUND = 2;
@@ -39,12 +48,17 @@
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(TAG, "MyForegroundService.onStartCommand(): " + intent);
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(new NotificationChannel(
+ NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID,
+ NotificationManager.IMPORTANCE_DEFAULT));
switch (intent.getFlags()) {
case FLAG_START_FOREGROUND:
Log.d(TAG, "Starting foreground");
- startForeground(42, new Notification.Builder(this)
+ startForeground(42, new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
.build());
+ Common.notifyNetworkStateObserver(this, intent);
break;
case FLAG_STOP_FOREGROUND:
Log.d(TAG, "Stopping foreground");
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
index e6454c7..2496c4a 100644
--- 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
@@ -20,31 +20,92 @@
import static com.android.cts.net.hostside.app2.Common.DYNAMIC_RECEIVER;
import static com.android.cts.net.hostside.app2.Common.TAG;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.os.IBinder;
+import android.os.Looper;
import android.util.Log;
+import android.widget.Toast;
+
+import com.android.cts.net.hostside.IMyService;
/**
* Service used to dynamically register a broadcast receiver.
*/
public class MyService extends Service {
+ private static final String NOTIFICATION_CHANNEL_ID = "MyService";
+
+ private MyBroadcastReceiver mReceiver;
+
+ // TODO: move MyBroadcast static functions here - they were kept there to make git diff easier.
+
+ private IMyService.Stub mBinder =
+ new IMyService.Stub() {
+
+ @Override
+ public void registerBroadcastReceiver() {
+ if (mReceiver != null) {
+ Log.d(TAG, "receiver already registered: " + mReceiver);
+ return;
+ }
+ final Context context = getApplicationContext();
+ mReceiver = new MyBroadcastReceiver(DYNAMIC_RECEIVER);
+ context.registerReceiver(mReceiver, new IntentFilter(ACTION_RECEIVER_READY));
+ context.registerReceiver(mReceiver,
+ new IntentFilter(ACTION_RESTRICT_BACKGROUND_CHANGED));
+ Log.d(TAG, "receiver registered");
+ }
+
+ @Override
+ public int getCounters(String receiverName, String action) {
+ return MyBroadcastReceiver.getCounter(getApplicationContext(), action, receiverName);
+ }
+
+ @Override
+ public String checkNetworkStatus() {
+ return MyBroadcastReceiver.checkNetworkStatus(getApplicationContext());
+ }
+
+ @Override
+ public String getRestrictBackgroundStatus() {
+ return MyBroadcastReceiver.getRestrictBackgroundStatus(getApplicationContext());
+ }
+
+ @Override
+ public void sendNotification(int notificationId, String notificationType) {
+ MyBroadcastReceiver .sendNotification(getApplicationContext(), NOTIFICATION_CHANNEL_ID,
+ notificationId, notificationType);
+ }
+ };
@Override
public IBinder onBind(Intent intent) {
- return null;
+ return mBinder;
}
@Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.d(TAG, "MyService.onStartCommand: " + intent);
+ public void onCreate() {
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;
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+ .createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
+ NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT));
+ }
+
+ @Override
+ public void onDestroy() {
+ final Context context = getApplicationContext();
+ ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+ .deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
+ if (mReceiver != null) {
+ Log.d(TAG, "onDestroy(): unregistering " + mReceiver);
+ getApplicationContext().unregisterReceiver(mReceiver);
+ }
+
+ super.onDestroy();
}
}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
index e96537c..c6df893 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -16,7 +16,7 @@
package com.android.cts.net;
-import com.android.cts.migration.MigrationHelper;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.ddmlib.Log;
import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
import com.android.ddmlib.testrunner.TestIdentifier;
@@ -78,7 +78,8 @@
protected void installPackage(String apk) throws FileNotFoundException,
DeviceNotAvailableException {
- assertNull(getDevice().installPackage(MigrationHelper.getTestFile(mCtsBuild, apk), false));
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
+ assertNull(getDevice().installPackage(buildHelper.getTestFile(apk), false));
}
protected void uninstallPackage(String packageName, boolean shouldSucceed)
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index faf75d9..bf3fc08 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -251,6 +251,16 @@
"testDataAndBatterySaverModes_nonMeteredNetwork");
}
+ public void testDozeAndBatterySaverMode_powerSaveWhitelists() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDozeAndBatterySaverMode_powerSaveWhitelists");
+ }
+
+ public void testDozeAndAppIdle_powerSaveWhitelists() throws Exception {
+ runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+ "testDozeAndAppIdle_powerSaveWhitelists");
+ }
+
/*******************
* Helper methods. *
*******************/
diff --git a/tests/cts/net/Android.mk b/tests/cts/net/Android.mk
index c553a9b..98cde9b 100644
--- a/tests/cts/net/Android.mk
+++ b/tests/cts/net/Android.mk
@@ -34,8 +34,14 @@
LOCAL_PACKAGE_NAME := CtsNetTestCases
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests-support ctsdeviceutil \
- ctstestrunner ctstestserver mockwebserver
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ core-tests-support \
+ compatibility-device-util \
+ ctstestrunner \
+ ctstestserver \
+ mockwebserver \
+ junit \
+ legacy-android-test
# uncomment when b/13249961 is fixed
#LOCAL_SDK_VERSION := current
diff --git a/tests/cts/net/AndroidTest.xml b/tests/cts/net/AndroidTest.xml
index 389b926..4a578ea 100644
--- a/tests/cts/net/AndroidTest.xml
+++ b/tests/cts/net/AndroidTest.xml
@@ -13,7 +13,8 @@
limitations under the License.
-->
<configuration description="Config for CTS Net test cases">
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+ <option name="config-descriptor:metadata" key="component" value="networking" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsNetTestCases.apk" />
<option name="test-file-name" value="CtsNetTestAppForApi23.apk" />
diff --git a/tests/cts/net/assets/HSR1ProfileWithCACert.base64 b/tests/cts/net/assets/HSR1ProfileWithCACert.base64
new file mode 100644
index 0000000..995963d
--- /dev/null
+++ b/tests/cts/net/assets/HSR1ProfileWithCACert.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
+IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
+SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
+YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
+UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
+V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
+M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
+MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
+VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
+RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
+QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
+QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
+bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
+MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
+Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
+ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
+YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
+VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
+YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
+RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
+bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
+MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
+UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
+QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
+OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
+dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
+S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
+dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
+TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
+WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
+VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
+V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
+a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
+VTJSbWx1WjJWeWNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
+VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
+UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
+WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
+V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
+bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
+QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
+LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
+UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
+VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
+bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
+azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
+VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
+TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
+TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
+dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
+RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
+U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
+ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
+M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
+CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
+TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
+U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
+YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
+MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
+akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
+MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
+amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
+ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
+OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
+MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
+MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
+aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
+S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
+a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
+RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
diff --git a/tests/cts/net/assets/PerProviderSubscription.xml b/tests/cts/net/assets/PerProviderSubscription.xml
new file mode 100644
index 0000000..de6c0c6
--- /dev/null
+++ b/tests/cts/net/assets/PerProviderSubscription.xml
@@ -0,0 +1,80 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+ <VerDTD>1.2</VerDTD>
+ <Node>
+ <NodeName>PerProviderSubscription</NodeName>
+ <RTProperties>
+ <Type>
+ <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+ </Type>
+ </RTProperties>
+ <Node>
+ <NodeName>i001</NodeName>
+ <Node>
+ <NodeName>HomeSP</NodeName>
+ <Node>
+ <NodeName>FriendlyName</NodeName>
+ <Value>Century House</Value>
+ </Node>
+ <Node>
+ <NodeName>FQDN</NodeName>
+ <Value>mi6.co.uk</Value>
+ </Node>
+ <Node>
+ <NodeName>RoamingConsortiumOI</NodeName>
+ <Value>112233,445566</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>Credential</NodeName>
+ <Node>
+ <NodeName>Realm</NodeName>
+ <Value>shaken.stirred.com</Value>
+ </Node>
+ <Node>
+ <NodeName>UsernamePassword</NodeName>
+ <Node>
+ <NodeName>Username</NodeName>
+ <Value>james</Value>
+ </Node>
+ <Node>
+ <NodeName>Password</NodeName>
+ <Value>Ym9uZDAwNw==</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPMethod</NodeName>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>21</Value>
+ </Node>
+ <Node>
+ <NodeName>InnerMethod</NodeName>
+ <Value>MS-CHAP-V2</Value>
+ </Node>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>DigitalCertificate</NodeName>
+ <Node>
+ <NodeName>CertificateType</NodeName>
+ <Value>x509v3</Value>
+ </Node>
+ <Node>
+ <NodeName>CertSHA256Fingerprint</NodeName>
+ <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+ </Node>
+ </Node>
+ <Node>
+ <NodeName>SIM</NodeName>
+ <Node>
+ <NodeName>IMSI</NodeName>
+ <Value>imsi</Value>
+ </Node>
+ <Node>
+ <NodeName>EAPType</NodeName>
+ <Value>24</Value>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+ </Node>
+</MgmtTree>
diff --git a/tests/cts/net/jni/NativeDnsJni.c b/tests/cts/net/jni/NativeDnsJni.c
index 1df9169..6d3d1c3 100644
--- a/tests/cts/net/jni/NativeDnsJni.c
+++ b/tests/cts/net/jni/NativeDnsJni.c
@@ -126,7 +126,7 @@
return JNI_FALSE;
}
- memset(buf, sizeof(buf), 0);
+ memset(buf, 0, sizeof(buf));
res = getnameinfo((const struct sockaddr*)&sa6, sizeof(sa6), buf, sizeof(buf), NULL, 0, flags);
if (res != 0) {
ALOGD("getnameinfo(%s (GoogleDNS) ) gave error %d (%s)", GoogleDNSIpV6Address2,
diff --git a/tests/cts/net/jni/NativeMultinetworkJni.c b/tests/cts/net/jni/NativeMultinetworkJni.c
index ad56b51..9156504 100644
--- a/tests/cts/net/jni/NativeMultinetworkJni.c
+++ b/tests/cts/net/jni/NativeMultinetworkJni.c
@@ -30,7 +30,7 @@
#include <sys/time.h>
#include <android/multinetwork.h>
-#define UNUSED(X) ((void) X)
+#define UNUSED(X) ((void) (X))
static const char kHostname[] = "connectivitycheck.android.com";
@@ -88,7 +88,9 @@
return rval == 0 ? 0 : -saved_errno;
}
-static const int kSockaddrStrLen = INET6_ADDRSTRLEN + strlen("[]:65535");
+// Use sizeof("x") - 1 because we need a compile-time constant, and strlen("x")
+// isn't guaranteed to fold to a constant.
+static const int kSockaddrStrLen = INET6_ADDRSTRLEN + sizeof("[]:65535") - 1;
void sockaddr_ntop(const struct sockaddr *sa, socklen_t salen, char *dst, const size_t size) {
char addrstr[INET6_ADDRSTRLEN];
diff --git a/tests/cts/net/native/qtaguid/AndroidTest.xml b/tests/cts/net/native/qtaguid/AndroidTest.xml
index 6f4e8f5..2eea82e 100644
--- a/tests/cts/net/native/qtaguid/AndroidTest.xml
+++ b/tests/cts/net/native/qtaguid/AndroidTest.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Config for CTS Native Network xt_qtaguid test cases">
+ <option name="config-descriptor:metadata" key="component" value="networking" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" />
<option name="push" value="CtsNativeNetTestCases->/data/local/tmp/CtsNativeNetTestCases" />
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 185ebfa..9f54c8e 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -18,6 +18,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -44,12 +45,15 @@
import com.android.internal.telephony.PhoneConstants;
+import java.io.File;
+import java.io.FileNotFoundException;
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.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -73,6 +77,17 @@
"Host: " + TEST_HOST + "\r\n" +
"Connection: keep-alive\r\n\r\n";
+ // Base path for IPv6 sysctls
+ private static final String IPV6_SYSCTL_DIR = "/proc/sys/net/ipv6/conf";
+
+ // Expected values for MIN|MAX_PLEN.
+ private static final int IPV6_WIFI_ACCEPT_RA_RT_INFO_MIN_PLEN = 48;
+ private static final int IPV6_WIFI_ACCEPT_RA_RT_INFO_MAX_PLEN = 64;
+
+ // Expected values for RFC 7559 router soliciations.
+ // Maximum number of router solicitations to send. -1 means no limit.
+ private static final int IPV6_WIFI_ROUTER_SOLICITATIONS = -1;
+
// Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent.
private static final String NETWORK_CALLBACK_ACTION =
"ConnectivityManagerTest.NetworkCallbackAction";
@@ -90,6 +105,7 @@
private PackageManager mPackageManager;
private final HashMap<Integer, NetworkConfig> mNetworks =
new HashMap<Integer, NetworkConfig>();
+ boolean mWifiConnectAttempted;
@Override
protected void setUp() throws Exception {
@@ -98,6 +114,7 @@
mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
mPackageManager = mContext.getPackageManager();
+ mWifiConnectAttempted = false;
// Get com.android.internal.R.array.networkAttributes
int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
@@ -115,6 +132,27 @@
}
}
+ @Override
+ protected void tearDown() throws Exception {
+ // Return WiFi to its original disabled state after tests that explicitly connect.
+ if (mWifiConnectAttempted) {
+ disconnectFromWifi(null);
+ }
+ }
+
+ /**
+ * Make sure WiFi is connected to an access point if it is not already. If
+ * WiFi is enabled as a result of this function, it will be disabled
+ * automatically in tearDown().
+ */
+ private Network ensureWifiConnected() {
+ if (mWifiManager.isWifiEnabled()) {
+ return getWifiNetwork();
+ }
+ mWifiConnectAttempted = true;
+ return connectToWifi();
+ }
+
public void testIsNetworkTypeValid() {
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_MOBILE));
assertTrue(ConnectivityManager.isNetworkTypeValid(ConnectivityManager.TYPE_WIFI));
@@ -297,14 +335,10 @@
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.
- if (!previousWifiEnabledState) {
- connectToWifi();
- }
+ ensureWifiConnected();
// 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
@@ -320,11 +354,6 @@
} finally {
mCm.unregisterNetworkCallback(callback);
mCm.unregisterNetworkCallback(defaultTrackingCallback);
-
- // Return WiFi to its original enabled/disabled state.
- if (!previousWifiEnabledState) {
- disconnectFromWifi(wifiNetwork);
- }
}
}
@@ -356,13 +385,8 @@
// We will register for a WIFI network being available or lost.
mCm.registerNetworkCallback(makeWifiNetworkRequest(), pendingIntent);
- final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
-
try {
- // Make sure WiFi is connected to an access point to start with.
- if (!previousWifiEnabledState) {
- connectToWifi();
- }
+ ensureWifiConnected();
// Now we expect to get the Intent delivered notifying of the availability of the wifi
// network even if it was already connected as a state-based action when the callback
@@ -375,10 +399,56 @@
mCm.unregisterNetworkCallback(pendingIntent);
pendingIntent.cancel();
mContext.unregisterReceiver(receiver);
+ }
+ }
- // Return WiFi to its original enabled/disabled state.
- if (!previousWifiEnabledState) {
- disconnectFromWifi(null);
+ /**
+ * Exercises the requestNetwork with NetworkCallback API. This checks to
+ * see if we get a callback for an INTERNET request.
+ */
+ public void testRequestNetworkCallback() {
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder()
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build(), callback);
+
+ try {
+ // Wait to get callback for availability of internet
+ Network internetNetwork = callback.waitForAvailable();
+ assertNotNull("Did not receive NetworkCallback#onAvailable for INTERNET",
+ internetNetwork);
+ } catch (InterruptedException e) {
+ fail("NetworkCallback wait was interrupted.");
+ } finally {
+ mCm.unregisterNetworkCallback(callback);
+ }
+ }
+
+ /**
+ * Exercises the requestNetwork with NetworkCallback API with timeout - expected to
+ * fail. Use WIFI and switch Wi-Fi off.
+ */
+ public void testRequestNetworkCallback_onUnavailable() {
+ final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+ if (previousWifiEnabledState) {
+ disconnectFromWifi(null);
+ }
+
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .build(), callback, 100);
+
+ try {
+ // Wait to get callback for unavailability of requested network
+ assertTrue("Did not receive NetworkCallback#onUnavailable",
+ callback.waitForUnavailable());
+ } catch (InterruptedException e) {
+ fail("NetworkCallback wait was interrupted.");
+ } finally {
+ mCm.unregisterNetworkCallback(callback);
+ if (previousWifiEnabledState) {
+ connectToWifi();
}
}
}
@@ -432,6 +502,7 @@
startIntent.setComponent(new ComponentName("android.net.cts.appForApi23",
"android.net.cts.appForApi23.ConnectivityListeningActivity"));
mContext.startActivity(startIntent);
+ Thread.sleep(200);
toggleWifi();
@@ -639,6 +710,7 @@
private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
private final CountDownLatch mLostLatch = new CountDownLatch(1);
+ private final CountDownLatch mUnavailableLatch = new CountDownLatch(1);
public Network currentNetwork;
public Network lastLostNetwork;
@@ -651,6 +723,11 @@
return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null;
}
+ public boolean waitForUnavailable() throws InterruptedException {
+ return mUnavailableLatch.await(2, TimeUnit.SECONDS);
+ }
+
+
@Override
public void onAvailable(Network network) {
currentNetwork = network;
@@ -665,6 +742,11 @@
}
mLostLatch.countDown();
}
+
+ @Override
+ public void onUnavailable() {
+ mUnavailableLatch.countDown();
+ }
}
private Network getWifiNetwork() {
@@ -698,4 +780,58 @@
fail("No exception thrown when restricted network requested.");
} catch (SecurityException expected) {}
}
+
+ private Scanner makeWifiSysctlScanner(String key) throws FileNotFoundException {
+ Network network = ensureWifiConnected();
+ String iface = mCm.getLinkProperties(network).getInterfaceName();
+ String path = IPV6_SYSCTL_DIR + "/" + iface + "/" + key;
+ return new Scanner(new File(path));
+ }
+
+ /** Verify that accept_ra_rt_info_min_plen exists and is set to the expected value */
+ public void testAcceptRaRtInfoMinPlen() throws Exception {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
+ Scanner s = makeWifiSysctlScanner("accept_ra_rt_info_min_plen");
+ assertEquals(IPV6_WIFI_ACCEPT_RA_RT_INFO_MIN_PLEN, s.nextInt());
+ }
+
+ /** Verify that accept_ra_rt_info_max_plen exists and is set to the expected value */
+ public void testAcceptRaRtInfoMaxPlen() throws Exception {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
+ Scanner s = makeWifiSysctlScanner("accept_ra_rt_info_max_plen");
+ assertEquals(IPV6_WIFI_ACCEPT_RA_RT_INFO_MAX_PLEN, s.nextInt());
+ }
+
+ /** Verify that router_solicitations exists and is set to the expected value */
+ public void testRouterSolicitations() throws Exception {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
+ Scanner s = makeWifiSysctlScanner("router_solicitations");
+ assertEquals(IPV6_WIFI_ROUTER_SOLICITATIONS, s.nextInt());
+ }
+
+ /** Verify that router_solicitation_max_interval exists and is in an acceptable interval */
+ public void testRouterSolicitationMaxInterval() throws Exception {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
+ return;
+ }
+ Scanner s = makeWifiSysctlScanner("router_solicitation_max_interval");
+ int interval = s.nextInt();
+ // Verify we're in the interval [15 minutes, 60 minutes]. Lower values may adversely
+ // impact battery life and higher values can decrease the probability of detecting
+ // network changes.
+ final int lowerBoundSec = 15 * 60;
+ final int upperBoundSec = 60 * 60;
+ assertTrue(lowerBoundSec <= interval);
+ assertTrue(interval <= upperBoundSec);
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java b/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java
index 103d1da..7c5a1b3 100644
--- a/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java
+++ b/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java
@@ -38,6 +38,9 @@
clientSocket.connect(new LocalSocketAddress(address));
LocalSocket serverSocket = localServerSocket.accept();
+ assertTrue(serverSocket.isConnected());
+ assertTrue(serverSocket.isBound());
+
// send data from client to server
OutputStream clientOutStream = clientSocket.getOutputStream();
clientOutStream.write(12);
diff --git a/tests/cts/net/src/android/net/cts/LocalSocketTest.java b/tests/cts/net/src/android/net/cts/LocalSocketTest.java
index 0ff4a30..6e61705 100644
--- a/tests/cts/net/src/android/net/cts/LocalSocketTest.java
+++ b/tests/cts/net/src/android/net/cts/LocalSocketTest.java
@@ -50,7 +50,20 @@
assertFalse(clientSocket.isConnected());
clientSocket.connect(locSockAddr);
assertTrue(clientSocket.isConnected());
+
LocalSocket serverSocket = localServerSocket.accept();
+ assertTrue(serverSocket.isConnected());
+ assertTrue(serverSocket.isBound());
+ try {
+ serverSocket.bind(localServerSocket.getLocalSocketAddress());
+ fail("Cannot bind a LocalSocket from accept()");
+ } catch (IOException expected) {
+ }
+ try {
+ serverSocket.connect(locSockAddr);
+ fail("Cannot connect a LocalSocket from accept()");
+ } catch (IOException expected) {
+ }
Credentials credent = clientSocket.getPeerCredentials();
assertTrue(0 != credent.getPid());
@@ -274,6 +287,42 @@
}
}
+ // http://b/34095140
+ public void testLocalSocketCreatedFromFileDescriptor() throws Exception {
+ String address = ADDRESS_PREFIX + "_testLocalSocketCreatedFromFileDescriptor";
+
+ // Establish connection between a local client and server to get a valid client socket file
+ // descriptor.
+ try (LocalSocketPair socketPair = LocalSocketPair.createConnectedSocketPair(address)) {
+ // Extract the client FileDescriptor we can use.
+ FileDescriptor fileDescriptor = socketPair.clientSocket.getFileDescriptor();
+ assertTrue(fileDescriptor.valid());
+
+ // Create the LocalSocket we want to test.
+ LocalSocket clientSocketCreatedFromFileDescriptor =
+ LocalSocket.createConnectedLocalSocket(fileDescriptor);
+ assertTrue(clientSocketCreatedFromFileDescriptor.isConnected());
+ assertTrue(clientSocketCreatedFromFileDescriptor.isBound());
+
+ // Test the LocalSocket can be used for communication.
+ LocalSocket serverSocket = socketPair.serverSocket.accept();
+ OutputStream clientOutputStream =
+ clientSocketCreatedFromFileDescriptor.getOutputStream();
+ InputStream serverInputStream = serverSocket.getInputStream();
+
+ clientOutputStream.write(12);
+ assertEquals(12, serverInputStream.read());
+
+ // Closing clientSocketCreatedFromFileDescriptor does not close the file descriptor.
+ clientSocketCreatedFromFileDescriptor.close();
+ assertTrue(fileDescriptor.valid());
+
+ // .. while closing the LocalSocket that owned the file descriptor does.
+ socketPair.clientSocket.close();
+ assertFalse(fileDescriptor.valid());
+ }
+ }
+
public void testFlush() throws Exception {
String address = ADDRESS_PREFIX + "_testFlush";
diff --git a/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java b/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java
index 545541d..198f973 100644
--- a/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java
+++ b/tests/cts/net/src/android/net/http/cts/HttpResponseCacheTest.java
@@ -21,9 +21,10 @@
import junit.framework.TestCase;
-import android.cts.util.FileUtils;
import android.net.http.HttpResponseCache;
+import com.android.compatibility.common.util.FileUtils;
+
import java.io.File;
import java.io.InputStream;
import java.net.CacheRequest;
@@ -38,10 +39,11 @@
public final class HttpResponseCacheTest extends TestCase {
private File cacheDir;
- private MockWebServer server = new MockWebServer();
+ private MockWebServer server;
@Override public void setUp() throws Exception {
super.setUp();
+ server = new MockWebServer();
String tmp = System.getProperty("java.io.tmpdir");
cacheDir = new File(tmp, "HttpCache-" + UUID.randomUUID());
cacheDir.mkdirs();
diff --git a/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
new file mode 100644
index 0000000..87e22d8
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiManager;
+import android.net.wifi.aware.AttachCallback;
+import android.net.wifi.aware.Characteristics;
+import android.net.wifi.aware.DiscoverySessionCallback;
+import android.net.wifi.aware.IdentityChangedListener;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.PublishDiscoverySession;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.SubscribeDiscoverySession;
+import android.net.wifi.aware.WifiAwareManager;
+import android.net.wifi.aware.WifiAwareSession;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Wi-Fi Aware CTS test suite: single device testing. Performs tests on a single
+ * device to validate Wi-Fi Aware.
+ */
+public class SingleDeviceTest extends AndroidTestCase {
+ private static final String TAG = "WifiAwareCtsTests";
+
+ // wait for Wi-Fi Aware to become available
+ static private final int WAIT_FOR_AWARE_CHANGE_SECS = 10;
+
+ private final Object mLock = new Object();
+ private final HandlerThread mHandlerThread = new HandlerThread("SingleDeviceTest");
+ private final Handler mHandler;
+ {
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ }
+
+ private WifiAwareManager mWifiAwareManager;
+ private WifiManager mWifiManager;
+ private WifiManager.WifiLock mWifiLock;
+ private ConnectivityManager mConnectivityManager;
+
+ // used to store any WifiAwareSession allocated during tests - will clean-up after tests
+ private List<WifiAwareSession> mSessions = new ArrayList<>();
+
+ // Return true if location is enabled.
+ private boolean isLocationEnabled() {
+ return Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF) !=
+ Settings.Secure.LOCATION_MODE_OFF;
+ }
+
+ private class WifiAwareBroadcastReceiver extends BroadcastReceiver {
+ private CountDownLatch mBlocker = new CountDownLatch(1);
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED.equals(intent.getAction())) {
+ mBlocker.countDown();
+ }
+ }
+
+ boolean waitForStateChange() throws InterruptedException {
+ return mBlocker.await(WAIT_FOR_AWARE_CHANGE_SECS, TimeUnit.SECONDS);
+ }
+ }
+
+ private class AttachCallbackTest extends AttachCallback {
+ static final int ATTACHED = 0;
+ static final int ATTACH_FAILED = 1;
+ static final int ERROR = 2; // no callback: timeout, interruption
+
+ private CountDownLatch mBlocker = new CountDownLatch(1);
+ private int mCallbackCalled = ERROR; // garbage init
+ private WifiAwareSession mSession = null;
+
+ @Override
+ public void onAttached(WifiAwareSession session) {
+ mCallbackCalled = ATTACHED;
+ mSession = session;
+ synchronized (mLock) {
+ mSessions.add(session);
+ }
+ mBlocker.countDown();
+ }
+
+ @Override
+ public void onAttachFailed() {
+ mCallbackCalled = ATTACH_FAILED;
+ mBlocker.countDown();
+ }
+
+ /**
+ * Waits for any of the callbacks to be called - or an error (timeout, interruption).
+ * Returns one of the ATTACHED, ATTACH_FAILED, or ERROR values.
+ */
+ int waitForAnyCallback() {
+ try {
+ boolean noTimeout = mBlocker.await(WAIT_FOR_AWARE_CHANGE_SECS, TimeUnit.SECONDS);
+ if (noTimeout) {
+ return mCallbackCalled;
+ } else {
+ return ERROR;
+ }
+ } catch (InterruptedException e) {
+ return ERROR;
+ }
+ }
+
+ /**
+ * Access the session created by a callback. Only useful to be called after calling
+ * waitForAnyCallback() and getting the ATTACHED code back.
+ */
+ WifiAwareSession getSession() {
+ return mSession;
+ }
+ }
+
+ private class IdentityChangedListenerTest extends IdentityChangedListener {
+ private CountDownLatch mBlocker = new CountDownLatch(1);
+ private byte[] mMac = null;
+
+ @Override
+ public void onIdentityChanged(byte[] mac) {
+ mMac = mac;
+ mBlocker.countDown();
+ }
+
+ /**
+ * Waits for the listener callback to be called - or an error (timeout, interruption).
+ * Returns true on callback called, false on error (timeout, interruption).
+ */
+ boolean waitForListener() {
+ try {
+ return mBlocker.await(WAIT_FOR_AWARE_CHANGE_SECS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the MAC address of the discovery interface supplied to the triggered callback.
+ */
+ byte[] getMac() {
+ return mMac;
+ }
+ }
+
+ private class DiscoverySessionCallbackTest extends DiscoverySessionCallback {
+ static final int ON_PUBLISH_STARTED = 0;
+ static final int ON_SUBSCRIBE_STARTED = 1;
+ static final int ON_SESSION_CONFIG_UPDATED = 2;
+ static final int ON_SESSION_CONFIG_FAILED = 3;
+ static final int ON_SESSION_TERMINATED = 4;
+ static final int ON_SERVICE_DISCOVERED = 5;
+ static final int ON_MESSAGE_SEND_SUCCEEDED = 6;
+ static final int ON_MESSAGE_SEND_FAILED = 7;
+ static final int ON_MESSAGE_RECEIVED = 8;
+
+ private final Object mLocalLock = new Object();
+
+ private CountDownLatch mBlocker;
+ private int mCurrentWaitForCallback;
+ private ArrayDeque<Integer> mCallbackQueue = new ArrayDeque<>();
+
+ private PublishDiscoverySession mPublishDiscoverySession;
+ private SubscribeDiscoverySession mSubscribeDiscoverySession;
+
+ private void processCallback(int callback) {
+ synchronized (mLocalLock) {
+ if (mBlocker != null && mCurrentWaitForCallback == callback) {
+ mBlocker.countDown();
+ } else {
+ mCallbackQueue.addLast(callback);
+ }
+ }
+ }
+
+ @Override
+ public void onPublishStarted(PublishDiscoverySession session) {
+ mPublishDiscoverySession = session;
+ processCallback(ON_PUBLISH_STARTED);
+ }
+
+ @Override
+ public void onSubscribeStarted(SubscribeDiscoverySession session) {
+ mSubscribeDiscoverySession = session;
+ processCallback(ON_SUBSCRIBE_STARTED);
+ }
+
+ @Override
+ public void onSessionConfigUpdated() {
+ processCallback(ON_SESSION_CONFIG_UPDATED);
+ }
+
+ @Override
+ public void onSessionConfigFailed() {
+ processCallback(ON_SESSION_CONFIG_FAILED);
+ }
+
+ @Override
+ public void onSessionTerminated() {
+ processCallback(ON_SESSION_TERMINATED);
+ }
+
+ @Override
+ public void onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo,
+ List<byte[]> matchFilter) {
+ processCallback(ON_SERVICE_DISCOVERED);
+ }
+
+ @Override
+ public void onMessageSendSucceeded(int messageId) {
+ processCallback(ON_MESSAGE_SEND_SUCCEEDED);
+ }
+
+ @Override
+ public void onMessageSendFailed(int messageId) {
+ processCallback(ON_MESSAGE_SEND_FAILED);
+ }
+
+ @Override
+ public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
+ processCallback(ON_MESSAGE_RECEIVED);
+ }
+
+ /**
+ * Wait for the specified callback - any of the ON_* constants. Returns a true
+ * on success (specified callback triggered) or false on failure (timed-out or
+ * interrupted while waiting for the requested callback).
+ *
+ * Note: other callbacks happening while while waiting for the specified callback will
+ * be queued.
+ */
+ boolean waitForCallback(int callback) {
+ return waitForCallback(callback, WAIT_FOR_AWARE_CHANGE_SECS);
+ }
+
+ /**
+ * Wait for the specified callback - any of the ON_* constants. Returns a true
+ * on success (specified callback triggered) or false on failure (timed-out or
+ * interrupted while waiting for the requested callback).
+ *
+ * Same as waitForCallback(int callback) execpt that allows specifying a custom timeout.
+ * The default timeout is a short value expected to be sufficient for all behaviors which
+ * should happen relatively quickly. Specifying a custom timeout should only be done for
+ * those cases which are known to take a specific longer period of time.
+ *
+ * Note: other callbacks happening while while waiting for the specified callback will
+ * be queued.
+ */
+ boolean waitForCallback(int callback, int timeoutSec) {
+ synchronized (mLocalLock) {
+ boolean found = mCallbackQueue.remove(callback);
+ if (found) {
+ return true;
+ }
+
+ mCurrentWaitForCallback = callback;
+ mBlocker = new CountDownLatch(1);
+ }
+
+ try {
+ return mBlocker.await(timeoutSec, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Indicates whether the specified callback (any of the ON_* constants) has already
+ * happened and in the queue. Useful when the order of events is important.
+ */
+ boolean hasCallbackAlreadyHappened(int callback) {
+ synchronized (mLocalLock) {
+ return mCallbackQueue.contains(callback);
+ }
+ }
+
+ /**
+ * Returns the last created publish discovery session.
+ */
+ PublishDiscoverySession getPublishDiscoverySession() {
+ PublishDiscoverySession session = mPublishDiscoverySession;
+ mPublishDiscoverySession = null;
+ return session;
+ }
+
+ /**
+ * Returns the last created subscribe discovery session.
+ */
+ SubscribeDiscoverySession getSubscribeDiscoverySession() {
+ SubscribeDiscoverySession session = mSubscribeDiscoverySession;
+ mSubscribeDiscoverySession = null;
+ return session;
+ }
+ }
+
+ private class NetworkCallbackTest extends ConnectivityManager.NetworkCallback {
+ private CountDownLatch mBlocker = new CountDownLatch(1);
+
+ @Override
+ public void onUnavailable() {
+ mBlocker.countDown();
+ }
+
+ /**
+ * Wait for the onUnavailable() callback to be triggered. Returns true if triggered,
+ * otherwise (timed-out, interrupted) returns false.
+ */
+ boolean waitForOnUnavailable() {
+ try {
+ return mBlocker.await(WAIT_FOR_AWARE_CHANGE_SECS, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ return false;
+ }
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ mWifiAwareManager = (WifiAwareManager) getContext().getSystemService(
+ Context.WIFI_AWARE_SERVICE);
+ assertNotNull("Wi-Fi Aware Manager", mWifiAwareManager);
+
+ mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+ assertNotNull("Wi-Fi Manager", mWifiManager);
+ mWifiLock = mWifiManager.createWifiLock(TAG);
+ mWifiLock.acquire();
+ if (!mWifiManager.isWifiEnabled()) {
+ mWifiManager.setWifiEnabled(true);
+ }
+
+ mConnectivityManager = (ConnectivityManager) getContext().getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ assertNotNull("Connectivity Manager", mConnectivityManager);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
+ WifiAwareBroadcastReceiver receiver = new WifiAwareBroadcastReceiver();
+ mContext.registerReceiver(receiver, intentFilter);
+ if (!mWifiAwareManager.isAvailable()) {
+ assertTrue("Timeout waiting for Wi-Fi Aware to change status",
+ receiver.waitForStateChange());
+ assertTrue("Wi-Fi Aware is not available (should be)", mWifiAwareManager.isAvailable());
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ super.tearDown();
+ return;
+ }
+
+ synchronized (mLock) {
+ for (WifiAwareSession session : mSessions) {
+ // no damage from destroying twice (i.e. ok if test cleaned up after itself already)
+ session.close();
+ }
+ mSessions.clear();
+ }
+
+ super.tearDown();
+ }
+
+ /**
+ * Validate:
+ * - Characteristics are available
+ * - Characteristics values are legitimate. Not in the CDD. However, the tested values are
+ * based on the Wi-Fi Aware protocol.
+ */
+ public void testCharacteristics() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ Characteristics characteristics = mWifiAwareManager.getCharacteristics();
+ assertNotNull("Wi-Fi Aware characteristics are null", characteristics);
+ assertEquals("Service Name Length", characteristics.getMaxServiceNameLength(), 255);
+ assertEquals("Service Specific Information Length",
+ characteristics.getMaxServiceSpecificInfoLength(), 255);
+ assertEquals("Match Filter Length", characteristics.getMaxMatchFilterLength(), 255);
+ }
+
+ /**
+ * Validate that on Wi-Fi Aware availability change we get a broadcast + the API returns
+ * correct status.
+ */
+ public void testAvailabilityStatusChange() throws Exception {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ if (isLocationEnabled()) {
+ /* Can't execute this test with location on since it means that Aware will not get
+ * disabled even if we disable Wi-Fi (which when location is enabled does not correspond
+ * to disabling the Wi-Fi chip).
+ *
+ * Considering other tests may require locationing to be enable we can't also fail the
+ * test in such a case. Hence it is skipped.
+ */
+ Log.d(TAG, "Skipping test since location scans are enabled");
+ return;
+ }
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
+
+ // 1. Disable Wi-Fi
+ WifiAwareBroadcastReceiver receiver1 = new WifiAwareBroadcastReceiver();
+ mContext.registerReceiver(receiver1, intentFilter);
+ mWifiManager.setWifiEnabled(false);
+
+ assertTrue("Timeout waiting for Wi-Fi Aware to change status",
+ receiver1.waitForStateChange());
+ assertFalse("Wi-Fi Aware is available (should not be)", mWifiAwareManager.isAvailable());
+
+ // 2. Enable Wi-Fi
+ WifiAwareBroadcastReceiver receiver2 = new WifiAwareBroadcastReceiver();
+ mContext.registerReceiver(receiver2, intentFilter);
+ mWifiManager.setWifiEnabled(true);
+
+ assertTrue("Timeout waiting for Wi-Fi Aware to change status",
+ receiver2.waitForStateChange());
+ assertTrue("Wi-Fi Aware is not available (should be)", mWifiAwareManager.isAvailable());
+ }
+
+ /**
+ * Validate that can attach to Wi-Fi Aware.
+ */
+ public void testAttachNoIdentity() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ WifiAwareSession session = attachAndGetSession();
+ session.close();
+ }
+
+ /**
+ * Validate that can attach to Wi-Fi Aware and get identity information. Use the identity
+ * information to validate that MAC address changes on every attach.
+ *
+ * Note: relies on no other entity using Wi-Fi Aware during the CTS test. Since if it is used
+ * then the attach/destroy will not correspond to enable/disable and will not result in a new
+ * MAC address being generated.
+ */
+ public void testAttachDiscoveryAddressChanges() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ final int numIterations = 10;
+ Set<TestUtils.MacWrapper> macs = new HashSet<>();
+
+ for (int i = 0; i < numIterations; ++i) {
+ AttachCallbackTest attachCb = new AttachCallbackTest();
+ IdentityChangedListenerTest identityL = new IdentityChangedListenerTest();
+ mWifiAwareManager.attach(attachCb, identityL, mHandler);
+ assertEquals("Wi-Fi Aware attach: iteration " + i, AttachCallbackTest.ATTACHED,
+ attachCb.waitForAnyCallback());
+ assertTrue("Wi-Fi Aware attach: iteration " + i, identityL.waitForListener());
+
+ WifiAwareSession session = attachCb.getSession();
+ assertNotNull("Wi-Fi Aware session: iteration " + i, session);
+
+ byte[] mac = identityL.getMac();
+ assertNotNull("Wi-Fi Aware discovery MAC: iteration " + i, mac);
+
+ session.close();
+
+ macs.add(new TestUtils.MacWrapper(mac));
+ }
+
+ assertEquals("", numIterations, macs.size());
+ }
+
+ /**
+ * Validate a successful publish discovery session lifetime: publish, update publish, destroy.
+ */
+ public void testPublishDiscoverySuccess() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ final String serviceName = "ValidName";
+
+ WifiAwareSession session = attachAndGetSession();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ serviceName).build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+
+ // 1. publish
+ session.publish(publishConfig, discoveryCb, mHandler);
+ assertTrue("Publish started",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+ PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
+ assertNotNull("Publish session", discoverySession);
+
+ // 2. update-publish
+ publishConfig = new PublishConfig.Builder().setServiceName(
+ serviceName).setServiceSpecificInfo("extras".getBytes()).build();
+ discoverySession.updatePublish(publishConfig);
+ assertTrue("Publish update", discoveryCb.waitForCallback(
+ DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+ // 3. destroy
+ assertFalse("Publish not terminated", discoveryCb.hasCallbackAlreadyHappened(
+ DiscoverySessionCallbackTest.ON_SESSION_TERMINATED));
+ discoverySession.close();
+
+ // 4. try update post-destroy: should time-out waiting for cb
+ discoverySession.updatePublish(publishConfig);
+ assertFalse("Publish update post destroy", discoveryCb.waitForCallback(
+ DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+ session.close();
+ }
+
+ /**
+ * Validate that publish with a Time To Live (TTL) setting expires within the specified
+ * time (and validates that the terminate callback is triggered).
+ */
+ public void testPublishLimitedTtlSuccess() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ final String serviceName = "ValidName";
+ final int ttlSec = 5;
+
+ WifiAwareSession session = attachAndGetSession();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ serviceName).setTtlSec(ttlSec).setTerminateNotificationEnabled(true).build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+
+ // 1. publish
+ session.publish(publishConfig, discoveryCb, mHandler);
+ assertTrue("Publish started",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+ PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
+ assertNotNull("Publish session", discoverySession);
+
+ // 2. wait for terminate within 'ttlSec'.
+ assertTrue("Publish terminated",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_SESSION_TERMINATED,
+ ttlSec + 5));
+
+ // 3. try update post-termination: should time-out waiting for cb
+ publishConfig = new PublishConfig.Builder().setServiceName(
+ serviceName).setServiceSpecificInfo("extras".getBytes()).build();
+ discoverySession.updatePublish(publishConfig);
+ assertFalse("Publish update post terminate", discoveryCb.waitForCallback(
+ DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+ session.close();
+ }
+
+ /**
+ * Validate a successful subscribe discovery session lifetime: subscribe, update subscribe,
+ * destroy.
+ */
+ public void testSubscribeDiscoverySuccess() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ final String serviceName = "ValidName";
+
+ WifiAwareSession session = attachAndGetSession();
+
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(
+ serviceName).build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+
+ // 1. subscribe
+ session.subscribe(subscribeConfig, discoveryCb, mHandler);
+ assertTrue("Subscribe started",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_SUBSCRIBE_STARTED));
+ SubscribeDiscoverySession discoverySession = discoveryCb.getSubscribeDiscoverySession();
+ assertNotNull("Subscribe session", discoverySession);
+
+ // 2. update-subscribe
+ subscribeConfig = new SubscribeConfig.Builder().setServiceName(
+ serviceName).setServiceSpecificInfo("extras".getBytes()).build();
+ discoverySession.updateSubscribe(subscribeConfig);
+ assertTrue("Subscribe update", discoveryCb.waitForCallback(
+ DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+ // 3. destroy
+ assertFalse("Subscribe not terminated", discoveryCb.hasCallbackAlreadyHappened(
+ DiscoverySessionCallbackTest.ON_SESSION_TERMINATED));
+ discoverySession.close();
+
+ // 4. try update post-destroy: should time-out waiting for cb
+ discoverySession.updateSubscribe(subscribeConfig);
+ assertFalse("Subscribe update post destroy", discoveryCb.waitForCallback(
+ DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+ session.close();
+ }
+
+ /**
+ * Validate that subscribe with a Time To Live (TTL) setting expires within the specified
+ * time (and validates that the terminate callback is triggered).
+ */
+ public void testSubscribeLimitedTtlSuccess() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ final String serviceName = "ValidName";
+ final int ttlSec = 5;
+
+ WifiAwareSession session = attachAndGetSession();
+
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(
+ serviceName).setTtlSec(ttlSec).setTerminateNotificationEnabled(true).build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+
+ // 1. subscribe
+ session.subscribe(subscribeConfig, discoveryCb, mHandler);
+ assertTrue("Subscribe started",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_SUBSCRIBE_STARTED));
+ SubscribeDiscoverySession discoverySession = discoveryCb.getSubscribeDiscoverySession();
+ assertNotNull("Subscribe session", discoverySession);
+
+ // 2. wait for terminate within 'ttlSec'.
+ assertTrue("Subscribe terminated",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_SESSION_TERMINATED,
+ ttlSec + 5));
+
+ // 3. try update post-termination: should time-out waiting for cb
+ subscribeConfig = new SubscribeConfig.Builder().setServiceName(
+ serviceName).setServiceSpecificInfo("extras".getBytes()).build();
+ discoverySession.updateSubscribe(subscribeConfig);
+ assertFalse("Subscribe update post terminate", discoveryCb.waitForCallback(
+ DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+ session.close();
+ }
+
+ /**
+ * Test the send message flow. Since testing single device cannot send to a real peer -
+ * validate that sending to a bogus peer fails.
+ */
+ public void testSendMessageFail() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ WifiAwareSession session = attachAndGetSession();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ "ValidName").build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+
+ // 1. publish
+ session.publish(publishConfig, discoveryCb, mHandler);
+ assertTrue("Publish started",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+ PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
+ assertNotNull("Publish session", discoverySession);
+
+ // 2. send a message with a null peer-handle - expect exception
+ try {
+ discoverySession.sendMessage(null, -1290, "some message".getBytes());
+ fail("Expected IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // empty
+ }
+
+ discoverySession.close();
+ session.close();
+ }
+
+ /**
+ * Request an Aware data-path (open) on a Publish discovery session (which can be done with a
+ * null peer - to accept all requests). Validate that times-out.
+ */
+ public void testDataPathOpenInContextOfDiscoveryFail() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ WifiAwareSession session = attachAndGetSession();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ "ValidName").build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+ NetworkCallbackTest networkCb = new NetworkCallbackTest();
+
+ // 1. publish
+ session.publish(publishConfig, discoveryCb, mHandler);
+ assertTrue("Publish started",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+ PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
+ assertNotNull("Publish session", discoverySession);
+
+ // 2. request an AWARE network
+ NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+ NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+ discoverySession.createNetworkSpecifierOpen(null)).build();
+ mConnectivityManager.requestNetwork(nr, networkCb, 2000);
+ assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
+
+ discoverySession.close();
+ session.close();
+ }
+
+ /**
+ * Request an Aware data-path (encrypted) on a Publish discovery session (which can be done
+ * with a null peer - to accept all requests). Validate that times-out.
+ */
+ public void testDataPathPassphraseInContextOfDiscoveryFail() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ WifiAwareSession session = attachAndGetSession();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ "ValidName").build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+ NetworkCallbackTest networkCb = new NetworkCallbackTest();
+
+ // 1. publish
+ session.publish(publishConfig, discoveryCb, mHandler);
+ assertTrue("Publish started",
+ discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+ PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
+ assertNotNull("Publish session", discoverySession);
+
+ // 2. request an AWARE network
+ NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+ NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+ discoverySession.createNetworkSpecifierPassphrase(null,
+ "Some very long but not very good passphrase")).build();
+ mConnectivityManager.requestNetwork(nr, networkCb, 2000);
+ assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
+
+ discoverySession.close();
+ session.close();
+ }
+
+ /**
+ * Request an Aware data-path (open) as a Responder with no peer MAC address (i.e. accept any
+ * peer request). Validate that times-out.
+ */
+ public void testDataPathOpenOutOfBandFail() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ WifiAwareSession session = attachAndGetSession();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ "ValidName").build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+ NetworkCallbackTest networkCb = new NetworkCallbackTest();
+
+ // 1. request an AWARE network
+ NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+ NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+ session.createNetworkSpecifierOpen(
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, null)).build();
+ mConnectivityManager.requestNetwork(nr, networkCb, 2000);
+ assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
+
+ session.close();
+ }
+
+ /**
+ * Request an Aware data-path (encrypted) as a Responder with no peer MAC address (i.e.
+ * accept any peer request). Validate that times-out.
+ */
+ public void testDataPathPassphraseOutOfBandFail() {
+ if (!TestUtils.shouldTestWifiAware(getContext())) {
+ return;
+ }
+
+ WifiAwareSession session = attachAndGetSession();
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+ "ValidName").build();
+ DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+ NetworkCallbackTest networkCb = new NetworkCallbackTest();
+
+ // 1. request an AWARE network
+ NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+ NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+ session.createNetworkSpecifierPassphrase(
+ WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, null,
+ "abcdefghihk")).build();
+ mConnectivityManager.requestNetwork(nr, networkCb, 2000);
+ assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
+
+ session.close();
+ }
+
+ // local utilities
+
+ private WifiAwareSession attachAndGetSession() {
+ AttachCallbackTest attachCb = new AttachCallbackTest();
+ mWifiAwareManager.attach(attachCb, mHandler);
+ int cbCalled = attachCb.waitForAnyCallback();
+ assertEquals("Wi-Fi Aware attach", AttachCallbackTest.ATTACHED, cbCalled);
+
+ WifiAwareSession session = attachCb.getSession();
+ assertNotNull("Wi-Fi Aware session", session);
+
+ return session;
+ }
+}
diff --git a/tests/cts/net/src/android/net/wifi/aware/cts/TestUtils.java b/tests/cts/net/src/android/net/wifi/aware/cts/TestUtils.java
new file mode 100644
index 0000000..a12c8bb
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/aware/cts/TestUtils.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import java.util.Arrays;
+
+/**
+ * Test utilities for Wi-Fi Aware CTS test suite.
+ */
+class TestUtils {
+ static final String TAG = "WifiAwareCtsTests";
+
+ /**
+ * Returns a flag indicating whether or not Wi-Fi Aware should be tested. Wi-Fi Aware
+ * should be tested if the feature is supported on the current device.
+ */
+ static boolean shouldTestWifiAware(Context context) {
+ final PackageManager pm = context.getPackageManager();
+ return pm.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
+ }
+
+ /**
+ * Wraps a byte[] (MAC address representation). Intended to provide hash and equality operators
+ * so that the MAC address can be used in containers.
+ */
+ static class MacWrapper {
+ private byte[] mMac;
+
+ MacWrapper(byte[] mac) {
+ mMac = mac;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof MacWrapper)) {
+ return false;
+ }
+
+ MacWrapper lhs = (MacWrapper) o;
+ return Arrays.equals(mMac, lhs.mMac);
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(mMac);
+ }
+ }
+}
diff --git a/tests/cts/net/src/android/net/wifi/cts/ConfigParserTest.java b/tests/cts/net/src/android/net/wifi/cts/ConfigParserTest.java
new file mode 100644
index 0000000..52ed2a6
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/ConfigParserTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net.wifi.cts;
+
+import android.net.wifi.hotspot2.ConfigParser;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.test.AndroidTestCase;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+/**
+ * CTS tests for Hotspot 2.0 Release 1 installation file parsing API.
+ */
+public class ConfigParserTest extends AndroidTestCase {
+ /**
+ * Hotspot 2.0 Release 1 installation file that contains a Passpoint profile and a
+ * CA (Certificate Authority) X.509 certificate {@link FakeKeys#CA_CERT0}.
+ */
+ private static final String PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT =
+ "assets/HSR1ProfileWithCACert.base64";
+
+ /**
+ * Read the content of the given resource file into a String.
+ *
+ * @param filename String name of the file
+ * @return String
+ * @throws IOException
+ */
+ private String loadResourceFile(String filename) throws IOException {
+ InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ StringBuilder builder = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ builder.append(line).append("\n");
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Generate a {@link PasspointConfiguration} that matches the configuration specified in the
+ * XML file {@link #PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT}.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ private PasspointConfiguration generateConfigurationFromProfile() {
+ PasspointConfiguration config = new PasspointConfiguration();
+
+ // HomeSP configuration.
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFriendlyName("Century House");
+ homeSp.setFqdn("mi6.co.uk");
+ homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L});
+ config.setHomeSp(homeSp);
+
+ // Credential configuration.
+ Credential credential = new Credential();
+ credential.setRealm("shaken.stirred.com");
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setUsername("james");
+ userCredential.setPassword("Ym9uZDAwNw==");
+ userCredential.setEapType(21);
+ userCredential.setNonEapInnerMethod("MS-CHAP-V2");
+ credential.setUserCredential(userCredential);
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertType("x509v3");
+ byte[] certSha256Fingerprint = new byte[32];
+ Arrays.fill(certSha256Fingerprint, (byte)0x1f);
+ certCredential.setCertSha256Fingerprint(certSha256Fingerprint);
+ credential.setCertCredential(certCredential);
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi("imsi");
+ simCredential.setEapType(24);
+ credential.setSimCredential(simCredential);
+ credential.setCaCertificate(FakeKeys.CA_CERT0);
+ config.setCredential(credential);
+ return config;
+ }
+
+ /**
+ * Verify a valid installation file is parsed successfully with the matching contents.
+ *
+ * @throws Exception
+ */
+ public void testParseConfigFile() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT);
+ PasspointConfiguration expectedConfig = generateConfigurationFromProfile();
+ PasspointConfiguration actualConfig =
+ ConfigParser.parsePasspointConfig(
+ "application/x-wifi-config", configStr.getBytes());
+ assertTrue(actualConfig.equals(expectedConfig));
+ }
+}
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/wifi/cts/FakeKeys.java b/tests/cts/net/src/android/net/wifi/cts/FakeKeys.java
new file mode 100644
index 0000000..f875301
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/FakeKeys.java
@@ -0,0 +1,257 @@
+/*
+ * 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.wifi.cts;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+/**
+ * A class containing test certificates and private keys.
+ */
+public class FakeKeys {
+ private static final String CA_CERT0_STRING = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDKDCCAhCgAwIBAgIJAILlFdwzLVurMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
+ "BAMTB0VBUCBDQTEwHhcNMTYwMTEyMTE1MDE1WhcNMjYwMTA5MTE1MDE1WjASMRAw\n" +
+ "DgYDVQQDEwdFQVAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
+ "znAPUz26Msae4ws43czR41/J2QtrSIZUKmVUsVumDbYHrPNvTXKSMXAcewORDQYX\n" +
+ "RqvHvpn8CscB1+oGXZvHwxj4zV0WKoK2zeXkau3vcyl3HIKupJfq2TEACefVjj0t\n" +
+ "JW+X35PGWp9/H5zIUNVNVjS7Ums84IvKhRB8512PB9UyHagXYVX5GWpAcVpyfrlR\n" +
+ "FI9Qdhh+Pbk0uyktdbf/CdfgHOoebrTtwRljM0oDtX+2Cv6j0wBK7hD8pPvf1+uy\n" +
+ "GzczigAU/4Kw7eZqydf9B+5RupR+IZipX41xEiIrKRwqi517WWzXcjaG2cNbf451\n" +
+ "xpH5PnV3i1tq04jMGQUzFwIDAQABo4GAMH4wHQYDVR0OBBYEFIwX4vs8BiBcScod\n" +
+ "5noZHRM8E4+iMEIGA1UdIwQ7MDmAFIwX4vs8BiBcScod5noZHRM8E4+ioRakFDAS\n" +
+ "MRAwDgYDVQQDEwdFQVAgQ0ExggkAguUV3DMtW6swDAYDVR0TBAUwAwEB/zALBgNV\n" +
+ "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFfQqOTA7Rv7K+luQ7pnas4BYwHE\n" +
+ "9GEP/uohv6KOy0TGQFbrRTjFoLVNB9BZ1ymMDZ0/TIwIUc7wi7a8t5mEqYH153wW\n" +
+ "aWooiSjyLLhuI4sNrNCOtisdBq2r2MFXt6h0mAQYOPv8R8K7/fgSxGFqzhyNmmVL\n" +
+ "1qBJldx34SpwsTALQVPb4hGwJzZfr1PcpEQx6xMnTl8xEWZE3Ms99uaUxbQqIwRu\n" +
+ "LgAOkNCmY2m89VhzaHJ1uV85AdM/tD+Ysmlnnjt9LRCejbBipjIGjOXrg1JP+lxV\n" +
+ "muM4vH+P/mlmxsPPz0d65b+EGmJZpoLkO/tdNNvCYzjJpTEWpEsO6NMhKYo=\n" +
+ "-----END CERTIFICATE-----\n";
+ public static final X509Certificate CA_CERT0 = loadCertificate(CA_CERT0_STRING);
+
+ private static final String CA_CERT1_STRING = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDKDCCAhCgAwIBAgIJAOM5SzKO2pzCMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
+ "BAMTB0VBUCBDQTAwHhcNMTYwMTEyMDAxMDQ3WhcNMjYwMTA5MDAxMDQ3WjASMRAw\n" +
+ "DgYDVQQDEwdFQVAgQ0EwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
+ "89ug+IEKVQXnJGKg5g4uVHg6J/8iRUxR5k2eH5o03hrJNMfN2D+cBe/wCiZcnWbI\n" +
+ "GbGZACWm2nQth2wy9Zgm2LOd3b4ocrHYls3XLq6Qb5Dd7a0JKU7pdGufiNVEkrmF\n" +
+ "EB+N64wgwH4COTvCiN4erp5kyJwkfqAl2xLkZo0C464c9XoyQOXbmYD9A8v10wZu\n" +
+ "jyNsEo7Nr2USyw+qhjWSbFbEirP77Tvx+7pJQJwdtk1V9Tn73T2dGF2WHYejei9S\n" +
+ "mcWpdIUqsu9etYH+zDmtu7I1xlkwiaVsNr2+D+qaCJyOYqrDTKVNK5nmbBPXDWZc\n" +
+ "NoDbTOoqquX7xONpq9M6jQIDAQABo4GAMH4wHQYDVR0OBBYEFAZ3A2S4qJZZwuNY\n" +
+ "wkJ6mAdc0gVdMEIGA1UdIwQ7MDmAFAZ3A2S4qJZZwuNYwkJ6mAdc0gVdoRakFDAS\n" +
+ "MRAwDgYDVQQDEwdFQVAgQ0EwggkA4zlLMo7anMIwDAYDVR0TBAUwAwEB/zALBgNV\n" +
+ "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAHmdMwEhtys4d0E+t7owBmoVR+lU\n" +
+ "hMCcRtWs8YKX5WIM2kTweT0h/O1xwE1mWmRv/IbDAEb8od4BjAQLhIcolStr2JaO\n" +
+ "9ZzyxjOnNzqeErh/1DHDbb/moPpqfeJ8YiEz7nH/YU56Q8iCPO7TsgS0sNNE7PfN\n" +
+ "IUsBW0yHRgpQ4OxWmiZG2YZWiECRzAC0ecPzo59N5iH4vLQIMTMYquiDeMPQnn1e\n" +
+ "NDGxG8gCtDKIaS6tMg3a28MvWB094pr2ETou8O1C8Ji0Y4hE8QJmSdT7I4+GZjgW\n" +
+ "g94DZ5RiL7sdp3vC48CXOmeT61YBIvhGUsE1rPhXqkpqQ3Z3C4TFF0jXZZc=\n" +
+ "-----END CERTIFICATE-----\n";
+ public static final X509Certificate CA_CERT1 = loadCertificate(CA_CERT1_STRING);
+
+ private static final String CA_PUBLIC_CERT_STRING = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkGA1UEBhMCQkUx\n" +
+ "GTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jvb3QgQ0ExGzAZBgNVBAMTEkds\n" +
+ "b2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAwMDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNV\n" +
+ "BAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYD\n" +
+ "VQQDExJHbG9iYWxTaWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDa\n" +
+ "DuaZjc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavpxy0Sy6sc\n" +
+ "THAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp1Wrjsok6Vjk4bwY8iGlb\n" +
+ "Kk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdGsnUOhugZitVtbNV4FpWi6cgKOOvyJBNP\n" +
+ "c1STE4U6G7weNLWLBYy5d4ux2x8gkasJU26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrX\n" +
+ "gzT/LCrBbBlDSgeF59N89iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n" +
+ "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0BAQUF\n" +
+ "AAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOzyj1hTdNGCbM+w6Dj\n" +
+ "Y1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE38NflNUVyRRBnMRddWQVDf9VMOyG\n" +
+ "j/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymPAbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhH\n" +
+ "hm4qxFYxldBniYUr+WymXUadDKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveC\n" +
+ "X4XSQRjbgbMEHMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A==\n" +
+ "-----END CERTIFICATE-----\n";
+ public static final X509Certificate CA_PUBLIC_CERT = loadCertificate(CA_PUBLIC_CERT_STRING);
+
+ private static final String CLIENT_CERT_STR = "-----BEGIN CERTIFICATE-----\n" +
+ "MIIE/DCCAuQCAQEwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxCzAJBgNV\n" +
+ "BAgMAkNBMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdUZXN0aW5n\n" +
+ "MB4XDTE2MDkzMDIwNTQyOFoXDTE3MDkzMDIwNTQyOFowRDELMAkGA1UEBhMCVVMx\n" +
+ "CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdU\n" +
+ "ZXN0aW5nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnpmcbuaeHfnJ\n" +
+ "k+2QNvxmdVFTawyFMNk0USCq5sexscwmxbewG/Rb8YnixwJWS44v2XkSujB67z5C\n" +
+ "s2qudFEhRXKdEuC6idbAuA97KjipHh0AAniWMsyv61fvbgsUC0b0canx3LiDq81p\n" +
+ "y28NNGmAvoazLZUZ4AhBRiwYZY6FKk723gmZoGbEIeG7J1dlXPusc1662rIjz4eU\n" +
+ "zlmmlvqyHfNqnNk8L14Vug6Xh+lOEGN85xhu1YHAEKGrS89kZxs5rum/cZU8KH2V\n" +
+ "v6eKnY03kxjiVLQtnLpm/7VUEoCMGHyruRj+p3my4+DgqMsmsH52RZCBsjyGlpbU\n" +
+ "NOwOTIX6xh+Rqloduz4AnrMYYIiIw2s8g+2zJM7VbcVKx0fGS26BKdrxgrXWfmNE\n" +
+ "nR0/REQ5AxDGw0jfTUvtdTkXAf+K4MDjcNLEZ+MA4rHfAfQWZtUR5BkHCQYxNpJk\n" +
+ "pA0gyk+BpKdC4WdzI14NSWsu5sRCmBCFqH6BTOSEq/V1cNorBxNwLSSTwFFqUDqx\n" +
+ "Y5nQLXygkJf9WHZWtSKeSjtOYgilz7UKzC2s3CsjmIyGFe+SwpuHJnuE4Uc8Z5Cb\n" +
+ "bjNGHPzqL6XnmzZHJp7RF8kBdKdjGC7dCUltzOfICZeKlzOOq+Kw42T/nXjuXvpb\n" +
+ "nkXNxg741Nwd6RecykXJbseFwm3EYxkCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEA\n" +
+ "Ga1mGwI9aXkL2fTPXO9YkAPzoGeX8aeuVYSQaSkNq+5vnogYCyAt3YDHjRG+ewTT\n" +
+ "WbnPA991xRAPac+biJeXWmwvgGj0YuT7e79phAiGkTTnbAjFHGfYnBy/tI/v7btO\n" +
+ "hRNElA5yTJ1m2fVbBEKXzMR83jrT9iyI+YLRN86zUZIaC86xxSbqnrdWN2jOK6MX\n" +
+ "dS8Arp9tPQjC/4gW+2Ilxv68jiYh+5auWHQZVjppWVY//iu4mAbkq1pTwQEhZ8F8\n" +
+ "Zrmh9DHh60hLFcfSuhIAwf/NMzppwdkjy1ruKVrpijhGKGp4OWu8nvOUgHSzxc7F\n" +
+ "PwpVZ5N2Ku4L8MLO6BG2VasRJK7l17TzDXlfLZHJjkuryOFxVaQKt8ZNFgTOaCXS\n" +
+ "E+gpTLksKU7riYckoiP4+H1sn9qcis0e8s4o/uf1UVc8GSdDw61ReGM5oZEDm1u8\n" +
+ "H9x20QU6igLqzyBpqvCKv7JNgU1uB2PAODHH78zJiUfnKd1y+o+J1iWzaGj3EFji\n" +
+ "T8AXksbTP733FeFXfggXju2dyBH+Z1S5BBTEOd1brWgXlHSAZGm97MKZ94r6/tkX\n" +
+ "qfv3fCos0DKz0oV7qBxYS8wiYhzrRVxG6ITAoH8uuUVVQaZF+G4nJ2jEqNbfuKyX\n" +
+ "ATQsVNjNNlDA0J33GobPMjT326wa4YAWMx8PI5PJZ3g=\n" +
+ "-----END CERTIFICATE-----\n";
+ public static final X509Certificate CLIENT_CERT = loadCertificate(CLIENT_CERT_STR);
+
+ private static final byte[] FAKE_RSA_KEY_1 = new byte[] {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
+ (byte) 0x00, (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) 0x04, (byte) 0x82,
+ (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
+ (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
+ (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
+ (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
+ (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
+ (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
+ (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
+ (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
+ (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
+ (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
+ (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
+ (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
+ (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
+ (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
+ (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
+ (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
+ (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
+ (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
+ (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
+ (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
+ (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
+ (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
+ (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
+ (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
+ (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
+ (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
+ (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
+ (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
+ (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
+ (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
+ (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
+ (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
+ (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
+ (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
+ (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
+ (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
+ (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
+ (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
+ (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
+ (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
+ (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
+ (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
+ (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
+ (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
+ (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
+ (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
+ (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
+ (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
+ (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
+ (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
+ (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
+ (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
+ (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
+ (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
+ (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
+ (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
+ (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
+ (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
+ (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
+ (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
+ (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
+ (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
+ (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
+ (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
+ (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
+ (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
+ (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
+ (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
+ (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
+ (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
+ (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
+ (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
+ (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
+ (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
+ (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
+ (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
+ (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
+ (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
+ (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
+ (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
+ (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
+ (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
+ (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
+ (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
+ (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
+ (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
+ (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
+ (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
+ (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
+ (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
+ (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
+ (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
+ (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
+ (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
+ (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
+ (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
+ (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
+ (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
+ (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
+ (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
+ (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
+ };
+ public static final PrivateKey RSA_KEY1 = loadPrivateRSAKey(FAKE_RSA_KEY_1);
+
+ private static X509Certificate loadCertificate(String blob) {
+ try {
+ final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ InputStream stream = new ByteArrayInputStream(blob.getBytes(StandardCharsets.UTF_8));
+
+ return (X509Certificate) certFactory.generateCertificate(stream);
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private static PrivateKey loadPrivateRSAKey(byte[] fakeKey) {
+ try {
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ return kf.generatePrivate(new PKCS8EncodedKeySpec(fakeKey));
+ } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
+ return null;
+ }
+ }
+}
diff --git a/tests/cts/net/src/android/net/wifi/cts/PpsMoParserTest.java b/tests/cts/net/src/android/net/wifi/cts/PpsMoParserTest.java
new file mode 100644
index 0000000..feafd43
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/PpsMoParserTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.cts;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.omadm.PpsMoParser;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.test.AndroidTestCase;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CTS tests for PPS MO (PerProviderSubscription Management Object) XML string parsing API.
+ */
+public class PpsMoParserTest extends AndroidTestCase {
+ private static final String PPS_MO_XML_FILE = "assets/PerProviderSubscription.xml";
+
+ /**
+ * Read the content of the given resource file into a String.
+ *
+ * @param filename String name of the file
+ * @return String
+ * @throws IOException
+ */
+ private String loadResourceFile(String filename) throws IOException {
+ InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ StringBuilder builder = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ builder.append(line).append("\n");
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Generate a {@link PasspointConfiguration} that matches the configuration specified in the
+ * XML file {@link #PPS_MO_XML_FILE}.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ private PasspointConfiguration generateConfigurationFromPPSMOTree() throws Exception {
+ DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ byte[] certFingerprint = new byte[32];
+ Arrays.fill(certFingerprint, (byte) 0x1f);
+
+ PasspointConfiguration config = new PasspointConfiguration();
+
+ // HomeSP configuration.
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFriendlyName("Century House");
+ assertEquals("Century House", homeSp.getFriendlyName());
+ homeSp.setFqdn("mi6.co.uk");
+ assertEquals("mi6.co.uk", homeSp.getFqdn());
+ homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L});
+ assertTrue(Arrays.equals(new long[] {0x112233L, 0x445566L},
+ homeSp.getRoamingConsortiumOis()));
+ config.setHomeSp(homeSp);
+ assertEquals(homeSp, config.getHomeSp());
+
+ // Credential configuration.
+ Credential credential = new Credential();
+ credential.setRealm("shaken.stirred.com");
+ assertEquals("shaken.stirred.com", credential.getRealm());
+ Credential.UserCredential userCredential = new Credential.UserCredential();
+ userCredential.setUsername("james");
+ assertEquals("james", userCredential.getUsername());
+ userCredential.setPassword("Ym9uZDAwNw==");
+ assertEquals("Ym9uZDAwNw==", userCredential.getPassword());
+ userCredential.setEapType(21);
+ assertEquals(21, userCredential.getEapType());
+ userCredential.setNonEapInnerMethod("MS-CHAP-V2");
+ assertEquals("MS-CHAP-V2", userCredential.getNonEapInnerMethod());
+ credential.setUserCredential(userCredential);
+ assertEquals(userCredential, credential.getUserCredential());
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertType("x509v3");
+ assertEquals("x509v3", certCredential.getCertType());
+ certCredential.setCertSha256Fingerprint(certFingerprint);
+ assertTrue(Arrays.equals(certFingerprint, certCredential.getCertSha256Fingerprint()));
+ credential.setCertCredential(certCredential);
+ assertEquals(certCredential, credential.getCertCredential());
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi("imsi");
+ assertEquals("imsi", simCredential.getImsi());
+ simCredential.setEapType(24);
+ assertEquals(24, simCredential.getEapType());
+ credential.setSimCredential(simCredential);
+ assertEquals(simCredential, credential.getSimCredential());
+ config.setCredential(credential);
+ assertEquals(credential, config.getCredential());
+ return config;
+ }
+
+ /**
+ * Parse and verify all supported fields under PPS MO tree.
+ *
+ * @throws Exception
+ */
+ public void testParsePPSMOTree() throws Exception {
+ String ppsMoTree = loadResourceFile(PPS_MO_XML_FILE);
+ PasspointConfiguration expectedConfig = generateConfigurationFromPPSMOTree();
+ PasspointConfiguration actualConfig = PpsMoParser.parseMoText(ppsMoTree);
+ assertTrue(actualConfig.equals(expectedConfig));
+ }
+}
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 f3eb4e9..d3235da 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -26,8 +26,11 @@
import android.test.AndroidTestCase;
import java.io.ByteArrayInputStream;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.security.spec.PKCS8EncodedKeySpec;
public class WifiEnterpriseConfigTest extends AndroidTestCase {
private WifiManager mWifiManager;
@@ -432,6 +435,246 @@
(byte) 0x9e, (byte) 0x34, (byte) 0x73
};
+ /**
+ * Client certificate generated from above and converted with:
+ *
+ * openssl x509 -outform d -in usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+ */
+ private static final byte[] FAKE_EC_3 = {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0xdf, (byte) 0x30, (byte) 0x82,
+ (byte) 0x01, (byte) 0xc7, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+ (byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (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) 0x64, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+ (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+ (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x08, (byte) 0x0c, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
+ (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x07, (byte) 0x0c, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
+ (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
+ (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
+ (byte) 0x0f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x06, (byte) 0x47, (byte) 0x6f,
+ (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65, (byte) 0x31, (byte) 0x10,
+ (byte) 0x30, (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x0b, (byte) 0x0c, (byte) 0x07, (byte) 0x41, (byte) 0x6e, (byte) 0x64,
+ (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x31, (byte) 0x0d,
+ (byte) 0x30, (byte) 0x0b, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+ (byte) 0x03, (byte) 0x0c, (byte) 0x04, (byte) 0x54, (byte) 0x45, (byte) 0x53,
+ (byte) 0x54, (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31,
+ (byte) 0x37, (byte) 0x30, (byte) 0x31, (byte) 0x32, (byte) 0x37, (byte) 0x31,
+ (byte) 0x37, (byte) 0x35, (byte) 0x38, (byte) 0x31, (byte) 0x32, (byte) 0x5a,
+ (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x37, (byte) 0x30, (byte) 0x31,
+ (byte) 0x32, (byte) 0x35, (byte) 0x31, (byte) 0x37, (byte) 0x35, (byte) 0x38,
+ (byte) 0x31, (byte) 0x32, (byte) 0x5a, (byte) 0x30, (byte) 0x50, (byte) 0x31,
+ (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+ (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53,
+ (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x02, (byte) 0x43,
+ (byte) 0x41, (byte) 0x31, (byte) 0x0f, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x06,
+ (byte) 0x47, (byte) 0x6f, (byte) 0x6f, (byte) 0x67, (byte) 0x6c, (byte) 0x65,
+ (byte) 0x31, (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x07, (byte) 0x41,
+ (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64,
+ (byte) 0x31, (byte) 0x11, (byte) 0x30, (byte) 0x0f, (byte) 0x06, (byte) 0x03,
+ (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x08, (byte) 0x54,
+ (byte) 0x45, (byte) 0x53, (byte) 0x54, (byte) 0x2d, (byte) 0x55, (byte) 0x53,
+ (byte) 0x52, (byte) 0x30, (byte) 0x59, (byte) 0x30, (byte) 0x13, (byte) 0x06,
+ (byte) 0x07, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0xce, (byte) 0x3d,
+ (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x08, (byte) 0x2a, (byte) 0x86,
+ (byte) 0x48, (byte) 0xce, (byte) 0x3d, (byte) 0x03, (byte) 0x01, (byte) 0x07,
+ (byte) 0x03, (byte) 0x42, (byte) 0x00, (byte) 0x04, (byte) 0x4a, (byte) 0xb8,
+ (byte) 0x60, (byte) 0x17, (byte) 0x40, (byte) 0x91, (byte) 0x30, (byte) 0xf7,
+ (byte) 0xdf, (byte) 0x36, (byte) 0x83, (byte) 0x31, (byte) 0xb5, (byte) 0x3a,
+ (byte) 0xf4, (byte) 0xd4, (byte) 0xa1, (byte) 0xce, (byte) 0xd5, (byte) 0x54,
+ (byte) 0x97, (byte) 0x93, (byte) 0x7e, (byte) 0x7b, (byte) 0x08, (byte) 0x63,
+ (byte) 0x37, (byte) 0x62, (byte) 0xf1, (byte) 0x4e, (byte) 0x6a, (byte) 0x2e,
+ (byte) 0x35, (byte) 0x4e, (byte) 0x9f, (byte) 0x48, (byte) 0xcd, (byte) 0x09,
+ (byte) 0x17, (byte) 0xb3, (byte) 0xc1, (byte) 0x58, (byte) 0x02, (byte) 0x49,
+ (byte) 0x7b, (byte) 0x4c, (byte) 0xf7, (byte) 0x9b, (byte) 0xbb, (byte) 0x1b,
+ (byte) 0x2b, (byte) 0x9c, (byte) 0xe9, (byte) 0x36, (byte) 0xc4, (byte) 0x00,
+ (byte) 0x81, (byte) 0x2c, (byte) 0x28, (byte) 0xd9, (byte) 0x6b, (byte) 0xad,
+ (byte) 0xe3, (byte) 0xe8, (byte) 0xa3, (byte) 0x7b, (byte) 0x30, (byte) 0x79,
+ (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d,
+ (byte) 0x13, (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00, (byte) 0x30,
+ (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x60, (byte) 0x86, (byte) 0x48,
+ (byte) 0x01, (byte) 0x86, (byte) 0xf8, (byte) 0x42, (byte) 0x01, (byte) 0x0d,
+ (byte) 0x04, (byte) 0x1f, (byte) 0x16, (byte) 0x1d, (byte) 0x4f, (byte) 0x70,
+ (byte) 0x65, (byte) 0x6e, (byte) 0x53, (byte) 0x53, (byte) 0x4c, (byte) 0x20,
+ (byte) 0x47, (byte) 0x65, (byte) 0x6e, (byte) 0x65, (byte) 0x72, (byte) 0x61,
+ (byte) 0x74, (byte) 0x65, (byte) 0x64, (byte) 0x20, (byte) 0x43, (byte) 0x65,
+ (byte) 0x72, (byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x63,
+ (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x1d, (byte) 0x06,
+ (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16,
+ (byte) 0x04, (byte) 0x14, (byte) 0xef, (byte) 0xf0, (byte) 0x15, (byte) 0xd7,
+ (byte) 0xc9, (byte) 0x3e, (byte) 0x9a, (byte) 0x73, (byte) 0xfa, (byte) 0x38,
+ (byte) 0xc5, (byte) 0x81, (byte) 0x84, (byte) 0x74, (byte) 0xd3, (byte) 0x83,
+ (byte) 0x74, (byte) 0x26, (byte) 0xf1, (byte) 0x0b, (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) 0x38,
+ (byte) 0x6a, (byte) 0x9b, (byte) 0xf8, (byte) 0x3c, (byte) 0x0d, (byte) 0x54,
+ (byte) 0x9f, (byte) 0xdf, (byte) 0xf8, (byte) 0x53, (byte) 0x32, (byte) 0xa8,
+ (byte) 0xf7, (byte) 0x09, (byte) 0x15, (byte) 0x08, (byte) 0x76, (byte) 0xab,
+ (byte) 0x8d, (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) 0xa6, (byte) 0x6c, (byte) 0x18,
+ (byte) 0xa9, (byte) 0x67, (byte) 0x16, (byte) 0x6a, (byte) 0x9e, (byte) 0x23,
+ (byte) 0xb3, (byte) 0x2a, (byte) 0xb8, (byte) 0x16, (byte) 0x7b, (byte) 0xb4,
+ (byte) 0xc8, (byte) 0xbc, (byte) 0x51, (byte) 0xe0, (byte) 0x6f, (byte) 0x05,
+ (byte) 0x66, (byte) 0xa1, (byte) 0x6f, (byte) 0x96, (byte) 0xde, (byte) 0x5b,
+ (byte) 0x41, (byte) 0x60, (byte) 0xe5, (byte) 0x29, (byte) 0x99, (byte) 0x12,
+ (byte) 0xfc, (byte) 0xa9, (byte) 0x91, (byte) 0x23, (byte) 0xb7, (byte) 0x9e,
+ (byte) 0x00, (byte) 0x5f, (byte) 0x93, (byte) 0xd4, (byte) 0xf7, (byte) 0x27,
+ (byte) 0x29, (byte) 0x77, (byte) 0xfb, (byte) 0x53, (byte) 0x09, (byte) 0xdc,
+ (byte) 0xe9, (byte) 0xd0, (byte) 0x5c, (byte) 0x92, (byte) 0x6d, (byte) 0xb7,
+ (byte) 0xcf, (byte) 0x04, (byte) 0xab, (byte) 0xf1, (byte) 0x39, (byte) 0xb9,
+ (byte) 0x49, (byte) 0x23, (byte) 0x7c, (byte) 0x0f, (byte) 0x15, (byte) 0x27,
+ (byte) 0xcd, (byte) 0x65, (byte) 0x3c, (byte) 0x6b, (byte) 0x91, (byte) 0x42,
+ (byte) 0x5a, (byte) 0xfe, (byte) 0xbe, (byte) 0xb8, (byte) 0xa2, (byte) 0xfd,
+ (byte) 0x67, (byte) 0x43, (byte) 0x4b, (byte) 0xc9, (byte) 0x28, (byte) 0x65,
+ (byte) 0x1b, (byte) 0x82, (byte) 0x5b, (byte) 0x25, (byte) 0x20, (byte) 0x9b,
+ (byte) 0xea, (byte) 0x99, (byte) 0xbb, (byte) 0x66, (byte) 0xc1, (byte) 0x8e,
+ (byte) 0x46, (byte) 0x0b, (byte) 0x4e, (byte) 0x06, (byte) 0xdd, (byte) 0x50,
+ (byte) 0x51, (byte) 0x64, (byte) 0xe8, (byte) 0x83, (byte) 0x99, (byte) 0x8e,
+ (byte) 0x53, (byte) 0xe9, (byte) 0x48, (byte) 0x47, (byte) 0x0e, (byte) 0x08,
+ (byte) 0x5e, (byte) 0x0d, (byte) 0x4a, (byte) 0x54, (byte) 0x17, (byte) 0xc1,
+ (byte) 0xf8, (byte) 0xcf, (byte) 0xba, (byte) 0x5c, (byte) 0x38, (byte) 0x70,
+ (byte) 0x33, (byte) 0x31, (byte) 0x22, (byte) 0x03, (byte) 0x6f, (byte) 0x54,
+ (byte) 0x3c, (byte) 0x41, (byte) 0xf0, (byte) 0x89, (byte) 0x85, (byte) 0xbc,
+ (byte) 0x77, (byte) 0x3c, (byte) 0xe8, (byte) 0xec, (byte) 0xb4, (byte) 0x35,
+ (byte) 0x7a, (byte) 0xcc, (byte) 0x8c, (byte) 0x5f, (byte) 0xa1, (byte) 0xed,
+ (byte) 0xa6, (byte) 0x28, (byte) 0x14, (byte) 0xc7, (byte) 0x8a, (byte) 0xef,
+ (byte) 0x56, (byte) 0x26, (byte) 0x35, (byte) 0x46, (byte) 0xab, (byte) 0xb0,
+ (byte) 0x97, (byte) 0xd2, (byte) 0xbd, (byte) 0xa9, (byte) 0x6a, (byte) 0xe4,
+ (byte) 0x3e, (byte) 0x87, (byte) 0xfb, (byte) 0xe1, (byte) 0x09, (byte) 0x8d,
+ (byte) 0x33, (byte) 0x12, (byte) 0xcf, (byte) 0xf0, (byte) 0xc0, (byte) 0xb8,
+ (byte) 0x9b, (byte) 0x9f, (byte) 0xb1, (byte) 0xcb, (byte) 0xac, (byte) 0x76,
+ (byte) 0xa8, (byte) 0x05, (byte) 0x6b, (byte) 0xcc, (byte) 0x41, (byte) 0xd2,
+ (byte) 0x26, (byte) 0x73, (byte) 0xfa, (byte) 0x69, (byte) 0xd3, (byte) 0x1f,
+ (byte) 0xa9, (byte) 0x0c, (byte) 0x6a, (byte) 0xd6, (byte) 0xc9, (byte) 0x35,
+ (byte) 0xc5, (byte) 0xad, (byte) 0xa1, (byte) 0x98, (byte) 0xc9, (byte) 0x78,
+ (byte) 0xa0, (byte) 0xe8, (byte) 0x02, (byte) 0x69, (byte) 0x80, (byte) 0x44,
+ (byte) 0xd9, (byte) 0xe6, (byte) 0xe5, (byte) 0x26, (byte) 0x4f, (byte) 0xcf,
+ (byte) 0x38, (byte) 0xcb, (byte) 0x55, (byte) 0x8c, (byte) 0x7d, (byte) 0x3c,
+ (byte) 0xa8, (byte) 0x82, (byte) 0x69, (byte) 0xa3, (byte) 0xdf, (byte) 0x0a,
+ (byte) 0x79, (byte) 0x7b, (byte) 0xdd, (byte) 0x24, (byte) 0x6a, (byte) 0x21,
+ (byte) 0x7b, (byte) 0x20, (byte) 0x94, (byte) 0xcd, (byte) 0x15, (byte) 0x92,
+ (byte) 0xad, (byte) 0x4a, (byte) 0x72, (byte) 0x0b, (byte) 0x0e, (byte) 0xb2,
+ (byte) 0xc9
+ };
+
+ private static final byte[] FAKE_KEY_3 = {
+ (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
+ (byte) 0x00, (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) 0x04, (byte) 0x82,
+ (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
+ (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
+ (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
+ (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
+ (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
+ (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
+ (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
+ (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
+ (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
+ (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
+ (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
+ (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
+ (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
+ (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
+ (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
+ (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
+ (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
+ (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
+ (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
+ (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
+ (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
+ (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
+ (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
+ (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
+ (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
+ (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
+ (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
+ (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
+ (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
+ (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
+ (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
+ (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
+ (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
+ (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
+ (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
+ (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
+ (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
+ (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
+ (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
+ (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
+ (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
+ (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
+ (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
+ (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
+ (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
+ (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
+ (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
+ (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
+ (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
+ (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
+ (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
+ (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
+ (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
+ (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
+ (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
+ (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
+ (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
+ (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
+ (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
+ (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
+ (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
+ (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
+ (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
+ (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
+ (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
+ (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
+ (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
+ (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
+ (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
+ (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
+ (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
+ (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
+ (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
+ (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
+ (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
+ (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
+ (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
+ (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
+ (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
+ (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
+ (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
+ (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
+ (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
+ (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
+ (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
+ (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
+ (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
+ (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
+ (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
+ (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
+ (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
+ (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
+ (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
+ (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
+ (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
+ (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
+ (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
+ (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
+ (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
+ (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
+ (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
+ (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
+ };
private boolean hasWifi() {
return getContext().getPackageManager().hasSystemFeature(
@@ -492,7 +735,32 @@
X509Certificate[] certs = config.getCaCertificates();
assertTrue(cert1.getSerialNumber().equals(certs[0].getSerialNumber()));
assertTrue(cert2.getSerialNumber().equals(certs[1].getSerialNumber()));
+
+ X509Certificate clientCert = (X509Certificate) factory.generateCertificate(
+ new ByteArrayInputStream(FAKE_EC_3));
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ PrivateKey clientKey = kf.generatePrivate(new PKCS8EncodedKeySpec(FAKE_KEY_3));
+
+ config.setClientKeyEntry(clientKey, clientCert);
+ X509Certificate testClientCert = config.getClientCertificate();
+ X509Certificate[] testClientCertChain = config.getClientCertificateChain();
+ assertTrue(clientCert.getSerialNumber().equals(testClientCert.getSerialNumber()));
+ assertTrue(testClientCertChain.length == 1);
+ assertTrue(testClientCertChain[0] == testClientCert);
+
config.setClientKeyEntry(null, null);
+ assertTrue(config.getClientCertificate() == null);
+ assertTrue(config.getClientCertificateChain() == null);
+
+ config.setClientKeyEntryWithCertificateChain(clientKey,
+ new X509Certificate[]{clientCert, cert1});
+ testClientCert = config.getClientCertificate();
+ testClientCertChain = config.getClientCertificateChain();
+ assertTrue(clientCert.getSerialNumber().equals(testClientCert.getSerialNumber()));
+ assertTrue(testClientCertChain.length == 2);
+ assertTrue(testClientCertChain[0] == testClientCert);
+ assertTrue(testClientCertChain[1] == cert1);
+
config.setSubjectMatch(SUBJECT_MATCH);
assertTrue(config.getSubjectMatch().equals(SUBJECT_MATCH));
// Hotspot 2.0 related attributes
@@ -532,4 +800,15 @@
}
return false;
}
+
+ public void testEnterpriseConfigDoesNotPrintPassword() {
+ WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+ final String identity = "IdentityIsOkayToBeDisplayedHere";
+ final String password = "PasswordIsNotOkayToBeDisplayedHere";
+ enterpriseConfig.setIdentity(identity);
+ enterpriseConfig.setPassword(password);
+ final String stringRepresentation = enterpriseConfig.toString();
+ assertTrue(stringRepresentation.contains(identity));
+ assertFalse(stringRepresentation.contains(password));
+ }
}
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
index 8719b6b..5983cb7 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
@@ -21,13 +21,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.cts.util.PollingCheck;
import android.net.wifi.SupplicantState;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.WifiLock;
+import android.net.wifi.WifiSsid;
import android.test.AndroidTestCase;
+import com.android.compatibility.common.util.PollingCheck;
+
import java.util.concurrent.Callable;
public class WifiInfoTest extends AndroidTestCase {
@@ -123,7 +125,7 @@
SupplicantState.isValidState(wifiInfo.getSupplicantState());
WifiInfo.getDetailedStateOf(SupplicantState.DISCONNECTED);
String ssid = wifiInfo.getSSID();
- if (ssid.startsWith("0x") == false) {
+ if (!ssid.startsWith("0x") && !ssid.equals(WifiSsid.NONE)) {
// Non-hex string should be quoted
assertTrue(ssid.charAt(0) == '"');
assertTrue(ssid.charAt(ssid.length() - 1) == '"');
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 897e5cf..44b49c0 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -22,7 +22,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
-import android.location.LocationManager;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
@@ -30,13 +29,20 @@
import android.net.wifi.WifiManager;
import android.net.wifi.WifiManager.TxPacketCountListener;
import android.net.wifi.WifiManager.WifiLock;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
import android.os.SystemClock;
import android.provider.Settings;
import android.test.AndroidTestCase;
import android.util.Log;
+import com.android.compatibility.common.util.WifiConfigCreator;
+
import java.net.HttpURLConnection;
import java.net.URL;
+import java.security.MessageDigest;
+import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -53,6 +59,7 @@
private static MySync mMySync;
private List<ScanResult> mScanResults = null;
private NetworkInfo mNetworkInfo;
+ private Object mLOHSLock = new Object();
// Please refer to WifiManager
private static final int MIN_RSSI = -100;
@@ -68,6 +75,8 @@
private static final String TAG = "WifiManagerTest";
private static final String SSID1 = "\"WifiManagerTest\"";
private static final String SSID2 = "\"WifiManagerTestModified\"";
+ private static final String PROXY_TEST_SSID = "SomeProxyAp";
+ private static final String ADD_NETWORK_EXCEPTION_SUBSTR = "addNetwork";
private static final int TIMEOUT_MSEC = 6000;
private static final int WAIT_MSEC = 60;
private static final int DURATION = 10000;
@@ -75,6 +84,8 @@
private static final int WIFI_SCAN_TEST_CACHE_DELAY_MILLIS = 3 * 60 * 1000;
private static final int WIFI_SCAN_TEST_ITERATIONS = 5;
+ private static final String TEST_PAC_URL = "http://www.example.com/proxy.pac";
+
private IntentFilter mIntentFilter;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
@@ -163,14 +174,25 @@
private void setWifiEnabled(boolean enable) throws Exception {
synchronized (mMySync) {
- assertTrue(mWifiManager.setWifiEnabled(enable));
if (mWifiManager.isWifiEnabled() != enable) {
+ // the new state is different, we expect it to change
mMySync.expectedState = STATE_WIFI_CHANGING;
- long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
- int expectedState = (enable ? STATE_WIFI_ENABLED : STATE_WIFI_DISABLED);
- while (System.currentTimeMillis() < timeout
- && mMySync.expectedState != expectedState)
- mMySync.wait(WAIT_MSEC);
+ } else {
+ mMySync.expectedState = (enable ? STATE_WIFI_ENABLED : STATE_WIFI_DISABLED);
+ }
+ // now trigger the change
+ assertTrue(mWifiManager.setWifiEnabled(enable));
+ waitForExpectedWifiState(enable);
+ }
+ }
+
+ private void waitForExpectedWifiState(boolean enabled) throws InterruptedException {
+ synchronized (mMySync) {
+ long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
+ int expected = (enabled ? STATE_WIFI_ENABLED : STATE_WIFI_DISABLED);
+ while (System.currentTimeMillis() < timeout
+ && mMySync.expectedState != expected) {
+ mMySync.wait(WAIT_MSEC);
}
}
}
@@ -178,6 +200,7 @@
private void startScan() throws Exception {
synchronized (mMySync) {
mMySync.expectedState = STATE_SCANNING;
+ mScanResults = null;
assertTrue(mWifiManager.startScan());
long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
while (System.currentTimeMillis() < timeout && mMySync.expectedState == STATE_SCANNING)
@@ -213,16 +236,6 @@
return -1;
}
- private void assertDisableOthers(WifiConfiguration wifiConfiguration, boolean disableOthers) {
- for (WifiConfiguration w : mWifiManager.getConfiguredNetworks()) {
- if ((!w.SSID.equals(wifiConfiguration.SSID)) && w.status != Status.CURRENT) {
- if (disableOthers) {
- assertEquals(Status.DISABLED, w.status);
- }
- }
- }
- }
-
/**
* test point of wifiManager actions:
* 1.reconnect
@@ -239,11 +252,17 @@
assertTrue(mWifiManager.reconnect());
assertTrue(mWifiManager.reassociate());
assertTrue(mWifiManager.disconnect());
- assertTrue(mWifiManager.pingSupplicant());
- startScan();
setWifiEnabled(false);
+ startScan();
Thread.sleep(DURATION);
- assertTrue(mWifiManager.pingSupplicant() == mWifiManager.isScanAlwaysAvailable());
+ if (mWifiManager.isScanAlwaysAvailable()) {
+ // Make sure at least one AP is found.
+ assertNotNull("mScanResult should not be null!", mScanResults);
+ assertFalse("empty scan results!", mScanResults.isEmpty());
+ } else {
+ // Make sure no scan results are available.
+ assertNull("mScanResult should be null!", mScanResults);
+ }
final String TAG = "Test";
assertNotNull(mWifiManager.createWifiLock(TAG));
assertNotNull(mWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG));
@@ -366,6 +385,7 @@
wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = SSID1;
+ wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
int netId = mWifiManager.addNetwork(wifiConfiguration);
assertTrue(existSSID(SSID1));
@@ -378,7 +398,6 @@
boolean disableOthers = true;
assertTrue(mWifiManager.enableNetwork(netId, disableOthers));
wifiConfiguration = mWifiManager.getConfiguredNetworks().get(pos);
- assertDisableOthers(wifiConfiguration, disableOthers);
assertEquals(Status.ENABLED, wifiConfiguration.status);
assertTrue(mWifiManager.disableNetwork(netId));
@@ -405,6 +424,31 @@
}
}
+ /**
+ * Verifies that addNetwork() fails for WifiConfigurations containing a non-null http proxy when
+ * the caller doesn't have OVERRIDE_WIFI_CONFIG permission, DeviceOwner or ProfileOwner device
+ * management policies
+ */
+ public void testSetHttpProxy_PermissionFail() throws Exception {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ WifiConfigCreator configCreator = new WifiConfigCreator(getContext());
+ boolean exceptionThrown = false;
+ try {
+ configCreator.addHttpProxyNetworkVerifyAndRemove(
+ PROXY_TEST_SSID, TEST_PAC_URL);
+ } catch (IllegalStateException e) {
+ // addHttpProxyNetworkVerifyAndRemove throws three IllegalStateException,
+ // expect it to throw for the addNetwork operation
+ if (e.getMessage().contains(ADD_NETWORK_EXCEPTION_SUBSTR)) {
+ exceptionThrown = true;
+ }
+ }
+ assertTrue(exceptionThrown);
+ }
+
private Set<String> getEnabledNetworks(List<WifiConfiguration> configuredNetworks) {
Set<String> ssids = new HashSet<String>();
for (WifiConfiguration wifiConfig : configuredNetworks) {
@@ -486,6 +530,13 @@
}
assertTrue(mWifiManager.isWifiEnabled());
+ // give the test a chance to autoconnect
+ Thread.sleep(DURATION);
+ if (mNetworkInfo.getState() != NetworkInfo.State.CONNECTED) {
+ // this test requires a connectable network be configured
+ fail("This test requires a wifi network connection.");
+ }
+
// This will generate a distinct stack trace if the initial connection fails.
connectWifi();
@@ -524,4 +575,328 @@
}
assertTrue(i < 15);
}
+
+ /**
+ * Verify Passpoint configuration management APIs (add, remove, get) for a Passpoint
+ * configuration with an user credential.
+ *
+ * @throws Exception
+ */
+ public void testAddPasspointConfigWithUserCredential() throws Exception {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ testAddPasspointConfig(generatePasspointConfig(generateUserCredential()));
+ }
+
+ /**
+ * Verify Passpoint configuration management APIs (add, remove, get) for a Passpoint
+ * configuration with a certificate credential.
+ *
+ * @throws Exception
+ */
+ public void testAddPasspointConfigWithCertCredential() throws Exception {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ testAddPasspointConfig(generatePasspointConfig(generateCertCredential()));
+ }
+
+ /**
+ * Verify Passpoint configuration management APIs (add, remove, get) for a Passpoint
+ * configuration with a SIm credential.
+ *
+ * @throws Exception
+ */
+ public void testAddPasspointConfigWithSimCredential() throws Exception {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ testAddPasspointConfig(generatePasspointConfig(generateSimCredential()));
+ }
+
+ /**
+ * Helper function for generating a {@link PasspointConfiguration} for testing.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ private PasspointConfiguration generatePasspointConfig(Credential credential) {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.setCredential(credential);
+
+ // Setup HomeSp.
+ HomeSp homeSp = new HomeSp();
+ homeSp.setFqdn("Test.com");
+ homeSp.setFriendlyName("Test Provider");
+ homeSp.setRoamingConsortiumOis(new long[] {0x11223344});
+ config.setHomeSp(homeSp);
+
+ return config;
+ }
+
+ /**
+ * Helper function for generating an user credential for testing.
+ *
+ * @return {@link Credential}
+ */
+ private Credential generateUserCredential() {
+ Credential credential = new Credential();
+ credential.setRealm("test.net");
+ Credential.UserCredential userCred = new Credential.UserCredential();
+ userCred.setEapType(21 /* EAP_TTLS */);
+ userCred.setUsername("username");
+ userCred.setPassword("password");
+ userCred.setNonEapInnerMethod("PAP");
+ credential.setUserCredential(userCred);
+ credential.setCaCertificate(FakeKeys.CA_PUBLIC_CERT);
+ return credential;
+ }
+
+ /**
+ * Helper function for generating a certificate credential for testing.
+ *
+ * @return {@link Credential}
+ */
+ private Credential generateCertCredential() throws Exception {
+ Credential credential = new Credential();
+ credential.setRealm("test.net");
+ Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+ certCredential.setCertType("x509v3");
+ certCredential.setCertSha256Fingerprint(
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
+ credential.setCertCredential(certCredential);
+ credential.setCaCertificate(FakeKeys.CA_PUBLIC_CERT);
+ credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT});
+ credential.setClientPrivateKey(FakeKeys.RSA_KEY1);
+ return credential;
+ }
+
+ /**
+ * Helper function for generating a SIM credential for testing.
+ *
+ * @return {@link Credential}
+ */
+ private Credential generateSimCredential() throws Exception {
+ Credential credential = new Credential();
+ credential.setRealm("test.net");
+ Credential.SimCredential simCredential = new Credential.SimCredential();
+ simCredential.setImsi("1234*");
+ simCredential.setEapType(18 /* EAP_SIM */);
+ credential.setSimCredential(simCredential);
+ return credential;
+ }
+
+ /**
+ * Helper function verifying Passpoint configuration management APIs (add, remove, get) for
+ * a given configuration.
+ *
+ * @param config The configuration to test with
+ */
+ private void testAddPasspointConfig(PasspointConfiguration config) throws Exception {
+ try {
+
+ // obtain number of passpoint networks already present in device (preloaded)
+ List<PasspointConfiguration> preConfigList = mWifiManager.getPasspointConfigurations();
+ int numOfNetworks = preConfigList.size();
+
+ // add new (test) configuration
+ mWifiManager.addOrUpdatePasspointConfiguration(config);
+
+ // Certificates and keys will be set to null after it is installed to the KeyStore by
+ // WifiManager. Reset them in the expected config so that it can be used to compare
+ // against the retrieved config.
+ config.getCredential().setCaCertificate(null);
+ config.getCredential().setClientCertificateChain(null);
+ config.getCredential().setClientPrivateKey(null);
+
+ // retrieve the configuration and verify it. The retrieved list may not be in order -
+ // check all configs to see if any match
+ List<PasspointConfiguration> configList = mWifiManager.getPasspointConfigurations();
+ assertEquals(numOfNetworks + 1, configList.size());
+
+ boolean anyMatch = false;
+ for (PasspointConfiguration passpointConfiguration : configList) {
+ if (passpointConfiguration.equals(config)) {
+ anyMatch = true;
+ break;
+ }
+ }
+ assertTrue(anyMatch);
+
+ // remove the (test) configuration and verify number of installed configurations
+ mWifiManager.removePasspointConfiguration(config.getHomeSp().getFqdn());
+ assertEquals(mWifiManager.getPasspointConfigurations().size(), numOfNetworks);
+ } catch (UnsupportedOperationException e) {
+ // Passpoint build config |config_wifi_hotspot2_enabled| is disabled, so noop.
+ }
+ }
+
+ public class TestLocalOnlyHotspotCallback extends WifiManager.LocalOnlyHotspotCallback {
+ Object hotspotLock;
+ WifiManager.LocalOnlyHotspotReservation reservation = null;
+ boolean onStartedCalled = false;
+ boolean onStoppedCalled = false;
+ boolean onFailedCalled = false;
+ int failureReason = -1;
+
+ TestLocalOnlyHotspotCallback(Object lock) {
+ hotspotLock = lock;
+ }
+
+ @Override
+ public void onStarted(WifiManager.LocalOnlyHotspotReservation r) {
+ synchronized (hotspotLock) {
+ reservation = r;
+ onStartedCalled = true;
+ hotspotLock.notify();
+ }
+ }
+
+ @Override
+ public void onStopped() {
+ synchronized (hotspotLock) {
+ onStoppedCalled = true;
+ hotspotLock.notify();
+ }
+ }
+
+ @Override
+ public void onFailed(int reason) {
+ synchronized (hotspotLock) {
+ onFailedCalled = true;
+ failureReason = reason;
+ hotspotLock.notify();
+ }
+ }
+ }
+
+ private TestLocalOnlyHotspotCallback startLocalOnlyHotspot() {
+ // Location mode must be enabled for this test
+ if (!isLocationEnabled()) {
+ fail("Please enable location for this test");
+ }
+
+ TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(mLOHSLock);
+ synchronized (mLOHSLock) {
+ try {
+ mWifiManager.startLocalOnlyHotspot(callback, null);
+ // now wait for callback
+ mLOHSLock.wait(DURATION);
+ } catch (InterruptedException e) {
+ }
+ // check if we got the callback
+ assertTrue(callback.onStartedCalled);
+ assertNotNull(callback.reservation.getWifiConfiguration());
+ assertFalse(callback.onFailedCalled);
+ assertFalse(callback.onStoppedCalled);
+ }
+ return callback;
+ }
+
+ private void stopLocalOnlyHotspot(TestLocalOnlyHotspotCallback callback, boolean wifiEnabled) {
+ synchronized (mMySync) {
+ // we are expecting a new state
+ mMySync.expectedState = STATE_WIFI_CHANGING;
+
+ // now shut down LocalOnlyHotspot
+ callback.reservation.close();
+
+ try {
+ waitForExpectedWifiState(wifiEnabled);
+ } catch (InterruptedException e) {}
+ }
+ }
+
+ /**
+ * Verify that calls to startLocalOnlyHotspot succeed with proper permissions.
+ *
+ * Note: Location mode must be enabled for this test.
+ */
+ public void testStartLocalOnlyHotspotSuccess() {
+ // first check that softap mode is supported by the device
+ if (!mWifiManager.isPortableHotspotSupported()) {
+ return;
+ }
+
+ boolean wifiEnabled = mWifiManager.isWifiEnabled();
+
+ TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
+
+ stopLocalOnlyHotspot(callback, wifiEnabled);
+
+ // wifi should either stay on, or come back on
+ assertEquals(wifiEnabled, mWifiManager.isWifiEnabled());
+ }
+
+ /**
+ * Verify calls to setWifiEnabled from a non-settings app while softap mode is active do not
+ * exit softap mode.
+ *
+ * This test uses the LocalOnlyHotspot API to enter softap mode. This should also be true when
+ * tethering is started.
+ * Note: Location mode must be enabled for this test.
+ */
+
+ public void testSetWifiEnabledByAppDoesNotStopHotspot() throws Exception {
+ if (!WifiFeature.isWifiSupported(getContext())) {
+ // skip the test if WiFi is not supported
+ return;
+ }
+ // check that softap mode is supported by the device
+ if (!mWifiManager.isPortableHotspotSupported()) {
+ return;
+ }
+
+ boolean wifiEnabled = mWifiManager.isWifiEnabled();
+
+ if (wifiEnabled) {
+ // disable wifi so we have something to turn on (some devices may be able to run
+ // simultaneous modes)
+ setWifiEnabled(false);
+ }
+
+ TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
+
+ // now we should fail to turn on wifi
+ assertFalse(mWifiManager.setWifiEnabled(true));
+
+ stopLocalOnlyHotspot(callback, wifiEnabled);
+ }
+
+ /**
+ * Verify that applications can only have one registered LocalOnlyHotspot request at a time.
+ *
+ * Note: Location mode must be enabled for this test.
+ */
+ public void testStartLocalOnlyHotspotSingleRequestByApps() {
+ // first check that softap mode is supported by the device
+ if (!mWifiManager.isPortableHotspotSupported()) {
+ return;
+ }
+
+ boolean caughtException = false;
+
+ boolean wifiEnabled = mWifiManager.isWifiEnabled();
+
+ TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
+
+ // now make a second request - this should fail.
+ TestLocalOnlyHotspotCallback callback2 = new TestLocalOnlyHotspotCallback(mLOHSLock);
+ try {
+ mWifiManager.startLocalOnlyHotspot(callback2, null);
+ } catch (IllegalStateException e) {
+ Log.d(TAG, "Caught the IllegalStateException we expected: called startLOHS twice");
+ caughtException = true;
+ }
+ if (!caughtException) {
+ // second start did not fail, should clean up the hotspot.
+ stopLocalOnlyHotspot(callback2, wifiEnabled);
+ }
+ assertTrue(caughtException);
+
+ stopLocalOnlyHotspot(callback, wifiEnabled);
+ }
}