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