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