1 /* 2 * Copyright (C) 2021 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 com.android.server.scheduling; 18 19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; 20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong; 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; 28 29 import static com.google.common.truth.Truth.assertThat; 30 31 import static org.mockito.ArgumentMatchers.eq; 32 import static org.mockito.Mockito.atLeastOnce; 33 import static org.mockito.Mockito.spy; 34 import static org.mockito.Mockito.times; 35 36 import android.Manifest; 37 import android.app.ActivityManager; 38 import android.app.AlarmManager; 39 import android.content.BroadcastReceiver; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.content.IntentFilter; 43 import android.content.pm.PackageManager; 44 import android.net.TetheringManager; 45 import android.os.Binder; 46 import android.os.Bundle; 47 import android.os.Handler; 48 import android.os.IBinder; 49 import android.os.PowerManager; 50 import android.os.RemoteCallback; 51 import android.os.UserHandle; 52 import android.provider.DeviceConfig; 53 import android.scheduling.IRequestRebootReadinessStatusListener; 54 import android.scheduling.RebootReadinessManager; 55 56 import androidx.test.InstrumentationRegistry; 57 58 import com.android.dx.mockito.inline.extended.ExtendedMockito; 59 60 import org.junit.After; 61 import org.junit.Before; 62 import org.junit.Test; 63 import org.mockito.Answers; 64 import org.mockito.ArgumentCaptor; 65 import org.mockito.Captor; 66 import org.mockito.Mock; 67 import org.mockito.MockitoSession; 68 import org.mockito.quality.Strictness; 69 70 import java.io.BufferedReader; 71 import java.io.File; 72 import java.io.FileDescriptor; 73 import java.io.FileReader; 74 import java.io.PrintWriter; 75 import java.util.ArrayList; 76 import java.util.List; 77 import java.util.concurrent.CountDownLatch; 78 import java.util.concurrent.TimeUnit; 79 80 81 /** 82 * Tests for {@link com.android.server.scheduling.RebootReadinessManagerService}. 83 */ 84 public class RebootReadinessUnitTest { 85 86 private Context mMockContext; 87 88 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 89 private ActivityManager mActivityManager; 90 91 private AlarmManager mAlarmManager; 92 93 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 94 private PowerManager mPowerManager; 95 96 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 97 private TetheringManager mTetheringManager; 98 99 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 100 private PackageManager mPackageManager; 101 102 @Captor 103 private ArgumentCaptor<Intent> mIntentArgumentCaptor; 104 105 @Captor 106 private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverArgumentCaptor; 107 108 @Captor 109 private ArgumentCaptor<IntentFilter> mIntentFilters; 110 111 @Captor 112 private ArgumentCaptor<Integer> mIntCaptor; 113 114 @Captor 115 private ArgumentCaptor<Long> mLongCaptor; 116 117 118 private MockitoSession mSession; 119 120 private RebootReadinessManagerService mService; 121 private RebootReadinessLogger mLogger; 122 123 private static final String TEST_PACKAGE = "test.package"; 124 private static final String TEST_PACKAGE2 = "test.package2"; 125 126 private static final String PROPERTY_ACTIVE_POLLING_INTERVAL_MS = "active_polling_interval_ms"; 127 private static final String PROPERTY_DISABLE_INTERACTIVITY_CHECK = 128 "disable_interactivity_check"; 129 private static final String PROPERTY_INTERACTIVITY_THRESHOLD_MS = "interactivity_threshold_ms"; 130 private static final String PROPERTY_DISABLE_APP_ACTIVITY_CHECK = "disable_app_activity_check"; 131 private static final String PROPERTY_DISABLE_SUBSYSTEMS_CHECK = "disable_subsystems_check"; 132 private static final String PROPERTY_ALARM_CLOCK_THRESHOLD_MS = "alarm_clock_threshold_ms"; 133 private static final String PROPERTY_LOGGING_BLOCKING_ENTITY_THRESHOLD_MS = 134 "logging_blocking_entity_threshold_ms"; 135 136 // The prefix "TESTCOMPONENT" is used to allow testing when subsystem checks are disabled. 137 private static final String COMPONENT_NAME = "TESTCOMPONENT_component"; 138 139 // Small delay to allow DeviceConfig updates to propagate. 140 private static final int DEVICE_CONFIG_DELAY = 1000; 141 142 // Small delay to allow reboot readiness state to change. 143 private static final int STATE_CHANGE_DELAY = 5000; 144 145 @Before setup()146 public void setup() { 147 mSession = 148 ExtendedMockito.mockitoSession().initMocks( 149 this) 150 .strictness(Strictness.LENIENT) 151 .spyStatic(SchedulingStatsLog.class) 152 .startMocking(); 153 mMockContext = spy(InstrumentationRegistry.getContext()); 154 mAlarmManager = spy(mMockContext.getSystemService(AlarmManager.class)); 155 doNothing().when(mMockContext).enforceCallingPermission(anyString(), anyString()); 156 doNothing().when(mMockContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class), 157 anyString()); 158 doReturn(PackageManager.PERMISSION_GRANTED).when(mMockContext) 159 .checkCallingOrSelfPermission(anyString()); 160 161 doReturn(mActivityManager).when(mMockContext).getSystemService(eq(ActivityManager.class)); 162 doReturn(mAlarmManager).when(mMockContext).getSystemService(eq(AlarmManager.class)); 163 doCallRealMethod().when(mAlarmManager).setExact(anyInt(), anyLong(), anyString(), any( 164 AlarmManager.OnAlarmListener.class), any(Handler.class)); 165 doReturn(mPowerManager).when(mMockContext).getSystemService(eq(PowerManager.class)); 166 when(mPowerManager.isInteractive()).thenReturn(true); 167 doReturn(mTetheringManager).when(mMockContext).getSystemService(eq(TetheringManager.class)); 168 when(mAlarmManager.getNextAlarmClock()).thenReturn(null); 169 when(mMockContext.getPackageManager()).thenReturn(mPackageManager); 170 InstrumentationRegistry.getInstrumentation().getUiAutomation() 171 .adoptShellPermissionIdentity(Manifest.permission.WRITE_DEVICE_CONFIG, 172 Manifest.permission.READ_DEVICE_CONFIG, 173 Manifest.permission.SCHEDULE_EXACT_ALARM, 174 Manifest.permission.ACCESS_NETWORK_STATE); 175 initializeDeviceConfig(); 176 File testDir = new File(mMockContext.getFilesDir(), "reboot-readiness"); 177 mLogger = spy(new RebootReadinessLogger(testDir, mMockContext)); 178 mService = new RebootReadinessManagerService(mMockContext, mLogger); 179 } 180 initializeDeviceConfig()181 private void initializeDeviceConfig() { 182 // Checks may only be scheduled every 5000ms due to AlarmManager scheduling restrictions. 183 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 184 PROPERTY_ACTIVE_POLLING_INTERVAL_MS, "5000", false); 185 186 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 187 PROPERTY_DISABLE_INTERACTIVITY_CHECK, "true", false); 188 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 189 PROPERTY_DISABLE_APP_ACTIVITY_CHECK, "true", false); 190 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 191 PROPERTY_DISABLE_SUBSYSTEMS_CHECK, "true", false); 192 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 193 PROPERTY_ALARM_CLOCK_THRESHOLD_MS, "500000", false); 194 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 195 PROPERTY_INTERACTIVITY_THRESHOLD_MS, "1000", false); 196 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 197 PROPERTY_LOGGING_BLOCKING_ENTITY_THRESHOLD_MS, "1000", false); 198 } 199 200 @After teardown()201 public void teardown() { 202 mService.cancelPendingReboot(TEST_PACKAGE); 203 mService.cancelPendingReboot(TEST_PACKAGE2); 204 mSession.finishMocking(); 205 } 206 207 /** 208 * Test that calling packages are tracked correctly when reboot readiness checks are started 209 * or stopped. 210 */ 211 @Test testCallingPackages()212 public void testCallingPackages() { 213 mService.markRebootPending(TEST_PACKAGE); 214 assertThat(mService.getCallingPackages().valueAt(0)).contains(TEST_PACKAGE); 215 mService.cancelPendingReboot(TEST_PACKAGE); 216 assertThat(mService.getCallingPackages().size()).isEqualTo(0); 217 } 218 219 /** 220 * Test that a registered listener is called as part of the reboot readiness checks. 221 */ 222 @Test testRegisteredListenerIsCalled()223 public void testRegisteredListenerIsCalled() throws Exception { 224 CountDownLatch latch = new CountDownLatch(1); 225 mService.addRequestRebootReadinessStatusListener(getStatusListener(latch)); 226 mService.markRebootPending(TEST_PACKAGE); 227 assertThat(latch.await(2, TimeUnit.SECONDS)).isTrue(); 228 } 229 230 /** Test that the correct intents are sent when the reboot readiness state changes. */ 231 @Test testCorrectIntentsSent()232 public void testCorrectIntentsSent() throws Exception { 233 mService.markRebootPending(TEST_PACKAGE); 234 Thread.sleep(STATE_CHANGE_DELAY); 235 assertThat(mService.isReadyToReboot()).isTrue(); 236 237 // Make device interactive, and therefore not ready to reboot. 238 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 239 PROPERTY_DISABLE_INTERACTIVITY_CHECK, "false", false); 240 Thread.sleep(DEVICE_CONFIG_DELAY); 241 setScreenState(true); 242 Thread.sleep(STATE_CHANGE_DELAY); 243 assertThat(mService.isReadyToReboot()).isFalse(); 244 245 // 2 broadcasts should have been sent: one when the device became ready to reboot, and 246 // another when it became no longer ready to reboot. 247 verify(mMockContext, times(2)).sendBroadcastAsUser(mIntentArgumentCaptor.capture(), 248 any(UserHandle.class), anyString()); 249 List<Intent> intents = mIntentArgumentCaptor.getAllValues(); 250 251 Intent readyIntent = intents.get(0); 252 Intent notReadyIntent = intents.get(1); 253 boolean sentExtra; 254 255 assertThat(readyIntent.getAction()).isEqualTo(RebootReadinessManager.ACTION_REBOOT_READY); 256 assertThat(readyIntent.getPackage()).isEqualTo(TEST_PACKAGE); 257 sentExtra = readyIntent.getBooleanExtra( 258 RebootReadinessManager.EXTRA_IS_READY_TO_REBOOT, false); 259 assertThat(sentExtra).isTrue(); 260 261 assertThat(notReadyIntent.getAction()).isEqualTo( 262 RebootReadinessManager.ACTION_REBOOT_READY); 263 assertThat(notReadyIntent.getPackage()).isEqualTo(TEST_PACKAGE); 264 sentExtra = notReadyIntent.getBooleanExtra( 265 RebootReadinessManager.EXTRA_IS_READY_TO_REBOOT, false); 266 assertThat(sentExtra).isFalse(); 267 } 268 269 /** Test that a second client will receive an immediate broadcast when they register. */ 270 @Test testSecondClientReceivesBroadcastImmediately()271 public void testSecondClientReceivesBroadcastImmediately() throws Exception { 272 mService.markRebootPending(TEST_PACKAGE); 273 Thread.sleep(STATE_CHANGE_DELAY); 274 assertThat(mService.isReadyToReboot()).isTrue(); 275 276 mService.markRebootPending(TEST_PACKAGE2); 277 verify(mMockContext, times(2)).sendBroadcastAsUser(mIntentArgumentCaptor.capture(), 278 any(UserHandle.class), anyString()); 279 List<Intent> intents = mIntentArgumentCaptor.getAllValues(); 280 281 Intent secondIntent = intents.get(1); 282 283 assertThat(secondIntent.getPackage()).isEqualTo(TEST_PACKAGE2); 284 assertThat(secondIntent.getAction()).isEqualTo(RebootReadinessManager.ACTION_REBOOT_READY); 285 boolean sentExtra = secondIntent.getBooleanExtra( 286 RebootReadinessManager.EXTRA_IS_READY_TO_REBOOT, false); 287 assertThat(sentExtra).isTrue(); 288 } 289 290 @Test testWritingAfterRebootReadyBroadcast()291 public void testWritingAfterRebootReadyBroadcast() throws Exception { 292 mService.markRebootPending(TEST_PACKAGE); 293 Thread.sleep(STATE_CHANGE_DELAY); 294 assertThat(mService.isReadyToReboot()).isTrue(); 295 verify(mLogger, times(1)).writeAfterRebootReadyBroadcast( 296 anyLong(), anyLong(), eq(0), eq(0), eq(0)); 297 298 // Make device interactive and allow the device to become not reboot-ready for a while. Then 299 // check that the fact that interactivity has blocked the reboot is noted. 300 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 301 PROPERTY_DISABLE_INTERACTIVITY_CHECK, "false", false); 302 Thread.sleep(DEVICE_CONFIG_DELAY); 303 setScreenState(true); 304 Thread.sleep(STATE_CHANGE_DELAY); 305 306 setScreenState(false); 307 Thread.sleep(STATE_CHANGE_DELAY); 308 verify(mLogger, times(2)).writeAfterRebootReadyBroadcast( 309 anyLong(), anyLong(), mIntCaptor.capture(), eq(0), eq(0)); 310 assertThat(mIntCaptor.getAllValues().get(1)).isAtLeast(1); 311 312 313 } 314 315 /** 316 * Test that the device interactivity checks return the correct result based on the screen 317 * state. 318 */ 319 @Test testInteractivityChecks()320 public void testInteractivityChecks() throws Exception { 321 assertThat(mService.checkDeviceInteractivity()).isFalse(); 322 323 setScreenState(false); 324 // Screen needs to be off for 1000ms to be ready to reboot 325 Thread.sleep(1000); 326 assertThat(mService.checkDeviceInteractivity()).isTrue(); 327 328 setScreenState(true); 329 assertThat(mService.checkDeviceInteractivity()).isFalse(); 330 } 331 332 /** Test that foreground services block the reboot. */ 333 @Test testAppActivityChecks()334 public void testAppActivityChecks() { 335 List<ActivityManager.RunningServiceInfo> runningServicesList = new ArrayList<>(); 336 when(mActivityManager.getRunningServices(anyInt())).thenReturn(runningServicesList); 337 assertThat(mService.checkBackgroundAppActivity()).isTrue(); 338 339 // Ensure that non-foreground services do not block the reboot 340 ActivityManager.RunningServiceInfo nonForeground = new ActivityManager.RunningServiceInfo(); 341 nonForeground.foreground = false; 342 runningServicesList.add(nonForeground); 343 assertThat(mService.checkBackgroundAppActivity()).isTrue(); 344 345 // Ensure that foreground services do block the reboot 346 ActivityManager.RunningServiceInfo foreground = new ActivityManager.RunningServiceInfo(); 347 foreground.foreground = true; 348 runningServicesList.add(foreground); 349 assertThat(mService.checkBackgroundAppActivity()).isFalse(); 350 } 351 352 /** 353 * Test that pending alarm clocks block the reboot if they are within a given threshold from 354 * the current time. 355 */ 356 @Test testAlarmClockBlocksReboot()357 public void testAlarmClockBlocksReboot() throws Exception { 358 long threshold = TimeUnit.MINUTES.toMillis(15); 359 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 360 PROPERTY_DISABLE_SUBSYSTEMS_CHECK, "false", false); 361 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 362 PROPERTY_ALARM_CLOCK_THRESHOLD_MS, Long.toString(threshold), false); 363 Thread.sleep(DEVICE_CONFIG_DELAY); 364 assertThat(mService.checkSystemComponentsState()).isTrue(); 365 366 // Alarm is within the threshold, therefore the reboot should be blocked. 367 AlarmManager.AlarmClockInfo alarm = new AlarmManager.AlarmClockInfo( 368 System.currentTimeMillis() + threshold / 2, null); 369 when(mAlarmManager.getNextAlarmClock()).thenReturn(alarm); 370 assertThat(mService.checkSystemComponentsState()).isFalse(); 371 372 // Alarm is outside the threshold, therefore the reboot should not be blocked. 373 alarm = new AlarmManager.AlarmClockInfo(System.currentTimeMillis() + threshold * 2, null); 374 when(mAlarmManager.getNextAlarmClock()).thenReturn(alarm); 375 assertThat(mService.checkSystemComponentsState()).isTrue(); 376 377 } 378 379 /** 380 * Test that metrics are logged for long-blocking subsystems. 381 */ 382 @Test testLongSubsystemBlockingReported()383 public void testLongSubsystemBlockingReported() throws Exception { 384 CountDownLatch latch = new CountDownLatch(2); 385 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 386 PROPERTY_DISABLE_SUBSYSTEMS_CHECK, "false", false); 387 Thread.sleep(DEVICE_CONFIG_DELAY); 388 mService.addRequestRebootReadinessStatusListener(getStatusListener(latch)); 389 mService.markRebootPending(TEST_PACKAGE); 390 391 assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue(); 392 verify(() -> SchedulingStatsLog.write(eq(SchedulingStatsLog.LONG_REBOOT_BLOCKING_REPORTED), 393 eq(SchedulingStatsLog 394 .LONG_REBOOT_BLOCKING_REPORTED__REBOOT_BLOCK_REASON__SYSTEM_COMPONENT), 395 eq(COMPONENT_NAME), anyInt())); 396 } 397 398 /** 399 * Test that metrics are logged for long-blocking apps. 400 */ 401 @Test testLongAppBlockingReported()402 public void testLongAppBlockingReported() throws Exception { 403 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 404 PROPERTY_DISABLE_APP_ACTIVITY_CHECK, "false", false); 405 Thread.sleep(DEVICE_CONFIG_DELAY); 406 List<ActivityManager.RunningServiceInfo> runningServicesList = new ArrayList<>(); 407 ActivityManager.RunningServiceInfo service = new ActivityManager.RunningServiceInfo(); 408 service.foreground = true; 409 service.uid = 1000; 410 runningServicesList.add(service); 411 when(mActivityManager.getRunningServices(anyInt())).thenReturn(runningServicesList); 412 mService.markRebootPending(TEST_PACKAGE); 413 414 // Allow the app to block the reboot for a small amount of time. 415 Thread.sleep(6000); 416 verify(() -> SchedulingStatsLog.write(eq(SchedulingStatsLog.LONG_REBOOT_BLOCKING_REPORTED), 417 eq(SchedulingStatsLog.LONG_REBOOT_BLOCKING_REPORTED__REBOOT_BLOCK_REASON__APP_UID), 418 anyString(), eq(1000)), atLeastOnce()); 419 } 420 421 /** 422 * Test that logging information is correctly written and read before and after the reboot. 423 */ 424 @Test testReadingAndWritingUnattendedReboot()425 public void testReadingAndWritingUnattendedReboot() { 426 mLogger.writeAfterRebootReadyBroadcast(1000, 2000, 0, 0, 0); 427 mLogger.readMetricsPostReboot(); 428 mLogger.writePostRebootMetrics(); 429 verify(() -> SchedulingStatsLog.write(eq(SchedulingStatsLog.UNATTENDED_REBOOT_OCCURRED), 430 eq(1000L), anyLong(), eq(0), eq(0), eq(0), anyLong())); 431 } 432 433 /** 434 * Test that logging information is correctly written in the case that the device becomes not 435 * ready to reboot. 436 */ 437 @Test testMetricsLoggedWhenNotReadyToReboot()438 public void testMetricsLoggedWhenNotReadyToReboot() throws Exception { 439 mService.markRebootPending(TEST_PACKAGE); 440 Thread.sleep(STATE_CHANGE_DELAY); 441 assertThat(mService.isReadyToReboot()).isTrue(); 442 443 // Make device interactive and allow the device to become not reboot-ready for a while. 444 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 445 PROPERTY_DISABLE_INTERACTIVITY_CHECK, "false", false); 446 Thread.sleep(DEVICE_CONFIG_DELAY); 447 setScreenState(true); 448 Thread.sleep(STATE_CHANGE_DELAY); 449 assertThat(mService.isReadyToReboot()).isFalse(); 450 451 // The device has become not ready to reboot, therefore no metrics should be logged. 452 mLogger.readMetricsPostReboot(); 453 mLogger.writePostRebootMetrics(); 454 455 // Verify that the time_to_next_interaction_millis field is correctly populated. 456 verify(() -> SchedulingStatsLog.write(eq(SchedulingStatsLog.UNATTENDED_REBOOT_OCCURRED), 457 anyLong(), anyLong(), anyInt(), anyInt(), 458 anyInt(), mLongCaptor.capture()), times(1)); 459 assertThat(mLongCaptor.getValue()).isGreaterThan(STATE_CHANGE_DELAY + DEVICE_CONFIG_DELAY); 460 assertThat(mLongCaptor.getValue()).isLessThan(TimeUnit.MINUTES.toMillis(1)); 461 } 462 463 /** 464 * Test that a pending alarm clock will cause the reboot readiness state to be checked before 465 * the alarm triggers. 466 */ 467 @Test testAlarmClockCheckWhileReadyToReboot()468 public void testAlarmClockCheckWhileReadyToReboot() throws Exception { 469 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 470 PROPERTY_DISABLE_SUBSYSTEMS_CHECK, "false", false); 471 DeviceConfig.setProperty(DeviceConfig.NAMESPACE_REBOOT_READINESS, 472 PROPERTY_ALARM_CLOCK_THRESHOLD_MS, Long.toString(5000), false); 473 Thread.sleep(DEVICE_CONFIG_DELAY); 474 475 // Alarm clock is scheduled for 6 seconds from now. The device will initially be ready 476 // to reboot, but another check will be scheduled before the alarm triggers, causing the 477 // device to no longer be ready to reboot. 478 AlarmManager.AlarmClockInfo alarm = new AlarmManager.AlarmClockInfo( 479 System.currentTimeMillis() + 6000, null); 480 when(mAlarmManager.getNextAlarmClock()).thenReturn(alarm); 481 mService.markRebootPending(TEST_PACKAGE); 482 assertThat(mService.isReadyToReboot()).isTrue(); 483 Thread.sleep(STATE_CHANGE_DELAY); 484 assertThat(mService.isReadyToReboot()).isFalse(); 485 } 486 487 /** 488 * Test that reboot readiness checks happen for the duration supplied by the timeout flag. 489 */ 490 @Test testStateChangeListenerShellCommand()491 public void testStateChangeListenerShellCommand() throws Exception { 492 Long startTime = System.currentTimeMillis(); 493 executeShellCommand("start-readiness-checks --timeout-secs 10"); 494 Long endTime = System.currentTimeMillis(); 495 assertThat(endTime - startTime).isGreaterThan(10000); 496 } 497 498 /** 499 * Test that the check-interactivity-state shell command prints the correct output. 500 */ 501 @Test testInteractivityShellCommand()502 public void testInteractivityShellCommand() throws Exception { 503 setScreenState(false); 504 String output = executeShellCommand( 505 "check-interactivity-state --interactivity-threshold-ms 0"); 506 assertThat(output).contains("true"); 507 508 setScreenState(true); 509 output = executeShellCommand("check-interactivity-state --interactivity-threshold-ms 0"); 510 assertThat(output).contains("false"); 511 } 512 513 /** 514 * Test that the check-subsystems-state shell command prints the correct output. 515 */ 516 @Test testSubsystemsShellCommand()517 public void testSubsystemsShellCommand() throws Exception { 518 String output = executeShellCommand("check-subsystems-state"); 519 assertThat(output).contains("true"); 520 521 mService.addRequestRebootReadinessStatusListener(getStatusListener(new CountDownLatch(1))); 522 output = executeShellCommand("check-subsystems-state --list-blocking"); 523 assertThat(output).contains("false"); 524 assertThat(output).contains("Blocking subsystem: " + COMPONENT_NAME); 525 } 526 527 /** 528 * Test that the check-app-activity-state shell command prints the correct output. 529 */ 530 @Test testAppActivityShellCommand()531 public void testAppActivityShellCommand() throws Exception { 532 List<ActivityManager.RunningServiceInfo> runningServicesList = new ArrayList<>(); 533 when(mActivityManager.getRunningServices(anyInt())).thenReturn(runningServicesList); 534 String output = executeShellCommand("check-app-activity-state --list-blocking"); 535 assertThat(output).contains("true"); 536 537 ActivityManager.RunningServiceInfo service = new ActivityManager.RunningServiceInfo(); 538 service.foreground = true; 539 service.uid = 1234; 540 runningServicesList.add(service); 541 542 doReturn(new String[]{"test.package"}).when(mPackageManager).getPackagesForUid(anyInt()); 543 output = executeShellCommand("check-app-activity-state --list-blocking"); 544 assertThat(output).contains("false"); 545 assertThat(output).containsMatch("Blocking uid: 1234.*test.package"); 546 } 547 548 /** 549 * Test that DeviceConfig values may be tuned by the shell command interface. 550 */ 551 @Test testShellCommandDeviceConfig()552 public void testShellCommandDeviceConfig() throws Exception { 553 executeShellCommand("check-interactivity-state --polling-interval-ms 1234" 554 + " --interactivity-threshold-ms 2468 --disable-interactivity-checks" 555 + " --disable-subsystem-checks --disable-app-activity-checks"); 556 assertThat(DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_REBOOT_READINESS, 557 PROPERTY_DISABLE_INTERACTIVITY_CHECK, false)).isTrue(); 558 assertThat(DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_REBOOT_READINESS, 559 PROPERTY_DISABLE_SUBSYSTEMS_CHECK, false)).isTrue(); 560 assertThat(DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_REBOOT_READINESS, 561 PROPERTY_DISABLE_APP_ACTIVITY_CHECK, false)).isTrue(); 562 assertThat(DeviceConfig.getLong(DeviceConfig.NAMESPACE_REBOOT_READINESS, 563 PROPERTY_ACTIVE_POLLING_INTERVAL_MS, 0)).isEqualTo(1234); 564 assertThat(DeviceConfig.getLong(DeviceConfig.NAMESPACE_REBOOT_READINESS, 565 PROPERTY_INTERACTIVITY_THRESHOLD_MS, 0)).isEqualTo(2468); 566 } 567 568 /** 569 * Calls the equivalent of "adb shell cmd reboot_readiness" and returns the output. 570 */ executeShellCommand(String command)571 private String executeShellCommand(String command) throws Exception { 572 RebootReadinessShellCommand commandHandler = 573 spy(new RebootReadinessShellCommand(mService, mMockContext)); 574 File tmpFile = File.createTempFile("output", ".txt"); 575 PrintWriter pw = new PrintWriter(tmpFile); 576 doReturn(pw).when(commandHandler).getOutPrintWriter(); 577 commandHandler.exec(new Binder(), new FileDescriptor(), new FileDescriptor(), 578 new FileDescriptor(), command.split(" ")); 579 pw.flush(); 580 BufferedReader reader = new BufferedReader(new FileReader(tmpFile)); 581 String line; 582 StringBuilder sb = new StringBuilder(); 583 while ((line = reader.readLine()) != null) { 584 sb.append(line); 585 } 586 return sb.toString(); 587 } 588 589 /** 590 * Sets the device interactivity on or off, and delays to allow the change to be processed. 591 */ setScreenState(boolean screenOn)592 private void setScreenState(boolean screenOn) throws Exception { 593 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); 594 filter.addAction(Intent.ACTION_SCREEN_OFF); 595 verify(mMockContext, atLeastOnce()).registerReceiver( 596 mBroadcastReceiverArgumentCaptor.capture(), mIntentFilters.capture()); 597 598 // Get the broadcast receiver that receives interactivity broadcasts. 599 int index = -1; 600 List<IntentFilter> capturedFilters = mIntentFilters.getAllValues(); 601 for (int i = 0; i < capturedFilters.size(); i++) { 602 if (capturedFilters.get(i).hasAction(Intent.ACTION_SCREEN_ON)) { 603 index = i; 604 } 605 } 606 assertThat(index).isNotEqualTo(-1); 607 BroadcastReceiver receiver = mBroadcastReceiverArgumentCaptor.getAllValues().get(index); 608 609 // Send updated interactivity state to receiver. 610 if (screenOn) { 611 receiver.onReceive(mMockContext, new Intent(Intent.ACTION_SCREEN_ON)); 612 } else { 613 receiver.onReceive(mMockContext, new Intent(Intent.ACTION_SCREEN_OFF)); 614 } 615 616 // ALlow the state change to be processed. 617 Thread.sleep(1000); 618 } 619 620 /** 621 * Returns a status listener which will decrement a passed CountdownLatch when it is called. 622 */ getStatusListener(CountDownLatch latch)623 private IRequestRebootReadinessStatusListener getStatusListener(CountDownLatch latch) { 624 return new IRequestRebootReadinessStatusListener() { 625 @Override 626 public void onRequestRebootReadinessStatus(RemoteCallback remoteCallback) { 627 latch.countDown(); 628 Bundle bundle = new Bundle(); 629 bundle.putBoolean(RebootReadinessManager.IS_REBOOT_READY_KEY, false); 630 bundle.putLong(RebootReadinessManager.ESTIMATED_FINISH_TIME_KEY, 1000L); 631 bundle.putString(RebootReadinessManager.SUBSYSTEM_NAME_KEY, COMPONENT_NAME); 632 remoteCallback.sendResult(bundle); 633 } 634 635 @Override 636 public IBinder asBinder() { 637 return new Binder(); 638 } 639 }; 640 } 641 } 642