1 /* 2 * Copyright (C) 2016 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 17 package com.android.cts.net.hostside; 18 19 import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED; 20 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED; 21 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED; 22 import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED; 23 import static android.os.BatteryManager.BATTERY_PLUGGED_AC; 24 import static android.os.BatteryManager.BATTERY_PLUGGED_USB; 25 import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS; 26 27 import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 28 29 import android.app.ActivityManager; 30 import android.app.Instrumentation; 31 import android.app.NotificationManager; 32 import android.content.BroadcastReceiver; 33 import android.content.ComponentName; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.pm.PackageManager; 38 import android.net.ConnectivityManager; 39 import android.net.NetworkInfo; 40 import android.net.NetworkInfo.DetailedState; 41 import android.net.NetworkInfo.State; 42 import android.net.wifi.WifiManager; 43 import android.os.BatteryManager; 44 import android.os.Binder; 45 import android.os.Bundle; 46 import android.os.SystemClock; 47 import android.os.SystemProperties; 48 import android.provider.Settings; 49 import android.service.notification.NotificationListenerService; 50 import android.test.InstrumentationTestCase; 51 import android.text.TextUtils; 52 import android.util.Log; 53 54 import com.android.compatibility.common.util.BatteryUtils; 55 56 import java.util.concurrent.CountDownLatch; 57 import java.util.concurrent.LinkedBlockingQueue; 58 import java.util.concurrent.TimeUnit; 59 60 /** 61 * Superclass for tests related to background network restrictions. 62 */ 63 abstract class AbstractRestrictBackgroundNetworkTestCase extends InstrumentationTestCase { 64 protected static final String TAG = "RestrictBackgroundNetworkTests"; 65 66 protected static final String TEST_PKG = "com.android.cts.net.hostside"; 67 protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2"; 68 69 private static final String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity"; 70 private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService"; 71 72 private static final int SLEEP_TIME_SEC = 1; 73 private static final boolean DEBUG = true; 74 75 // Constants below must match values defined on app2's Common.java 76 private static final String MANIFEST_RECEIVER = "ManifestReceiver"; 77 private static final String DYNAMIC_RECEIVER = "DynamicReceiver"; 78 79 private static final String ACTION_RECEIVER_READY = 80 "com.android.cts.net.hostside.app2.action.RECEIVER_READY"; 81 static final String ACTION_SHOW_TOAST = 82 "com.android.cts.net.hostside.app2.action.SHOW_TOAST"; 83 84 protected static final String NOTIFICATION_TYPE_CONTENT = "CONTENT"; 85 protected static final String NOTIFICATION_TYPE_DELETE = "DELETE"; 86 protected static final String NOTIFICATION_TYPE_FULL_SCREEN = "FULL_SCREEN"; 87 protected static final String NOTIFICATION_TYPE_BUNDLE = "BUNDLE"; 88 protected static final String NOTIFICATION_TYPE_ACTION = "ACTION"; 89 protected static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE"; 90 protected static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT"; 91 92 // TODO: Update BatteryManager.BATTERY_PLUGGED_ANY as @TestApi 93 public static final int BATTERY_PLUGGED_ANY = 94 BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS; 95 96 private static final String NETWORK_STATUS_SEPARATOR = "\\|"; 97 private static final int SECOND_IN_MS = 1000; 98 static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS; 99 private static int PROCESS_STATE_FOREGROUND_SERVICE; 100 101 private static final int PROCESS_STATE_TOP = 2; 102 103 private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer"; 104 105 protected static final int TYPE_COMPONENT_ACTIVTIY = 0; 106 protected static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1; 107 108 private static final int BATTERY_STATE_TIMEOUT_MS = 5000; 109 private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 500; 110 111 private static final int FOREGROUND_PROC_NETWORK_TIMEOUT_MS = 6000; 112 113 // Must be higher than NETWORK_TIMEOUT_MS 114 private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4; 115 116 private static final IntentFilter BATTERY_CHANGED_FILTER = 117 new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 118 119 private static final String APP_NOT_FOREGROUND_ERROR = "app_not_fg"; 120 121 protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 5_000; // 5 sec 122 123 protected Context mContext; 124 protected Instrumentation mInstrumentation; 125 protected ConnectivityManager mCm; 126 protected WifiManager mWfm; 127 protected int mUid; 128 private int mMyUid; 129 private String mMeteredWifi; 130 private MyServiceClient mServiceClient; 131 private String mDeviceIdleConstantsSetting; 132 private boolean mSupported; 133 private boolean mIsLocationOn; 134 135 @Override setUp()136 protected void setUp() throws Exception { 137 super.setUp(); 138 139 PROCESS_STATE_FOREGROUND_SERVICE = (Integer) ActivityManager.class 140 .getDeclaredField("PROCESS_STATE_FOREGROUND_SERVICE").get(null); 141 mInstrumentation = getInstrumentation(); 142 mContext = mInstrumentation.getContext(); 143 mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); 144 mWfm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE); 145 mUid = getUid(TEST_APP2_PKG); 146 mMyUid = getUid(mContext.getPackageName()); 147 mServiceClient = new MyServiceClient(mContext); 148 mServiceClient.bind(); 149 mDeviceIdleConstantsSetting = "device_idle_constants"; 150 mIsLocationOn = isLocationOn(); 151 if (!mIsLocationOn) { 152 enableLocation(); 153 } 154 mSupported = setUpActiveNetworkMeteringState(); 155 setAppIdle(false); 156 157 Log.i(TAG, "Apps status on " + getName() + ":\n" 158 + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n" 159 + "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid)); 160 161 // app_idle_constants set in NetPolicyTestsPreparer.setUp() is not always sucessful (suspect 162 // timing issue), here we set it again to make sure. 163 final String appIdleConstants = "parole_duration=0,stable_charging_threshold=0"; 164 executeShellCommand("settings put global app_idle_constants " + appIdleConstants); 165 final String currentConstants = 166 executeShellCommand("settings get global app_idle_constants"); 167 assertEquals(appIdleConstants, currentConstants); 168 } 169 170 @Override tearDown()171 protected void tearDown() throws Exception { 172 if (!mIsLocationOn) { 173 disableLocation(); 174 } 175 mServiceClient.unbind(); 176 177 super.tearDown(); 178 } 179 enableLocation()180 private void enableLocation() throws Exception { 181 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE, 182 Settings.Secure.LOCATION_MODE_SENSORS_ONLY); 183 assertEquals(Settings.Secure.LOCATION_MODE_SENSORS_ONLY, 184 Settings.Secure.getInt(mContext.getContentResolver(), 185 Settings.Secure.LOCATION_MODE)); 186 } 187 disableLocation()188 private void disableLocation() throws Exception { 189 Settings.Secure.putInt(mContext.getContentResolver(), Settings.Secure.LOCATION_MODE, 190 Settings.Secure.LOCATION_MODE_OFF); 191 assertEquals(Settings.Secure.LOCATION_MODE_OFF, 192 Settings.Secure.getInt(mContext.getContentResolver(), 193 Settings.Secure.LOCATION_MODE)); 194 } 195 isLocationOn()196 private boolean isLocationOn() throws Exception { 197 return Settings.Secure.getInt(mContext.getContentResolver(), 198 Settings.Secure.LOCATION_MODE) != Settings.Secure.LOCATION_MODE_OFF; 199 } 200 getUid(String packageName)201 protected int getUid(String packageName) throws Exception { 202 return mContext.getPackageManager().getPackageUid(packageName, 0); 203 } 204 assertRestrictBackgroundChangedReceived(int expectedCount)205 protected void assertRestrictBackgroundChangedReceived(int expectedCount) throws Exception { 206 assertRestrictBackgroundChangedReceived(DYNAMIC_RECEIVER, expectedCount); 207 assertRestrictBackgroundChangedReceived(MANIFEST_RECEIVER, 0); 208 } 209 assertRestrictBackgroundChangedReceived(String receiverName, int expectedCount)210 protected void assertRestrictBackgroundChangedReceived(String receiverName, int expectedCount) 211 throws Exception { 212 int attempts = 0; 213 int count = 0; 214 final int maxAttempts = 5; 215 do { 216 attempts++; 217 count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED); 218 if (count >= expectedCount) { 219 break; 220 } 221 Log.d(TAG, "Expecting count " + expectedCount + " but actual is " + count + " after " 222 + attempts + " attempts; sleeping " 223 + SLEEP_TIME_SEC + " seconds before trying again"); 224 SystemClock.sleep(SLEEP_TIME_SEC * SECOND_IN_MS); 225 } while (attempts <= maxAttempts); 226 assertEquals("Number of expected broadcasts for " + receiverName + " not reached after " 227 + maxAttempts * SLEEP_TIME_SEC + " seconds", expectedCount, count); 228 } 229 sendOrderedBroadcast(Intent intent)230 protected String sendOrderedBroadcast(Intent intent) throws Exception { 231 return sendOrderedBroadcast(intent, ORDERED_BROADCAST_TIMEOUT_MS); 232 } 233 sendOrderedBroadcast(Intent intent, int timeoutMs)234 protected String sendOrderedBroadcast(Intent intent, int timeoutMs) throws Exception { 235 final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1); 236 Log.d(TAG, "Sending ordered broadcast: " + intent); 237 mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { 238 239 @Override 240 public void onReceive(Context context, Intent intent) { 241 final String resultData = getResultData(); 242 if (resultData == null) { 243 Log.e(TAG, "Received null data from ordered intent"); 244 return; 245 } 246 result.offer(resultData); 247 } 248 }, null, 0, null, null); 249 250 final String resultData = result.poll(timeoutMs, TimeUnit.MILLISECONDS); 251 Log.d(TAG, "Ordered broadcast response after " + timeoutMs + "ms: " + resultData ); 252 return resultData; 253 } 254 getNumberBroadcastsReceived(String receiverName, String action)255 protected int getNumberBroadcastsReceived(String receiverName, String action) throws Exception { 256 return mServiceClient.getCounters(receiverName, action); 257 } 258 assertRestrictBackgroundStatus(int expectedStatus)259 protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception { 260 final String status = mServiceClient.getRestrictBackgroundStatus(); 261 assertNotNull("didn't get API status from app2", status); 262 final String actualStatus = toString(Integer.parseInt(status)); 263 assertEquals("wrong status", toString(expectedStatus), actualStatus); 264 } 265 assertMyRestrictBackgroundStatus(int expectedStatus)266 protected void assertMyRestrictBackgroundStatus(int expectedStatus) throws Exception { 267 final int actualStatus = mCm.getRestrictBackgroundStatus(); 268 assertEquals("Wrong status", toString(expectedStatus), toString(actualStatus)); 269 } 270 isMyRestrictBackgroundStatus(int expectedStatus)271 protected boolean isMyRestrictBackgroundStatus(int expectedStatus) throws Exception { 272 final int actualStatus = mCm.getRestrictBackgroundStatus(); 273 if (expectedStatus != actualStatus) { 274 Log.d(TAG, "Expected: " + toString(expectedStatus) 275 + " but actual: " + toString(actualStatus)); 276 return false; 277 } 278 return true; 279 } 280 assertBackgroundNetworkAccess(boolean expectAllowed)281 protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception { 282 assertBackgroundState(); // Sanity check. 283 assertNetworkAccess(expectAllowed /* expectAvailable */, false /* needScreenOn */); 284 } 285 assertForegroundNetworkAccess()286 protected void assertForegroundNetworkAccess() throws Exception { 287 assertForegroundState(); // Sanity check. 288 // We verified that app is in foreground state but if the screen turns-off while 289 // verifying for network access, the app will go into background state (in case app's 290 // foreground status was due to top activity). So, turn the screen on when verifying 291 // network connectivity. 292 assertNetworkAccess(true /* expectAvailable */, true /* needScreenOn */); 293 } 294 assertForegroundServiceNetworkAccess()295 protected void assertForegroundServiceNetworkAccess() throws Exception { 296 assertForegroundServiceState(); // Sanity check. 297 assertNetworkAccess(true /* expectAvailable */, false /* needScreenOn */); 298 } 299 300 /** 301 * Whether this device suport this type of test. 302 * 303 * <p>Should be overridden when necessary (but always calling 304 * {@code super.isSupported()} first), and explicitly used before each test 305 * Example: 306 * 307 * <pre><code> 308 * public void testSomething() { 309 * if (!isSupported()) return; 310 * </code></pre> 311 * 312 * @return {@code true} by default. 313 */ isSupported()314 protected boolean isSupported() throws Exception { 315 return mSupported; 316 } 317 isBatterySaverSupported()318 protected boolean isBatterySaverSupported() { 319 return BatteryUtils.isBatterySaverSupported(); 320 } 321 322 /** 323 * Asserts that an app always have access while on foreground or running a foreground service. 324 * 325 * <p>This method will launch an activity and a foreground service to make the assertion, but 326 * will finish the activity / stop the service afterwards. 327 */ assertsForegroundAlwaysHasNetworkAccess()328 protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception{ 329 // Checks foreground first. 330 launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY); 331 finishActivity(); 332 333 // Then foreground service 334 launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE); 335 stopForegroundService(); 336 } 337 assertBackgroundState()338 protected final void assertBackgroundState() throws Exception { 339 final int maxTries = 30; 340 ProcessState state = null; 341 for (int i = 1; i <= maxTries; i++) { 342 state = getProcessStateByUid(mUid); 343 Log.v(TAG, "assertBackgroundState(): status for app2 (" + mUid + ") on attempt #" + i 344 + ": " + state); 345 if (isBackground(state.state)) { 346 return; 347 } 348 Log.d(TAG, "App not on background state (" + state + ") on attempt #" + i 349 + "; sleeping 1s before trying again"); 350 SystemClock.sleep(SECOND_IN_MS); 351 } 352 fail("App2 is not on background state after " + maxTries + " attempts: " + state ); 353 } 354 assertForegroundState()355 protected final void assertForegroundState() throws Exception { 356 final int maxTries = 30; 357 ProcessState state = null; 358 for (int i = 1; i <= maxTries; i++) { 359 state = getProcessStateByUid(mUid); 360 Log.v(TAG, "assertForegroundState(): status for app2 (" + mUid + ") on attempt #" + i 361 + ": " + state); 362 if (!isBackground(state.state)) { 363 return; 364 } 365 Log.d(TAG, "App not on foreground state on attempt #" + i 366 + "; sleeping 1s before trying again"); 367 turnScreenOn(); 368 SystemClock.sleep(SECOND_IN_MS); 369 } 370 fail("App2 is not on foreground state after " + maxTries + " attempts: " + state ); 371 } 372 assertForegroundServiceState()373 protected final void assertForegroundServiceState() throws Exception { 374 final int maxTries = 30; 375 ProcessState state = null; 376 for (int i = 1; i <= maxTries; i++) { 377 state = getProcessStateByUid(mUid); 378 Log.v(TAG, "assertForegroundServiceState(): status for app2 (" + mUid + ") on attempt #" 379 + i + ": " + state); 380 if (state.state == PROCESS_STATE_FOREGROUND_SERVICE) { 381 return; 382 } 383 Log.d(TAG, "App not on foreground service state on attempt #" + i 384 + "; sleeping 1s before trying again"); 385 SystemClock.sleep(SECOND_IN_MS); 386 } 387 fail("App2 is not on foreground service state after " + maxTries + " attempts: " + state ); 388 } 389 390 /** 391 * As per CDD requirements, if the device doesn't support data saver mode then 392 * ConnectivityManager.getRestrictBackgroundStatus() will always return 393 * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if 394 * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns 395 * RESTRICT_BACKGROUND_STATUS_DISABLED or not. 396 */ isDataSaverSupported()397 protected boolean isDataSaverSupported() throws Exception { 398 assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED); 399 try { 400 setRestrictBackground(true); 401 return !isMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED); 402 } finally { 403 setRestrictBackground(false); 404 } 405 } 406 407 /** 408 * Returns whether an app state should be considered "background" for restriction purposes. 409 */ isBackground(int state)410 protected boolean isBackground(int state) { 411 return state > PROCESS_STATE_FOREGROUND_SERVICE; 412 } 413 414 /** 415 * Asserts whether the active network is available or not. 416 */ assertNetworkAccess(boolean expectAvailable, boolean needScreenOn)417 private void assertNetworkAccess(boolean expectAvailable, boolean needScreenOn) 418 throws Exception { 419 final int maxTries = 5; 420 String error = null; 421 int timeoutMs = 500; 422 423 for (int i = 1; i <= maxTries; i++) { 424 error = checkNetworkAccess(expectAvailable); 425 426 if (error.isEmpty()) return; 427 428 // TODO: ideally, it should retry only when it cannot connect to an external site, 429 // or no retry at all! But, currently, the initial change fails almost always on 430 // battery saver tests because the netd changes are made asynchronously. 431 // Once b/27803922 is fixed, this retry mechanism should be revisited. 432 433 Log.w(TAG, "Network status didn't match for expectAvailable=" + expectAvailable 434 + " on attempt #" + i + ": " + error + "\n" 435 + "Sleeping " + timeoutMs + "ms before trying again"); 436 if (needScreenOn) { 437 turnScreenOn(); 438 } 439 // No sleep after the last turn 440 if (i < maxTries) { 441 SystemClock.sleep(timeoutMs); 442 } 443 // Exponential back-off. 444 timeoutMs = Math.min(timeoutMs*2, NETWORK_TIMEOUT_MS); 445 } 446 dumpOnFailure(); 447 fail("Invalid state for expectAvailable=" + expectAvailable + " after " + maxTries 448 + " attempts.\nLast error: " + error); 449 } 450 dumpOnFailure()451 private void dumpOnFailure() throws Exception { 452 dumpAllNetworkRules(); 453 Log.d(TAG, "Usagestats dump: " + getUsageStatsDump()); 454 executeShellCommand("settings get global app_idle_constants"); 455 } 456 dumpAllNetworkRules()457 private void dumpAllNetworkRules() throws Exception { 458 final String networkManagementDump = runShellCommand(mInstrumentation, 459 "dumpsys network_management").trim(); 460 final String networkPolicyDump = runShellCommand(mInstrumentation, 461 "dumpsys netpolicy").trim(); 462 TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n'); 463 splitter.setString(networkManagementDump); 464 String next; 465 Log.d(TAG, ">>> Begin network_management dump"); 466 while (splitter.hasNext()) { 467 next = splitter.next(); 468 Log.d(TAG, next); 469 } 470 Log.d(TAG, "<<< End network_management dump"); 471 splitter.setString(networkPolicyDump); 472 Log.d(TAG, ">>> Begin netpolicy dump"); 473 while (splitter.hasNext()) { 474 next = splitter.next(); 475 Log.d(TAG, next); 476 } 477 Log.d(TAG, "<<< End netpolicy dump"); 478 } 479 480 /** 481 * Checks whether the network is available as expected. 482 * 483 * @return error message with the mismatch (or empty if assertion passed). 484 */ checkNetworkAccess(boolean expectAvailable)485 private String checkNetworkAccess(boolean expectAvailable) throws Exception { 486 final String resultData = mServiceClient.checkNetworkStatus(); 487 return checkForAvailabilityInResultData(resultData, expectAvailable); 488 } 489 checkForAvailabilityInResultData(String resultData, boolean expectAvailable)490 private String checkForAvailabilityInResultData(String resultData, boolean expectAvailable) { 491 if (resultData == null) { 492 assertNotNull("Network status from app2 is null", resultData); 493 } 494 // Network status format is described on MyBroadcastReceiver.checkNetworkStatus() 495 final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR); 496 assertEquals("Wrong network status: " + resultData, 5, parts.length); // Sanity check 497 final State state = parts[0].equals("null") ? null : State.valueOf(parts[0]); 498 final DetailedState detailedState = parts[1].equals("null") 499 ? null : DetailedState.valueOf(parts[1]); 500 final boolean connected = Boolean.valueOf(parts[2]); 501 final String connectionCheckDetails = parts[3]; 502 final String networkInfo = parts[4]; 503 504 final StringBuilder errors = new StringBuilder(); 505 final State expectedState; 506 final DetailedState expectedDetailedState; 507 if (expectAvailable) { 508 expectedState = State.CONNECTED; 509 expectedDetailedState = DetailedState.CONNECTED; 510 } else { 511 expectedState = State.DISCONNECTED; 512 expectedDetailedState = DetailedState.BLOCKED; 513 } 514 515 if (expectAvailable != connected) { 516 errors.append(String.format("External site connection failed: expected %s, got %s\n", 517 expectAvailable, connected)); 518 } 519 if (expectedState != state || expectedDetailedState != detailedState) { 520 errors.append(String.format("Connection state mismatch: expected %s/%s, got %s/%s\n", 521 expectedState, expectedDetailedState, state, detailedState)); 522 } 523 524 if (errors.length() > 0) { 525 errors.append("\tnetworkInfo: " + networkInfo + "\n"); 526 errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n"); 527 } 528 return errors.toString(); 529 } 530 isLowRamDevice()531 protected boolean isLowRamDevice() { 532 final ActivityManager am = (ActivityManager) mContext.getSystemService( 533 Context.ACTIVITY_SERVICE); 534 return am.isLowRamDevice(); 535 } 536 executeShellCommand(String command)537 protected String executeShellCommand(String command) throws Exception { 538 final String result = runShellCommand(mInstrumentation, command).trim(); 539 if (DEBUG) Log.d(TAG, "Command '" + command + "' returned '" + result + "'"); 540 return result; 541 } 542 543 /** 544 * Runs a Shell command which is not expected to generate output. 545 */ executeSilentShellCommand(String command)546 protected void executeSilentShellCommand(String command) throws Exception { 547 final String result = executeShellCommand(command); 548 assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty()); 549 } 550 551 /** 552 * Asserts the result of a command, wait and re-running it a couple times if necessary. 553 */ assertDelayedShellCommand(String command, final String expectedResult)554 protected void assertDelayedShellCommand(String command, final String expectedResult) 555 throws Exception { 556 assertDelayedShellCommand(command, 5, 1, expectedResult); 557 } 558 assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds, final String expectedResult)559 protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds, 560 final String expectedResult) throws Exception { 561 assertDelayedShellCommand(command, maxTries, napTimeSeconds, new ExpectResultChecker() { 562 563 @Override 564 public boolean isExpected(String result) { 565 return expectedResult.equals(result); 566 } 567 568 @Override 569 public String getExpected() { 570 return expectedResult; 571 } 572 }); 573 } 574 assertDelayedShellCommand(String command, ExpectResultChecker checker)575 protected void assertDelayedShellCommand(String command, ExpectResultChecker checker) 576 throws Exception { 577 assertDelayedShellCommand(command, 5, 1, checker); 578 } assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds, ExpectResultChecker checker)579 protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds, 580 ExpectResultChecker checker) throws Exception { 581 String result = ""; 582 for (int i = 1; i <= maxTries; i++) { 583 result = executeShellCommand(command).trim(); 584 if (checker.isExpected(result)) return; 585 Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '" 586 + checker.getExpected() + "' on attempt #" + i 587 + "; sleeping " + napTimeSeconds + "s before trying again"); 588 SystemClock.sleep(napTimeSeconds * SECOND_IN_MS); 589 } 590 fail("Command '" + command + "' did not return '" + checker.getExpected() + "' after " 591 + maxTries 592 + " attempts. Last result: '" + result + "'"); 593 } 594 595 /** 596 * Sets the initial metering state for the active network. 597 * 598 * <p>It's called on setup and by default does nothing - it's up to the 599 * subclasses to override. 600 * 601 * @return whether the tests in the subclass are supported on this device. 602 */ setUpActiveNetworkMeteringState()603 protected boolean setUpActiveNetworkMeteringState() throws Exception { 604 return true; 605 } 606 607 /** 608 * Makes sure the active network is not metered. 609 * 610 * <p>If the device does not supoprt un-metered networks (for example if it 611 * only has cellular data but not wi-fi), it should return {@code false}; 612 * otherwise, it should return {@code true} (or fail if the un-metered 613 * network could not be set). 614 * 615 * @return {@code true} if the network is now unmetered. 616 */ setUnmeteredNetwork()617 protected boolean setUnmeteredNetwork() throws Exception { 618 final NetworkInfo info = mCm.getActiveNetworkInfo(); 619 assertNotNull("Could not get active network", info); 620 if (!mCm.isActiveNetworkMetered()) { 621 Log.d(TAG, "Active network is not metered: " + info); 622 } else if (info.getType() == ConnectivityManager.TYPE_WIFI) { 623 Log.i(TAG, "Setting active WI-FI network as not metered: " + info ); 624 setWifiMeteredStatus(false); 625 } else { 626 Log.d(TAG, "Active network cannot be set to un-metered: " + info); 627 return false; 628 } 629 assertActiveNetworkMetered(false); // Sanity check. 630 return true; 631 } 632 633 /** 634 * Enables metering on the active network if supported. 635 * 636 * <p>If the device does not support metered networks it should return 637 * {@code false}; otherwise, it should return {@code true} (or fail if the 638 * metered network could not be set). 639 * 640 * @return {@code true} if the network is now metered. 641 */ setMeteredNetwork()642 protected boolean setMeteredNetwork() throws Exception { 643 final NetworkInfo info = mCm.getActiveNetworkInfo(); 644 final boolean metered = mCm.isActiveNetworkMetered(); 645 if (metered) { 646 Log.d(TAG, "Active network already metered: " + info); 647 return true; 648 } else if (info.getType() != ConnectivityManager.TYPE_WIFI) { 649 Log.w(TAG, "Active network does not support metering: " + info); 650 return false; 651 } else { 652 Log.w(TAG, "Active network not metered: " + info); 653 } 654 final String netId = setWifiMeteredStatus(true); 655 656 // Set flag so status is reverted on resetMeteredNetwork(); 657 mMeteredWifi = netId; 658 // Sanity check. 659 assertWifiMeteredStatus(netId, true); 660 assertActiveNetworkMetered(true); 661 return true; 662 } 663 664 /** 665 * Resets the device metering state to what it was before the test started. 666 * 667 * <p>This reverts any metering changes made by {@code setMeteredNetwork}. 668 */ resetMeteredNetwork()669 protected void resetMeteredNetwork() throws Exception { 670 if (mMeteredWifi != null) { 671 Log.i(TAG, "resetMeteredNetwork(): SID '" + mMeteredWifi 672 + "' was set as metered by test case; resetting it"); 673 setWifiMeteredStatus(mMeteredWifi, false); 674 assertActiveNetworkMetered(false); // Sanity check. 675 } 676 } 677 assertActiveNetworkMetered(boolean expected)678 private void assertActiveNetworkMetered(boolean expected) throws Exception { 679 final int maxTries = 5; 680 NetworkInfo info = null; 681 for (int i = 1; i <= maxTries; i++) { 682 info = mCm.getActiveNetworkInfo(); 683 if (info == null) { 684 Log.v(TAG, "No active network info on attempt #" + i 685 + "; sleeping 1s before polling again"); 686 } else if (mCm.isActiveNetworkMetered() != expected) { 687 Log.v(TAG, "Wrong metered status for active network " + info + "; expected=" 688 + expected + "; sleeping 1s before polling again"); 689 } else { 690 break; 691 } 692 Thread.sleep(SECOND_IN_MS); 693 } 694 assertNotNull("No active network after " + maxTries + " attempts", info); 695 assertEquals("Wrong metered status for active network " + info, expected, 696 mCm.isActiveNetworkMetered()); 697 } 698 setWifiMeteredStatus(boolean metered)699 private String setWifiMeteredStatus(boolean metered) throws Exception { 700 // We could call setWifiEnabled() here, but it might take sometime to be in a consistent 701 // state (for example, if one of the saved network is not properly authenticated), so it's 702 // better to let the hostside test take care of that. 703 assertTrue("wi-fi is disabled", mWfm.isWifiEnabled()); 704 // TODO: if it's not guaranteed the device has wi-fi, we need to change the tests 705 // to make the actual verification of restrictions optional. 706 final String ssid = mWfm.getConnectionInfo().getSSID(); 707 return setWifiMeteredStatus(ssid, metered); 708 } 709 setWifiMeteredStatus(String ssid, boolean metered)710 private String setWifiMeteredStatus(String ssid, boolean metered) throws Exception { 711 assertNotNull("null SSID", ssid); 712 final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any. 713 assertFalse("empty SSID", ssid.isEmpty()); 714 715 Log.i(TAG, "Setting wi-fi network " + netId + " metered status to " + metered); 716 final String setCommand = "cmd netpolicy set metered-network " + netId + " " + metered; 717 assertDelayedShellCommand(setCommand, ""); 718 719 return netId; 720 } 721 assertWifiMeteredStatus(String netId, boolean status)722 private void assertWifiMeteredStatus(String netId, boolean status) throws Exception { 723 final String command = "cmd netpolicy list wifi-networks"; 724 final String expectedLine = netId + ";" + status; 725 assertDelayedShellCommand(command, new ExpectResultChecker() { 726 727 @Override 728 public boolean isExpected(String result) { 729 return result.contains(expectedLine); 730 } 731 732 @Override 733 public String getExpected() { 734 return "line containing " + expectedLine; 735 } 736 }); 737 } 738 setRestrictBackground(boolean enabled)739 protected void setRestrictBackground(boolean enabled) throws Exception { 740 executeShellCommand("cmd netpolicy set restrict-background " + enabled); 741 final String output = executeShellCommand("cmd netpolicy get restrict-background "); 742 final String expectedSuffix = enabled ? "enabled" : "disabled"; 743 // TODO: use MoreAsserts? 744 assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'", 745 output.endsWith(expectedSuffix)); 746 } 747 addRestrictBackgroundWhitelist(int uid)748 protected void addRestrictBackgroundWhitelist(int uid) throws Exception { 749 executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid); 750 assertRestrictBackgroundWhitelist(uid, true); 751 // UID policies live by the Highlander rule: "There can be only one". 752 // Hence, if app is whitelisted, it should not be blacklisted. 753 assertRestrictBackgroundBlacklist(uid, false); 754 } 755 removeRestrictBackgroundWhitelist(int uid)756 protected void removeRestrictBackgroundWhitelist(int uid) throws Exception { 757 executeShellCommand("cmd netpolicy remove restrict-background-whitelist " + uid); 758 assertRestrictBackgroundWhitelist(uid, false); 759 } 760 assertRestrictBackgroundWhitelist(int uid, boolean expected)761 protected void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception { 762 assertRestrictBackground("restrict-background-whitelist", uid, expected); 763 } 764 addRestrictBackgroundBlacklist(int uid)765 protected void addRestrictBackgroundBlacklist(int uid) throws Exception { 766 executeShellCommand("cmd netpolicy add restrict-background-blacklist " + uid); 767 assertRestrictBackgroundBlacklist(uid, true); 768 // UID policies live by the Highlander rule: "There can be only one". 769 // Hence, if app is blacklisted, it should not be whitelisted. 770 assertRestrictBackgroundWhitelist(uid, false); 771 } 772 removeRestrictBackgroundBlacklist(int uid)773 protected void removeRestrictBackgroundBlacklist(int uid) throws Exception { 774 executeShellCommand("cmd netpolicy remove restrict-background-blacklist " + uid); 775 assertRestrictBackgroundBlacklist(uid, false); 776 } 777 assertRestrictBackgroundBlacklist(int uid, boolean expected)778 protected void assertRestrictBackgroundBlacklist(int uid, boolean expected) throws Exception { 779 assertRestrictBackground("restrict-background-blacklist", uid, expected); 780 } 781 addAppIdleWhitelist(int uid)782 protected void addAppIdleWhitelist(int uid) throws Exception { 783 executeShellCommand("cmd netpolicy add app-idle-whitelist " + uid); 784 assertAppIdleWhitelist(uid, true); 785 } 786 removeAppIdleWhitelist(int uid)787 protected void removeAppIdleWhitelist(int uid) throws Exception { 788 executeShellCommand("cmd netpolicy remove app-idle-whitelist " + uid); 789 assertAppIdleWhitelist(uid, false); 790 } 791 assertAppIdleWhitelist(int uid, boolean expected)792 protected void assertAppIdleWhitelist(int uid, boolean expected) throws Exception { 793 assertRestrictBackground("app-idle-whitelist", uid, expected); 794 } 795 assertRestrictBackground(String list, int uid, boolean expected)796 private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception { 797 final int maxTries = 5; 798 boolean actual = false; 799 final String expectedUid = Integer.toString(uid); 800 String uids = ""; 801 for (int i = 1; i <= maxTries; i++) { 802 final String output = 803 executeShellCommand("cmd netpolicy list " + list); 804 uids = output.split(":")[1]; 805 for (String candidate : uids.split(" ")) { 806 actual = candidate.trim().equals(expectedUid); 807 if (expected == actual) { 808 return; 809 } 810 } 811 Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected " 812 + expected + ", got " + actual + "); sleeping 1s before polling again"); 813 SystemClock.sleep(SECOND_IN_MS); 814 } 815 fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual 816 + ". Full list: " + uids); 817 } 818 addTempPowerSaveModeWhitelist(String packageName, long duration)819 protected void addTempPowerSaveModeWhitelist(String packageName, long duration) 820 throws Exception { 821 Log.i(TAG, "Adding pkg " + packageName + " to temp-power-save-mode whitelist"); 822 executeShellCommand("dumpsys deviceidle tempwhitelist -d " + duration + " " + packageName); 823 } 824 assertPowerSaveModeWhitelist(String packageName, boolean expected)825 protected void assertPowerSaveModeWhitelist(String packageName, boolean expected) 826 throws Exception { 827 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 828 // need to use netpolicy for whitelisting 829 assertDelayedShellCommand("dumpsys deviceidle whitelist =" + packageName, 830 Boolean.toString(expected)); 831 } 832 addPowerSaveModeWhitelist(String packageName)833 protected void addPowerSaveModeWhitelist(String packageName) throws Exception { 834 Log.i(TAG, "Adding package " + packageName + " to power-save-mode whitelist"); 835 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 836 // need to use netpolicy for whitelisting 837 executeShellCommand("dumpsys deviceidle whitelist +" + packageName); 838 assertPowerSaveModeWhitelist(packageName, true); // Sanity check 839 } 840 removePowerSaveModeWhitelist(String packageName)841 protected void removePowerSaveModeWhitelist(String packageName) throws Exception { 842 Log.i(TAG, "Removing package " + packageName + " from power-save-mode whitelist"); 843 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 844 // need to use netpolicy for whitelisting 845 executeShellCommand("dumpsys deviceidle whitelist -" + packageName); 846 assertPowerSaveModeWhitelist(packageName, false); // Sanity check 847 } 848 assertPowerSaveModeExceptIdleWhitelist(String packageName, boolean expected)849 protected void assertPowerSaveModeExceptIdleWhitelist(String packageName, boolean expected) 850 throws Exception { 851 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 852 // need to use netpolicy for whitelisting 853 assertDelayedShellCommand("dumpsys deviceidle except-idle-whitelist =" + packageName, 854 Boolean.toString(expected)); 855 } 856 addPowerSaveModeExceptIdleWhitelist(String packageName)857 protected void addPowerSaveModeExceptIdleWhitelist(String packageName) throws Exception { 858 Log.i(TAG, "Adding package " + packageName + " to power-save-mode-except-idle whitelist"); 859 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 860 // need to use netpolicy for whitelisting 861 executeShellCommand("dumpsys deviceidle except-idle-whitelist +" + packageName); 862 assertPowerSaveModeExceptIdleWhitelist(packageName, true); // Sanity check 863 } 864 removePowerSaveModeExceptIdleWhitelist(String packageName)865 protected void removePowerSaveModeExceptIdleWhitelist(String packageName) throws Exception { 866 Log.i(TAG, "Removing package " + packageName 867 + " from power-save-mode-except-idle whitelist"); 868 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 869 // need to use netpolicy for whitelisting 870 executeShellCommand("dumpsys deviceidle except-idle-whitelist reset"); 871 assertPowerSaveModeExceptIdleWhitelist(packageName, false); // Sanity check 872 } 873 turnBatteryOn()874 protected void turnBatteryOn() throws Exception { 875 executeSilentShellCommand("cmd battery unplug"); 876 executeSilentShellCommand("cmd battery set status " 877 + BatteryManager.BATTERY_STATUS_DISCHARGING); 878 assertBatteryState(false); 879 } 880 turnBatteryOff()881 protected void turnBatteryOff() throws Exception { 882 executeSilentShellCommand("cmd battery set ac " + BATTERY_PLUGGED_ANY); 883 executeSilentShellCommand("cmd battery set level 100"); 884 executeSilentShellCommand("cmd battery set status " 885 + BatteryManager.BATTERY_STATUS_CHARGING); 886 assertBatteryState(true); 887 } 888 assertBatteryState(boolean pluggedIn)889 private void assertBatteryState(boolean pluggedIn) throws Exception { 890 final long endTime = SystemClock.elapsedRealtime() + BATTERY_STATE_TIMEOUT_MS; 891 while (isDevicePluggedIn() != pluggedIn && SystemClock.elapsedRealtime() <= endTime) { 892 Thread.sleep(BATTERY_STATE_CHECK_INTERVAL_MS); 893 } 894 if (isDevicePluggedIn() != pluggedIn) { 895 fail("Timed out waiting for the plugged-in state to change," 896 + " expected pluggedIn: " + pluggedIn); 897 } 898 } 899 isDevicePluggedIn()900 private boolean isDevicePluggedIn() { 901 final Intent batteryIntent = mContext.registerReceiver(null, BATTERY_CHANGED_FILTER); 902 return batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) > 0; 903 } 904 turnScreenOff()905 protected void turnScreenOff() throws Exception { 906 executeSilentShellCommand("input keyevent KEYCODE_SLEEP"); 907 } 908 turnScreenOn()909 protected void turnScreenOn() throws Exception { 910 executeSilentShellCommand("input keyevent KEYCODE_WAKEUP"); 911 executeSilentShellCommand("wm dismiss-keyguard"); 912 } 913 setBatterySaverMode(boolean enabled)914 protected void setBatterySaverMode(boolean enabled) throws Exception { 915 Log.i(TAG, "Setting Battery Saver Mode to " + enabled); 916 if (enabled) { 917 turnBatteryOn(); 918 executeSilentShellCommand("cmd power set-mode 1"); 919 } else { 920 executeSilentShellCommand("cmd power set-mode 0"); 921 turnBatteryOff(); 922 } 923 } 924 setDozeMode(boolean enabled)925 protected void setDozeMode(boolean enabled) throws Exception { 926 // Sanity check, since tests should check beforehand.... 927 assertTrue("Device does not support Doze Mode", isDozeModeEnabled()); 928 929 Log.i(TAG, "Setting Doze Mode to " + enabled); 930 if (enabled) { 931 turnBatteryOn(); 932 turnScreenOff(); 933 executeShellCommand("dumpsys deviceidle force-idle deep"); 934 } else { 935 turnScreenOn(); 936 turnBatteryOff(); 937 executeShellCommand("dumpsys deviceidle unforce"); 938 } 939 // Sanity check. 940 assertDozeMode(enabled); 941 } 942 assertDozeMode(boolean enabled)943 protected void assertDozeMode(boolean enabled) throws Exception { 944 assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE"); 945 } 946 isDozeModeEnabled()947 protected boolean isDozeModeEnabled() throws Exception { 948 final String result = executeShellCommand("cmd deviceidle enabled deep").trim(); 949 return result.equals("1"); 950 } 951 setAppIdle(boolean enabled)952 protected void setAppIdle(boolean enabled) throws Exception { 953 Log.i(TAG, "Setting app idle to " + enabled); 954 executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled ); 955 assertAppIdle(enabled); // Sanity check 956 } 957 getUsageStatsDump()958 private String getUsageStatsDump() throws Exception { 959 final String output = runShellCommand(mInstrumentation, "dumpsys usagestats").trim(); 960 final StringBuilder sb = new StringBuilder(); 961 final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter('\n'); 962 splitter.setString(output); 963 String str; 964 while (splitter.hasNext()) { 965 str = splitter.next(); 966 if (str.contains("package=") 967 && !str.contains(TEST_PKG) && !str.contains(TEST_APP2_PKG)) { 968 continue; 969 } 970 if (str.trim().startsWith("config=") || str.trim().startsWith("time=")) { 971 continue; 972 } 973 sb.append(str).append('\n'); 974 } 975 return sb.toString(); 976 } 977 assertAppIdle(boolean enabled)978 protected void assertAppIdle(boolean enabled) throws Exception { 979 try { 980 assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 15, 2, "Idle=" + enabled); 981 } catch (Throwable e) { 982 Log.d(TAG, "UsageStats dump:\n" + getUsageStatsDump()); 983 executeShellCommand("settings get global app_idle_constants"); 984 throw e; 985 } 986 } 987 988 /** 989 * Starts a service that will register a broadcast receiver to receive 990 * {@code RESTRICT_BACKGROUND_CHANGE} intents. 991 * <p> 992 * The service must run in a separate app because otherwise it would be killed every time 993 * {@link #runDeviceTests(String, String)} is executed. 994 */ registerBroadcastReceiver()995 protected void registerBroadcastReceiver() throws Exception { 996 mServiceClient.registerBroadcastReceiver(); 997 998 final Intent intent = new Intent(ACTION_RECEIVER_READY) 999 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 1000 // Wait until receiver is ready. 1001 final int maxTries = 10; 1002 for (int i = 1; i <= maxTries; i++) { 1003 final String message = sendOrderedBroadcast(intent, SECOND_IN_MS * 4); 1004 Log.d(TAG, "app2 receiver acked: " + message); 1005 if (message != null) { 1006 return; 1007 } 1008 Log.v(TAG, "app2 receiver is not ready yet; sleeping 1s before polling again"); 1009 SystemClock.sleep(SECOND_IN_MS); 1010 } 1011 fail("app2 receiver is not ready"); 1012 } 1013 registerNetworkCallback(INetworkCallback cb)1014 protected void registerNetworkCallback(INetworkCallback cb) throws Exception { 1015 mServiceClient.registerNetworkCallback(cb); 1016 } 1017 1018 /** 1019 * Registers a {@link NotificationListenerService} implementation that will execute the 1020 * notification actions right after the notification is sent. 1021 */ registerNotificationListenerService()1022 protected void registerNotificationListenerService() throws Exception { 1023 executeShellCommand("cmd notification allow_listener " 1024 + MyNotificationListenerService.getId()); 1025 final NotificationManager nm = mContext.getSystemService(NotificationManager.class); 1026 final ComponentName listenerComponent = MyNotificationListenerService.getComponentName(); 1027 assertTrue(listenerComponent + " has not been granted access", 1028 nm.isNotificationListenerAccessGranted(listenerComponent)); 1029 } 1030 setPendingIntentWhitelistDuration(int durationMs)1031 protected void setPendingIntentWhitelistDuration(int durationMs) throws Exception { 1032 executeSilentShellCommand(String.format( 1033 "settings put global %s %s=%d", mDeviceIdleConstantsSetting, 1034 "notification_whitelist_duration", durationMs)); 1035 } 1036 resetDeviceIdleSettings()1037 protected void resetDeviceIdleSettings() throws Exception { 1038 executeShellCommand(String.format("settings delete global %s", 1039 mDeviceIdleConstantsSetting)); 1040 } 1041 launchComponentAndAssertNetworkAccess(int type)1042 protected void launchComponentAndAssertNetworkAccess(int type) throws Exception { 1043 if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) { 1044 startForegroundService(); 1045 assertForegroundServiceNetworkAccess(); 1046 return; 1047 } else if (type == TYPE_COMPONENT_ACTIVTIY) { 1048 turnScreenOn(); 1049 // Wait for screen-on state to propagate through the system. 1050 SystemClock.sleep(2000); 1051 final CountDownLatch latch = new CountDownLatch(1); 1052 final Intent launchIntent = getIntentForComponent(type); 1053 final Bundle extras = new Bundle(); 1054 final String[] errors = new String[]{null}; 1055 extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, errors)); 1056 launchIntent.putExtras(extras); 1057 mContext.startActivity(launchIntent); 1058 if (latch.await(FOREGROUND_PROC_NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 1059 if (!errors[0].isEmpty()) { 1060 if (errors[0] == APP_NOT_FOREGROUND_ERROR) { 1061 // App didn't come to foreground when the activity is started, so try again. 1062 assertForegroundNetworkAccess(); 1063 } else { 1064 dumpOnFailure(); 1065 fail("Network is not available for app2 (" + mUid + "): " + errors[0]); 1066 } 1067 } 1068 } else { 1069 dumpOnFailure(); 1070 fail("Timed out waiting for network availability status from app2 (" + mUid + ")"); 1071 } 1072 } else { 1073 throw new IllegalArgumentException("Unknown type: " + type); 1074 } 1075 } 1076 startForegroundService()1077 private void startForegroundService() throws Exception { 1078 final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_FOREGROUND_SERVICE); 1079 mContext.startForegroundService(launchIntent); 1080 assertForegroundServiceState(); 1081 } 1082 getIntentForComponent(int type)1083 private Intent getIntentForComponent(int type) { 1084 final Intent intent = new Intent(); 1085 if (type == TYPE_COMPONENT_ACTIVTIY) { 1086 intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS)) 1087 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1088 } else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) { 1089 intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS)) 1090 .setFlags(1); 1091 } else { 1092 fail("Unknown type: " + type); 1093 } 1094 return intent; 1095 } 1096 stopForegroundService()1097 protected void stopForegroundService() throws Exception { 1098 executeShellCommand(String.format("am startservice -f 2 %s/%s", 1099 TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS)); 1100 // NOTE: cannot assert state because it depends on whether activity was on top before. 1101 } 1102 getNewNetworkStateObserver(final CountDownLatch latch, final String[] errors)1103 private Binder getNewNetworkStateObserver(final CountDownLatch latch, 1104 final String[] errors) { 1105 return new INetworkStateObserver.Stub() { 1106 @Override 1107 public boolean isForeground() { 1108 try { 1109 final ProcessState state = getProcessStateByUid(mUid); 1110 return !isBackground(state.state); 1111 } catch (Exception e) { 1112 Log.d(TAG, "Error while reading the proc state for " + mUid + ": " + e); 1113 return false; 1114 } 1115 } 1116 1117 @Override 1118 public void onNetworkStateChecked(String resultData) { 1119 errors[0] = resultData == null 1120 ? APP_NOT_FOREGROUND_ERROR 1121 : checkForAvailabilityInResultData(resultData, true); 1122 latch.countDown(); 1123 } 1124 }; 1125 } 1126 1127 /** 1128 * Finishes an activity on app2 so its process is demoted fromforeground status. 1129 */ 1130 protected void finishActivity() throws Exception { 1131 executeShellCommand("am broadcast -a " 1132 + " com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY " 1133 + "--receiver-foreground --receiver-registered-only"); 1134 } 1135 1136 protected void sendNotification(int notificationId, String notificationType) throws Exception { 1137 Log.d(TAG, "Sending notification broadcast (id=" + notificationId 1138 + ", type=" + notificationType); 1139 mServiceClient.sendNotification(notificationId, notificationType); 1140 } 1141 1142 protected String showToast() { 1143 final Intent intent = new Intent(ACTION_SHOW_TOAST); 1144 intent.setPackage(TEST_APP2_PKG); 1145 Log.d(TAG, "Sending request to show toast"); 1146 try { 1147 return sendOrderedBroadcast(intent, 3 * SECOND_IN_MS); 1148 } catch (Exception e) { 1149 return ""; 1150 } 1151 } 1152 1153 private String toString(int status) { 1154 switch (status) { 1155 case RESTRICT_BACKGROUND_STATUS_DISABLED: 1156 return "DISABLED"; 1157 case RESTRICT_BACKGROUND_STATUS_WHITELISTED: 1158 return "WHITELISTED"; 1159 case RESTRICT_BACKGROUND_STATUS_ENABLED: 1160 return "ENABLED"; 1161 default: 1162 return "UNKNOWN_STATUS_" + status; 1163 } 1164 } 1165 1166 private ProcessState getProcessStateByUid(int uid) throws Exception { 1167 return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid)); 1168 } 1169 1170 private static class ProcessState { 1171 private final String fullState; 1172 final int state; 1173 1174 ProcessState(String fullState) { 1175 this.fullState = fullState; 1176 try { 1177 this.state = Integer.parseInt(fullState.split(" ")[0]); 1178 } catch (Exception e) { 1179 throw new IllegalArgumentException("Could not parse " + fullState); 1180 } 1181 } 1182 1183 @Override 1184 public String toString() { 1185 return fullState; 1186 } 1187 } 1188 1189 /** 1190 * Helper class used to assert the result of a Shell command. 1191 */ 1192 protected static interface ExpectResultChecker { 1193 /** 1194 * Checkes whether the result of the command matched the expectation. 1195 */ 1196 boolean isExpected(String result); 1197 /** 1198 * Gets the expected result so it's displayed on log and failure messages. 1199 */ 1200 String getExpected(); 1201 } 1202 } 1203