1 package com.android.server.notification; 2 3 import static android.app.AlarmManager.RTC_WAKEUP; 4 5 import static com.google.common.truth.Truth.assertThat; 6 7 import static org.junit.Assert.assertEquals; 8 import static org.junit.Assert.assertFalse; 9 import static org.junit.Assert.assertTrue; 10 import static org.mockito.ArgumentMatchers.any; 11 import static org.mockito.ArgumentMatchers.anyInt; 12 import static org.mockito.ArgumentMatchers.eq; 13 import static org.mockito.Mockito.mock; 14 import static org.mockito.Mockito.never; 15 import static org.mockito.Mockito.reset; 16 import static org.mockito.Mockito.times; 17 import static org.mockito.Mockito.verify; 18 import static org.mockito.Mockito.when; 19 20 import static java.time.temporal.ChronoUnit.HOURS; 21 import static java.time.temporal.ChronoUnit.MINUTES; 22 23 import android.app.ActivityManager; 24 import android.app.AlarmManager; 25 import android.app.Application; 26 import android.app.PendingIntent; 27 import android.content.BroadcastReceiver; 28 import android.content.ComponentName; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.net.Uri; 32 import android.os.Bundle; 33 import android.os.SimpleClock; 34 import android.platform.test.annotations.EnableFlags; 35 import android.platform.test.flag.junit.SetFlagsRule; 36 import android.service.notification.Condition; 37 import android.service.notification.ScheduleCalendar; 38 import android.service.notification.ZenModeConfig; 39 import android.testing.AndroidTestingRunner; 40 import android.testing.TestableLooper.RunWithLooper; 41 42 import androidx.test.filters.SmallTest; 43 44 import com.android.server.UiServiceTestCase; 45 import com.android.server.pm.PackageManagerService; 46 47 import com.google.common.collect.ImmutableList; 48 49 import org.junit.Before; 50 import org.junit.Rule; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.mockito.ArgumentCaptor; 54 import org.mockito.Mock; 55 import org.mockito.MockitoAnnotations; 56 57 import java.time.Instant; 58 import java.time.ZoneId; 59 import java.time.ZoneOffset; 60 import java.time.ZonedDateTime; 61 import java.util.Calendar; 62 import java.util.GregorianCalendar; 63 64 @RunWith(AndroidTestingRunner.class) 65 @SmallTest 66 @RunWithLooper 67 public class ScheduleConditionProviderTest extends UiServiceTestCase { 68 @Rule 69 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 70 71 private ScheduleConditionProvider mService; 72 private TestClock mClock = new TestClock(); 73 @Mock private AlarmManager mAlarmManager; 74 75 @Before setUp()76 public void setUp() throws Exception { 77 MockitoAnnotations.initMocks(this); 78 mContext.addMockSystemService(AlarmManager.class, mAlarmManager); 79 80 Intent startIntent = 81 new Intent("com.android.server.notification.ScheduleConditionProvider"); 82 startIntent.setPackage("android"); 83 ScheduleConditionProvider service = new ScheduleConditionProvider(mClock); 84 service.attach( 85 getContext(), 86 null, // ActivityThread not actually used in Service 87 ScheduleConditionProvider.class.getName(), 88 null, // token not needed when not talking with the activity manager 89 mock(Application.class), 90 null // mocked services don't talk with the activity manager 91 ); 92 service.onCreate(); 93 service.onBind(startIntent); 94 mService = service; 95 } 96 97 @Test getComponent_returnsComponent()98 public void getComponent_returnsComponent() { 99 assertThat(mService.getComponent()).isEqualTo(new ComponentName("android", 100 "com.android.server.notification.ScheduleConditionProvider")); 101 } 102 103 @Test testIsValidConditionId_incomplete()104 public void testIsValidConditionId_incomplete() throws Exception { 105 Uri badConditionId = Uri.EMPTY; 106 assertFalse(mService.isValidConditionId(badConditionId)); 107 assertEquals(Condition.STATE_ERROR, 108 mService.evaluateSubscriptionLocked(badConditionId, null, 0, 1000).state); 109 } 110 111 @Test testIsValidConditionId()112 public void testIsValidConditionId() throws Exception { 113 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 114 info.days = new int[] {1, 2, 4}; 115 info.startHour = 8; 116 info.startMinute = 56; 117 info.nextAlarm = 1000; 118 info.exitAtAlarm = true; 119 info.endHour = 12; 120 info.endMinute = 9; 121 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 122 assertTrue(mService.isValidConditionId(conditionId)); 123 } 124 125 @Test testEvaluateSubscription_noAlarmExit_InSchedule()126 public void testEvaluateSubscription_noAlarmExit_InSchedule() { 127 Calendar now = getNow(); 128 129 // Schedule - 1 hour long; starts now 130 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 131 info.days = new int[] {Calendar.FRIDAY}; 132 info.startHour = now.get(Calendar.HOUR_OF_DAY); 133 info.startMinute = now.get(Calendar.MINUTE); 134 info.nextAlarm = 0; 135 info.exitAtAlarm = false; 136 info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1; 137 info.endMinute = info.startMinute; 138 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 139 ScheduleCalendar cal = new ScheduleCalendar(); 140 cal.setSchedule(info); 141 assertTrue(cal.isInSchedule(now.getTimeInMillis())); 142 143 Condition condition = mService.evaluateSubscriptionLocked( 144 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 145 146 assertEquals(Condition.STATE_TRUE, condition.state); 147 } 148 149 @Test testEvaluateSubscription_noAlarmExit_InScheduleSnoozed()150 public void testEvaluateSubscription_noAlarmExit_InScheduleSnoozed() { 151 Calendar now = getNow(); 152 153 // Schedule - 1 hour long; starts now 154 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 155 info.days = new int[] {Calendar.FRIDAY}; 156 info.startHour = now.get(Calendar.HOUR_OF_DAY); 157 info.startMinute = now.get(Calendar.MINUTE); 158 info.nextAlarm = 0; 159 info.exitAtAlarm = false; 160 info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1; 161 info.endMinute = info.startMinute; 162 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 163 ScheduleCalendar cal = new ScheduleCalendar(); 164 cal.setSchedule(info); 165 assertTrue(cal.isInSchedule(now.getTimeInMillis())); 166 167 mService.addSnoozed(conditionId); 168 169 Condition condition = mService.evaluateSubscriptionLocked( 170 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 171 172 assertEquals(Condition.STATE_FALSE, condition.state); 173 } 174 175 @Test testEvaluateSubscription_noAlarmExit_beforeSchedule()176 public void testEvaluateSubscription_noAlarmExit_beforeSchedule() { 177 Calendar now = new GregorianCalendar(); 178 now.set(Calendar.HOUR_OF_DAY, 14); 179 now.set(Calendar.MINUTE, 15); 180 now.set(Calendar.SECOND, 59); 181 now.set(Calendar.MILLISECOND, 0); 182 now.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); 183 184 // Schedule - 1 hour long; starts in 1 second 185 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 186 info.days = new int[] {Calendar.FRIDAY}; 187 info.startHour = now.get(Calendar.HOUR_OF_DAY); 188 info.startMinute = now.get(Calendar.MINUTE) + 1; 189 info.nextAlarm = 0; 190 info.exitAtAlarm = false; 191 info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1; 192 info.endMinute = info.startMinute; 193 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 194 ScheduleCalendar cal = new ScheduleCalendar(); 195 cal.setSchedule(info); 196 197 Condition condition = mService.evaluateSubscriptionLocked( 198 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 199 200 assertEquals(Condition.STATE_FALSE, condition.state); 201 } 202 203 @Test testEvaluateSubscription_noAlarmExit_endSchedule()204 public void testEvaluateSubscription_noAlarmExit_endSchedule() { 205 Calendar now = getNow(); 206 207 // Schedule - 1 hour long; ends now 208 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 209 info.days = new int[] {Calendar.FRIDAY}; 210 info.startHour = now.get(Calendar.HOUR_OF_DAY) - 1; 211 info.startMinute = now.get(Calendar.MINUTE); 212 info.nextAlarm = 0; 213 info.exitAtAlarm = false; 214 info.endHour = now.get(Calendar.HOUR_OF_DAY); 215 info.endMinute = now.get(Calendar.MINUTE); 216 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 217 ScheduleCalendar cal = new ScheduleCalendar(); 218 cal.setSchedule(info); 219 220 Condition condition = mService.evaluateSubscriptionLocked( 221 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 222 223 assertEquals(Condition.STATE_FALSE, condition.state); 224 } 225 226 @Test testEvaluateSubscription_alarmSetBeforeInSchedule()227 public void testEvaluateSubscription_alarmSetBeforeInSchedule() { 228 Calendar now = getNow(); 229 230 // Schedule - 1 hour long; starts now, ends with alarm 231 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 232 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 233 ScheduleCalendar cal = new ScheduleCalendar(); 234 cal.setSchedule(info); 235 236 // an hour before start, update with an alarm that will fire during the schedule 237 mService.evaluateSubscriptionLocked( 238 conditionId, cal, now.getTimeInMillis() - 1000, now.getTimeInMillis() + 1000); 239 240 // at start, should be in dnd 241 Condition condition = mService.evaluateSubscriptionLocked( 242 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 243 assertEquals(Condition.STATE_TRUE, condition.state); 244 245 // at alarm fire time, should exit dnd 246 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000)); 247 assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(), 248 cal.shouldExitForAlarm(now.getTimeInMillis() + 1000)); 249 condition = mService.evaluateSubscriptionLocked( 250 conditionId, cal, now.getTimeInMillis() + 1000, 0); 251 assertEquals(Condition.STATE_FALSE, condition.state); 252 } 253 254 @Test testEvaluateSubscription_alarmSetInSchedule()255 public void testEvaluateSubscription_alarmSetInSchedule() { 256 Calendar now = getNow(); 257 258 // Schedule - 1 hour long; starts now, ends with alarm 259 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 260 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 261 ScheduleCalendar cal = new ScheduleCalendar(); 262 cal.setSchedule(info); 263 264 // at start, should be in dnd 265 Condition condition = mService.evaluateSubscriptionLocked( 266 conditionId, cal, now.getTimeInMillis(), 0); 267 assertEquals(Condition.STATE_TRUE, condition.state); 268 269 // in schedule, update with alarm time, should be in dnd 270 condition = mService.evaluateSubscriptionLocked( 271 conditionId, cal, now.getTimeInMillis() + 500, now.getTimeInMillis() + 1000); 272 assertEquals(Condition.STATE_TRUE, condition.state); 273 274 // at alarm fire time, should exit dnd 275 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000)); 276 assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(), 277 cal.shouldExitForAlarm(now.getTimeInMillis() + 1000)); 278 condition = mService.evaluateSubscriptionLocked( 279 conditionId, cal, now.getTimeInMillis() + 1000, 0); 280 assertEquals(Condition.STATE_FALSE, condition.state); 281 } 282 283 @Test testEvaluateSubscription_earlierAlarmSet()284 public void testEvaluateSubscription_earlierAlarmSet() { 285 Calendar now = getNow(); 286 287 // Schedule - 1 hour long; starts now, ends with alarm 288 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 289 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 290 ScheduleCalendar cal = new ScheduleCalendar(); 291 cal.setSchedule(info); 292 293 // at start, should be in dnd, alarm in 2000 ms 294 Condition condition = mService.evaluateSubscriptionLocked( 295 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 2000); 296 assertEquals(Condition.STATE_TRUE, condition.state); 297 298 // in schedule, update with earlier alarm time, should be in dnd 299 condition = mService.evaluateSubscriptionLocked( 300 conditionId, cal, now.getTimeInMillis() + 500, now.getTimeInMillis() + 1000); 301 assertEquals(Condition.STATE_TRUE, condition.state); 302 303 // at earliest alarm fire time, should exit dnd 304 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000)); 305 assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(), 306 cal.shouldExitForAlarm(now.getTimeInMillis() + 1000)); 307 condition = mService.evaluateSubscriptionLocked( 308 conditionId, cal, now.getTimeInMillis() + 1000, 0); 309 assertEquals(Condition.STATE_FALSE, condition.state); 310 } 311 312 @Test testEvaluateSubscription_laterAlarmSet()313 public void testEvaluateSubscription_laterAlarmSet() { 314 Calendar now = getNow(); 315 316 // Schedule - 1 hour long; starts now, ends with alarm 317 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 318 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 319 ScheduleCalendar cal = new ScheduleCalendar(); 320 cal.setSchedule(info); 321 322 // at start, should be in dnd, alarm in 500 ms 323 Condition condition = mService.evaluateSubscriptionLocked( 324 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 500); 325 assertEquals(Condition.STATE_TRUE, condition.state); 326 327 // in schedule, update with nextAlarm = later alarm time (1000), should be in dnd 328 condition = mService.evaluateSubscriptionLocked( 329 conditionId, cal, now.getTimeInMillis() + 250, now.getTimeInMillis() + 1000); 330 assertEquals(Condition.STATE_TRUE, condition.state); 331 332 // at next alarm fire time (1000), should exit dnd 333 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000)); 334 assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(), 335 cal.shouldExitForAlarm(now.getTimeInMillis() + 1000)); 336 condition = mService.evaluateSubscriptionLocked( 337 conditionId, cal, now.getTimeInMillis() + 1000, 0); 338 assertEquals(Condition.STATE_FALSE, condition.state); 339 } 340 341 @Test testEvaluateSubscription_alarmCanceled()342 public void testEvaluateSubscription_alarmCanceled() { 343 Calendar now = getNow(); 344 345 // Schedule - 1 hour long; starts now, ends with alarm 346 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 347 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 348 ScheduleCalendar cal = new ScheduleCalendar(); 349 cal.setSchedule(info); 350 351 // at start, should be in dnd, alarm in 500 ms 352 Condition condition = mService.evaluateSubscriptionLocked( 353 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 500); 354 assertEquals(Condition.STATE_TRUE, condition.state); 355 356 // in schedule, cancel alarm 357 condition = mService.evaluateSubscriptionLocked( 358 conditionId, cal, now.getTimeInMillis() + 250, 0); 359 assertEquals(Condition.STATE_TRUE, condition.state); 360 361 // at previous alarm time, should not exit DND 362 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 500)); 363 assertFalse(cal.shouldExitForAlarm(now.getTimeInMillis() + 500)); 364 condition = mService.evaluateSubscriptionLocked( 365 conditionId, cal, now.getTimeInMillis() + 500, 0); 366 assertEquals(Condition.STATE_TRUE, condition.state); 367 368 // end of schedule, exit DND 369 now.add(Calendar.HOUR_OF_DAY, 1); 370 condition = mService.evaluateSubscriptionLocked(conditionId, cal, now.getTimeInMillis(), 0); 371 assertEquals(Condition.STATE_FALSE, condition.state); 372 } 373 374 @Test testGetPendingIntent()375 public void testGetPendingIntent() { 376 PendingIntent pi = mService.getPendingIntent(1000); 377 assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage()); 378 } 379 380 @Test 381 @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) onSubscribe_registersReceiverForAllUsers()382 public void onSubscribe_registersReceiverForAllUsers() { 383 Calendar now = getNow(); 384 Uri condition = ZenModeConfig.toScheduleConditionId(getScheduleEndsInHour(now)); 385 386 mService.onSubscribe(condition); 387 388 ArgumentCaptor<IntentFilter> filterCaptor = ArgumentCaptor.forClass(IntentFilter.class); 389 verify(mContext).registerReceiverForAllUsers(any(), filterCaptor.capture(), any(), any()); 390 IntentFilter filter = filterCaptor.getValue(); 391 assertThat(filter.actionsIterator()).isNotNull(); 392 assertThat(ImmutableList.copyOf(filter.actionsIterator())) 393 .contains(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); 394 } 395 396 @Test 397 @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) onAlarmClockChanged_storesNextAlarm()398 public void onAlarmClockChanged_storesNextAlarm() { 399 Instant scheduleStart = Instant.parse("2024-10-22T16:00:00Z"); 400 Instant scheduleEnd = scheduleStart.plus(1, HOURS); 401 402 Instant now = scheduleStart.plus(15, MINUTES); 403 mClock.setNowMillis(now.toEpochMilli()); 404 405 Uri condition = ZenModeConfig.toScheduleConditionId( 406 getOneHourSchedule(scheduleStart.atZone(ZoneId.systemDefault()))); 407 mService.onSubscribe(condition); 408 409 // Now prepare to send an "alarm set for 16:30" broadcast. 410 Instant alarm = scheduleStart.plus(30, MINUTES); 411 ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass( 412 BroadcastReceiver.class); 413 verify(mContext).registerReceiverForAllUsers(receiverCaptor.capture(), any(), any(), any()); 414 BroadcastReceiver receiver = receiverCaptor.getValue(); 415 receiver.setPendingResult(pendingResultForUserBroadcast(ActivityManager.getCurrentUser())); 416 when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn( 417 new AlarmManager.AlarmClockInfo(alarm.toEpochMilli(), null)); 418 419 Intent intent = new Intent(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); 420 receiver.onReceive(mContext, intent); 421 422 // The time for the alarm was stored in the ScheduleCalendar, meaning the rule will end when 423 // the next evaluation after that point happens. 424 ScheduleCalendar scheduleCalendar = 425 mService.getSubscriptions().values().stream().findFirst().get(); 426 assertThat(scheduleCalendar.shouldExitForAlarm(alarm.toEpochMilli() - 1)).isFalse(); 427 assertThat(scheduleCalendar.shouldExitForAlarm(alarm.toEpochMilli() + 1)).isTrue(); 428 429 // But the next wakeup is unchanged, at the time of the schedule end (17:00). 430 verify(mAlarmManager, times(2)).setExact(eq(RTC_WAKEUP), eq(scheduleEnd.toEpochMilli()), 431 any()); 432 } 433 434 @Test 435 @EnableFlags(android.app.Flags.FLAG_MODES_HSUM) onAlarmClockChanged_forAnotherUser_isIgnored()436 public void onAlarmClockChanged_forAnotherUser_isIgnored() { 437 Instant scheduleStart = Instant.parse("2024-10-22T16:00:00Z"); 438 Instant now = scheduleStart.plus(15, MINUTES); 439 mClock.setNowMillis(now.toEpochMilli()); 440 441 Uri condition = ZenModeConfig.toScheduleConditionId( 442 getOneHourSchedule(scheduleStart.atZone(ZoneId.systemDefault()))); 443 mService.onSubscribe(condition); 444 445 // Now prepare to send an "alarm set for a different user" broadcast. 446 ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass( 447 BroadcastReceiver.class); 448 verify(mContext).registerReceiverForAllUsers(receiverCaptor.capture(), any(), any(), any()); 449 BroadcastReceiver receiver = receiverCaptor.getValue(); 450 451 reset(mAlarmManager); 452 int anotherUser = ActivityManager.getCurrentUser() + 1; 453 receiver.setPendingResult(pendingResultForUserBroadcast(anotherUser)); 454 Intent intent = new Intent(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); 455 receiver.onReceive(mContext, intent); 456 457 // The alarm data was not read. 458 verify(mAlarmManager, never()).getNextAlarmClock(anyInt()); 459 } 460 getNow()461 private Calendar getNow() { 462 Calendar now = new GregorianCalendar(); 463 now.set(Calendar.HOUR_OF_DAY, 14); 464 now.set(Calendar.MINUTE, 16); 465 now.set(Calendar.SECOND, 0); 466 now.set(Calendar.MILLISECOND, 0); 467 now.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); 468 return now; 469 } 470 getScheduleEndsInHour(Calendar now)471 private ZenModeConfig.ScheduleInfo getScheduleEndsInHour(Calendar now) { 472 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 473 info.days = new int[] {Calendar.FRIDAY}; 474 info.startHour = now.get(Calendar.HOUR_OF_DAY); 475 info.startMinute = now.get(Calendar.MINUTE); 476 info.exitAtAlarm = true; 477 info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1; 478 info.endMinute = now.get(Calendar.MINUTE); 479 return info; 480 } 481 getOneHourSchedule(ZonedDateTime start)482 private static ZenModeConfig.ScheduleInfo getOneHourSchedule(ZonedDateTime start) { 483 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 484 // Note: DayOfWeek.MONDAY doesn't match Calendar.MONDAY 485 info.days = new int[] { (start.getDayOfWeek().getValue() % 7) + 1 }; 486 info.startHour = start.getHour(); 487 info.startMinute = start.getMinute(); 488 info.endHour = start.plusHours(1).getHour(); 489 info.endMinute = start.plusHours(1).getMinute(); 490 info.exitAtAlarm = true; 491 return info; 492 } 493 pendingResultForUserBroadcast(int userId)494 private static BroadcastReceiver.PendingResult pendingResultForUserBroadcast(int userId) { 495 return new BroadcastReceiver.PendingResult(0, "", new Bundle(), 0, false, false, null, 496 userId, 0); 497 } 498 499 private static class TestClock extends SimpleClock { 500 private long mNowMillis = 441644400000L; 501 TestClock()502 private TestClock() { 503 super(ZoneOffset.UTC); 504 } 505 506 @Override millis()507 public long millis() { 508 return mNowMillis; 509 } 510 setNowMillis(long millis)511 private void setNowMillis(long millis) { 512 mNowMillis = millis; 513 } 514 } 515 } 516