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