1/* 2 * Copyright (C) 2016 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 */ 16package com.android.server.notification; 17 18import static android.app.Notification.GROUP_ALERT_ALL; 19import static android.app.Notification.GROUP_ALERT_CHILDREN; 20import static android.app.Notification.GROUP_ALERT_SUMMARY; 21import static android.app.NotificationManager.IMPORTANCE_HIGH; 22 23import static junit.framework.Assert.assertNull; 24import static junit.framework.Assert.assertTrue; 25 26import static org.mockito.Matchers.anyBoolean; 27import static org.mockito.Matchers.anyInt; 28import static org.mockito.Matchers.anyObject; 29import static org.mockito.Matchers.anyString; 30import static org.mockito.Matchers.argThat; 31import static org.mockito.Matchers.eq; 32import static org.mockito.Mockito.never; 33import static org.mockito.Mockito.spy; 34import static org.mockito.Mockito.timeout; 35import static org.mockito.Mockito.times; 36import static org.mockito.Mockito.verify; 37import static org.mockito.Mockito.when; 38 39import android.app.ActivityManager; 40import android.app.Notification; 41import android.app.Notification.Builder; 42import android.app.NotificationChannel; 43import android.app.NotificationManager; 44import android.content.Context; 45import android.content.pm.PackageManager; 46import android.graphics.Color; 47import android.media.AudioAttributes; 48import android.media.AudioManager; 49import android.net.Uri; 50import android.os.Handler; 51import android.os.RemoteException; 52import android.os.UserHandle; 53import android.os.VibrationEffect; 54import android.os.Vibrator; 55import android.provider.Settings; 56import android.service.notification.StatusBarNotification; 57import android.support.test.runner.AndroidJUnit4; 58import android.test.suitebuilder.annotation.SmallTest; 59 60import com.android.server.lights.Light; 61 62import org.junit.Before; 63import org.junit.Test; 64import org.junit.runner.RunWith; 65import org.mockito.ArgumentMatcher; 66import org.mockito.Mock; 67import org.mockito.Mockito; 68import org.mockito.MockitoAnnotations; 69 70@SmallTest 71@RunWith(AndroidJUnit4.class) 72public class BuzzBeepBlinkTest extends NotificationTestCase { 73 74 @Mock AudioManager mAudioManager; 75 @Mock Vibrator mVibrator; 76 @Mock android.media.IRingtonePlayer mRingtonePlayer; 77 @Mock Light mLight; 78 @Mock Handler mHandler; 79 80 private NotificationManagerService mService; 81 private String mPkg = "com.android.server.notification"; 82 private int mId = 1001; 83 private int mOtherId = 1002; 84 private String mTag = null; 85 private int mUid = 1000; 86 private int mPid = 2000; 87 private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser()); 88 89 private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1); 90 private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0); 91 92 private static final long[] CUSTOM_VIBRATION = new long[] { 93 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 94 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 95 300, 400, 300, 400, 300, 400, 300, 400, 300, 400, 300, 400 }; 96 private static final Uri CUSTOM_SOUND = Settings.System.DEFAULT_ALARM_ALERT_URI; 97 private static final AudioAttributes CUSTOM_ATTRIBUTES = new AudioAttributes.Builder() 98 .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN) 99 .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) 100 .build(); 101 private static final int CUSTOM_LIGHT_COLOR = Color.BLACK; 102 private static final int CUSTOM_LIGHT_ON = 10000; 103 private static final int CUSTOM_LIGHT_OFF = 10000; 104 private static final long[] FALLBACK_VIBRATION_PATTERN = new long[] {100, 100, 100}; 105 private static final VibrationEffect FALLBACK_VIBRATION = 106 VibrationEffect.createWaveform(FALLBACK_VIBRATION_PATTERN, -1); 107 private static final int MAX_VIBRATION_DELAY = 1000; 108 109 @Before 110 public void setUp() { 111 MockitoAnnotations.initMocks(this); 112 113 when(mAudioManager.isAudioFocusExclusive()).thenReturn(false); 114 when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer); 115 when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10); 116 when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); 117 118 mService = new NotificationManagerService(getContext()); 119 mService.setAudioManager(mAudioManager); 120 mService.setVibrator(mVibrator); 121 mService.setSystemReady(true); 122 mService.setHandler(mHandler); 123 mService.setLights(mLight); 124 mService.setScreenOn(false); 125 mService.setFallbackVibrationPattern(FALLBACK_VIBRATION_PATTERN); 126 } 127 128 // 129 // Convenience functions for creating notification records 130 // 131 132 private NotificationRecord getNoisyOtherNotification() { 133 return getNotificationRecord(mOtherId, false /* insistent */, false /* once */, 134 true /* noisy */, true /* buzzy*/, false /* lights */); 135 } 136 137 private NotificationRecord getBeepyNotification() { 138 return getNotificationRecord(mId, false /* insistent */, false /* once */, 139 true /* noisy */, false /* buzzy*/, false /* lights */); 140 } 141 142 private NotificationRecord getBeepyOnceNotification() { 143 return getNotificationRecord(mId, false /* insistent */, true /* once */, 144 true /* noisy */, false /* buzzy*/, false /* lights */); 145 } 146 147 private NotificationRecord getQuietNotification() { 148 return getNotificationRecord(mId, false /* insistent */, false /* once */, 149 false /* noisy */, false /* buzzy*/, false /* lights */); 150 } 151 152 private NotificationRecord getQuietOtherNotification() { 153 return getNotificationRecord(mOtherId, false /* insistent */, false /* once */, 154 false /* noisy */, false /* buzzy*/, false /* lights */); 155 } 156 157 private NotificationRecord getQuietOnceNotification() { 158 return getNotificationRecord(mId, false /* insistent */, true /* once */, 159 false /* noisy */, false /* buzzy*/, false /* lights */); 160 } 161 162 private NotificationRecord getInsistentBeepyNotification() { 163 return getNotificationRecord(mId, true /* insistent */, false /* once */, 164 true /* noisy */, false /* buzzy*/, false /* lights */); 165 } 166 167 private NotificationRecord getInsistentBeepyLeanbackNotification() { 168 return getLeanbackNotificationRecord(mId, true /* insistent */, false /* once */, 169 true /* noisy */, false /* buzzy*/, false /* lights */); 170 } 171 172 private NotificationRecord getBuzzyNotification() { 173 return getNotificationRecord(mId, false /* insistent */, false /* once */, 174 false /* noisy */, true /* buzzy*/, false /* lights */); 175 } 176 177 private NotificationRecord getBuzzyOnceNotification() { 178 return getNotificationRecord(mId, false /* insistent */, true /* once */, 179 false /* noisy */, true /* buzzy*/, false /* lights */); 180 } 181 182 private NotificationRecord getInsistentBuzzyNotification() { 183 return getNotificationRecord(mId, true /* insistent */, false /* once */, 184 false /* noisy */, true /* buzzy*/, false /* lights */); 185 } 186 187 private NotificationRecord getBuzzyBeepyNotification() { 188 return getNotificationRecord(mId, false /* insistent */, false /* once */, 189 true /* noisy */, true /* buzzy*/, false /* lights */); 190 } 191 192 private NotificationRecord getLightsNotification() { 193 return getNotificationRecord(mId, false /* insistent */, true /* once */, 194 false /* noisy */, true /* buzzy*/, true /* lights */); 195 } 196 197 private NotificationRecord getCustomLightsNotification() { 198 return getNotificationRecord(mId, false /* insistent */, true /* once */, 199 false /* noisy */, true /* buzzy*/, true /* lights */, 200 true /* defaultVibration */, true /* defaultSound */, false /* defaultLights */, 201 null, Notification.GROUP_ALERT_ALL, false); 202 } 203 204 private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, 205 boolean noisy, boolean buzzy, boolean lights) { 206 return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true, 207 null, Notification.GROUP_ALERT_ALL, false); 208 } 209 210 private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, boolean once, 211 boolean noisy, boolean buzzy, boolean lights) { 212 return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true, 213 null, Notification.GROUP_ALERT_ALL, true); 214 } 215 216 private NotificationRecord getBeepyNotificationRecord(String groupKey, int groupAlertBehavior) { 217 return getNotificationRecord(mId, false, false, true, false, false, true, true, true, 218 groupKey, groupAlertBehavior, false); 219 } 220 221 private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, 222 boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration, 223 boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior, 224 boolean isLeanback) { 225 NotificationChannel channel = 226 new NotificationChannel("test", "test", IMPORTANCE_HIGH); 227 final Builder builder = new Builder(getContext()) 228 .setContentTitle("foo") 229 .setSmallIcon(android.R.drawable.sym_def_app_icon) 230 .setPriority(Notification.PRIORITY_HIGH) 231 .setOnlyAlertOnce(once); 232 233 int defaults = 0; 234 if (noisy) { 235 if (defaultSound) { 236 defaults |= Notification.DEFAULT_SOUND; 237 channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, 238 Notification.AUDIO_ATTRIBUTES_DEFAULT); 239 } else { 240 builder.setSound(CUSTOM_SOUND); 241 channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES); 242 } 243 } else { 244 channel.setSound(null, null); 245 } 246 if (buzzy) { 247 if (defaultVibration) { 248 defaults |= Notification.DEFAULT_VIBRATE; 249 } else { 250 builder.setVibrate(CUSTOM_VIBRATION); 251 channel.setVibrationPattern(CUSTOM_VIBRATION); 252 } 253 channel.enableVibration(true); 254 } 255 if (lights) { 256 if (defaultLights) { 257 defaults |= Notification.DEFAULT_LIGHTS; 258 } else { 259 builder.setLights(CUSTOM_LIGHT_COLOR, CUSTOM_LIGHT_ON, CUSTOM_LIGHT_OFF); 260 } 261 channel.enableLights(true); 262 } 263 builder.setDefaults(defaults); 264 265 builder.setGroup(groupKey); 266 builder.setGroupAlertBehavior(groupAlertBehavior); 267 268 Notification n = builder.build(); 269 if (insistent) { 270 n.flags |= Notification.FLAG_INSISTENT; 271 } 272 273 Context context = spy(getContext()); 274 PackageManager packageManager = spy(context.getPackageManager()); 275 when(context.getPackageManager()).thenReturn(packageManager); 276 when(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) 277 .thenReturn(isLeanback); 278 279 StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, 280 mPid, n, mUser, null, System.currentTimeMillis()); 281 NotificationRecord r = new NotificationRecord(context, sbn, channel); 282 mService.addNotification(r); 283 return r; 284 } 285 286 // 287 // Convenience functions for interacting with mocks 288 // 289 290 private void verifyNeverBeep() throws RemoteException { 291 verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(), 292 anyBoolean(), (AudioAttributes) anyObject()); 293 } 294 295 private void verifyBeep() throws RemoteException { 296 verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(), 297 eq(true), (AudioAttributes) anyObject()); 298 } 299 300 private void verifyBeepLooped() throws RemoteException { 301 verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(), 302 eq(false), (AudioAttributes) anyObject()); 303 } 304 305 private void verifyCustomBeep() throws RemoteException { 306 verify(mRingtonePlayer, times(1)).playAsync(eq(CUSTOM_SOUND), (UserHandle) anyObject(), 307 eq(false), (AudioAttributes) anyObject()); 308 } 309 310 private void verifyNeverStopAudio() throws RemoteException { 311 verify(mRingtonePlayer, never()).stopAsync(); 312 } 313 314 private void verifyStopAudio() throws RemoteException { 315 verify(mRingtonePlayer, times(1)).stopAsync(); 316 } 317 318 private void verifyNeverVibrate() { 319 verify(mVibrator, never()).vibrate(anyInt(), anyString(), (VibrationEffect) anyObject(), 320 (AudioAttributes) anyObject()); 321 } 322 323 private void verifyVibrate() { 324 verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher), 325 (AudioAttributes) anyObject()); 326 } 327 328 private void verifyVibrateLooped() { 329 verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher), 330 (AudioAttributes) anyObject()); 331 } 332 333 private void verifyDelayedVibrateLooped() { 334 verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(), 335 argThat(mVibrateLoopMatcher), (AudioAttributes) anyObject()); 336 } 337 338 private void verifyStopVibrate() { 339 verify(mVibrator, times(1)).cancel(); 340 } 341 342 private void verifyNeverStopVibrate() throws RemoteException { 343 verify(mVibrator, never()).cancel(); 344 } 345 346 private void verifyLights() { 347 verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt()); 348 } 349 350 private void verifyCustomLights() { 351 verify(mLight, times(1)).setFlashing( 352 eq(CUSTOM_LIGHT_COLOR), anyInt(), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF)); 353 } 354 355 // 356 // Tests 357 // 358 359 @Test 360 public void testLights() throws Exception { 361 NotificationRecord r = getLightsNotification(); 362 r.setImportance(NotificationManager.IMPORTANCE_DEFAULT, "for testing"); 363 364 mService.buzzBeepBlinkLocked(r); 365 366 verifyLights(); 367 } 368 369 @Test 370 public void testBeep() throws Exception { 371 NotificationRecord r = getBeepyNotification(); 372 373 mService.buzzBeepBlinkLocked(r); 374 375 verifyBeepLooped(); 376 verifyNeverVibrate(); 377 } 378 379 @Test 380 public void testBeepInsistently() throws Exception { 381 NotificationRecord r = getInsistentBeepyNotification(); 382 383 mService.buzzBeepBlinkLocked(r); 384 385 verifyBeep(); 386 } 387 388 @Test 389 public void testNoLeanbackBeep() throws Exception { 390 NotificationRecord r = getInsistentBeepyLeanbackNotification(); 391 392 mService.buzzBeepBlinkLocked(r); 393 394 verifyNeverBeep(); 395 } 396 397 @Test 398 public void testNoInterruptionForMin() throws Exception { 399 NotificationRecord r = getBeepyNotification(); 400 r.setImportance(NotificationManager.IMPORTANCE_MIN, "foo"); 401 402 mService.buzzBeepBlinkLocked(r); 403 404 verifyNeverBeep(); 405 verifyNeverVibrate(); 406 } 407 408 @Test 409 public void testNoInterruptionForIntercepted() throws Exception { 410 NotificationRecord r = getBeepyNotification(); 411 r.setIntercepted(true); 412 413 mService.buzzBeepBlinkLocked(r); 414 415 verifyNeverBeep(); 416 verifyNeverVibrate(); 417 } 418 419 @Test 420 public void testBeepTwice() throws Exception { 421 NotificationRecord r = getBeepyNotification(); 422 423 // set up internal state 424 mService.buzzBeepBlinkLocked(r); 425 Mockito.reset(mRingtonePlayer); 426 427 // update should beep 428 r.isUpdate = true; 429 mService.buzzBeepBlinkLocked(r); 430 verifyBeepLooped(); 431 } 432 433 @Test 434 public void testHonorAlertOnlyOnceForBeep() throws Exception { 435 NotificationRecord r = getBeepyNotification(); 436 NotificationRecord s = getBeepyOnceNotification(); 437 s.isUpdate = true; 438 439 // set up internal state 440 mService.buzzBeepBlinkLocked(r); 441 Mockito.reset(mRingtonePlayer); 442 443 // update should not beep 444 mService.buzzBeepBlinkLocked(s); 445 verifyNeverBeep(); 446 } 447 448 @Test 449 public void testNoisyUpdateDoesNotCancelAudio() throws Exception { 450 NotificationRecord r = getBeepyNotification(); 451 452 mService.buzzBeepBlinkLocked(r); 453 r.isUpdate = true; 454 mService.buzzBeepBlinkLocked(r); 455 456 verifyNeverStopAudio(); 457 } 458 459 @Test 460 public void testNoisyOnceUpdateDoesNotCancelAudio() throws Exception { 461 NotificationRecord r = getBeepyNotification(); 462 NotificationRecord s = getBeepyOnceNotification(); 463 s.isUpdate = true; 464 465 mService.buzzBeepBlinkLocked(r); 466 mService.buzzBeepBlinkLocked(s); 467 468 verifyNeverStopAudio(); 469 } 470 471 @Test 472 public void testQuietUpdateDoesNotCancelAudioFromOther() throws Exception { 473 NotificationRecord r = getBeepyNotification(); 474 NotificationRecord s = getQuietNotification(); 475 s.isUpdate = true; 476 NotificationRecord other = getNoisyOtherNotification(); 477 478 // set up internal state 479 mService.buzzBeepBlinkLocked(r); 480 mService.buzzBeepBlinkLocked(other); // this takes the audio stream 481 Mockito.reset(mRingtonePlayer); 482 483 // should not stop noise, since we no longer own it 484 mService.buzzBeepBlinkLocked(s); // this no longer owns the stream 485 verifyNeverStopAudio(); 486 } 487 488 @Test 489 public void testQuietInterloperDoesNotCancelAudio() throws Exception { 490 NotificationRecord r = getBeepyNotification(); 491 NotificationRecord other = getQuietOtherNotification(); 492 493 // set up internal state 494 mService.buzzBeepBlinkLocked(r); 495 Mockito.reset(mRingtonePlayer); 496 497 // should not stop noise, since it does not own it 498 mService.buzzBeepBlinkLocked(other); 499 verifyNeverStopAudio(); 500 } 501 502 @Test 503 public void testQuietUpdateCancelsAudio() throws Exception { 504 NotificationRecord r = getBeepyNotification(); 505 NotificationRecord s = getQuietNotification(); 506 s.isUpdate = true; 507 508 // set up internal state 509 mService.buzzBeepBlinkLocked(r); 510 Mockito.reset(mRingtonePlayer); 511 512 // quiet update should stop making noise 513 mService.buzzBeepBlinkLocked(s); 514 verifyStopAudio(); 515 } 516 517 @Test 518 public void testQuietOnceUpdateCancelsAudio() throws Exception { 519 NotificationRecord r = getBeepyNotification(); 520 NotificationRecord s = getQuietOnceNotification(); 521 s.isUpdate = true; 522 523 // set up internal state 524 mService.buzzBeepBlinkLocked(r); 525 Mockito.reset(mRingtonePlayer); 526 527 // stop making noise - this is a weird corner case, but quiet should override once 528 mService.buzzBeepBlinkLocked(s); 529 verifyStopAudio(); 530 } 531 532 @Test 533 public void testNoDemoteSoundToVibrateIfVibrateGiven() throws Exception { 534 NotificationRecord r = getBuzzyBeepyNotification(); 535 assertTrue(r.getSound() != null); 536 537 // the phone is quiet 538 when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); 539 540 mService.buzzBeepBlinkLocked(r); 541 542 VibrationEffect effect = VibrationEffect.createWaveform(r.getVibration(), -1); 543 544 verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(), 545 eq(effect), (AudioAttributes) anyObject()); 546 } 547 548 @Test 549 public void testDemoteSoundToVibrate() throws Exception { 550 NotificationRecord r = getBeepyNotification(); 551 assertTrue(r.getSound() != null); 552 assertNull(r.getVibration()); 553 554 // the phone is quiet 555 when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); 556 557 mService.buzzBeepBlinkLocked(r); 558 559 verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(), 560 eq(FALLBACK_VIBRATION), (AudioAttributes) anyObject()); 561 verify(mRingtonePlayer, never()).playAsync 562 (anyObject(), anyObject(), anyBoolean(), anyObject()); 563 } 564 565 @Test 566 public void testDemoteInsistentSoundToVibrate() throws Exception { 567 NotificationRecord r = getInsistentBeepyNotification(); 568 assertTrue(r.getSound() != null); 569 assertNull(r.getVibration()); 570 571 // the phone is quiet 572 when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0); 573 when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); 574 575 mService.buzzBeepBlinkLocked(r); 576 577 verifyDelayedVibrateLooped(); 578 } 579 580 @Test 581 public void testVibrate() throws Exception { 582 NotificationRecord r = getBuzzyNotification(); 583 584 mService.buzzBeepBlinkLocked(r); 585 586 verifyNeverBeep(); 587 verifyVibrate(); 588 } 589 590 @Test 591 public void testInsistentVibrate() throws Exception { 592 NotificationRecord r = getInsistentBuzzyNotification(); 593 594 mService.buzzBeepBlinkLocked(r); 595 verifyVibrateLooped(); 596 } 597 598 @Test 599 public void testVibrateTwice() throws Exception { 600 NotificationRecord r = getBuzzyNotification(); 601 602 // set up internal state 603 mService.buzzBeepBlinkLocked(r); 604 Mockito.reset(mVibrator); 605 606 // update should vibrate 607 r.isUpdate = true; 608 mService.buzzBeepBlinkLocked(r); 609 verifyVibrate(); 610 } 611 612 @Test 613 public void testGroupAlertSummarySilenceChild() throws Exception { 614 NotificationRecord child = getBeepyNotificationRecord("a", GROUP_ALERT_SUMMARY); 615 616 mService.buzzBeepBlinkLocked(child); 617 618 verifyNeverBeep(); 619 } 620 621 @Test 622 public void testGroupAlertSummaryNoSilenceSummary() throws Exception { 623 NotificationRecord summary = getBeepyNotificationRecord("a", GROUP_ALERT_SUMMARY); 624 summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY; 625 626 mService.buzzBeepBlinkLocked(summary); 627 628 verifyBeepLooped(); 629 } 630 631 @Test 632 public void testGroupAlertSummaryNoSilenceNonGroupChild() throws Exception { 633 NotificationRecord nonGroup = getBeepyNotificationRecord(null, GROUP_ALERT_SUMMARY); 634 635 mService.buzzBeepBlinkLocked(nonGroup); 636 637 verifyBeepLooped(); 638 } 639 640 @Test 641 public void testGroupAlertChildSilenceSummary() throws Exception { 642 NotificationRecord summary = getBeepyNotificationRecord("a", GROUP_ALERT_CHILDREN); 643 summary.getNotification().flags |= Notification.FLAG_GROUP_SUMMARY; 644 645 mService.buzzBeepBlinkLocked(summary); 646 647 verifyNeverBeep(); 648 } 649 650 @Test 651 public void testGroupAlertChildNoSilenceChild() throws Exception { 652 NotificationRecord child = getBeepyNotificationRecord("a", GROUP_ALERT_CHILDREN); 653 654 mService.buzzBeepBlinkLocked(child); 655 656 verifyBeepLooped(); 657 } 658 659 @Test 660 public void testGroupAlertChildNoSilenceNonGroupSummary() throws Exception { 661 NotificationRecord nonGroup = getBeepyNotificationRecord(null, GROUP_ALERT_CHILDREN); 662 663 mService.buzzBeepBlinkLocked(nonGroup); 664 665 verifyBeepLooped(); 666 } 667 668 @Test 669 public void testGroupAlertAllNoSilenceGroup() throws Exception { 670 NotificationRecord group = getBeepyNotificationRecord("a", GROUP_ALERT_ALL); 671 672 mService.buzzBeepBlinkLocked(group); 673 674 verifyBeepLooped(); 675 } 676 677 @Test 678 public void testHonorAlertOnlyOnceForBuzz() throws Exception { 679 NotificationRecord r = getBuzzyNotification(); 680 NotificationRecord s = getBuzzyOnceNotification(); 681 s.isUpdate = true; 682 683 // set up internal state 684 mService.buzzBeepBlinkLocked(r); 685 Mockito.reset(mVibrator); 686 687 // update should not beep 688 mService.buzzBeepBlinkLocked(s); 689 verifyNeverVibrate(); 690 } 691 692 @Test 693 public void testNoisyUpdateDoesNotCancelVibrate() throws Exception { 694 NotificationRecord r = getBuzzyNotification(); 695 696 mService.buzzBeepBlinkLocked(r); 697 r.isUpdate = true; 698 mService.buzzBeepBlinkLocked(r); 699 700 verifyNeverStopVibrate(); 701 } 702 703 @Test 704 public void testNoisyOnceUpdateDoesNotCancelVibrate() throws Exception { 705 NotificationRecord r = getBuzzyNotification(); 706 NotificationRecord s = getBuzzyOnceNotification(); 707 s.isUpdate = true; 708 709 mService.buzzBeepBlinkLocked(r); 710 mService.buzzBeepBlinkLocked(s); 711 712 verifyNeverStopVibrate(); 713 } 714 715 @Test 716 public void testQuietUpdateDoesNotCancelVibrateFromOther() throws Exception { 717 NotificationRecord r = getBuzzyNotification(); 718 NotificationRecord s = getQuietNotification(); 719 s.isUpdate = true; 720 NotificationRecord other = getNoisyOtherNotification(); 721 722 // set up internal state 723 mService.buzzBeepBlinkLocked(r); 724 mService.buzzBeepBlinkLocked(other); // this takes the vibrate stream 725 Mockito.reset(mVibrator); 726 727 // should not stop vibrate, since we no longer own it 728 mService.buzzBeepBlinkLocked(s); // this no longer owns the stream 729 verifyNeverStopVibrate(); 730 } 731 732 @Test 733 public void testQuietInterloperDoesNotCancelVibrate() throws Exception { 734 NotificationRecord r = getBuzzyNotification(); 735 NotificationRecord other = getQuietOtherNotification(); 736 737 // set up internal state 738 mService.buzzBeepBlinkLocked(r); 739 Mockito.reset(mVibrator); 740 741 // should not stop noise, since it does not own it 742 mService.buzzBeepBlinkLocked(other); 743 verifyNeverStopVibrate(); 744 } 745 746 @Test 747 public void testQuietUpdateCancelsVibrate() throws Exception { 748 NotificationRecord r = getBuzzyNotification(); 749 NotificationRecord s = getQuietNotification(); 750 s.isUpdate = true; 751 752 // set up internal state 753 mService.buzzBeepBlinkLocked(r); 754 verifyVibrate(); 755 756 // quiet update should stop making noise 757 mService.buzzBeepBlinkLocked(s); 758 verifyStopVibrate(); 759 } 760 761 @Test 762 public void testQuietOnceUpdateCancelVibrate() throws Exception { 763 NotificationRecord r = getBuzzyNotification(); 764 NotificationRecord s = getQuietOnceNotification(); 765 s.isUpdate = true; 766 767 // set up internal state 768 mService.buzzBeepBlinkLocked(r); 769 verifyVibrate(); 770 771 // stop making noise - this is a weird corner case, but quiet should override once 772 mService.buzzBeepBlinkLocked(s); 773 verifyStopVibrate(); 774 } 775 776 @Test 777 public void testQuietUpdateCancelsDemotedVibrate() throws Exception { 778 NotificationRecord r = getBeepyNotification(); 779 NotificationRecord s = getQuietNotification(); 780 781 // the phone is quiet 782 when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0); 783 when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); 784 785 mService.buzzBeepBlinkLocked(r); 786 787 // quiet update should stop making noise 788 mService.buzzBeepBlinkLocked(s); 789 verifyStopVibrate(); 790 } 791 792 @Test 793 public void testEmptyUriSoundTreatedAsNoSound() throws Exception { 794 NotificationChannel channel = new NotificationChannel("test", "test", IMPORTANCE_HIGH); 795 channel.setSound(Uri.EMPTY, null); 796 final Notification n = new Builder(getContext(), "test") 797 .setSmallIcon(android.R.drawable.sym_def_app_icon).build(); 798 799 StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid, 800 mPid, n, mUser, null, System.currentTimeMillis()); 801 NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); 802 mService.addNotification(r); 803 804 mService.buzzBeepBlinkLocked(r); 805 806 verifyNeverBeep(); 807 } 808 809 static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> { 810 private final int mRepeatIndex; 811 812 VibrateRepeatMatcher(int repeatIndex) { 813 mRepeatIndex = repeatIndex; 814 } 815 816 @Override 817 public boolean matches(VibrationEffect actual) { 818 if (actual instanceof VibrationEffect.Waveform && 819 ((VibrationEffect.Waveform) actual).getRepeatIndex() == mRepeatIndex) { 820 return true; 821 } 822 // All non-waveform effects are essentially one shots. 823 return mRepeatIndex == -1; 824 } 825 826 @Override 827 public String toString() { 828 return "repeatIndex=" + mRepeatIndex; 829 } 830 } 831} 832