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