• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 android.jobscheduler.cts;
18 
19 import static android.app.job.JobInfo.NETWORK_TYPE_ANY;
20 import static android.app.job.JobInfo.NETWORK_TYPE_NONE;
21 import static android.jobscheduler.cts.TestAppInterface.TEST_APP_PACKAGE;
22 import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED;
23 
24 import static com.android.compatibility.common.util.TestUtils.waitUntil;
25 
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertFalse;
28 import static org.junit.Assert.assertTrue;
29 import static org.junit.Assume.assumeFalse;
30 import static org.junit.Assume.assumeTrue;
31 
32 import android.app.AppOpsManager;
33 import android.app.job.JobInfo;
34 import android.app.job.JobParameters;
35 import android.content.BroadcastReceiver;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.content.IntentFilter;
39 import android.content.pm.PackageManager;
40 import android.jobscheduler.cts.jobtestapp.TestJobSchedulerReceiver;
41 import android.os.PowerManager;
42 import android.os.SystemClock;
43 import android.os.Temperature;
44 import android.os.UserHandle;
45 import android.platform.test.annotations.RequiresDevice;
46 import android.provider.DeviceConfig;
47 import android.provider.Settings;
48 import android.support.test.uiautomator.UiDevice;
49 import android.util.Log;
50 
51 import androidx.test.InstrumentationRegistry;
52 import androidx.test.filters.LargeTest;
53 import androidx.test.runner.AndroidJUnit4;
54 
55 import com.android.compatibility.common.util.AppOpsUtils;
56 import com.android.compatibility.common.util.AppStandbyUtils;
57 import com.android.compatibility.common.util.BatteryUtils;
58 import com.android.compatibility.common.util.DeviceConfigStateHelper;
59 import com.android.compatibility.common.util.ThermalUtils;
60 
61 import org.junit.After;
62 import org.junit.Before;
63 import org.junit.Test;
64 import org.junit.runner.RunWith;
65 
66 /**
67  * Tests related to job throttling -- device idle, app standby and battery saver.
68  */
69 @RunWith(AndroidJUnit4.class)
70 @LargeTest
71 public class JobThrottlingTest {
72     private static final String TAG = JobThrottlingTest.class.getSimpleName();
73     private static final long BACKGROUND_JOBS_EXPECTED_DELAY = 3_000;
74     private static final long POLL_INTERVAL = 500;
75     private static final long DEFAULT_WAIT_TIMEOUT = 5000;
76     private static final long SHELL_TIMEOUT = 3_000;
77     // TODO: mark Settings.System.SCREEN_OFF_TIMEOUT as @TestApi
78     private static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
79 
80     enum Bucket {
81         ACTIVE,
82         WORKING_SET,
83         FREQUENT,
84         RARE,
85         RESTRICTED,
86         NEVER
87     }
88 
89     private Context mContext;
90     private UiDevice mUiDevice;
91     private NetworkingHelper mNetworkingHelper;
92     private PowerManager mPowerManager;
93     private int mTestJobId;
94     private int mTestPackageUid;
95     private boolean mDeviceInDoze;
96     private boolean mDeviceIdleEnabled;
97     private boolean mAppStandbyEnabled;
98     private String mInitialDisplayTimeout;
99     private String mInitialRestrictedBucketEnabled;
100     private String mInitialBatteryStatsConstants;
101     private boolean mAutomotiveDevice;
102     private boolean mLeanbackOnly;
103 
104     private TestAppInterface mTestAppInterface;
105     private DeviceConfigStateHelper mDeviceConfigStateHelper;
106     private DeviceConfigStateHelper mActivityManagerDeviceConfigStateHelper;
107 
108     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
109         @Override
110         public void onReceive(Context context, Intent intent) {
111             Log.d(TAG, "Received action " + intent.getAction());
112             switch (intent.getAction()) {
113                 case ACTION_DEVICE_IDLE_MODE_CHANGED:
114                     synchronized (JobThrottlingTest.this) {
115                         mDeviceInDoze = mPowerManager.isDeviceIdleMode();
116                         Log.d(TAG, "mDeviceInDoze: " + mDeviceInDoze);
117                     }
118                     break;
119             }
120         }
121     };
122 
isDeviceIdleEnabled(UiDevice uiDevice)123     private static boolean isDeviceIdleEnabled(UiDevice uiDevice) throws Exception {
124         final String output = uiDevice.executeShellCommand("cmd deviceidle enabled deep").trim();
125         return Integer.parseInt(output) != 0;
126     }
127 
128     @Before
setUp()129     public void setUp() throws Exception {
130         mContext = InstrumentationRegistry.getTargetContext();
131         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
132         mNetworkingHelper =
133                 new NetworkingHelper(InstrumentationRegistry.getInstrumentation(), mContext);
134         mPowerManager = mContext.getSystemService(PowerManager.class);
135         mDeviceInDoze = mPowerManager.isDeviceIdleMode();
136         mTestPackageUid = mContext.getPackageManager().getPackageUid(TEST_APP_PACKAGE, 0);
137         mTestJobId = (int) (SystemClock.uptimeMillis() / 1000);
138         mTestAppInterface = new TestAppInterface(mContext, mTestJobId);
139         final IntentFilter intentFilter = new IntentFilter();
140         intentFilter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED);
141         mContext.registerReceiver(mReceiver, intentFilter);
142         assertFalse("Test package already in temp whitelist", isTestAppTempWhitelisted());
143         makeTestPackageIdle();
144         mDeviceIdleEnabled = isDeviceIdleEnabled(mUiDevice);
145         mAppStandbyEnabled = AppStandbyUtils.isAppStandbyEnabled();
146         if (mAppStandbyEnabled) {
147             setTestPackageStandbyBucket(Bucket.ACTIVE);
148         } else {
149             Log.w(TAG, "App standby not enabled on test device");
150         }
151         mInitialRestrictedBucketEnabled = Settings.Global.getString(mContext.getContentResolver(),
152                 Settings.Global.ENABLE_RESTRICTED_BUCKET);
153         mInitialBatteryStatsConstants = Settings.Global.getString(mContext.getContentResolver(),
154                 Settings.Global.BATTERY_STATS_CONSTANTS);
155         // Make sure ACTION_CHARGING is sent immediately.
156         Settings.Global.putString(mContext.getContentResolver(),
157                 Settings.Global.BATTERY_STATS_CONSTANTS, "battery_charged_delay_ms=0");
158         // Make sure test jobs can run regardless of bucket.
159         mDeviceConfigStateHelper =
160                 new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
161         mDeviceConfigStateHelper.set(
162                 new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
163                         .setInt("min_ready_non_active_jobs_count", 0).build());
164         mActivityManagerDeviceConfigStateHelper =
165                 new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER);
166         toggleAutoRestrictedBucketOnBgRestricted(false);
167         // Make sure the screen doesn't turn off when the test turns it on.
168         mInitialDisplayTimeout =
169                 Settings.System.getString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT);
170         Settings.System.putString(mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, "300000");
171 
172         // In automotive device, always-on screen and endless battery charging are assumed.
173         mAutomotiveDevice =
174                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
175         // In leanback devices, it is assumed that there is no battery.
176         mLeanbackOnly =
177                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY);
178         if (mAutomotiveDevice || mLeanbackOnly) {
179             setScreenState(true);
180             // TODO(b/159176758): make sure that initial power supply is on.
181             setChargingState(true);
182         }
183 
184         // Kill as many things in the background as possible so we avoid LMK interfering with the
185         // test.
186         mUiDevice.executeShellCommand("am kill-all");
187     }
188 
189     @Test
testAllowWhileIdleJobInTempwhitelist()190     public void testAllowWhileIdleJobInTempwhitelist() throws Exception {
191         assumeTrue("device idle not enabled", mDeviceIdleEnabled);
192 
193         toggleDozeState(true);
194         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
195         sendScheduleJobBroadcast(true);
196         assertFalse("Job started without being tempwhitelisted",
197                 mTestAppInterface.awaitJobStart(5_000));
198         tempWhitelistTestApp(5_000);
199         assertTrue("Job with allow_while_idle flag did not start when the app was tempwhitelisted",
200                 mTestAppInterface.awaitJobStart(5_000));
201     }
202 
203     @Test
testForegroundJobsStartImmediately()204     public void testForegroundJobsStartImmediately() throws Exception {
205         assumeTrue("device idle not enabled", mDeviceIdleEnabled);
206 
207         sendScheduleJobBroadcast(false);
208         runJob();
209         assertTrue("Job did not start after scheduling",
210                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
211         toggleDozeState(true);
212         assertTrue("Job did not stop on entering doze",
213                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
214         Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF);
215         // The adb command will force idle even with the screen on, so we need to turn Doze off
216         // explicitly.
217         toggleDozeState(false);
218         // Turn the screen on to ensure the test app ends up in TOP.
219         setScreenState(true);
220         mTestAppInterface.startAndKeepTestActivity();
221         assertTrue("Job for foreground app did not start immediately when device exited doze",
222                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
223     }
224 
225     @Test
testBackgroundJobsDelayed()226     public void testBackgroundJobsDelayed() throws Exception {
227         assumeTrue("device idle not enabled", mDeviceIdleEnabled);
228 
229         sendScheduleJobBroadcast(false);
230         runJob();
231         assertTrue("Job did not start after scheduling",
232                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
233         toggleDozeState(true);
234         assertTrue("Job did not stop on entering doze",
235                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
236         Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF);
237         toggleDozeState(false);
238         assertFalse("Job for background app started immediately when device exited doze",
239                 mTestAppInterface.awaitJobStart(2000));
240         Thread.sleep(BACKGROUND_JOBS_EXPECTED_DELAY - 2000);
241         assertTrue("Job for background app did not start after the expected delay of "
242                         + BACKGROUND_JOBS_EXPECTED_DELAY + "ms",
243                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
244     }
245 
246     @Test
testJobStoppedWhenRestricted()247     public void testJobStoppedWhenRestricted() throws Exception {
248         sendScheduleJobBroadcast(false);
249         runJob();
250         assertTrue("Job did not start after scheduling",
251                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
252         toggleAutoRestrictedBucketOnBgRestricted(true);
253         setTestPackageRestricted(true);
254         assertFalse("Job stopped after test app was restricted with auto-restricted-bucket on",
255                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
256         toggleAutoRestrictedBucketOnBgRestricted(false);
257         assertTrue("Job did not stop after test app was restricted",
258                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
259         assertEquals(JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
260                 mTestAppInterface.getLastParams().getStopReason());
261     }
262 
263     @Test
testRestrictedJobStartedWhenUnrestricted()264     public void testRestrictedJobStartedWhenUnrestricted() throws Exception {
265         setTestPackageRestricted(true);
266         sendScheduleJobBroadcast(false);
267         assertFalse("Job started for restricted app",
268                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
269         setTestPackageRestricted(false);
270         assertTrue("Job did not start when app was unrestricted",
271                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
272     }
273 
274     @Test
testRestrictedJobAllowedWhenUidActive()275     public void testRestrictedJobAllowedWhenUidActive() throws Exception {
276         setTestPackageRestricted(true);
277         sendScheduleJobBroadcast(false);
278         assertFalse("Job started for restricted app",
279                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
280         // Turn the screen on to ensure the app gets into the TOP state.
281         setScreenState(true);
282         mTestAppInterface.startAndKeepTestActivity(true);
283         assertTrue("Job did not start when app had an activity",
284                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
285 
286         mTestAppInterface.closeActivity();
287         // Don't put full minute as the timeout to give some leeway with test timing/processing.
288         assertFalse("Job stopped within grace period after activity closed",
289                 mTestAppInterface.awaitJobStop(55_000L));
290         assertTrue("Job did not stop after grace period ended",
291                 mTestAppInterface.awaitJobStop(15_000L));
292         assertEquals(JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
293                 mTestAppInterface.getLastParams().getStopReason());
294     }
295 
296     @Test
testRestrictedJobAllowedWhenAutoRestrictedBucketFeatureOn()297     public void testRestrictedJobAllowedWhenAutoRestrictedBucketFeatureOn() throws Exception {
298         setTestPackageRestricted(true);
299         sendScheduleJobBroadcast(false);
300         assertFalse("Job started for restricted app",
301                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
302         toggleAutoRestrictedBucketOnBgRestricted(true);
303         assertTrue("Job did not start after scheduling",
304                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
305     }
306 
307     @Test
testEJStoppedWhenRestricted()308     public void testEJStoppedWhenRestricted() throws Exception {
309         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
310         runJob();
311         assertTrue("Job did not start after scheduling",
312                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
313         toggleAutoRestrictedBucketOnBgRestricted(true);
314         setTestPackageRestricted(true);
315         assertFalse("Job stopped after test app was restricted with auto-restricted-bucket on",
316                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
317         toggleAutoRestrictedBucketOnBgRestricted(false);
318         assertTrue("Job did not stop after test app was restricted",
319                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
320         assertEquals(JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
321                 mTestAppInterface.getLastParams().getStopReason());
322     }
323 
324     @Test
testRestrictedEJStartedWhenUnrestricted()325     public void testRestrictedEJStartedWhenUnrestricted() throws Exception {
326         setTestPackageRestricted(true);
327         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
328         assertFalse("Job started for restricted app",
329                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
330         setTestPackageRestricted(false);
331         assertTrue("Job did not start when app was unrestricted",
332                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
333     }
334 
335     @Test
testRestrictedEJAllowedWhenUidActive()336     public void testRestrictedEJAllowedWhenUidActive() throws Exception {
337         setTestPackageRestricted(true);
338         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
339         assertFalse("Job started for restricted app",
340                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
341         // Turn the screen on to ensure the app gets into the TOP state.
342         setScreenState(true);
343         mTestAppInterface.startAndKeepTestActivity(true);
344         assertTrue("Job did not start when app had an activity",
345                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
346 
347         mTestAppInterface.closeActivity();
348         // Don't put full minute as the timeout to give some leeway with test timing/processing.
349         assertFalse("Job stopped within grace period after activity closed",
350                 mTestAppInterface.awaitJobStop(55_000L));
351         assertTrue("Job did not stop after grace period ended",
352                 mTestAppInterface.awaitJobStop(15_000L));
353         assertEquals(JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
354                 mTestAppInterface.getLastParams().getStopReason());
355     }
356 
357     @Test
testRestrictedEJAllowedWhenAutoRestrictedBucketFeatureOn()358     public void testRestrictedEJAllowedWhenAutoRestrictedBucketFeatureOn() throws Exception {
359         setTestPackageRestricted(true);
360         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
361         assertFalse("Job started for restricted app",
362                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
363 
364         toggleAutoRestrictedBucketOnBgRestricted(true);
365         assertTrue("Job did not start when app was background unrestricted",
366                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
367     }
368 
369     @Test
testBackgroundRegJobsThermal()370     public void testBackgroundRegJobsThermal() throws Exception {
371         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, false);
372         runJob();
373         assertTrue("Job did not start after scheduling",
374                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
375 
376         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_LIGHT);
377         assertFalse("Job stopped below thermal throttling threshold",
378                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
379 
380         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_SEVERE);
381         assertTrue("Job did not stop on thermal throttling",
382                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
383         final long jobStopTime = System.currentTimeMillis();
384 
385         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_CRITICAL);
386         runJob();
387         assertFalse("Job started above thermal throttling threshold",
388                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
389 
390         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_EMERGENCY);
391         runJob();
392         assertFalse("Job started above thermal throttling threshold",
393                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
394 
395         Thread.sleep(Math.max(0, TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF
396                 - (System.currentTimeMillis() - jobStopTime)));
397         ThermalUtils.overrideThermalNotThrottling();
398         runJob();
399         assertTrue("Job did not start back from throttling",
400                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
401     }
402 
403     @Test
testBackgroundEJsThermal()404     public void testBackgroundEJsThermal() throws Exception {
405         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, true);
406         runJob();
407         assertTrue("Job did not start after scheduling",
408                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
409 
410         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_MODERATE);
411         assertFalse("Job stopped below thermal throttling threshold",
412                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
413 
414         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_SEVERE);
415         assertTrue("Job did not stop on thermal throttling",
416                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
417         final long jobStopTime = System.currentTimeMillis();
418 
419         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_CRITICAL);
420         runJob();
421         assertFalse("Job started above thermal throttling threshold",
422                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
423 
424         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_EMERGENCY);
425         runJob();
426         assertFalse("Job started above thermal throttling threshold",
427                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
428 
429         Thread.sleep(Math.max(0, TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF
430                 - (System.currentTimeMillis() - jobStopTime)));
431         ThermalUtils.overrideThermalNotThrottling();
432         runJob();
433         assertTrue("Job did not start back from throttling",
434                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
435     }
436 
437     @Test
testForegroundJobsThermal()438     public void testForegroundJobsThermal() throws Exception {
439         // Turn the screen on to ensure the app gets into the TOP state.
440         setScreenState(true);
441         mTestAppInterface.startAndKeepTestActivity(true);
442         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, false);
443         runJob();
444         assertTrue("Job did not start after scheduling",
445                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
446 
447         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_MODERATE);
448         assertFalse("Job stopped below thermal throttling threshold",
449                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
450 
451         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_SEVERE);
452         assertFalse("Job stopped despite being TOP app",
453                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
454 
455         ThermalUtils.overrideThermalStatus(Temperature.THROTTLING_CRITICAL);
456         assertFalse("Job stopped despite being TOP app",
457                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
458     }
459 
460     /** Tests that apps in the RESTRICTED bucket still get their one parole session per day. */
461     @Test
testJobsInRestrictedBucket_ParoleSession()462     public void testJobsInRestrictedBucket_ParoleSession() throws Exception {
463         assumeTrue("app standby not enabled", mAppStandbyEnabled);
464         assumeFalse("not testable in automotive device", mAutomotiveDevice);
465         assumeFalse("not testable in leanback device", mLeanbackOnly);
466 
467         setRestrictedBucketEnabled(true);
468 
469         // Disable coalescing
470         mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0");
471 
472         setScreenState(true);
473 
474         setChargingState(false);
475         setTestPackageStandbyBucket(Bucket.RESTRICTED);
476         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
477         sendScheduleJobBroadcast(false);
478         runJob();
479         assertTrue("Parole job didn't start in RESTRICTED bucket",
480                 mTestAppInterface.awaitJobStart(3_000));
481 
482         sendScheduleJobBroadcast(false);
483         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
484     }
485 
486     /**
487      * Tests that apps in the RESTRICTED bucket have their parole sessions properly counted even
488      * when charging (but not idle).
489      */
490     @Test
testJobsInRestrictedBucket_CorrectParoleWhileCharging()491     public void testJobsInRestrictedBucket_CorrectParoleWhileCharging() throws Exception {
492         assumeTrue("app standby not enabled", mAppStandbyEnabled);
493         assumeFalse("not testable in automotive device", mAutomotiveDevice);
494         assumeFalse("not testable in leanback device", mLeanbackOnly);
495 
496         setRestrictedBucketEnabled(true);
497 
498         // Disable coalescing
499         mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0");
500         mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "1");
501 
502         setScreenState(true);
503         setChargingState(true);
504         BatteryUtils.runDumpsysBatterySetLevel(100);
505 
506         setTestPackageStandbyBucket(Bucket.RESTRICTED);
507         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
508         sendScheduleJobBroadcast(false);
509         runJob();
510         assertTrue("Parole job didn't start in RESTRICTED bucket",
511                 mTestAppInterface.awaitJobStart(3_000));
512 
513         sendScheduleJobBroadcast(false);
514         assertFalse("New job started in RESTRICTED bucket after parole used",
515                 mTestAppInterface.awaitJobStart(3_000));
516     }
517 
518     /**
519      * Tests that apps in the RESTRICTED bucket that have used their one parole session per day
520      * don't get to run again until the device is charging + idle.
521      */
522     @Test
testJobsInRestrictedBucket_DeferredUntilFreeResources()523     public void testJobsInRestrictedBucket_DeferredUntilFreeResources() throws Exception {
524         assumeTrue("app standby not enabled", mAppStandbyEnabled);
525         assumeFalse("not testable in automotive device", mAutomotiveDevice);
526         assumeFalse("not testable in leanback device", mLeanbackOnly);
527 
528         setRestrictedBucketEnabled(true);
529 
530         // Disable coalescing
531         mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0");
532 
533         setScreenState(true);
534 
535         setChargingState(false);
536         setTestPackageStandbyBucket(Bucket.RESTRICTED);
537         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
538         sendScheduleJobBroadcast(false);
539         runJob();
540         assertTrue("Parole job didn't start in RESTRICTED bucket",
541                 mTestAppInterface.awaitJobStart(3_000));
542 
543         sendScheduleJobBroadcast(false);
544         assertFalse("New job started in RESTRICTED bucket after parole used",
545                 mTestAppInterface.awaitJobStart(3_000));
546 
547         setChargingState(true);
548         BatteryUtils.runDumpsysBatterySetLevel(100);
549         assertFalse("New job started in RESTRICTED bucket after parole when charging but not idle",
550                 mTestAppInterface.awaitJobStart(3_000));
551 
552         setScreenState(false);
553         triggerJobIdle();
554         assertTrue("Job didn't start in RESTRICTED bucket when charging + idle",
555                 mTestAppInterface.awaitJobStart(3_000));
556 
557         // Make sure job can be stopped and started again when charging + idle
558         sendScheduleJobBroadcast(false);
559         runJob();
560         assertTrue("Job didn't restart in RESTRICTED bucket when charging + idle",
561                 mTestAppInterface.awaitJobStart(3_000));
562     }
563 
564     @Test
testJobsInRestrictedBucket_NoRequiredNetwork()565     public void testJobsInRestrictedBucket_NoRequiredNetwork() throws Exception {
566         assumeTrue("app standby not enabled", mAppStandbyEnabled);
567         assumeFalse("not testable in automotive device", mAutomotiveDevice);
568         assumeFalse("not testable in leanback device", mLeanbackOnly);
569 
570         setRestrictedBucketEnabled(true);
571 
572         // Disable coalescing and the parole session
573         mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0");
574         mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0");
575 
576         mNetworkingHelper.setAllNetworksEnabled(false);
577         setScreenState(true);
578 
579         setChargingState(false);
580         setTestPackageStandbyBucket(Bucket.RESTRICTED);
581         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
582         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, false);
583         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
584 
585         // Slowly add back required bucket constraints.
586 
587         // Battery charging and high.
588         setChargingState(true);
589         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
590         BatteryUtils.runDumpsysBatterySetLevel(100);
591         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
592 
593         // Device is idle.
594         setScreenState(false);
595         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
596         triggerJobIdle();
597         assertTrue("New job didn't start in RESTRICTED bucket",
598                 mTestAppInterface.awaitJobStart(3_000));
599     }
600 
601     @RequiresDevice // Emulators don't always have access to wifi/network
602     @Test
testJobsInRestrictedBucket_WithRequiredNetwork()603     public void testJobsInRestrictedBucket_WithRequiredNetwork() throws Exception {
604         assumeTrue("app standby not enabled", mAppStandbyEnabled);
605         assumeFalse("not testable in automotive device", mAutomotiveDevice);
606         assumeFalse("not testable in leanback device", mLeanbackOnly);
607         assumeFalse("not testable, since ethernet is connected", hasEthernetConnection());
608         assumeTrue(mNetworkingHelper.hasWifiFeature());
609         mNetworkingHelper.ensureSavedWifiNetwork();
610 
611         setRestrictedBucketEnabled(true);
612 
613         // Disable coalescing and the parole session
614         mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0");
615         mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0");
616 
617         mNetworkingHelper.setAllNetworksEnabled(false);
618         setScreenState(true);
619 
620         setChargingState(false);
621         setTestPackageStandbyBucket(Bucket.RESTRICTED);
622         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
623         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false);
624         runJob();
625         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
626 
627         // Slowly add back required bucket constraints.
628 
629         // Battery charging and high.
630         setChargingState(true);
631         runJob();
632         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
633         BatteryUtils.runDumpsysBatterySetLevel(100);
634         runJob();
635         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
636 
637         // Device is idle.
638         setScreenState(false);
639         runJob();
640         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
641         triggerJobIdle();
642         runJob();
643         assertFalse("New job started in RESTRICTED bucket", mTestAppInterface.awaitJobStart(3_000));
644 
645         // Add network
646         mNetworkingHelper.setAllNetworksEnabled(true);
647         mNetworkingHelper.setWifiMeteredState(false);
648         runJob();
649         assertTrue("New job didn't start in RESTRICTED bucket",
650                 mTestAppInterface.awaitJobStart(5_000));
651     }
652 
653     @Test
testJobsInNeverApp()654     public void testJobsInNeverApp() throws Exception {
655         assumeTrue("app standby not enabled", mAppStandbyEnabled);
656         assumeFalse("not testable in automotive device", mAutomotiveDevice);
657         assumeFalse("not testable in leanback device", mLeanbackOnly);
658 
659         setChargingState(false);
660         setTestPackageStandbyBucket(Bucket.NEVER);
661         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
662         sendScheduleJobBroadcast(false);
663         assertFalse("New job started in NEVER bucket", mTestAppInterface.awaitJobStart(3_000));
664     }
665 
666     @Test
testUidActiveBypassesStandby()667     public void testUidActiveBypassesStandby() throws Exception {
668         assumeFalse("not testable in automotive device", mAutomotiveDevice);
669         assumeFalse("not testable in leanback device", mLeanbackOnly);
670 
671         setChargingState(false);
672         setTestPackageStandbyBucket(Bucket.NEVER);
673         tempWhitelistTestApp(6_000);
674         Thread.sleep(DEFAULT_WAIT_TIMEOUT);
675         sendScheduleJobBroadcast(false);
676         assertTrue("New job in uid-active app failed to start in NEVER standby",
677                 mTestAppInterface.awaitJobStart(4_000));
678     }
679 
680     @Test
testBatterySaverOff()681     public void testBatterySaverOff() throws Exception {
682         assumeFalse("not testable in automotive device", mAutomotiveDevice);
683         assumeFalse("not testable in leanback device", mLeanbackOnly);
684 
685         BatteryUtils.assumeBatterySaverFeature();
686 
687         setChargingState(false);
688         BatteryUtils.enableBatterySaver(false);
689         sendScheduleJobBroadcast(false);
690         assertTrue("New job failed to start with battery saver OFF",
691                 mTestAppInterface.awaitJobStart(3_000));
692     }
693 
694     @Test
testBatterySaverOn()695     public void testBatterySaverOn() throws Exception {
696         assumeFalse("not testable in automotive device", mAutomotiveDevice);
697         assumeFalse("not testable in leanback device", mLeanbackOnly);
698 
699         BatteryUtils.assumeBatterySaverFeature();
700 
701         setChargingState(false);
702         BatteryUtils.enableBatterySaver(true);
703         sendScheduleJobBroadcast(false);
704         assertFalse("New job started with battery saver ON",
705                 mTestAppInterface.awaitJobStart(3_000));
706     }
707 
708     @Test
testUidActiveBypassesBatterySaverOn()709     public void testUidActiveBypassesBatterySaverOn() throws Exception {
710         assumeFalse("not testable in automotive device", mAutomotiveDevice);
711         assumeFalse("not testable in leanback device", mLeanbackOnly);
712 
713         BatteryUtils.assumeBatterySaverFeature();
714 
715         setChargingState(false);
716         BatteryUtils.enableBatterySaver(true);
717         tempWhitelistTestApp(6_000);
718         sendScheduleJobBroadcast(false);
719         assertTrue("New job in uid-active app failed to start with battery saver ON",
720                 mTestAppInterface.awaitJobStart(3_000));
721     }
722 
723     @Test
testBatterySaverOnThenUidActive()724     public void testBatterySaverOnThenUidActive() throws Exception {
725         assumeFalse("not testable in automotive device", mAutomotiveDevice);
726         assumeFalse("not testable in leanback device", mLeanbackOnly);
727 
728         BatteryUtils.assumeBatterySaverFeature();
729 
730         // Enable battery saver, and schedule a job. It shouldn't run.
731         setChargingState(false);
732         BatteryUtils.enableBatterySaver(true);
733         sendScheduleJobBroadcast(false);
734         assertFalse("New job started with battery saver ON",
735                 mTestAppInterface.awaitJobStart(3_000));
736 
737         // Then make the UID active. Now the job should run.
738         tempWhitelistTestApp(120_000);
739         assertTrue("New job in uid-active app failed to start with battery saver OFF",
740                 mTestAppInterface.awaitJobStart(120_000));
741     }
742 
743     @Test
testExpeditedJobBypassesBatterySaverOn()744     public void testExpeditedJobBypassesBatterySaverOn() throws Exception {
745         assumeFalse("not testable in automotive device", mAutomotiveDevice);
746         assumeFalse("not testable in leanback device", mLeanbackOnly);
747 
748         BatteryUtils.assumeBatterySaverFeature();
749 
750         setChargingState(false);
751         BatteryUtils.enableBatterySaver(true);
752         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
753         assertTrue("New expedited job failed to start with battery saver ON",
754                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
755     }
756 
757     @Test
testExpeditedJobBypassesBatterySaver_toggling()758     public void testExpeditedJobBypassesBatterySaver_toggling() throws Exception {
759         assumeFalse("not testable in automotive device", mAutomotiveDevice);
760         assumeFalse("not testable in leanback device", mLeanbackOnly);
761 
762         BatteryUtils.assumeBatterySaverFeature();
763 
764         setChargingState(false);
765         BatteryUtils.enableBatterySaver(false);
766         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
767         assertTrue("New expedited job failed to start with battery saver ON",
768                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
769         BatteryUtils.enableBatterySaver(true);
770         assertFalse("Job stopped when battery saver turned on",
771                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
772     }
773 
774     @Test
testExpeditedJobBypassesDeviceIdle()775     public void testExpeditedJobBypassesDeviceIdle() throws Exception {
776         assumeTrue("device idle not enabled", mDeviceIdleEnabled);
777 
778         toggleDozeState(true);
779         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
780         runJob();
781         assertTrue("Job did not start after scheduling",
782                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
783     }
784 
785     @Test
testExpeditedJobBypassesDeviceIdle_toggling()786     public void testExpeditedJobBypassesDeviceIdle_toggling() throws Exception {
787         assumeTrue("device idle not enabled", mDeviceIdleEnabled);
788 
789         toggleDozeState(false);
790         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
791         runJob();
792         assertTrue("Job did not start after scheduling",
793                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
794         toggleDozeState(true);
795         assertFalse("Job stopped when device enabled turned on",
796                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
797     }
798 
799     @Test
testExpeditedJobDeferredAfterTimeoutInDoze()800     public void testExpeditedJobDeferredAfterTimeoutInDoze() throws Exception {
801         assumeTrue("device idle not enabled", mDeviceIdleEnabled);
802         // Intentionally set a value below 1 minute to ensure the range checks work.
803         mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(30_000L));
804 
805         toggleDozeState(true);
806         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
807         runJob();
808         assertTrue("Job did not start after scheduling",
809                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
810         // Don't put full minute as the timeout to give some leeway with test timing/processing.
811         assertFalse("Job stopped before min runtime limit",
812                 mTestAppInterface.awaitJobStop(55_000L));
813         assertTrue("Job did not stop after timeout", mTestAppInterface.awaitJobStop(15_000L));
814         assertEquals(JobParameters.STOP_REASON_DEVICE_STATE,
815                 mTestAppInterface.getLastParams().getStopReason());
816         // Should be rescheduled.
817         assertJobNotReady();
818         assertJobWaiting();
819         Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF);
820         runJob();
821         assertFalse("Job started after timing out in Doze",
822                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
823 
824         // Should start when Doze is turned off.
825         toggleDozeState(false);
826         assertTrue("Job did not start after Doze turned off",
827                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
828     }
829 
830     @Test
testExpeditedJobDeferredAfterTimeoutInBatterySaver()831     public void testExpeditedJobDeferredAfterTimeoutInBatterySaver() throws Exception {
832         BatteryUtils.assumeBatterySaverFeature();
833 
834         // Intentionally set a value below 1 minute to ensure the range checks work.
835         mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(47_000L));
836 
837         setChargingState(false);
838         BatteryUtils.enableBatterySaver(true);
839         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
840         runJob();
841         assertTrue("Job did not start after scheduling",
842                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
843         // Don't put full minute as the timeout to give some leeway with test timing/processing.
844         assertFalse("Job stopped before min runtime limit",
845                 mTestAppInterface.awaitJobStop(55_000L));
846         assertTrue("Job did not stop after timeout", mTestAppInterface.awaitJobStop(15_000L));
847         assertEquals(JobParameters.STOP_REASON_DEVICE_STATE,
848                 mTestAppInterface.getLastParams().getStopReason());
849         // Should be rescheduled.
850         assertJobNotReady();
851         assertJobWaiting();
852         Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF);
853         runJob();
854         assertFalse("Job started after timing out in battery saver",
855                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
856 
857         // Should start when battery saver is turned off.
858         BatteryUtils.enableBatterySaver(false);
859         assertTrue("Job did not start after battery saver turned off",
860                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
861     }
862 
863     @Test
testExpeditedJobDeferredAfterTimeout_DozeAndBatterySaver()864     public void testExpeditedJobDeferredAfterTimeout_DozeAndBatterySaver() throws Exception {
865         BatteryUtils.assumeBatterySaverFeature();
866         assumeTrue("device idle not enabled", mDeviceIdleEnabled);
867         mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(60_000L));
868 
869         setChargingState(false);
870         toggleDozeState(true);
871         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
872         runJob();
873         assertTrue("Job did not start after scheduling",
874                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
875         // Don't put full minute as the timeout to give some leeway with test timing/processing.
876         assertFalse("Job stopped before min runtime limit",
877                 mTestAppInterface.awaitJobStop(55_000L));
878         assertTrue("Job did not stop after timeout", mTestAppInterface.awaitJobStop(15_000L));
879         assertEquals(JobParameters.STOP_REASON_DEVICE_STATE,
880                 mTestAppInterface.getLastParams().getStopReason());
881         // Should be rescheduled.
882         assertJobNotReady();
883         assertJobWaiting();
884         // Battery saver kicks in before Doze ends. Job shouldn't start while BS is on.
885         BatteryUtils.enableBatterySaver(true);
886         toggleDozeState(false);
887         Thread.sleep(TestJobSchedulerReceiver.JOB_INITIAL_BACKOFF);
888         runJob();
889         assertFalse("Job started while power restrictions active after timing out",
890                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
891 
892         // Should start when battery saver is turned off.
893         BatteryUtils.enableBatterySaver(false);
894         assertTrue("Job did not start after power restrictions turned off",
895                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
896     }
897 
898     @Test
testLongExpeditedJobStoppedByDoze()899     public void testLongExpeditedJobStoppedByDoze() throws Exception {
900         assumeTrue("device idle not enabled", mDeviceIdleEnabled);
901         // Intentionally set a value below 1 minute to ensure the range checks work.
902         mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(59_000L));
903 
904         toggleDozeState(false);
905         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
906         runJob();
907         assertTrue("Job did not start after scheduling",
908                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
909         // Should get to run past min runtime.
910         assertFalse("Job stopped after min runtime", mTestAppInterface.awaitJobStop(90_000L));
911 
912         // Should stop when Doze is turned on.
913         toggleDozeState(true);
914         assertTrue("Job did not stop after Doze turned on",
915                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
916         assertEquals(JobParameters.STOP_REASON_DEVICE_STATE,
917                 mTestAppInterface.getLastParams().getStopReason());
918     }
919 
920     @Test
testLongExpeditedJobStoppedByBatterySaver()921     public void testLongExpeditedJobStoppedByBatterySaver() throws Exception {
922         BatteryUtils.assumeBatterySaverFeature();
923 
924         // Intentionally set a value below 1 minute to ensure the range checks work.
925         mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", Long.toString(0L));
926 
927         setChargingState(false);
928         BatteryUtils.enableBatterySaver(false);
929         mTestAppInterface.scheduleJob(false, JobInfo.NETWORK_TYPE_NONE, true);
930         runJob();
931         assertTrue("Job did not start after scheduling",
932                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
933         // Should get to run past min runtime.
934         assertFalse("Job stopped after runtime", mTestAppInterface.awaitJobStop(90_000L));
935 
936         // Should stop when battery saver is turned on.
937         BatteryUtils.enableBatterySaver(true);
938         assertTrue("Job did not stop after battery saver turned on",
939                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
940         assertEquals(JobParameters.STOP_REASON_DEVICE_STATE,
941                 mTestAppInterface.getLastParams().getStopReason());
942     }
943 
944     @Test
testRestrictingStopReason_RestrictedBucket()945     public void testRestrictingStopReason_RestrictedBucket() throws Exception {
946         assumeTrue("app standby not enabled", mAppStandbyEnabled);
947         assumeFalse("not testable in automotive device", mAutomotiveDevice);
948         assumeFalse("not testable in leanback device", mLeanbackOnly);
949 
950         assumeTrue(BatteryUtils.hasBattery());
951         assumeTrue(mNetworkingHelper.hasWifiFeature());
952         mNetworkingHelper.ensureSavedWifiNetwork();
953 
954         setRestrictedBucketEnabled(true);
955         setTestPackageStandbyBucket(Bucket.RESTRICTED);
956 
957         // Disable coalescing and the parole session
958         mDeviceConfigStateHelper.set("qc_timing_session_coalescing_duration_ms", "0");
959         mDeviceConfigStateHelper.set("qc_max_session_count_restricted", "0");
960 
961         // Satisfy all additional constraints.
962         mNetworkingHelper.setAllNetworksEnabled(true);
963         mNetworkingHelper.setWifiMeteredState(false);
964         setChargingState(true);
965         BatteryUtils.runDumpsysBatterySetLevel(100);
966         setScreenState(false);
967         triggerJobIdle();
968 
969         // Toggle individual constraints
970 
971         // Connectivity
972         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false);
973         runJob();
974         assertTrue("New job didn't start in RESTRICTED bucket",
975                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
976         mNetworkingHelper.setAllNetworksEnabled(false);
977         assertTrue("New job didn't stop when connectivity dropped",
978                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
979         assertEquals(JobParameters.STOP_REASON_CONSTRAINT_CONNECTIVITY,
980                 mTestAppInterface.getLastParams().getStopReason());
981         mNetworkingHelper.setAllNetworksEnabled(true);
982 
983         // Idle
984         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false);
985         runJob();
986         assertTrue("New job didn't start in RESTRICTED bucket",
987                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
988         setScreenState(true);
989         assertTrue("New job didn't stop when device no longer idle",
990                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
991         assertEquals(JobParameters.STOP_REASON_APP_STANDBY,
992                 mTestAppInterface.getLastParams().getStopReason());
993         setScreenState(false);
994         triggerJobIdle();
995 
996         // Charging
997         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false);
998         runJob();
999         assertTrue("New job didn't start in RESTRICTED bucket",
1000                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
1001         setChargingState(false);
1002         assertTrue("New job didn't stop when device no longer charging",
1003                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
1004         assertEquals(JobParameters.STOP_REASON_APP_STANDBY,
1005                 mTestAppInterface.getLastParams().getStopReason());
1006         setChargingState(true);
1007         BatteryUtils.runDumpsysBatterySetLevel(100);
1008 
1009         // Battery not low
1010         setScreenState(false);
1011         triggerJobIdle();
1012         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_ANY, false);
1013         runJob();
1014         assertTrue("New job didn't start in RESTRICTED bucket",
1015                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
1016         BatteryUtils.runDumpsysBatterySetLevel(1);
1017         assertTrue("New job didn't stop when battery too low",
1018                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
1019         assertEquals(JobParameters.STOP_REASON_APP_STANDBY,
1020                 mTestAppInterface.getLastParams().getStopReason());
1021     }
1022 
1023     @Test
testRestrictingStopReason_Quota()1024     public void testRestrictingStopReason_Quota() throws Exception {
1025         assumeTrue("app standby not enabled", mAppStandbyEnabled);
1026         assumeFalse("not testable in automotive device", mAutomotiveDevice); // Test needs battery
1027         assumeFalse("not testable in leanback device", mLeanbackOnly); // Test needs battery
1028 
1029         // Reduce allowed time for testing.
1030         mDeviceConfigStateHelper.set("qc_allowed_time_per_period_rare_ms", "60000");
1031         setChargingState(false);
1032         setTestPackageStandbyBucket(Bucket.RARE);
1033 
1034         sendScheduleJobBroadcast(false);
1035         runJob();
1036         assertTrue("New job didn't start",
1037                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
1038 
1039         Thread.sleep(60000);
1040 
1041         assertTrue("New job didn't stop after using up quota",
1042                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
1043         assertEquals(JobParameters.STOP_REASON_QUOTA,
1044                 mTestAppInterface.getLastParams().getStopReason());
1045     }
1046 
1047     /*
1048     Tests currently disabled because they require changes inside the framework to lower the minimum
1049     EJ quota to one minute (from 5 minutes).
1050     TODO(224533485): make JS testable enough to enable these tests
1051 
1052     @Test
1053     public void testRestrictingStopReason_ExpeditedQuota_startOnCharging() throws Exception {
1054         assumeTrue("app standby not enabled", mAppStandbyEnabled);
1055         assumeFalse("not testable in automotive device", mAutomotiveDevice); // Test needs battery
1056         assumeFalse("not testable in leanback device", mLeanbackOnly); // Test needs battery
1057 
1058         // Reduce allowed time for testing. System to cap the time above 30 seconds.
1059         mDeviceConfigStateHelper.set("qc_ej_limit_rare_ms", "30000");
1060         mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", "30000");
1061         // Start with charging so JobScheduler thinks the job can run for the maximum amount of
1062         // time. We turn off charging later so quota clearly comes into effect.
1063         setChargingState(true);
1064         setTestPackageStandbyBucket(Bucket.RARE);
1065 
1066         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, true);
1067         runJob();
1068         assertTrue("New job didn't start",
1069                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
1070         assertTrue(mTestAppInterface.getLastParams().isExpeditedJob());
1071         setChargingState(false);
1072 
1073         assertFalse("Job stopped before using up quota",
1074                 mTestAppInterface.awaitJobStop(45_000));
1075         Thread.sleep(15_000);
1076 
1077         assertTrue("Job didn't stop after using up quota",
1078                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
1079         assertEquals(JobParameters.STOP_REASON_QUOTA,
1080                 mTestAppInterface.getLastParams().getStopReason());
1081     }
1082 
1083     @Test
1084     public void testRestrictingStopReason_ExpeditedQuota_noCharging() throws Exception {
1085         assumeTrue("app standby not enabled", mAppStandbyEnabled);
1086         assumeFalse("not testable in automotive device", mAutomotiveDevice); // Test needs battery
1087         assumeFalse("not testable in leanback device", mLeanbackOnly); // Test needs battery
1088 
1089         // Reduce allowed time for testing.
1090         mDeviceConfigStateHelper.set("qc_ej_limit_rare_ms", "30000");
1091         mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", "30000");
1092         setChargingState(false);
1093         setTestPackageStandbyBucket(Bucket.RARE);
1094 
1095         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, true);
1096         runJob();
1097         assertTrue("New job didn't start",
1098                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
1099         assertTrue(mTestAppInterface.getLastParams().isExpeditedJob());
1100 
1101         assertFalse("Job stopped before using up quota",
1102                 mTestAppInterface.awaitJobStop(45_000));
1103         Thread.sleep(15_000);
1104 
1105         assertTrue("Job didn't stop after using up quota",
1106                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
1107         // Charging state was false when the job started, so the trigger the timeout before
1108         // QuotaController officially marks the quota finished.
1109         final int stopReason = mTestAppInterface.getLastParams().getStopReason();
1110         assertTrue(stopReason == JobParameters.STOP_REASON_TIMEOUT
1111                 || stopReason == JobParameters.STOP_REASON_QUOTA);
1112     }
1113      */
1114 
1115     @Test
testRestrictingStopReason_BatterySaver()1116     public void testRestrictingStopReason_BatterySaver() throws Exception {
1117         BatteryUtils.assumeBatterySaverFeature();
1118 
1119         setChargingState(false);
1120         BatteryUtils.enableBatterySaver(false);
1121         sendScheduleJobBroadcast(false);
1122         runJob();
1123         assertTrue("Job did not start after scheduling",
1124                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
1125 
1126         BatteryUtils.enableBatterySaver(true);
1127         assertTrue("Job did not stop on entering battery saver",
1128                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
1129         assertEquals(JobParameters.STOP_REASON_DEVICE_STATE,
1130                 mTestAppInterface.getLastParams().getStopReason());
1131     }
1132 
1133     @Test
testRestrictingStopReason_Doze()1134     public void testRestrictingStopReason_Doze() throws Exception {
1135         assumeTrue("device idle not enabled", mDeviceIdleEnabled);
1136 
1137         toggleDozeState(false);
1138         mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, false);
1139         runJob();
1140         assertTrue("Job did not start after scheduling",
1141                 mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
1142 
1143         toggleDozeState(true);
1144         assertTrue("Job did not stop on entering doze",
1145                 mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
1146         assertEquals(JobParameters.STOP_REASON_DEVICE_STATE,
1147                 mTestAppInterface.getLastParams().getStopReason());
1148     }
1149 
1150     @After
tearDown()1151     public void tearDown() throws Exception {
1152         AppOpsUtils.reset(TEST_APP_PACKAGE);
1153         // Lock thermal service to not throttling
1154         ThermalUtils.overrideThermalNotThrottling();
1155         if (mDeviceIdleEnabled) {
1156             toggleDozeState(false);
1157         }
1158         mTestAppInterface.cleanup();
1159         mUiDevice.executeShellCommand("cmd jobscheduler monitor-battery off");
1160         BatteryUtils.runDumpsysBatteryReset();
1161         BatteryUtils.enableBatterySaver(false);
1162         Settings.Global.putString(mContext.getContentResolver(),
1163                 Settings.Global.BATTERY_STATS_CONSTANTS, mInitialBatteryStatsConstants);
1164         removeTestAppFromTempWhitelist();
1165 
1166         mNetworkingHelper.tearDown();
1167         mDeviceConfigStateHelper.restoreOriginalValues();
1168         mActivityManagerDeviceConfigStateHelper.restoreOriginalValues();
1169         Settings.Global.putString(mContext.getContentResolver(),
1170                 Settings.Global.ENABLE_RESTRICTED_BUCKET, mInitialRestrictedBucketEnabled);
1171 
1172         mUiDevice.executeShellCommand(
1173                 "cmd jobscheduler reset-execution-quota -u " + UserHandle.myUserId()
1174                         + " " + TEST_APP_PACKAGE);
1175 
1176         Settings.System.putString(
1177                 mContext.getContentResolver(), SCREEN_OFF_TIMEOUT, mInitialDisplayTimeout);
1178     }
1179 
setTestPackageRestricted(boolean restricted)1180     private void setTestPackageRestricted(boolean restricted) throws Exception {
1181         AppOpsUtils.setOpMode(TEST_APP_PACKAGE, "RUN_ANY_IN_BACKGROUND",
1182                 restricted ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED);
1183     }
1184 
setRestrictedBucketEnabled(boolean enabled)1185     private void setRestrictedBucketEnabled(boolean enabled) {
1186         Settings.Global.putString(mContext.getContentResolver(),
1187                 Settings.Global.ENABLE_RESTRICTED_BUCKET, enabled ? "1" : "0");
1188     }
1189 
toggleAutoRestrictedBucketOnBgRestricted(boolean enable)1190     private void toggleAutoRestrictedBucketOnBgRestricted(boolean enable) {
1191         mActivityManagerDeviceConfigStateHelper.set("bg_auto_restricted_bucket_on_bg_restricted",
1192                 Boolean.toString(enable));
1193     }
1194 
isTestAppTempWhitelisted()1195     private boolean isTestAppTempWhitelisted() throws Exception {
1196         final String output = mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist").trim();
1197         for (String line : output.split("\n")) {
1198             if (line.contains("UID=" + mTestPackageUid)) {
1199                 return true;
1200             }
1201         }
1202         return false;
1203     }
1204 
sendScheduleJobBroadcast(boolean allowWhileIdle)1205     private void sendScheduleJobBroadcast(boolean allowWhileIdle) throws Exception {
1206         mTestAppInterface.scheduleJob(allowWhileIdle, NETWORK_TYPE_NONE, false);
1207     }
1208 
toggleDozeState(final boolean idle)1209     private void toggleDozeState(final boolean idle) throws Exception {
1210         mUiDevice.executeShellCommand("cmd deviceidle " + (idle ? "force-idle" : "unforce"));
1211         if (!idle) {
1212             // Make sure the device doesn't stay idle, even after unforcing.
1213             mUiDevice.executeShellCommand("cmd deviceidle motion");
1214         }
1215         assertTrue("Could not change device idle state to " + idle,
1216                 waitUntilTrue(SHELL_TIMEOUT, () -> {
1217                     synchronized (JobThrottlingTest.this) {
1218                         return mDeviceInDoze == idle;
1219                     }
1220                 }));
1221     }
1222 
tempWhitelistTestApp(long duration)1223     private void tempWhitelistTestApp(long duration) throws Exception {
1224         mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist -d " + duration
1225                 + " " + TEST_APP_PACKAGE);
1226     }
1227 
makeTestPackageIdle()1228     private void makeTestPackageIdle() throws Exception {
1229         mUiDevice.executeShellCommand("am make-uid-idle --user current " + TEST_APP_PACKAGE);
1230     }
1231 
setTestPackageStandbyBucket(Bucket bucket)1232     void setTestPackageStandbyBucket(Bucket bucket) throws Exception {
1233         setTestPackageStandbyBucket(mUiDevice, bucket);
1234     }
1235 
setTestPackageStandbyBucket(UiDevice uiDevice, Bucket bucket)1236     static void setTestPackageStandbyBucket(UiDevice uiDevice, Bucket bucket) throws Exception {
1237         final String bucketName;
1238         switch (bucket) {
1239             case ACTIVE:
1240                 bucketName = "active";
1241                 break;
1242             case WORKING_SET:
1243                 bucketName = "working";
1244                 break;
1245             case FREQUENT:
1246                 bucketName = "frequent";
1247                 break;
1248             case RARE:
1249                 bucketName = "rare";
1250                 break;
1251             case RESTRICTED:
1252                 bucketName = "restricted";
1253                 break;
1254             case NEVER:
1255                 bucketName = "never";
1256                 break;
1257             default:
1258                 throw new IllegalArgumentException("Requested unknown bucket " + bucket);
1259         }
1260         uiDevice.executeShellCommand("am set-standby-bucket " + TEST_APP_PACKAGE
1261                 + " " + bucketName);
1262     }
1263 
removeTestAppFromTempWhitelist()1264     private boolean removeTestAppFromTempWhitelist() throws Exception {
1265         mUiDevice.executeShellCommand("cmd deviceidle tempwhitelist -r " + TEST_APP_PACKAGE);
1266         return waitUntilTrue(SHELL_TIMEOUT, () -> !isTestAppTempWhitelisted());
1267     }
1268 
1269     /**
1270      * Set the screen state.
1271      */
setScreenState(boolean on)1272     private void setScreenState(boolean on) throws Exception {
1273         if (on) {
1274             mUiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
1275             mUiDevice.executeShellCommand("wm dismiss-keyguard");
1276         } else {
1277             mUiDevice.executeShellCommand("input keyevent KEYCODE_SLEEP");
1278         }
1279         // Wait a little bit to make sure the screen state has changed.
1280         Thread.sleep(4_000);
1281     }
1282 
setChargingState(boolean isCharging)1283     private void setChargingState(boolean isCharging) throws Exception {
1284         mUiDevice.executeShellCommand("cmd jobscheduler monitor-battery on");
1285 
1286         final String command;
1287         if (isCharging) {
1288             mUiDevice.executeShellCommand("cmd battery set ac 1");
1289             final int curLevel = Integer.parseInt(
1290                     mUiDevice.executeShellCommand("dumpsys battery get level").trim());
1291             command = "cmd battery set -f level " + Math.min(100, curLevel + 1);
1292         } else {
1293             command = "cmd battery unplug -f";
1294         }
1295         int seq = Integer.parseInt(mUiDevice.executeShellCommand(command).trim());
1296 
1297         // Wait for the battery update to be processed by job scheduler before proceeding.
1298         waitUntil("JobScheduler didn't update charging status to " + isCharging, 15 /* seconds */,
1299                 () -> {
1300                     int curSeq;
1301                     boolean curCharging;
1302                     curSeq = Integer.parseInt(mUiDevice.executeShellCommand(
1303                             "cmd jobscheduler get-battery-seq").trim());
1304                     curCharging = Boolean.parseBoolean(mUiDevice.executeShellCommand(
1305                             "cmd jobscheduler get-battery-charging").trim());
1306                     return curSeq >= seq && curCharging == isCharging;
1307                 });
1308     }
1309 
1310     /**
1311      * Trigger job idle (not device idle);
1312      */
triggerJobIdle()1313     private void triggerJobIdle() throws Exception {
1314         mUiDevice.executeShellCommand("cmd activity idle-maintenance");
1315         // Wait a moment to let that happen before proceeding.
1316         Thread.sleep(2_000);
1317     }
1318 
1319     /** Asks (not forces) JobScheduler to run the job if constraints are met. */
runJob()1320     private void runJob() throws Exception {
1321         // Since connectivity is a functional constraint, calling the "run" command without force
1322         // will only get the job to run if the constraint is satisfied.
1323         mUiDevice.executeShellCommand("cmd jobscheduler run -s"
1324                 + " -u " + UserHandle.myUserId() + " " + TEST_APP_PACKAGE + " " + mTestJobId);
1325     }
1326 
hasEthernetConnection()1327     private boolean hasEthernetConnection() {
1328         return mNetworkingHelper.hasEthernetConnection();
1329     }
1330 
getJobState()1331     private String getJobState() throws Exception {
1332         return mUiDevice.executeShellCommand("cmd jobscheduler get-job-state --user cur "
1333                 + TEST_APP_PACKAGE + " " + mTestJobId).trim();
1334     }
1335 
assertJobWaiting()1336     private void assertJobWaiting() throws Exception {
1337         String state = getJobState();
1338         assertTrue("Job unexpectedly not waiting, in state: " + state, state.contains("waiting"));
1339     }
1340 
assertJobNotReady()1341     private void assertJobNotReady() throws Exception {
1342         String state = getJobState();
1343         assertFalse("Job unexpectedly ready, in state: " + state, state.contains("ready"));
1344     }
1345 
assertJobReady()1346     private void assertJobReady() throws Exception {
1347         String state = getJobState();
1348         assertTrue("Job unexpectedly not ready, in state: " + state, state.contains("ready"));
1349     }
1350 
waitUntilTrue(long maxWait, Condition condition)1351     private boolean waitUntilTrue(long maxWait, Condition condition) throws Exception {
1352         final long deadLine = SystemClock.uptimeMillis() + maxWait;
1353         do {
1354             Thread.sleep(POLL_INTERVAL);
1355         } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine);
1356         return condition.isTrue();
1357     }
1358 
1359     private interface Condition {
isTrue()1360         boolean isTrue() throws Exception;
1361     }
1362 }
1363