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