ConnOnActivityStartTest.java revision 1c17139f19201ecf0f53f4cfd2304219650f5f16
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.net; 18 19import static android.util.DebugUtils.valueToString; 20 21import static org.junit.Assert.assertEquals; 22import static org.junit.Assert.assertFalse; 23import static org.junit.Assert.assertNotNull; 24import static org.junit.Assert.assertTrue; 25import static org.junit.Assert.fail; 26 27import com.android.frameworks.servicestests.R; 28import com.android.servicestests.aidl.INetworkStateObserver; 29 30import android.app.PendingIntent; 31import android.content.BroadcastReceiver; 32import android.content.ComponentName; 33import android.content.Context; 34import android.content.Intent; 35import android.content.IntentFilter; 36import android.content.IntentSender; 37import android.content.pm.IPackageDeleteObserver; 38import android.content.pm.PackageInstaller; 39import android.content.pm.PackageManager; 40import android.net.ConnectivityManager; 41import android.net.NetworkInfo; 42import android.net.Uri; 43import android.os.BatteryManager; 44import android.os.Bundle; 45import android.os.RemoteException; 46import android.os.SystemClock; 47import android.support.test.InstrumentationRegistry; 48import android.support.test.filters.LargeTest; 49import android.support.test.runner.AndroidJUnit4; 50import android.support.test.uiautomator.UiDevice; 51import android.util.Log; 52 53import libcore.io.IoUtils; 54 55import org.junit.AfterClass; 56import org.junit.BeforeClass; 57import org.junit.Test; 58import org.junit.runner.RunWith; 59 60import java.io.IOException; 61import java.io.InputStream; 62import java.io.OutputStream; 63import java.util.concurrent.CountDownLatch; 64import java.util.concurrent.TimeUnit; 65 66/** 67 * Tests for verifying network availability on activity start. 68 * 69 * To run the tests, use 70 * 71 * runtest -c com.android.server.net.ConnOnActivityStartTest frameworks-services 72 * 73 * or the following steps: 74 * 75 * Build: m FrameworksServicesTests 76 * Install: adb install -r \ 77 * ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk 78 * Run: adb shell am instrument -e class com.android.server.net.ConnOnActivityStartTest -w \ 79 * com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner 80 */ 81@LargeTest 82@RunWith(AndroidJUnit4.class) 83public class ConnOnActivityStartTest { 84 private static final String TAG = ConnOnActivityStartTest.class.getSimpleName(); 85 86 private static final String ACTION_INSTALL_COMPLETE = "com.android.server.net.INSTALL_COMPLETE"; 87 88 private static final String TEST_APP_URI = 89 "android.resource://com.android.frameworks.servicestests/raw/conntestapp"; 90 private static final String TEST_PKG = "com.android.servicestests.apps.conntestapp"; 91 private static final String TEST_ACTIVITY_CLASS = TEST_PKG + ".ConnTestActivity"; 92 93 private static final String ACTION_FINISH_ACTIVITY = TEST_PKG + ".FINISH"; 94 95 private static final String EXTRA_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer"; 96 97 private static final long BATTERY_OFF_TIMEOUT_MS = 2000; // 2 sec 98 private static final long BATTERY_OFF_CHECK_INTERVAL_MS = 200; // 0.2 sec 99 100 private static final long WAIT_FOR_INSTALL_TIMEOUT_MS = 2000; // 2 sec 101 102 private static final long NETWORK_CHECK_TIMEOUT_MS = 6000; // 6 sec 103 104 private static final long SCREEN_ON_DELAY_MS = 500; // 0.5 sec 105 106 private static final String NETWORK_STATUS_SEPARATOR = "\\|"; 107 108 private static final int REPEAT_TEST_COUNT = 5; 109 110 private static Context mContext; 111 private static UiDevice mUiDevice; 112 private static int mTestPkgUid; 113 private static BatteryManager mBatteryManager; 114 private static ConnectivityManager mConnectivityManager; 115 116 @BeforeClass 117 public static void setUpOnce() throws Exception { 118 mContext = InstrumentationRegistry.getContext(); 119 mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); 120 121 installAppAndAssertInstalled(); 122 mContext.getPackageManager().setApplicationEnabledSetting(TEST_PKG, 123 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0); 124 mTestPkgUid = mContext.getPackageManager().getPackageUid(TEST_PKG, 0); 125 126 mBatteryManager = (BatteryManager) mContext.getSystemService(Context.BATTERY_SERVICE); 127 mConnectivityManager = (ConnectivityManager) mContext.getSystemService( 128 Context.CONNECTIVITY_SERVICE); 129 } 130 131 @AfterClass 132 public static void tearDownOnce() { 133 mContext.getPackageManager().deletePackage(TEST_PKG, 134 new IPackageDeleteObserver.Stub() { 135 @Override 136 public void packageDeleted(String packageName, int returnCode) 137 throws RemoteException { 138 Log.e(TAG, packageName + " deleted, returnCode: " + returnCode); 139 } 140 }, 0); 141 } 142 143 @Test 144 public void testStartActivity_batterySaver() throws Exception { 145 if (!isNetworkAvailable()) { 146 fail("Device doesn't have network connectivity"); 147 } 148 setBatterySaverMode(true); 149 try { 150 testConnOnActivityStart("testStartActivity_batterySaver"); 151 } finally { 152 setBatterySaverMode(false); 153 } 154 } 155 156 @Test 157 public void testStartActivity_dataSaver() throws Exception { 158 if (!isNetworkAvailable()) { 159 fail("Device doesn't have network connectivity"); 160 } 161 setDataSaverMode(true); 162 try { 163 testConnOnActivityStart("testStartActivity_dataSaver"); 164 } finally { 165 setDataSaverMode(false); 166 } 167 } 168 169 @Test 170 public void testStartActivity_dozeMode() throws Exception { 171 if (!isNetworkAvailable()) { 172 fail("Device doesn't have network connectivity"); 173 } 174 setDozeMode(true); 175 try { 176 testConnOnActivityStart("testStartActivity_dozeMode"); 177 } finally { 178 setDozeMode(false); 179 } 180 } 181 182 @Test 183 public void testStartActivity_appStandby() throws Exception { 184 if (!isNetworkAvailable()) { 185 fail("Device doesn't have network connectivity"); 186 } 187 try{ 188 turnBatteryOff(); 189 setAppIdle(true); 190 SystemClock.sleep(30000); 191 turnScreenOn(); 192 startActivityAndCheckNetworkAccess(); 193 } finally { 194 turnBatteryOn(); 195 setAppIdle(false); 196 } 197 } 198 199 @Test 200 public void testStartActivity_backgroundRestrict() throws Exception { 201 if (!isNetworkAvailable()) { 202 fail("Device doesn't have network connectivity"); 203 } 204 updateRestrictBackgroundBlacklist(true); 205 try { 206 testConnOnActivityStart("testStartActivity_backgroundRestrict"); 207 } finally { 208 updateRestrictBackgroundBlacklist(false); 209 } 210 } 211 212 private void testConnOnActivityStart(String testName) throws Exception { 213 for (int i = 1; i <= REPEAT_TEST_COUNT; ++i) { 214 try { 215 Log.d(TAG, testName + " Start #" + i); 216 turnScreenOn(); 217 SystemClock.sleep(SCREEN_ON_DELAY_MS); 218 startActivityAndCheckNetworkAccess(); 219 Log.d(TAG, testName + " end #" + i); 220 } finally { 221 finishActivity(); 222 } 223 } 224 } 225 226 // TODO: Some of these methods are also used in CTS, so instead of duplicating code, 227 // create a static library which can be used by both servicestests and cts. 228 private void setBatterySaverMode(boolean enabled) throws Exception { 229 if (enabled) { 230 turnBatteryOff(); 231 executeCommand("settings put global low_power 1"); 232 } else { 233 executeCommand("settings put global low_power 0"); 234 turnBatteryOn(); 235 } 236 final String result = executeCommand("settings get global low_power"); 237 assertEquals(enabled ? "1" : "0", result); 238 } 239 240 private void setDataSaverMode(boolean enabled) throws Exception { 241 executeCommand("cmd netpolicy set restrict-background " + enabled); 242 final String output = executeCommand("cmd netpolicy get restrict-background"); 243 final String expectedSuffix = enabled ? "enabled" : "disabled"; 244 assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'", 245 output.endsWith(expectedSuffix)); 246 } 247 248 private void setDozeMode(boolean enabled) throws Exception { 249 if (enabled) { 250 turnBatteryOff(); 251 turnScreenOff(); 252 executeCommand("dumpsys deviceidle force-idle deep"); 253 } else { 254 turnScreenOn(); 255 turnBatteryOn(); 256 executeCommand("dumpsys deviceidle unforce"); 257 } 258 assertDelayedCommandResult("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE", 259 5 /* maxTries */, 500 /* napTimeMs */); 260 } 261 262 private void setAppIdle(boolean enabled) throws Exception { 263 executeCommand("am set-inactive " + TEST_PKG + " " + enabled); 264 assertDelayedCommandResult("am get-inactive " + TEST_PKG, "Idle=" + enabled, 265 10 /* maxTries */, 2000 /* napTimeMs */); 266 } 267 268 private void updateRestrictBackgroundBlacklist(boolean add) throws Exception { 269 if (add) { 270 executeCommand("cmd netpolicy add restrict-background-blacklist " + mTestPkgUid); 271 } else { 272 executeCommand("cmd netpolicy remove restrict-background-blacklist " + mTestPkgUid); 273 } 274 assertRestrictBackground("restrict-background-blacklist", mTestPkgUid, add); 275 } 276 277 private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception { 278 final int maxTries = 5; 279 boolean actual = false; 280 final String expectedUid = Integer.toString(uid); 281 String uids = ""; 282 for (int i = 1; i <= maxTries; i++) { 283 final String output = executeCommand("cmd netpolicy list " + list); 284 uids = output.split(":")[1]; 285 for (String candidate : uids.split(" ")) { 286 actual = candidate.trim().equals(expectedUid); 287 if (expected == actual) { 288 return; 289 } 290 } 291 Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected " 292 + expected + ", got " + actual + "); sleeping 1s before polling again"); 293 SystemClock.sleep(1000); 294 } 295 fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual 296 + ". Full list: " + uids); 297 } 298 299 private void turnBatteryOff() throws Exception { 300 executeCommand("cmd battery unplug"); 301 assertBatteryOff(); 302 } 303 304 private void assertBatteryOff() throws Exception { 305 final long endTime = SystemClock.uptimeMillis() + BATTERY_OFF_TIMEOUT_MS; 306 while (mBatteryManager.isCharging() && SystemClock.uptimeMillis() < endTime) { 307 SystemClock.sleep(BATTERY_OFF_CHECK_INTERVAL_MS); 308 } 309 assertFalse("Power should be disconnected", mBatteryManager.isCharging()); 310 } 311 312 private void turnBatteryOn() throws Exception { 313 executeCommand("cmd battery reset"); 314 } 315 316 private void turnScreenOff() throws Exception { 317 executeCommand("input keyevent KEYCODE_SLEEP"); 318 } 319 320 private void turnScreenOn() throws Exception { 321 executeCommand("input keyevent KEYCODE_WAKEUP"); 322 executeCommand("wm dismiss-keyguard"); 323 } 324 325 private String executeCommand(String cmd) throws IOException { 326 final String result = mUiDevice.executeShellCommand(cmd).trim(); 327 Log.d(TAG, String.format("Result for '%s': %s", cmd, result)); 328 return result; 329 } 330 331 private void assertDelayedCommandResult(String cmd, String expectedResult, 332 int maxTries, int napTimeMs) throws IOException { 333 String result = ""; 334 for (int i = 1; i <= maxTries; ++i) { 335 result = executeCommand(cmd); 336 if (expectedResult.equals(result)) { 337 return; 338 } 339 Log.v(TAG, "Command '" + cmd + "' returned '" + result + " instead of '" 340 + expectedResult + "' on attempt #" + i 341 + "; sleeping " + napTimeMs + "ms before trying again"); 342 SystemClock.sleep(napTimeMs); 343 } 344 fail("Command '" + cmd + "' did not return '" + expectedResult + "' after " 345 + maxTries + " attempts. Last result: '" + result + "'"); 346 } 347 348 private boolean isNetworkAvailable() throws Exception { 349 final NetworkInfo networkInfo = mConnectivityManager.getActiveNetworkInfo(); 350 return networkInfo != null && networkInfo.isConnected(); 351 } 352 353 private void startActivityAndCheckNetworkAccess() throws Exception { 354 final CountDownLatch latch = new CountDownLatch(1); 355 final Intent launchIntent = new Intent().setComponent( 356 new ComponentName(TEST_PKG, TEST_ACTIVITY_CLASS)); 357 final Bundle extras = new Bundle(); 358 final String[] errors = new String[] {null}; 359 extras.putBinder(EXTRA_NETWORK_STATE_OBSERVER, new INetworkStateObserver.Stub() { 360 @Override 361 public void onNetworkStateChecked(String resultData) { 362 errors[0] = checkForAvailability(resultData); 363 latch.countDown(); 364 } 365 }); 366 launchIntent.putExtras(extras); 367 mContext.startActivity(launchIntent); 368 if (latch.await(NETWORK_CHECK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 369 if (!errors[0].isEmpty()) { 370 fail("Network not available for test app " + mTestPkgUid); 371 } 372 } else { 373 fail("Timed out waiting for network availability status from test app " + mTestPkgUid); 374 } 375 } 376 377 private void finishActivity() { 378 final Intent finishIntent = new Intent(ACTION_FINISH_ACTIVITY) 379 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 380 mContext.sendBroadcast(finishIntent); 381 } 382 383 private String checkForAvailability(String resultData) { 384 if (resultData == null) { 385 assertNotNull("Network status from app2 is null, Uid: " + mTestPkgUid, resultData); 386 } 387 // Network status format is described on MyBroadcastReceiver.checkNetworkStatus() 388 final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR); 389 assertEquals("Wrong network status: " + resultData + ", Uid: " + mTestPkgUid, 390 5, parts.length); // Sanity check 391 final NetworkInfo.State state = parts[0].equals("null") 392 ? null : NetworkInfo.State.valueOf(parts[0]); 393 final NetworkInfo.DetailedState detailedState = parts[1].equals("null") 394 ? null : NetworkInfo.DetailedState.valueOf(parts[1]); 395 final boolean connected = Boolean.valueOf(parts[2]); 396 final String connectionCheckDetails = parts[3]; 397 final String networkInfo = parts[4]; 398 399 final StringBuilder errors = new StringBuilder(); 400 final NetworkInfo.State expectedState = NetworkInfo.State.CONNECTED; 401 final NetworkInfo.DetailedState expectedDetailedState = NetworkInfo.DetailedState.CONNECTED; 402 403 if (true != connected) { 404 errors.append(String.format("External site connection failed: expected %s, got %s\n", 405 true, connected)); 406 } 407 if (expectedState != state || expectedDetailedState != detailedState) { 408 errors.append(String.format("Connection state mismatch: expected %s/%s, got %s/%s\n", 409 expectedState, expectedDetailedState, state, detailedState)); 410 } 411 412 if (errors.length() > 0) { 413 errors.append("\tnetworkInfo: " + networkInfo + "\n"); 414 errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n"); 415 } 416 return errors.toString(); 417 } 418 419 private static void installAppAndAssertInstalled() throws Exception { 420 final CountDownLatch latch = new CountDownLatch(1); 421 final int[] result = {PackageInstaller.STATUS_SUCCESS}; 422 final BroadcastReceiver installStatusReceiver = new BroadcastReceiver() { 423 @Override 424 public void onReceive(Context context, Intent intent) { 425 final String pkgName = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME); 426 if (!TEST_PKG.equals(pkgName)) { 427 return; 428 } 429 result[0] = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 430 PackageInstaller.STATUS_FAILURE); 431 latch.countDown(); 432 } 433 }; 434 mContext.registerReceiver(installStatusReceiver, new IntentFilter(ACTION_INSTALL_COMPLETE)); 435 try { 436 installApp(); 437 if (latch.await(WAIT_FOR_INSTALL_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 438 if (result[0] != PackageInstaller.STATUS_SUCCESS) { 439 fail("Couldn't install test app, result: " 440 + valueToString(PackageInstaller.class, "STATUS_", result[0])); 441 } 442 } else { 443 fail("Timed out waiting for the test app to install"); 444 } 445 } finally { 446 mContext.unregisterReceiver(installStatusReceiver); 447 } 448 } 449 450 private static void installApp() throws Exception { 451 final Uri packageUri = Uri.parse(TEST_APP_URI); 452 final InputStream in = mContext.getContentResolver().openInputStream(packageUri); 453 454 final PackageInstaller packageInstaller 455 = mContext.getPackageManager().getPackageInstaller(); 456 final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( 457 PackageInstaller.SessionParams.MODE_FULL_INSTALL); 458 params.setAppPackageName(TEST_PKG); 459 460 final int sessionId = packageInstaller.createSession(params); 461 final PackageInstaller.Session session = packageInstaller.openSession(sessionId); 462 463 OutputStream out = null; 464 try { 465 out = session.openWrite(TAG, 0, -1); 466 final byte[] buffer = new byte[65536]; 467 int c; 468 while ((c = in.read(buffer)) != -1) { 469 out.write(buffer, 0, c); 470 } 471 session.fsync(out); 472 } finally { 473 IoUtils.closeQuietly(in); 474 IoUtils.closeQuietly(out); 475 } 476 session.commit(createIntentSender(mContext, sessionId)); 477 } 478 479 private static IntentSender createIntentSender(Context context, int sessionId) { 480 PendingIntent pendingIntent = PendingIntent.getBroadcast( 481 context, sessionId, new Intent(ACTION_INSTALL_COMPLETE), 0); 482 return pendingIntent.getIntentSender(); 483 } 484}