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