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