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.os.BatteryManager.BATTERY_PLUGGED_AC; 21 import static android.os.BatteryManager.BATTERY_PLUGGED_USB; 22 import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS; 23 24 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.executeShellCommand; 25 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.forceRunJob; 26 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getConnectivityManager; 27 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getContext; 28 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.getInstrumentation; 29 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isAppStandbySupported; 30 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isBatterySaverSupported; 31 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.isDozeModeSupported; 32 import static com.android.cts.net.hostside.NetworkPolicyTestUtils.restrictBackgroundValueToString; 33 34 import static org.junit.Assert.assertEquals; 35 import static org.junit.Assert.assertFalse; 36 import static org.junit.Assert.assertNotNull; 37 import static org.junit.Assert.assertTrue; 38 import static org.junit.Assert.fail; 39 40 import android.app.ActivityManager; 41 import android.app.Instrumentation; 42 import android.app.NotificationManager; 43 import android.app.job.JobInfo; 44 import android.content.BroadcastReceiver; 45 import android.content.ComponentName; 46 import android.content.Context; 47 import android.content.Intent; 48 import android.content.IntentFilter; 49 import android.net.ConnectivityManager; 50 import android.net.NetworkInfo.DetailedState; 51 import android.net.NetworkInfo.State; 52 import android.net.NetworkRequest; 53 import android.os.BatteryManager; 54 import android.os.Binder; 55 import android.os.Bundle; 56 import android.os.SystemClock; 57 import android.provider.DeviceConfig; 58 import android.service.notification.NotificationListenerService; 59 import android.util.Log; 60 import android.util.Pair; 61 62 import com.android.compatibility.common.util.BatteryUtils; 63 import com.android.compatibility.common.util.DeviceConfigStateHelper; 64 65 import org.junit.Rule; 66 import org.junit.rules.RuleChain; 67 import org.junit.runner.RunWith; 68 69 import java.util.ArrayList; 70 import java.util.concurrent.CountDownLatch; 71 import java.util.concurrent.LinkedBlockingQueue; 72 import java.util.concurrent.TimeUnit; 73 74 /** 75 * Superclass for tests related to background network restrictions. 76 */ 77 @RunWith(NetworkPolicyTestRunner.class) 78 public abstract class AbstractRestrictBackgroundNetworkTestCase { 79 public static final String TAG = "RestrictBackgroundNetworkTests"; 80 81 protected static final String TEST_PKG = "com.android.cts.net.hostside"; 82 protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2"; 83 84 private static final String TEST_APP2_ACTIVITY_CLASS = TEST_APP2_PKG + ".MyActivity"; 85 private static final String TEST_APP2_SERVICE_CLASS = TEST_APP2_PKG + ".MyForegroundService"; 86 private static final String TEST_APP2_JOB_SERVICE_CLASS = TEST_APP2_PKG + ".MyJobService"; 87 88 private static final ComponentName TEST_JOB_COMPONENT = new ComponentName( 89 TEST_APP2_PKG, TEST_APP2_JOB_SERVICE_CLASS); 90 91 private static final int TEST_JOB_ID = 7357437; 92 93 private static final int SLEEP_TIME_SEC = 1; 94 95 // Constants below must match values defined on app2's Common.java 96 private static final String MANIFEST_RECEIVER = "ManifestReceiver"; 97 private static final String DYNAMIC_RECEIVER = "DynamicReceiver"; 98 private static final String ACTION_FINISH_ACTIVITY = 99 "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY"; 100 private static final String ACTION_FINISH_JOB = 101 "com.android.cts.net.hostside.app2.action.FINISH_JOB"; 102 // Copied from com.android.server.net.NetworkPolicyManagerService class 103 private static final String ACTION_SNOOZE_WARNING = 104 "com.android.server.net.action.SNOOZE_WARNING"; 105 106 private static final String ACTION_RECEIVER_READY = 107 "com.android.cts.net.hostside.app2.action.RECEIVER_READY"; 108 static final String ACTION_SHOW_TOAST = 109 "com.android.cts.net.hostside.app2.action.SHOW_TOAST"; 110 111 protected static final String NOTIFICATION_TYPE_CONTENT = "CONTENT"; 112 protected static final String NOTIFICATION_TYPE_DELETE = "DELETE"; 113 protected static final String NOTIFICATION_TYPE_FULL_SCREEN = "FULL_SCREEN"; 114 protected static final String NOTIFICATION_TYPE_BUNDLE = "BUNDLE"; 115 protected static final String NOTIFICATION_TYPE_ACTION = "ACTION"; 116 protected static final String NOTIFICATION_TYPE_ACTION_BUNDLE = "ACTION_BUNDLE"; 117 protected static final String NOTIFICATION_TYPE_ACTION_REMOTE_INPUT = "ACTION_REMOTE_INPUT"; 118 119 // TODO: Update BatteryManager.BATTERY_PLUGGED_ANY as @TestApi 120 public static final int BATTERY_PLUGGED_ANY = 121 BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS; 122 123 private static final String NETWORK_STATUS_SEPARATOR = "\\|"; 124 private static final int SECOND_IN_MS = 1000; 125 static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS; 126 127 private static int PROCESS_STATE_FOREGROUND_SERVICE; 128 129 private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer"; 130 private static final String KEY_SKIP_VALIDATION_CHECKS = TEST_PKG + ".skip_validation_checks"; 131 132 private static final String EMPTY_STRING = ""; 133 134 protected static final int TYPE_COMPONENT_ACTIVTIY = 0; 135 protected static final int TYPE_COMPONENT_FOREGROUND_SERVICE = 1; 136 protected static final int TYPE_EXPEDITED_JOB = 2; 137 138 private static final int BATTERY_STATE_TIMEOUT_MS = 5000; 139 private static final int BATTERY_STATE_CHECK_INTERVAL_MS = 500; 140 141 private static final int ACTIVITY_NETWORK_STATE_TIMEOUT_MS = 6_000; 142 private static final int JOB_NETWORK_STATE_TIMEOUT_MS = 10_000; 143 144 // Must be higher than NETWORK_TIMEOUT_MS 145 private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4; 146 147 private static final IntentFilter BATTERY_CHANGED_FILTER = 148 new IntentFilter(Intent.ACTION_BATTERY_CHANGED); 149 150 private static final String APP_NOT_FOREGROUND_ERROR = "app_not_fg"; 151 152 protected static final long TEMP_POWERSAVE_WHITELIST_DURATION_MS = 5_000; // 5 sec 153 154 private static final long BROADCAST_TIMEOUT_MS = 15_000; 155 156 protected Context mContext; 157 protected Instrumentation mInstrumentation; 158 protected ConnectivityManager mCm; 159 protected int mUid; 160 private int mMyUid; 161 private MyServiceClient mServiceClient; 162 private DeviceConfigStateHelper mDeviceIdleDeviceConfigStateHelper; 163 164 @Rule 165 public final RuleChain mRuleChain = RuleChain.outerRule(new RequiredPropertiesRule()) 166 .around(new MeterednessConfigurationRule()); 167 setUp()168 protected void setUp() throws Exception { 169 // TODO: Annotate these constants with @TestApi instead of obtaining them using reflection 170 PROCESS_STATE_FOREGROUND_SERVICE = (Integer) ActivityManager.class 171 .getDeclaredField("PROCESS_STATE_FOREGROUND_SERVICE").get(null); 172 mInstrumentation = getInstrumentation(); 173 mContext = getContext(); 174 mCm = getConnectivityManager(); 175 mDeviceIdleDeviceConfigStateHelper = 176 new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_DEVICE_IDLE); 177 mUid = getUid(TEST_APP2_PKG); 178 mMyUid = getUid(mContext.getPackageName()); 179 mServiceClient = new MyServiceClient(mContext); 180 mServiceClient.bind(); 181 executeShellCommand("cmd netpolicy start-watching " + mUid); 182 setAppIdle(false); 183 184 Log.i(TAG, "Apps status:\n" 185 + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\n" 186 + "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid)); 187 } 188 tearDown()189 protected void tearDown() throws Exception { 190 executeShellCommand("cmd netpolicy stop-watching"); 191 mServiceClient.unbind(); 192 } 193 getUid(String packageName)194 protected int getUid(String packageName) throws Exception { 195 return mContext.getPackageManager().getPackageUid(packageName, 0); 196 } 197 assertRestrictBackgroundChangedReceived(int expectedCount)198 protected void assertRestrictBackgroundChangedReceived(int expectedCount) throws Exception { 199 assertRestrictBackgroundChangedReceived(DYNAMIC_RECEIVER, expectedCount); 200 assertRestrictBackgroundChangedReceived(MANIFEST_RECEIVER, 0); 201 } 202 assertRestrictBackgroundChangedReceived(String receiverName, int expectedCount)203 protected void assertRestrictBackgroundChangedReceived(String receiverName, int expectedCount) 204 throws Exception { 205 int attempts = 0; 206 int count = 0; 207 final int maxAttempts = 5; 208 do { 209 attempts++; 210 count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED); 211 assertFalse("Expected count " + expectedCount + " but actual is " + count, 212 count > expectedCount); 213 if (count == expectedCount) { 214 break; 215 } 216 Log.d(TAG, "Expecting count " + expectedCount + " but actual is " + count + " after " 217 + attempts + " attempts; sleeping " 218 + SLEEP_TIME_SEC + " seconds before trying again"); 219 SystemClock.sleep(SLEEP_TIME_SEC * SECOND_IN_MS); 220 } while (attempts <= maxAttempts); 221 assertEquals("Number of expected broadcasts for " + receiverName + " not reached after " 222 + maxAttempts * SLEEP_TIME_SEC + " seconds", expectedCount, count); 223 } 224 assertSnoozeWarningNotReceived()225 protected void assertSnoozeWarningNotReceived() throws Exception { 226 // Wait for a while to take broadcast queue delays into account 227 SystemClock.sleep(BROADCAST_TIMEOUT_MS); 228 assertEquals(0, getNumberBroadcastsReceived(DYNAMIC_RECEIVER, ACTION_SNOOZE_WARNING)); 229 } 230 sendOrderedBroadcast(Intent intent)231 protected String sendOrderedBroadcast(Intent intent) throws Exception { 232 return sendOrderedBroadcast(intent, ORDERED_BROADCAST_TIMEOUT_MS); 233 } 234 sendOrderedBroadcast(Intent intent, int timeoutMs)235 protected String sendOrderedBroadcast(Intent intent, int timeoutMs) throws Exception { 236 final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1); 237 Log.d(TAG, "Sending ordered broadcast: " + intent); 238 mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() { 239 240 @Override 241 public void onReceive(Context context, Intent intent) { 242 final String resultData = getResultData(); 243 if (resultData == null) { 244 Log.e(TAG, "Received null data from ordered intent"); 245 // Offer an empty string so that the code waiting for the result can return. 246 result.offer(EMPTY_STRING); 247 return; 248 } 249 result.offer(resultData); 250 } 251 }, null, 0, null, null); 252 253 final String resultData = result.poll(timeoutMs, TimeUnit.MILLISECONDS); 254 Log.d(TAG, "Ordered broadcast response after " + timeoutMs + "ms: " + resultData ); 255 return resultData; 256 } 257 getNumberBroadcastsReceived(String receiverName, String action)258 protected int getNumberBroadcastsReceived(String receiverName, String action) throws Exception { 259 return mServiceClient.getCounters(receiverName, action); 260 } 261 assertRestrictBackgroundStatus(int expectedStatus)262 protected void assertRestrictBackgroundStatus(int expectedStatus) throws Exception { 263 final String status = mServiceClient.getRestrictBackgroundStatus(); 264 assertNotNull("didn't get API status from app2", status); 265 assertEquals(restrictBackgroundValueToString(expectedStatus), 266 restrictBackgroundValueToString(Integer.parseInt(status))); 267 } 268 assertBackgroundNetworkAccess(boolean expectAllowed)269 protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception { 270 assertBackgroundState(); 271 assertNetworkAccess(expectAllowed /* expectAvailable */, false /* needScreenOn */); 272 } 273 assertForegroundNetworkAccess()274 protected void assertForegroundNetworkAccess() throws Exception { 275 assertForegroundNetworkAccess(true); 276 } 277 assertForegroundNetworkAccess(boolean expectAllowed)278 protected void assertForegroundNetworkAccess(boolean expectAllowed) throws Exception { 279 assertForegroundState(); 280 // We verified that app is in foreground state but if the screen turns-off while 281 // verifying for network access, the app will go into background state (in case app's 282 // foreground status was due to top activity). So, turn the screen on when verifying 283 // network connectivity. 284 assertNetworkAccess(expectAllowed /* expectAvailable */, true /* needScreenOn */); 285 } 286 assertForegroundServiceNetworkAccess()287 protected void assertForegroundServiceNetworkAccess() throws Exception { 288 assertForegroundServiceState(); 289 assertNetworkAccess(true /* expectAvailable */, false /* needScreenOn */); 290 } 291 292 /** 293 * Asserts that an app always have access while on foreground or running a foreground service. 294 * 295 * <p>This method will launch an activity, a foreground service to make 296 * the assertion, but will finish the activity / stop the service afterwards. 297 */ assertsForegroundAlwaysHasNetworkAccess()298 protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception{ 299 // Checks foreground first. 300 launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_ACTIVTIY); 301 finishActivity(); 302 303 // Then foreground service 304 launchComponentAndAssertNetworkAccess(TYPE_COMPONENT_FOREGROUND_SERVICE); 305 stopForegroundService(); 306 } 307 assertExpeditedJobHasNetworkAccess()308 protected void assertExpeditedJobHasNetworkAccess() throws Exception { 309 launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB); 310 finishExpeditedJob(); 311 } 312 assertExpeditedJobHasNoNetworkAccess()313 protected void assertExpeditedJobHasNoNetworkAccess() throws Exception { 314 launchComponentAndAssertNetworkAccess(TYPE_EXPEDITED_JOB, false); 315 finishExpeditedJob(); 316 } 317 assertBackgroundState()318 protected final void assertBackgroundState() throws Exception { 319 final int maxTries = 30; 320 ProcessState state = null; 321 for (int i = 1; i <= maxTries; i++) { 322 state = getProcessStateByUid(mUid); 323 Log.v(TAG, "assertBackgroundState(): status for app2 (" + mUid + ") on attempt #" + i 324 + ": " + state); 325 if (isBackground(state.state)) { 326 return; 327 } 328 Log.d(TAG, "App not on background state (" + state + ") on attempt #" + i 329 + "; sleeping 1s before trying again"); 330 SystemClock.sleep(SECOND_IN_MS); 331 } 332 fail("App2 is not on background state after " + maxTries + " attempts: " + state ); 333 } 334 assertForegroundState()335 protected final void assertForegroundState() throws Exception { 336 final int maxTries = 30; 337 ProcessState state = null; 338 for (int i = 1; i <= maxTries; i++) { 339 state = getProcessStateByUid(mUid); 340 Log.v(TAG, "assertForegroundState(): status for app2 (" + mUid + ") on attempt #" + i 341 + ": " + state); 342 if (!isBackground(state.state)) { 343 return; 344 } 345 Log.d(TAG, "App not on foreground state on attempt #" + i 346 + "; sleeping 1s before trying again"); 347 turnScreenOn(); 348 SystemClock.sleep(SECOND_IN_MS); 349 } 350 fail("App2 is not on foreground state after " + maxTries + " attempts: " + state ); 351 } 352 assertForegroundServiceState()353 protected final void assertForegroundServiceState() throws Exception { 354 final int maxTries = 30; 355 ProcessState state = null; 356 for (int i = 1; i <= maxTries; i++) { 357 state = getProcessStateByUid(mUid); 358 Log.v(TAG, "assertForegroundServiceState(): status for app2 (" + mUid + ") on attempt #" 359 + i + ": " + state); 360 if (state.state == PROCESS_STATE_FOREGROUND_SERVICE) { 361 return; 362 } 363 Log.d(TAG, "App not on foreground service state on attempt #" + i 364 + "; sleeping 1s before trying again"); 365 SystemClock.sleep(SECOND_IN_MS); 366 } 367 fail("App2 is not on foreground service state after " + maxTries + " attempts: " + state ); 368 } 369 370 /** 371 * Returns whether an app state should be considered "background" for restriction purposes. 372 */ isBackground(int state)373 protected boolean isBackground(int state) { 374 return state > PROCESS_STATE_FOREGROUND_SERVICE; 375 } 376 377 /** 378 * Asserts whether the active network is available or not. 379 */ assertNetworkAccess(boolean expectAvailable, boolean needScreenOn)380 private void assertNetworkAccess(boolean expectAvailable, boolean needScreenOn) 381 throws Exception { 382 final int maxTries = 5; 383 String error = null; 384 int timeoutMs = 500; 385 386 for (int i = 1; i <= maxTries; i++) { 387 error = checkNetworkAccess(expectAvailable); 388 389 if (error == null) return; 390 391 // TODO: ideally, it should retry only when it cannot connect to an external site, 392 // or no retry at all! But, currently, the initial change fails almost always on 393 // battery saver tests because the netd changes are made asynchronously. 394 // Once b/27803922 is fixed, this retry mechanism should be revisited. 395 396 Log.w(TAG, "Network status didn't match for expectAvailable=" + expectAvailable 397 + " on attempt #" + i + ": " + error + "\n" 398 + "Sleeping " + timeoutMs + "ms before trying again"); 399 if (needScreenOn) { 400 turnScreenOn(); 401 } 402 // No sleep after the last turn 403 if (i < maxTries) { 404 SystemClock.sleep(timeoutMs); 405 } 406 // Exponential back-off. 407 timeoutMs = Math.min(timeoutMs*2, NETWORK_TIMEOUT_MS); 408 } 409 fail("Invalid state for expectAvailable=" + expectAvailable + " after " + maxTries 410 + " attempts.\nLast error: " + error); 411 } 412 413 /** 414 * Checks whether the network is available as expected. 415 * 416 * @return error message with the mismatch (or empty if assertion passed). 417 */ checkNetworkAccess(boolean expectAvailable)418 private String checkNetworkAccess(boolean expectAvailable) throws Exception { 419 final String resultData = mServiceClient.checkNetworkStatus(); 420 return checkForAvailabilityInResultData(resultData, expectAvailable); 421 } 422 checkForAvailabilityInResultData(String resultData, boolean expectAvailable)423 private String checkForAvailabilityInResultData(String resultData, boolean expectAvailable) { 424 if (resultData == null) { 425 assertNotNull("Network status from app2 is null", resultData); 426 } 427 // Network status format is described on MyBroadcastReceiver.checkNetworkStatus() 428 final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR); 429 assertEquals("Wrong network status: " + resultData, 5, parts.length); 430 final State state = parts[0].equals("null") ? null : State.valueOf(parts[0]); 431 final DetailedState detailedState = parts[1].equals("null") 432 ? null : DetailedState.valueOf(parts[1]); 433 final boolean connected = Boolean.valueOf(parts[2]); 434 final String connectionCheckDetails = parts[3]; 435 final String networkInfo = parts[4]; 436 437 final StringBuilder errors = new StringBuilder(); 438 final State expectedState; 439 final DetailedState expectedDetailedState; 440 if (expectAvailable) { 441 expectedState = State.CONNECTED; 442 expectedDetailedState = DetailedState.CONNECTED; 443 } else { 444 expectedState = State.DISCONNECTED; 445 expectedDetailedState = DetailedState.BLOCKED; 446 } 447 448 if (expectAvailable != connected) { 449 errors.append(String.format("External site connection failed: expected %s, got %s\n", 450 expectAvailable, connected)); 451 } 452 if (expectedState != state || expectedDetailedState != detailedState) { 453 errors.append(String.format("Connection state mismatch: expected %s/%s, got %s/%s\n", 454 expectedState, expectedDetailedState, state, detailedState)); 455 } 456 457 if (errors.length() > 0) { 458 errors.append("\tnetworkInfo: " + networkInfo + "\n"); 459 errors.append("\tconnectionCheckDetails: " + connectionCheckDetails + "\n"); 460 } 461 return errors.length() == 0 ? null : errors.toString(); 462 } 463 464 /** 465 * Runs a Shell command which is not expected to generate output. 466 */ executeSilentShellCommand(String command)467 protected void executeSilentShellCommand(String command) { 468 final String result = executeShellCommand(command); 469 assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty()); 470 } 471 472 /** 473 * Asserts the result of a command, wait and re-running it a couple times if necessary. 474 */ assertDelayedShellCommand(String command, final String expectedResult)475 protected void assertDelayedShellCommand(String command, final String expectedResult) 476 throws Exception { 477 assertDelayedShellCommand(command, 5, 1, expectedResult); 478 } 479 assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds, final String expectedResult)480 protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds, 481 final String expectedResult) throws Exception { 482 assertDelayedShellCommand(command, maxTries, napTimeSeconds, new ExpectResultChecker() { 483 484 @Override 485 public boolean isExpected(String result) { 486 return expectedResult.equals(result); 487 } 488 489 @Override 490 public String getExpected() { 491 return expectedResult; 492 } 493 }); 494 } 495 assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds, ExpectResultChecker checker)496 protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds, 497 ExpectResultChecker checker) throws Exception { 498 String result = ""; 499 for (int i = 1; i <= maxTries; i++) { 500 result = executeShellCommand(command).trim(); 501 if (checker.isExpected(result)) return; 502 Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '" 503 + checker.getExpected() + "' on attempt #" + i 504 + "; sleeping " + napTimeSeconds + "s before trying again"); 505 SystemClock.sleep(napTimeSeconds * SECOND_IN_MS); 506 } 507 fail("Command '" + command + "' did not return '" + checker.getExpected() + "' after " 508 + maxTries 509 + " attempts. Last result: '" + result + "'"); 510 } 511 addRestrictBackgroundWhitelist(int uid)512 protected void addRestrictBackgroundWhitelist(int uid) throws Exception { 513 executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid); 514 assertRestrictBackgroundWhitelist(uid, true); 515 // UID policies live by the Highlander rule: "There can be only one". 516 // Hence, if app is whitelisted, it should not be blacklisted. 517 assertRestrictBackgroundBlacklist(uid, false); 518 } 519 removeRestrictBackgroundWhitelist(int uid)520 protected void removeRestrictBackgroundWhitelist(int uid) throws Exception { 521 executeShellCommand("cmd netpolicy remove restrict-background-whitelist " + uid); 522 assertRestrictBackgroundWhitelist(uid, false); 523 } 524 assertRestrictBackgroundWhitelist(int uid, boolean expected)525 protected void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception { 526 assertRestrictBackground("restrict-background-whitelist", uid, expected); 527 } 528 addRestrictBackgroundBlacklist(int uid)529 protected void addRestrictBackgroundBlacklist(int uid) throws Exception { 530 executeShellCommand("cmd netpolicy add restrict-background-blacklist " + uid); 531 assertRestrictBackgroundBlacklist(uid, true); 532 // UID policies live by the Highlander rule: "There can be only one". 533 // Hence, if app is blacklisted, it should not be whitelisted. 534 assertRestrictBackgroundWhitelist(uid, false); 535 } 536 removeRestrictBackgroundBlacklist(int uid)537 protected void removeRestrictBackgroundBlacklist(int uid) throws Exception { 538 executeShellCommand("cmd netpolicy remove restrict-background-blacklist " + uid); 539 assertRestrictBackgroundBlacklist(uid, false); 540 } 541 assertRestrictBackgroundBlacklist(int uid, boolean expected)542 protected void assertRestrictBackgroundBlacklist(int uid, boolean expected) throws Exception { 543 assertRestrictBackground("restrict-background-blacklist", uid, expected); 544 } 545 addAppIdleWhitelist(int uid)546 protected void addAppIdleWhitelist(int uid) throws Exception { 547 executeShellCommand("cmd netpolicy add app-idle-whitelist " + uid); 548 assertAppIdleWhitelist(uid, true); 549 } 550 removeAppIdleWhitelist(int uid)551 protected void removeAppIdleWhitelist(int uid) throws Exception { 552 executeShellCommand("cmd netpolicy remove app-idle-whitelist " + uid); 553 assertAppIdleWhitelist(uid, false); 554 } 555 assertAppIdleWhitelist(int uid, boolean expected)556 protected void assertAppIdleWhitelist(int uid, boolean expected) throws Exception { 557 assertRestrictBackground("app-idle-whitelist", uid, expected); 558 } 559 assertRestrictBackground(String list, int uid, boolean expected)560 private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception { 561 final int maxTries = 5; 562 boolean actual = false; 563 final String expectedUid = Integer.toString(uid); 564 String uids = ""; 565 for (int i = 1; i <= maxTries; i++) { 566 final String output = 567 executeShellCommand("cmd netpolicy list " + list); 568 uids = output.split(":")[1]; 569 for (String candidate : uids.split(" ")) { 570 actual = candidate.trim().equals(expectedUid); 571 if (expected == actual) { 572 return; 573 } 574 } 575 Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected " 576 + expected + ", got " + actual + "); sleeping 1s before polling again"); 577 SystemClock.sleep(SECOND_IN_MS); 578 } 579 fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual 580 + ". Full list: " + uids); 581 } 582 addTempPowerSaveModeWhitelist(String packageName, long duration)583 protected void addTempPowerSaveModeWhitelist(String packageName, long duration) 584 throws Exception { 585 Log.i(TAG, "Adding pkg " + packageName + " to temp-power-save-mode whitelist"); 586 executeShellCommand("dumpsys deviceidle tempwhitelist -d " + duration + " " + packageName); 587 } 588 assertPowerSaveModeWhitelist(String packageName, boolean expected)589 protected void assertPowerSaveModeWhitelist(String packageName, boolean expected) 590 throws Exception { 591 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 592 // need to use netpolicy for whitelisting 593 assertDelayedShellCommand("dumpsys deviceidle whitelist =" + packageName, 594 Boolean.toString(expected)); 595 } 596 addPowerSaveModeWhitelist(String packageName)597 protected void addPowerSaveModeWhitelist(String packageName) throws Exception { 598 Log.i(TAG, "Adding package " + packageName + " to power-save-mode whitelist"); 599 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 600 // need to use netpolicy for whitelisting 601 executeShellCommand("dumpsys deviceidle whitelist +" + packageName); 602 assertPowerSaveModeWhitelist(packageName, true); 603 } 604 removePowerSaveModeWhitelist(String packageName)605 protected void removePowerSaveModeWhitelist(String packageName) throws Exception { 606 Log.i(TAG, "Removing package " + packageName + " from power-save-mode whitelist"); 607 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 608 // need to use netpolicy for whitelisting 609 executeShellCommand("dumpsys deviceidle whitelist -" + packageName); 610 assertPowerSaveModeWhitelist(packageName, false); 611 } 612 assertPowerSaveModeExceptIdleWhitelist(String packageName, boolean expected)613 protected void assertPowerSaveModeExceptIdleWhitelist(String packageName, boolean expected) 614 throws Exception { 615 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 616 // need to use netpolicy for whitelisting 617 assertDelayedShellCommand("dumpsys deviceidle except-idle-whitelist =" + packageName, 618 Boolean.toString(expected)); 619 } 620 addPowerSaveModeExceptIdleWhitelist(String packageName)621 protected void addPowerSaveModeExceptIdleWhitelist(String packageName) throws Exception { 622 Log.i(TAG, "Adding package " + packageName + " to power-save-mode-except-idle whitelist"); 623 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 624 // need to use netpolicy for whitelisting 625 executeShellCommand("dumpsys deviceidle except-idle-whitelist +" + packageName); 626 assertPowerSaveModeExceptIdleWhitelist(packageName, true); 627 } 628 removePowerSaveModeExceptIdleWhitelist(String packageName)629 protected void removePowerSaveModeExceptIdleWhitelist(String packageName) throws Exception { 630 Log.i(TAG, "Removing package " + packageName 631 + " from power-save-mode-except-idle whitelist"); 632 // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll 633 // need to use netpolicy for whitelisting 634 executeShellCommand("dumpsys deviceidle except-idle-whitelist reset"); 635 assertPowerSaveModeExceptIdleWhitelist(packageName, false); 636 } 637 turnBatteryOn()638 protected void turnBatteryOn() throws Exception { 639 executeSilentShellCommand("cmd battery unplug"); 640 executeSilentShellCommand("cmd battery set status " 641 + BatteryManager.BATTERY_STATUS_DISCHARGING); 642 assertBatteryState(false); 643 } 644 turnBatteryOff()645 protected void turnBatteryOff() throws Exception { 646 executeSilentShellCommand("cmd battery set ac " + BATTERY_PLUGGED_ANY); 647 executeSilentShellCommand("cmd battery set level 100"); 648 executeSilentShellCommand("cmd battery set status " 649 + BatteryManager.BATTERY_STATUS_CHARGING); 650 assertBatteryState(true); 651 } 652 resetBatteryState()653 protected void resetBatteryState() { 654 BatteryUtils.runDumpsysBatteryReset(); 655 } 656 assertBatteryState(boolean pluggedIn)657 private void assertBatteryState(boolean pluggedIn) throws Exception { 658 final long endTime = SystemClock.elapsedRealtime() + BATTERY_STATE_TIMEOUT_MS; 659 while (isDevicePluggedIn() != pluggedIn && SystemClock.elapsedRealtime() <= endTime) { 660 Thread.sleep(BATTERY_STATE_CHECK_INTERVAL_MS); 661 } 662 if (isDevicePluggedIn() != pluggedIn) { 663 fail("Timed out waiting for the plugged-in state to change," 664 + " expected pluggedIn: " + pluggedIn); 665 } 666 } 667 isDevicePluggedIn()668 private boolean isDevicePluggedIn() { 669 final Intent batteryIntent = mContext.registerReceiver(null, BATTERY_CHANGED_FILTER); 670 return batteryIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1) > 0; 671 } 672 turnScreenOff()673 protected void turnScreenOff() throws Exception { 674 executeSilentShellCommand("input keyevent KEYCODE_SLEEP"); 675 } 676 turnScreenOn()677 protected void turnScreenOn() throws Exception { 678 executeSilentShellCommand("input keyevent KEYCODE_WAKEUP"); 679 executeSilentShellCommand("wm dismiss-keyguard"); 680 } 681 setBatterySaverMode(boolean enabled)682 protected void setBatterySaverMode(boolean enabled) throws Exception { 683 if (!isBatterySaverSupported()) { 684 return; 685 } 686 Log.i(TAG, "Setting Battery Saver Mode to " + enabled); 687 if (enabled) { 688 turnBatteryOn(); 689 executeSilentShellCommand("cmd power set-mode 1"); 690 } else { 691 executeSilentShellCommand("cmd power set-mode 0"); 692 turnBatteryOff(); 693 } 694 } 695 setDozeMode(boolean enabled)696 protected void setDozeMode(boolean enabled) throws Exception { 697 if (!isDozeModeSupported()) { 698 return; 699 } 700 701 Log.i(TAG, "Setting Doze Mode to " + enabled); 702 if (enabled) { 703 turnBatteryOn(); 704 turnScreenOff(); 705 executeShellCommand("dumpsys deviceidle force-idle deep"); 706 } else { 707 turnScreenOn(); 708 turnBatteryOff(); 709 executeShellCommand("dumpsys deviceidle unforce"); 710 } 711 assertDozeMode(enabled); 712 } 713 assertDozeMode(boolean enabled)714 protected void assertDozeMode(boolean enabled) throws Exception { 715 assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE"); 716 } 717 setAppIdle(boolean enabled)718 protected void setAppIdle(boolean enabled) throws Exception { 719 if (!isAppStandbySupported()) { 720 return; 721 } 722 Log.i(TAG, "Setting app idle to " + enabled); 723 executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled ); 724 assertAppIdle(enabled); 725 } 726 setAppIdleNoAssert(boolean enabled)727 protected void setAppIdleNoAssert(boolean enabled) throws Exception { 728 if (!isAppStandbySupported()) { 729 return; 730 } 731 Log.i(TAG, "Setting app idle to " + enabled); 732 executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled ); 733 } 734 assertAppIdle(boolean enabled)735 protected void assertAppIdle(boolean enabled) throws Exception { 736 try { 737 assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 15, 2, "Idle=" + enabled); 738 } catch (Throwable e) { 739 throw e; 740 } 741 } 742 743 /** 744 * Starts a service that will register a broadcast receiver to receive 745 * {@code RESTRICT_BACKGROUND_CHANGE} intents. 746 * <p> 747 * The service must run in a separate app because otherwise it would be killed every time 748 * {@link #runDeviceTests(String, String)} is executed. 749 */ registerBroadcastReceiver()750 protected void registerBroadcastReceiver() throws Exception { 751 mServiceClient.registerBroadcastReceiver(); 752 753 final Intent intent = new Intent(ACTION_RECEIVER_READY) 754 .addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 755 // Wait until receiver is ready. 756 final int maxTries = 10; 757 for (int i = 1; i <= maxTries; i++) { 758 final String message = sendOrderedBroadcast(intent, SECOND_IN_MS * 4); 759 Log.d(TAG, "app2 receiver acked: " + message); 760 if (message != null) { 761 return; 762 } 763 Log.v(TAG, "app2 receiver is not ready yet; sleeping 1s before polling again"); 764 SystemClock.sleep(SECOND_IN_MS); 765 } 766 fail("app2 receiver is not ready"); 767 } 768 registerNetworkCallback(final NetworkRequest request, INetworkCallback cb)769 protected void registerNetworkCallback(final NetworkRequest request, INetworkCallback cb) 770 throws Exception { 771 Log.i(TAG, "Registering network callback for request: " + request); 772 mServiceClient.registerNetworkCallback(request, cb); 773 } 774 unregisterNetworkCallback()775 protected void unregisterNetworkCallback() throws Exception { 776 mServiceClient.unregisterNetworkCallback(); 777 } 778 779 /** 780 * Registers a {@link NotificationListenerService} implementation that will execute the 781 * notification actions right after the notification is sent. 782 */ registerNotificationListenerService()783 protected void registerNotificationListenerService() throws Exception { 784 executeShellCommand("cmd notification allow_listener " 785 + MyNotificationListenerService.getId()); 786 final NotificationManager nm = mContext.getSystemService(NotificationManager.class); 787 final ComponentName listenerComponent = MyNotificationListenerService.getComponentName(); 788 assertTrue(listenerComponent + " has not been granted access", 789 nm.isNotificationListenerAccessGranted(listenerComponent)); 790 } 791 setPendingIntentAllowlistDuration(long durationMs)792 protected void setPendingIntentAllowlistDuration(long durationMs) { 793 mDeviceIdleDeviceConfigStateHelper.set("notification_allowlist_duration_ms", 794 String.valueOf(durationMs)); 795 } 796 resetDeviceIdleSettings()797 protected void resetDeviceIdleSettings() { 798 mDeviceIdleDeviceConfigStateHelper.restoreOriginalValues(); 799 } 800 launchComponentAndAssertNetworkAccess(int type)801 protected void launchComponentAndAssertNetworkAccess(int type) throws Exception { 802 launchComponentAndAssertNetworkAccess(type, true); 803 } 804 launchComponentAndAssertNetworkAccess(int type, boolean expectAvailable)805 protected void launchComponentAndAssertNetworkAccess(int type, boolean expectAvailable) 806 throws Exception { 807 if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) { 808 startForegroundService(); 809 assertForegroundServiceNetworkAccess(); 810 return; 811 } else if (type == TYPE_COMPONENT_ACTIVTIY) { 812 turnScreenOn(); 813 // Wait for screen-on state to propagate through the system. 814 SystemClock.sleep(2000); 815 final CountDownLatch latch = new CountDownLatch(1); 816 final Intent launchIntent = getIntentForComponent(type); 817 final Bundle extras = new Bundle(); 818 final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1); 819 extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result)); 820 extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable); 821 launchIntent.putExtras(extras); 822 mContext.startActivity(launchIntent); 823 if (latch.await(ACTIVITY_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 824 final int resultCode = result.get(0).first; 825 final String resultData = result.get(0).second; 826 if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) { 827 final String error = checkForAvailabilityInResultData( 828 resultData, expectAvailable); 829 if (error != null) { 830 fail("Network is not available for activity in app2 (" + mUid + "): " 831 + error); 832 } 833 } else if (resultCode == INetworkStateObserver.RESULT_ERROR_UNEXPECTED_PROC_STATE) { 834 Log.d(TAG, resultData); 835 // App didn't come to foreground when the activity is started, so try again. 836 assertForegroundNetworkAccess(); 837 } else { 838 fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]"); 839 } 840 } else { 841 fail("Timed out waiting for network availability status from app2's activity (" 842 + mUid + ")"); 843 } 844 } else if (type == TYPE_EXPEDITED_JOB) { 845 final Bundle extras = new Bundle(); 846 final ArrayList<Pair<Integer, String>> result = new ArrayList<>(1); 847 final CountDownLatch latch = new CountDownLatch(1); 848 extras.putBinder(KEY_NETWORK_STATE_OBSERVER, getNewNetworkStateObserver(latch, result)); 849 extras.putBoolean(KEY_SKIP_VALIDATION_CHECKS, !expectAvailable); 850 final JobInfo jobInfo = new JobInfo.Builder(TEST_JOB_ID, TEST_JOB_COMPONENT) 851 .setExpedited(true) 852 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 853 .setTransientExtras(extras) 854 .build(); 855 mServiceClient.scheduleJob(jobInfo); 856 forceRunJob(TEST_APP2_PKG, TEST_JOB_ID); 857 if (latch.await(JOB_NETWORK_STATE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) { 858 final int resultCode = result.get(0).first; 859 final String resultData = result.get(0).second; 860 if (resultCode == INetworkStateObserver.RESULT_SUCCESS_NETWORK_STATE_CHECKED) { 861 final String error = checkForAvailabilityInResultData( 862 resultData, expectAvailable); 863 if (error != null) { 864 Log.d(TAG, "Network state is unexpected, checking again. " + error); 865 // Right now we could end up in an unexpected state if expedited job 866 // doesn't have network access immediately after starting, so check again. 867 assertNetworkAccess(expectAvailable, false /* needScreenOn */); 868 } 869 } else { 870 fail("Unexpected resultCode=" + resultCode + "; received=[" + resultData + "]"); 871 } 872 } else { 873 fail("Timed out waiting for network availability status from app2's expedited job (" 874 + mUid + ")"); 875 } 876 } else { 877 throw new IllegalArgumentException("Unknown type: " + type); 878 } 879 } 880 startForegroundService()881 private void startForegroundService() throws Exception { 882 final Intent launchIntent = getIntentForComponent(TYPE_COMPONENT_FOREGROUND_SERVICE); 883 mContext.startForegroundService(launchIntent); 884 assertForegroundServiceState(); 885 } 886 getIntentForComponent(int type)887 private Intent getIntentForComponent(int type) { 888 final Intent intent = new Intent(); 889 if (type == TYPE_COMPONENT_ACTIVTIY) { 890 intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_ACTIVITY_CLASS)) 891 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 892 } else if (type == TYPE_COMPONENT_FOREGROUND_SERVICE) { 893 intent.setComponent(new ComponentName(TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS)) 894 .setFlags(1); 895 } else { 896 fail("Unknown type: " + type); 897 } 898 return intent; 899 } 900 stopForegroundService()901 protected void stopForegroundService() throws Exception { 902 executeShellCommand(String.format("am startservice -f 2 %s/%s", 903 TEST_APP2_PKG, TEST_APP2_SERVICE_CLASS)); 904 // NOTE: cannot assert state because it depends on whether activity was on top before. 905 } 906 getNewNetworkStateObserver(final CountDownLatch latch, final ArrayList<Pair<Integer, String>> result)907 private Binder getNewNetworkStateObserver(final CountDownLatch latch, 908 final ArrayList<Pair<Integer, String>> result) { 909 return new INetworkStateObserver.Stub() { 910 @Override 911 public void onNetworkStateChecked(int resultCode, String resultData) { 912 result.add(Pair.create(resultCode, resultData)); 913 latch.countDown(); 914 } 915 }; 916 } 917 918 /** 919 * Finishes an activity on app2 so its process is demoted from foreground status. 920 */ 921 protected void finishActivity() throws Exception { 922 final Intent intent = new Intent(ACTION_FINISH_ACTIVITY) 923 .setPackage(TEST_APP2_PKG) 924 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY); 925 sendOrderedBroadcast(intent); 926 } 927 928 /** 929 * Finishes the expedited job on app2 so its process is demoted from foreground status. 930 */ 931 private void finishExpeditedJob() throws Exception { 932 final Intent intent = new Intent(ACTION_FINISH_JOB) 933 .setPackage(TEST_APP2_PKG) 934 .setFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY); 935 sendOrderedBroadcast(intent); 936 } 937 938 protected void sendNotification(int notificationId, String notificationType) throws Exception { 939 Log.d(TAG, "Sending notification broadcast (id=" + notificationId 940 + ", type=" + notificationType); 941 mServiceClient.sendNotification(notificationId, notificationType); 942 } 943 944 protected String showToast() { 945 final Intent intent = new Intent(ACTION_SHOW_TOAST); 946 intent.setPackage(TEST_APP2_PKG); 947 Log.d(TAG, "Sending request to show toast"); 948 try { 949 return sendOrderedBroadcast(intent, 3 * SECOND_IN_MS); 950 } catch (Exception e) { 951 return ""; 952 } 953 } 954 955 private ProcessState getProcessStateByUid(int uid) throws Exception { 956 return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid)); 957 } 958 959 private static class ProcessState { 960 private final String fullState; 961 final int state; 962 963 ProcessState(String fullState) { 964 this.fullState = fullState; 965 try { 966 this.state = Integer.parseInt(fullState.split(" ")[0]); 967 } catch (Exception e) { 968 throw new IllegalArgumentException("Could not parse " + fullState); 969 } 970 } 971 972 @Override 973 public String toString() { 974 return fullState; 975 } 976 } 977 978 /** 979 * Helper class used to assert the result of a Shell command. 980 */ 981 protected static interface ExpectResultChecker { 982 /** 983 * Checkes whether the result of the command matched the expectation. 984 */ 985 boolean isExpected(String result); 986 /** 987 * Gets the expected result so it's displayed on log and failure messages. 988 */ 989 String getExpected(); 990 } 991 992 protected void setRestrictedNetworkingMode(boolean enabled) throws Exception { 993 executeSilentShellCommand( 994 "settings put global restricted_networking_mode " + (enabled ? 1 : 0)); 995 assertRestrictedNetworkingModeState(enabled); 996 } 997 998 protected void assertRestrictedNetworkingModeState(boolean enabled) throws Exception { 999 assertDelayedShellCommand("cmd netpolicy get restricted-mode", 1000 "Restricted mode status: " + (enabled ? "enabled" : "disabled")); 1001 } 1002 } 1003