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