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