1 package com.android.server.notification; 2 3 import static org.junit.Assert.assertEquals; 4 import static org.junit.Assert.assertFalse; 5 import static org.junit.Assert.assertTrue; 6 import static org.mockito.Mockito.mock; 7 import static org.mockito.Mockito.spy; 8 9 import android.app.Application; 10 import android.app.PendingIntent; 11 import android.content.Intent; 12 import android.net.Uri; 13 import android.service.notification.Condition; 14 import android.service.notification.ScheduleCalendar; 15 import android.service.notification.ZenModeConfig; 16 import android.testing.AndroidTestingRunner; 17 import android.testing.TestableLooper.RunWithLooper; 18 19 import androidx.test.filters.SmallTest; 20 21 import com.android.server.UiServiceTestCase; 22 import com.android.server.pm.PackageManagerService; 23 24 import org.junit.Before; 25 import org.junit.Test; 26 import org.junit.runner.RunWith; 27 import org.mockito.MockitoAnnotations; 28 29 import java.util.Calendar; 30 import java.util.GregorianCalendar; 31 32 @RunWith(AndroidTestingRunner.class) 33 @SmallTest 34 @RunWithLooper 35 public class ScheduleConditionProviderTest extends UiServiceTestCase { 36 37 ScheduleConditionProvider mService; 38 39 @Before setUp()40 public void setUp() throws Exception { 41 MockitoAnnotations.initMocks(this); 42 43 Intent startIntent = 44 new Intent("com.android.server.notification.ScheduleConditionProvider"); 45 startIntent.setPackage("android"); 46 ScheduleConditionProvider service = new ScheduleConditionProvider(); 47 service.attach( 48 getContext(), 49 null, // ActivityThread not actually used in Service 50 ScheduleConditionProvider.class.getName(), 51 null, // token not needed when not talking with the activity manager 52 mock(Application.class), 53 null // mocked services don't talk with the activity manager 54 ); 55 service.onCreate(); 56 service.onBind(startIntent); 57 mService = spy(service); 58 } 59 60 @Test testIsValidConditionId_incomplete()61 public void testIsValidConditionId_incomplete() throws Exception { 62 Uri badConditionId = Uri.EMPTY; 63 assertFalse(mService.isValidConditionId(badConditionId)); 64 assertEquals(Condition.STATE_ERROR, 65 mService.evaluateSubscriptionLocked(badConditionId, null, 0, 1000).state); 66 } 67 68 @Test testIsValidConditionId()69 public void testIsValidConditionId() throws Exception { 70 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 71 info.days = new int[] {1, 2, 4}; 72 info.startHour = 8; 73 info.startMinute = 56; 74 info.nextAlarm = 1000; 75 info.exitAtAlarm = true; 76 info.endHour = 12; 77 info.endMinute = 9; 78 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 79 assertTrue(mService.isValidConditionId(conditionId)); 80 } 81 82 @Test testEvaluateSubscription_noAlarmExit_InSchedule()83 public void testEvaluateSubscription_noAlarmExit_InSchedule() { 84 Calendar now = getNow(); 85 86 // Schedule - 1 hour long; starts now 87 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 88 info.days = new int[] {Calendar.FRIDAY}; 89 info.startHour = now.get(Calendar.HOUR_OF_DAY); 90 info.startMinute = now.get(Calendar.MINUTE); 91 info.nextAlarm = 0; 92 info.exitAtAlarm = false; 93 info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1; 94 info.endMinute = info.startMinute; 95 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 96 ScheduleCalendar cal = new ScheduleCalendar(); 97 cal.setSchedule(info); 98 assertTrue(cal.isInSchedule(now.getTimeInMillis())); 99 100 Condition condition = mService.evaluateSubscriptionLocked( 101 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 102 103 assertEquals(Condition.STATE_TRUE, condition.state); 104 } 105 106 @Test testEvaluateSubscription_noAlarmExit_InScheduleSnoozed()107 public void testEvaluateSubscription_noAlarmExit_InScheduleSnoozed() { 108 Calendar now = getNow(); 109 110 // Schedule - 1 hour long; starts now 111 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 112 info.days = new int[] {Calendar.FRIDAY}; 113 info.startHour = now.get(Calendar.HOUR_OF_DAY); 114 info.startMinute = now.get(Calendar.MINUTE); 115 info.nextAlarm = 0; 116 info.exitAtAlarm = false; 117 info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1; 118 info.endMinute = info.startMinute; 119 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 120 ScheduleCalendar cal = new ScheduleCalendar(); 121 cal.setSchedule(info); 122 assertTrue(cal.isInSchedule(now.getTimeInMillis())); 123 124 mService.addSnoozed(conditionId); 125 126 Condition condition = mService.evaluateSubscriptionLocked( 127 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 128 129 assertEquals(Condition.STATE_FALSE, condition.state); 130 } 131 132 @Test testEvaluateSubscription_noAlarmExit_beforeSchedule()133 public void testEvaluateSubscription_noAlarmExit_beforeSchedule() { 134 Calendar now = new GregorianCalendar(); 135 now.set(Calendar.HOUR_OF_DAY, 14); 136 now.set(Calendar.MINUTE, 15); 137 now.set(Calendar.SECOND, 59); 138 now.set(Calendar.MILLISECOND, 0); 139 now.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); 140 141 // Schedule - 1 hour long; starts in 1 second 142 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 143 info.days = new int[] {Calendar.FRIDAY}; 144 info.startHour = now.get(Calendar.HOUR_OF_DAY); 145 info.startMinute = now.get(Calendar.MINUTE) + 1; 146 info.nextAlarm = 0; 147 info.exitAtAlarm = false; 148 info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1; 149 info.endMinute = info.startMinute; 150 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 151 ScheduleCalendar cal = new ScheduleCalendar(); 152 cal.setSchedule(info); 153 154 Condition condition = mService.evaluateSubscriptionLocked( 155 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 156 157 assertEquals(Condition.STATE_FALSE, condition.state); 158 } 159 160 @Test testEvaluateSubscription_noAlarmExit_endSchedule()161 public void testEvaluateSubscription_noAlarmExit_endSchedule() { 162 Calendar now = getNow(); 163 164 // Schedule - 1 hour long; ends now 165 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 166 info.days = new int[] {Calendar.FRIDAY}; 167 info.startHour = now.get(Calendar.HOUR_OF_DAY) - 1; 168 info.startMinute = now.get(Calendar.MINUTE); 169 info.nextAlarm = 0; 170 info.exitAtAlarm = false; 171 info.endHour = now.get(Calendar.HOUR_OF_DAY); 172 info.endMinute = now.get(Calendar.MINUTE); 173 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 174 ScheduleCalendar cal = new ScheduleCalendar(); 175 cal.setSchedule(info); 176 177 Condition condition = mService.evaluateSubscriptionLocked( 178 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 179 180 assertEquals(Condition.STATE_FALSE, condition.state); 181 } 182 183 @Test testEvaluateSubscription_alarmSetBeforeInSchedule()184 public void testEvaluateSubscription_alarmSetBeforeInSchedule() { 185 Calendar now = getNow(); 186 187 // Schedule - 1 hour long; starts now, ends with alarm 188 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 189 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 190 ScheduleCalendar cal = new ScheduleCalendar(); 191 cal.setSchedule(info); 192 193 // an hour before start, update with an alarm that will fire during the schedule 194 mService.evaluateSubscriptionLocked( 195 conditionId, cal, now.getTimeInMillis() - 1000, now.getTimeInMillis() + 1000); 196 197 // at start, should be in dnd 198 Condition condition = mService.evaluateSubscriptionLocked( 199 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 1000); 200 assertEquals(Condition.STATE_TRUE, condition.state); 201 202 // at alarm fire time, should exit dnd 203 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000)); 204 assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(), 205 cal.shouldExitForAlarm(now.getTimeInMillis() + 1000)); 206 condition = mService.evaluateSubscriptionLocked( 207 conditionId, cal, now.getTimeInMillis() + 1000, 0); 208 assertEquals(Condition.STATE_FALSE, condition.state); 209 } 210 211 @Test testEvaluateSubscription_alarmSetInSchedule()212 public void testEvaluateSubscription_alarmSetInSchedule() { 213 Calendar now = getNow(); 214 215 // Schedule - 1 hour long; starts now, ends with alarm 216 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 217 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 218 ScheduleCalendar cal = new ScheduleCalendar(); 219 cal.setSchedule(info); 220 221 // at start, should be in dnd 222 Condition condition = mService.evaluateSubscriptionLocked( 223 conditionId, cal, now.getTimeInMillis(), 0); 224 assertEquals(Condition.STATE_TRUE, condition.state); 225 226 // in schedule, update with alarm time, should be in dnd 227 condition = mService.evaluateSubscriptionLocked( 228 conditionId, cal, now.getTimeInMillis() + 500, now.getTimeInMillis() + 1000); 229 assertEquals(Condition.STATE_TRUE, condition.state); 230 231 // at alarm fire time, should exit dnd 232 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000)); 233 assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(), 234 cal.shouldExitForAlarm(now.getTimeInMillis() + 1000)); 235 condition = mService.evaluateSubscriptionLocked( 236 conditionId, cal, now.getTimeInMillis() + 1000, 0); 237 assertEquals(Condition.STATE_FALSE, condition.state); 238 } 239 240 @Test testEvaluateSubscription_earlierAlarmSet()241 public void testEvaluateSubscription_earlierAlarmSet() { 242 Calendar now = getNow(); 243 244 // Schedule - 1 hour long; starts now, ends with alarm 245 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 246 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 247 ScheduleCalendar cal = new ScheduleCalendar(); 248 cal.setSchedule(info); 249 250 // at start, should be in dnd, alarm in 2000 ms 251 Condition condition = mService.evaluateSubscriptionLocked( 252 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 2000); 253 assertEquals(Condition.STATE_TRUE, condition.state); 254 255 // in schedule, update with earlier alarm time, should be in dnd 256 condition = mService.evaluateSubscriptionLocked( 257 conditionId, cal, now.getTimeInMillis() + 500, now.getTimeInMillis() + 1000); 258 assertEquals(Condition.STATE_TRUE, condition.state); 259 260 // at earliest alarm fire time, should exit dnd 261 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000)); 262 assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(), 263 cal.shouldExitForAlarm(now.getTimeInMillis() + 1000)); 264 condition = mService.evaluateSubscriptionLocked( 265 conditionId, cal, now.getTimeInMillis() + 1000, 0); 266 assertEquals(Condition.STATE_FALSE, condition.state); 267 } 268 269 @Test testEvaluateSubscription_laterAlarmSet()270 public void testEvaluateSubscription_laterAlarmSet() { 271 Calendar now = getNow(); 272 273 // Schedule - 1 hour long; starts now, ends with alarm 274 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 275 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 276 ScheduleCalendar cal = new ScheduleCalendar(); 277 cal.setSchedule(info); 278 279 // at start, should be in dnd, alarm in 500 ms 280 Condition condition = mService.evaluateSubscriptionLocked( 281 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 500); 282 assertEquals(Condition.STATE_TRUE, condition.state); 283 284 // in schedule, update with nextAlarm = later alarm time (1000), should be in dnd 285 condition = mService.evaluateSubscriptionLocked( 286 conditionId, cal, now.getTimeInMillis() + 250, now.getTimeInMillis() + 1000); 287 assertEquals(Condition.STATE_TRUE, condition.state); 288 289 // at next alarm fire time (1000), should exit dnd 290 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 1000)); 291 assertTrue("" + info.nextAlarm + " " + now.getTimeInMillis(), 292 cal.shouldExitForAlarm(now.getTimeInMillis() + 1000)); 293 condition = mService.evaluateSubscriptionLocked( 294 conditionId, cal, now.getTimeInMillis() + 1000, 0); 295 assertEquals(Condition.STATE_FALSE, condition.state); 296 } 297 298 @Test testEvaluateSubscription_alarmCanceled()299 public void testEvaluateSubscription_alarmCanceled() { 300 Calendar now = getNow(); 301 302 // Schedule - 1 hour long; starts now, ends with alarm 303 ZenModeConfig.ScheduleInfo info = getScheduleEndsInHour(now); 304 Uri conditionId = ZenModeConfig.toScheduleConditionId(info); 305 ScheduleCalendar cal = new ScheduleCalendar(); 306 cal.setSchedule(info); 307 308 // at start, should be in dnd, alarm in 500 ms 309 Condition condition = mService.evaluateSubscriptionLocked( 310 conditionId, cal, now.getTimeInMillis(), now.getTimeInMillis() + 500); 311 assertEquals(Condition.STATE_TRUE, condition.state); 312 313 // in schedule, cancel alarm 314 condition = mService.evaluateSubscriptionLocked( 315 conditionId, cal, now.getTimeInMillis() + 250, 0); 316 assertEquals(Condition.STATE_TRUE, condition.state); 317 318 // at previous alarm time, should not exit DND 319 assertTrue(cal.isInSchedule(now.getTimeInMillis() + 500)); 320 assertFalse(cal.shouldExitForAlarm(now.getTimeInMillis() + 500)); 321 condition = mService.evaluateSubscriptionLocked( 322 conditionId, cal, now.getTimeInMillis() + 500, 0); 323 assertEquals(Condition.STATE_TRUE, condition.state); 324 325 // end of schedule, exit DND 326 now.add(Calendar.HOUR_OF_DAY, 1); 327 condition = mService.evaluateSubscriptionLocked(conditionId, cal, now.getTimeInMillis(), 0); 328 assertEquals(Condition.STATE_FALSE, condition.state); 329 } 330 331 @Test testGetPendingIntent()332 public void testGetPendingIntent() { 333 PendingIntent pi = mService.getPendingIntent(1000); 334 assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME, pi.getIntent().getPackage()); 335 } 336 getNow()337 private Calendar getNow() { 338 Calendar now = new GregorianCalendar(); 339 now.set(Calendar.HOUR_OF_DAY, 14); 340 now.set(Calendar.MINUTE, 16); 341 now.set(Calendar.SECOND, 0); 342 now.set(Calendar.MILLISECOND, 0); 343 now.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY); 344 return now; 345 } 346 getScheduleEndsInHour(Calendar now)347 private ZenModeConfig.ScheduleInfo getScheduleEndsInHour(Calendar now) { 348 ZenModeConfig.ScheduleInfo info = new ZenModeConfig.ScheduleInfo(); 349 info.days = new int[] {Calendar.FRIDAY}; 350 info.startHour = now.get(Calendar.HOUR_OF_DAY); 351 info.startMinute = now.get(Calendar.MINUTE); 352 info.exitAtAlarm = true; 353 info.endHour = now.get(Calendar.HOUR_OF_DAY) + 1; 354 info.endMinute = now.get(Calendar.MINUTE); 355 return info; 356 } 357 } 358