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