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