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