• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 android.Manifest;
20 import android.annotation.TargetApi;
21 import android.app.UiModeManager;
22 import android.app.job.JobInfo;
23 import android.content.pm.PackageManager;
24 import android.os.UserHandle;
25 
26 import androidx.test.InstrumentationRegistry;
27 import androidx.test.uiautomator.UiDevice;
28 
29 import com.android.compatibility.common.util.SystemUtil;
30 
31 /**
32  * Make sure the state of {@link android.app.job.JobScheduler} is correct.
33  */
34 public class IdleConstraintTest extends BaseJobSchedulerTest {
35     /** Unique identifier for the job scheduled by this suite of tests. */
36     private static final int STATE_JOB_ID = IdleConstraintTest.class.hashCode();
37     private static final String TAG = "IdleConstraintTest";
38 
39     private JobInfo.Builder mBuilder;
40     private UiDevice mUiDevice;
41 
42     private String mInitialDisplayTimeout;
43 
44     @Override
setUp()45     public void setUp() throws Exception {
46         super.setUp();
47         mBuilder = new JobInfo.Builder(STATE_JOB_ID, kJobServiceComponent);
48         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
49 
50         // Make sure the screen doesn't turn off when the test turns it on.
51         mInitialDisplayTimeout = mUiDevice.executeShellCommand(
52                 "settings get system screen_off_timeout");
53         mUiDevice.executeShellCommand("settings put system screen_off_timeout 300000");
54     }
55 
56     @Override
tearDown()57     public void tearDown() throws Exception {
58         mJobScheduler.cancel(STATE_JOB_ID);
59         // Put device back in to normal operation.
60         toggleScreenOn(true);
61         if (isAutomotiveProjectionSupported()) {
62             setAutomotiveProjection(false);
63         }
64 
65         mUiDevice.executeShellCommand(
66                 "settings put system screen_off_timeout " + mInitialDisplayTimeout);
67 
68         super.tearDown();
69     }
70 
assertJobReady()71     void assertJobReady() throws Exception {
72         assertJobReady(STATE_JOB_ID);
73     }
74 
assertJobWaiting()75     void assertJobWaiting() throws Exception {
76         assertJobWaiting(STATE_JOB_ID);
77     }
78 
assertJobNotReady()79     void assertJobNotReady() throws Exception {
80         assertJobNotReady(STATE_JOB_ID);
81     }
82 
83     /**
84      * Toggle device is dock idle or dock active.
85      */
toggleFakeDeviceDockState(final boolean idle)86     private void toggleFakeDeviceDockState(final boolean idle) throws Exception {
87         mUiDevice.executeShellCommand("cmd jobscheduler trigger-dock-state "
88                 + (idle ? "idle" : "active"));
89         // Wait a moment to let that happen before proceeding.
90         Thread.sleep(2_000);
91     }
92 
93     /**
94      * Performs idle maintenance, will update idle state if screen-on changed.
95      */
triggerIdleMaintenance()96     private void triggerIdleMaintenance() throws Exception {
97         triggerIdleMaintenance(mUiDevice);
98     }
99 
100     /**
101      * Performs idle maintenance, will update idle state if screen-on changed.
102      */
triggerIdleMaintenance(UiDevice uiDevice)103     static void triggerIdleMaintenance(UiDevice uiDevice) throws Exception {
104         uiDevice.executeShellCommand("cmd activity idle-maintenance");
105         // Wait a moment to let that happen before proceeding.
106         Thread.sleep(2_000);
107     }
108 
109     /**
110      * Schedule a job that requires the device is idle, and assert it fired to make
111      * sure the device is idle.
112      */
verifyIdleState()113     void verifyIdleState() throws Exception {
114         kTestEnvironment.setExpectedExecutions(1);
115         kTestEnvironment.setExpectedWaitForRun();
116         mJobScheduler.schedule(mBuilder.setRequiresDeviceIdle(true).build());
117         assertJobReady();
118         kTestEnvironment.readyToRun();
119         runSatisfiedJob();
120 
121         assertTrue("Job with idle constraint did not fire on idle",
122                 kTestEnvironment.awaitExecution());
123     }
124 
125     /**
126      * Schedule a job that requires the device is idle, and assert it failed to make
127      * sure the device is active.
128      */
verifyActiveState()129     void verifyActiveState() throws Exception {
130         kTestEnvironment.setExpectedExecutions(0);
131         kTestEnvironment.setExpectedWaitForRun();
132         mJobScheduler.schedule(mBuilder.setRequiresDeviceIdle(true).build());
133         assertJobWaiting();
134         assertJobNotReady();
135         kTestEnvironment.readyToRun();
136         runSatisfiedJob();
137 
138         assertFalse("Job with idle constraint fired while not on idle.",
139                 kTestEnvironment.awaitExecution(250));
140     }
141 
142     /**
143      * Ensure that device can switch state normally.
144      */
testDeviceChangeIdleActiveState()145     public void testDeviceChangeIdleActiveState() throws Exception {
146         toggleScreenOn(true);
147         verifyActiveState();
148 
149         // Assert device is idle when screen is off for a while.
150         toggleScreenOn(false);
151         triggerIdleMaintenance();
152         verifyIdleState();
153 
154         // Assert device is back to active when screen is on.
155         toggleScreenOn(true);
156         verifyActiveState();
157     }
158 
159     /**
160      * Check if the device is an auto.
161      */
isAutomotive()162     private boolean isAutomotive() {
163         return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
164     }
165 
166     /**
167      * Check if automotive projection is supported.
168      */
isAutomotiveProjectionSupported()169     private boolean isAutomotiveProjectionSupported() {
170         // Auto doesn't support automotive projection.
171         return !isAutomotive();
172     }
173 
174     /**
175      * Check if dock state is supported.
176      */
isDockStateSupported()177     private boolean isDockStateSupported() {
178         final boolean isLeanback = getContext().getPackageManager().hasSystemFeature(
179                 PackageManager.FEATURE_LEANBACK_ONLY);
180 
181         // Auto and Leanback do not support dock state.
182         return !isAutomotive() && !isLeanback;
183     }
184 
185     /**
186      * Ensure that device can switch state on dock normally.
187      */
188     @TargetApi(28)
testScreenOnDeviceOnDockChangeState()189     public void testScreenOnDeviceOnDockChangeState() throws Exception {
190         if (!isDockStateSupported()) {
191             return;
192         }
193         toggleScreenOn(true);
194         verifyActiveState();
195 
196         // Assert device go to idle if user doesn't interact with device for a while.
197         toggleFakeDeviceDockState(true /* idle */);
198         triggerIdleMaintenance();
199         verifyIdleState();
200 
201         // Assert device go back to active if user interacts with device.
202         toggleFakeDeviceDockState(false /* active */);
203         verifyActiveState();
204     }
205 
206     /**
207      *  Ensure that the tracker ignores this dock intent during screen off.
208      */
209     @TargetApi(28)
testScreenOffDeviceOnDockNoChangeState()210     public void testScreenOffDeviceOnDockNoChangeState() throws Exception {
211         if (!isDockStateSupported()) {
212             return;
213         }
214         toggleScreenOn(false);
215         triggerIdleMaintenance();
216         verifyIdleState();
217 
218         toggleFakeDeviceDockState(false /* active */);
219         verifyIdleState();
220     }
221 
setAutomotiveProjection(boolean on)222     private void setAutomotiveProjection(boolean on) throws Exception {
223         UiModeManager uiModeManager = getContext().getSystemService(UiModeManager.class);
224         if (on) {
225             assertTrue(SystemUtil.callWithShellPermissionIdentity(
226                     () -> uiModeManager.requestProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE),
227                     Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION));
228         } else {
229             SystemUtil.callWithShellPermissionIdentity(
230                     () -> uiModeManager.releaseProjection(UiModeManager.PROJECTION_TYPE_AUTOMOTIVE),
231                     Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION);
232         }
233         Thread.sleep(2_000);
234     }
235 
236     /**
237      * Ensure automotive projection is considered active.
238      */
testAutomotiveProjectionPreventsIdle()239     public void testAutomotiveProjectionPreventsIdle() throws Exception {
240         if (!isAutomotiveProjectionSupported()) {
241             return;
242         }
243 
244         toggleScreenOn(false);
245 
246         setAutomotiveProjection(true);
247         triggerIdleMaintenance();
248         verifyActiveState();
249 
250         setAutomotiveProjection(false);
251         triggerIdleMaintenance();
252         verifyIdleState();
253     }
254 
runIdleJobStartsOnlyWhenIdle()255     private void runIdleJobStartsOnlyWhenIdle() throws Exception {
256         toggleScreenOn(true);
257 
258         kTestEnvironment.setExpectedExecutions(0);
259         kTestEnvironment.setExpectedWaitForRun();
260         mJobScheduler.schedule(mBuilder.setRequiresDeviceIdle(true).build());
261         triggerIdleMaintenance();
262         assertJobWaiting();
263         assertJobNotReady();
264         kTestEnvironment.readyToRun();
265         runSatisfiedJob();
266         assertFalse("Job fired when the device was active.", kTestEnvironment.awaitExecution(500));
267 
268         if (isAutomotiveProjectionSupported()) {
269             kTestEnvironment.setExpectedExecutions(0);
270             kTestEnvironment.setExpectedWaitForRun();
271             setAutomotiveProjection(true);
272             toggleScreenOn(false);
273             triggerIdleMaintenance();
274             assertJobWaiting();
275             assertJobNotReady();
276             kTestEnvironment.readyToRun();
277             runSatisfiedJob();
278             assertFalse("Job fired when the device was active.",
279                     kTestEnvironment.awaitExecution(500));
280 
281             kTestEnvironment.setExpectedExecutions(1);
282             kTestEnvironment.setExpectedWaitForRun();
283             kTestEnvironment.setContinueAfterStart();
284             kTestEnvironment.setExpectedStopped();
285             setAutomotiveProjection(false);
286             triggerIdleMaintenance();
287             assertJobReady();
288             kTestEnvironment.readyToRun();
289             runSatisfiedJob();
290             assertTrue("Job didn't fire when the device became idle.",
291                     kTestEnvironment.awaitExecution());
292         } else {
293             kTestEnvironment.setExpectedExecutions(1);
294             kTestEnvironment.setExpectedWaitForRun();
295             kTestEnvironment.setContinueAfterStart();
296             kTestEnvironment.setExpectedStopped();
297             toggleScreenOn(false);
298             triggerIdleMaintenance();
299             assertJobReady();
300             kTestEnvironment.readyToRun();
301             runSatisfiedJob();
302             assertTrue("Job didn't fire when the device became idle.",
303                     kTestEnvironment.awaitExecution());
304         }
305     }
306 
testIdleJobStartsOnlyWhenIdle_settingProjectionEndsIdle()307     public void testIdleJobStartsOnlyWhenIdle_settingProjectionEndsIdle() throws Exception {
308         if (!isAutomotiveProjectionSupported()) {
309             return;
310         }
311 
312         runIdleJobStartsOnlyWhenIdle();
313 
314         setAutomotiveProjection(true);
315         assertTrue("Job didn't stop when the device became active.",
316                 kTestEnvironment.awaitStopped());
317     }
318 
testIdleJobStartsOnlyWhenIdle_screenEndsIdle()319     public void testIdleJobStartsOnlyWhenIdle_screenEndsIdle() throws Exception {
320         runIdleJobStartsOnlyWhenIdle();
321 
322         toggleScreenOn(true);
323         assertTrue("Job didn't stop when the device became active.",
324                 kTestEnvironment.awaitStopped());
325     }
326 
327     /** Asks (not forces) JobScheduler to run the job if constraints are met. */
runSatisfiedJob()328     private void runSatisfiedJob() throws Exception {
329         mUiDevice.executeShellCommand("cmd jobscheduler run -s"
330                 + " -u " + UserHandle.myUserId()
331                 + " " + kJobServiceComponent.getPackageName()
332                 + " " + STATE_JOB_ID);
333     }
334 }
335