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