1 /*
2  * Copyright (C) 2018 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.annotation.TargetApi;
20 import android.app.job.JobInfo;
21 import android.content.pm.PackageManager;
22 import android.os.SystemClock;
23 import android.support.test.uiautomator.UiDevice;
24 
25 import androidx.test.InstrumentationRegistry;
26 
27 /**
28  * Make sure the state of {@link android.app.job.JobScheduler} is correct.
29  */
30 @TargetApi(28)
31 public class DeviceStatesTest extends ConstraintTest {
32     /** Unique identifier for the job scheduled by this suite of tests. */
33     public static final int STATE_JOB_ID = DeviceStatesTest.class.hashCode();
34 
35     private JobInfo.Builder mBuilder;
36     private UiDevice mUiDevice;
37 
38     @Override
setUp()39     public void setUp() throws Exception {
40         super.setUp();
41         mBuilder = new JobInfo.Builder(STATE_JOB_ID, kJobServiceComponent);
42         mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
43     }
44 
45     @Override
tearDown()46     public void tearDown() throws Exception {
47         mJobScheduler.cancel(STATE_JOB_ID);
48         // Put device back in to normal operation.
49         toggleScreenOn(true /* screen on */);
50     }
51 
assertJobReady()52     void assertJobReady() throws Exception {
53         assertJobReady(STATE_JOB_ID);
54     }
55 
assertJobWaiting()56     void assertJobWaiting() throws Exception {
57         assertJobWaiting(STATE_JOB_ID);
58     }
59 
assertJobNotReady()60     void assertJobNotReady() throws Exception {
61         assertJobNotReady(STATE_JOB_ID);
62     }
63 
waitFor(long waitMillis)64     static void waitFor(long waitMillis) throws Exception {
65         final long deadline = SystemClock.uptimeMillis() + waitMillis;
66         do {
67              Thread.sleep(500L);
68         } while (SystemClock.uptimeMillis() < deadline);
69     }
70 
71     /**
72      * Toggle device is dock idle or dock active.
73      */
toggleFakeDeviceDockState(final boolean idle)74     private void toggleFakeDeviceDockState(final boolean idle) throws Exception {
75         mUiDevice.executeShellCommand("cmd jobscheduler trigger-dock-state "
76                 + (idle ? "idle" : "active"));
77         // Wait a moment to let that happen before proceeding.
78         waitFor(2_000);
79     }
80 
81     /**
82      * Make sure the screen state.
83      */
toggleScreenOn(final boolean screenon)84     private void toggleScreenOn(final boolean screenon) throws Exception {
85         if (screenon) {
86             mUiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
87         } else {
88             mUiDevice.executeShellCommand("input keyevent KEYCODE_SLEEP");
89         }
90         // Since the screen on/off intent is ordered, they will not be sent right now.
91         waitFor(2_000);
92     }
93 
94     /**
95      * Simulated for idle, and then perform idle maintenance now.
96      */
triggerIdleMaintenance()97     private void triggerIdleMaintenance() throws Exception {
98         mUiDevice.executeShellCommand("cmd activity idle-maintenance");
99         // Wait a moment to let that happen before proceeding.
100         waitFor(2_000);
101     }
102 
103     /**
104      * Schedule a job that requires the device is idle, and assert it fired to make
105      * sure the device is idle.
106      */
verifyIdleState()107     void verifyIdleState() throws Exception {
108         kTestEnvironment.setExpectedExecutions(1);
109         kTestEnvironment.setExpectedWaitForRun();
110         mJobScheduler.schedule(mBuilder.setRequiresDeviceIdle(true).build());
111         assertJobReady();
112         kTestEnvironment.readyToRun();
113 
114         assertTrue("Job with idle constraint did not fire on idle",
115                 kTestEnvironment.awaitExecution());
116     }
117 
118     /**
119      * Schedule a job that requires the device is idle, and assert it failed to make
120      * sure the device is active.
121      */
verifyActiveState()122     void verifyActiveState() throws Exception {
123         kTestEnvironment.setExpectedExecutions(0);
124         kTestEnvironment.setExpectedWaitForRun();
125         mJobScheduler.schedule(mBuilder.setRequiresDeviceIdle(true).build());
126         assertJobWaiting();
127         assertJobNotReady();
128         kTestEnvironment.readyToRun();
129 
130         assertFalse("Job with idle constraint fired while not on idle.",
131                 kTestEnvironment.awaitExecution(250));
132     }
133 
134     /**
135      * Ensure that device can switch state normally.
136      */
testDeviceChangeIdleActiveState()137     public void testDeviceChangeIdleActiveState() throws Exception {
138         toggleScreenOn(true /* screen on */);
139         verifyActiveState();
140 
141         // Assert device is idle when screen is off for a while.
142         toggleScreenOn(false /* screen off */);
143         triggerIdleMaintenance();
144         verifyIdleState();
145 
146         // Assert device is back to active when screen is on.
147         toggleScreenOn(true /* screen on */);
148         verifyActiveState();
149     }
150 
151     /**
152      * Check if dock state is supported.
153      */
isDockStateSupported()154     private boolean isDockStateSupported() {
155         // Car does not support dock state.
156         return !getContext().getPackageManager().hasSystemFeature(
157                 PackageManager.FEATURE_AUTOMOTIVE);
158     }
159 
160     /**
161      * Ensure that device can switch state on dock normally.
162      */
testScreenOnDeviceOnDockChangeState()163     public void testScreenOnDeviceOnDockChangeState() throws Exception {
164         if (!isDockStateSupported()) {
165             return;
166         }
167         toggleScreenOn(true /* screen on */);
168         verifyActiveState();
169 
170         // Assert device go to idle if user doesn't interact with device for a while.
171         toggleFakeDeviceDockState(true /* idle */);
172         triggerIdleMaintenance();
173         verifyIdleState();
174 
175         // Assert device go back to active if user interacts with device.
176         toggleFakeDeviceDockState(false /* active */);
177         verifyActiveState();
178     }
179 
180     /**
181      *  Ensure that ignores this dock intent during screen off.
182      */
testScreenOffDeviceOnDockNoChangeState()183     public void testScreenOffDeviceOnDockNoChangeState() throws Exception {
184         if (!isDockStateSupported()) {
185             return;
186         }
187         toggleScreenOn(false /* screen off */);
188         triggerIdleMaintenance();
189         verifyIdleState();
190 
191         toggleFakeDeviceDockState(false /* active */);
192         verifyIdleState();
193     }
194 }
195