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 18 19import android.app.ActivityManager; 20import android.app.Notification; 21import android.app.Notification.Builder; 22import android.media.AudioAttributes; 23import android.media.AudioManager; 24import android.net.Uri; 25import android.os.Handler; 26import android.os.RemoteException; 27import android.os.UserHandle; 28import android.os.Vibrator; 29import android.service.notification.NotificationListenerService.Ranking; 30import android.service.notification.StatusBarNotification; 31import android.test.AndroidTestCase; 32import android.test.suitebuilder.annotation.SmallTest; 33 34import org.mockito.Mock; 35import org.mockito.Mockito; 36import org.mockito.MockitoAnnotations; 37 38import static org.mockito.Matchers.anyBoolean; 39import static org.mockito.Matchers.anyInt; 40import static org.mockito.Matchers.anyObject; 41import static org.mockito.Matchers.anyString; 42import static org.mockito.Matchers.eq; 43import static org.mockito.Mockito.never; 44import static org.mockito.Mockito.times; 45import static org.mockito.Mockito.verify; 46import static org.mockito.Mockito.when; 47 48public class BuzzBeepBlinkTest extends AndroidTestCase { 49 50 @Mock AudioManager mAudioManager; 51 @Mock Vibrator mVibrator; 52 @Mock android.media.IRingtonePlayer mRingtonePlayer; 53 @Mock Handler mHandler; 54 55 private NotificationManagerService mService; 56 private String mPkg = "com.android.server.notification"; 57 private int mId = 1001; 58 private int mOtherId = 1002; 59 private String mTag = null; 60 private int mUid = 1000; 61 private int mPid = 2000; 62 private int mScore = 10; 63 private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser()); 64 65 @Override 66 public void setUp() { 67 MockitoAnnotations.initMocks(this); 68 69 when(mAudioManager.isAudioFocusExclusive()).thenReturn(false); 70 when(mAudioManager.getRingtonePlayer()).thenReturn(mRingtonePlayer); 71 when(mAudioManager.getStreamVolume(anyInt())).thenReturn(10); 72 when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); 73 74 mService = new NotificationManagerService(getContext()); 75 mService.setAudioManager(mAudioManager); 76 mService.setVibrator(mVibrator); 77 mService.setSystemReady(true); 78 mService.setHandler(mHandler); 79 } 80 81 // 82 // Convenience functions for creating notification records 83 // 84 85 private NotificationRecord getNoisyOtherNotification() { 86 return getNotificationRecord(mOtherId, false /* insistent */, false /* once */, 87 true /* noisy */, true /* buzzy*/); 88 } 89 90 private NotificationRecord getBeepyNotification() { 91 return getNotificationRecord(mId, false /* insistent */, false /* once */, 92 true /* noisy */, false /* buzzy*/); 93 } 94 95 private NotificationRecord getBeepyOnceNotification() { 96 return getNotificationRecord(mId, false /* insistent */, true /* once */, 97 true /* noisy */, false /* buzzy*/); 98 } 99 100 private NotificationRecord getQuietNotification() { 101 return getNotificationRecord(mId, false /* insistent */, false /* once */, 102 false /* noisy */, false /* buzzy*/); 103 } 104 105 private NotificationRecord getQuietOtherNotification() { 106 return getNotificationRecord(mOtherId, false /* insistent */, false /* once */, 107 false /* noisy */, false /* buzzy*/); 108 } 109 110 private NotificationRecord getQuietOnceNotification() { 111 return getNotificationRecord(mId, false /* insistent */, true /* once */, 112 false /* noisy */, false /* buzzy*/); 113 } 114 115 private NotificationRecord getInsistentBeepyNotification() { 116 return getNotificationRecord(mId, true /* insistent */, false /* once */, 117 true /* noisy */, false /* buzzy*/); 118 } 119 120 private NotificationRecord getBuzzyNotification() { 121 return getNotificationRecord(mId, false /* insistent */, false /* once */, 122 false /* noisy */, true /* buzzy*/); 123 } 124 125 private NotificationRecord getBuzzyOnceNotification() { 126 return getNotificationRecord(mId, false /* insistent */, true /* once */, 127 false /* noisy */, true /* buzzy*/); 128 } 129 130 private NotificationRecord getInsistentBuzzyNotification() { 131 return getNotificationRecord(mId, true /* insistent */, false /* once */, 132 false /* noisy */, true /* buzzy*/); 133 } 134 135 private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, 136 boolean noisy, boolean buzzy) { 137 final Builder builder = new Builder(getContext()) 138 .setContentTitle("foo") 139 .setSmallIcon(android.R.drawable.sym_def_app_icon) 140 .setPriority(Notification.PRIORITY_HIGH) 141 .setOnlyAlertOnce(once); 142 143 int defaults = 0; 144 if (noisy) { 145 defaults |= Notification.DEFAULT_SOUND; 146 } 147 if (buzzy) { 148 defaults |= Notification.DEFAULT_VIBRATE; 149 } 150 builder.setDefaults(defaults); 151 152 Notification n = builder.build(); 153 if (insistent) { 154 n.flags |= Notification.FLAG_INSISTENT; 155 } 156 StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid, 157 mScore, n, mUser, System.currentTimeMillis()); 158 return new NotificationRecord(getContext(), sbn); 159 } 160 161 // 162 // Convenience functions for interacting with mocks 163 // 164 165 private void verifyNeverBeep() throws RemoteException { 166 verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(), 167 anyBoolean(), (AudioAttributes) anyObject()); 168 } 169 170 private void verifyBeep() throws RemoteException { 171 verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(), 172 eq(true), (AudioAttributes) anyObject()); 173 } 174 175 private void verifyBeepLooped() throws RemoteException { 176 verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(), 177 eq(false), (AudioAttributes) anyObject()); 178 } 179 180 private void verifyNeverStopAudio() throws RemoteException { 181 verify(mRingtonePlayer, never()).stopAsync(); 182 } 183 184 private void verifyStopAudio() throws RemoteException { 185 verify(mRingtonePlayer, times(1)).stopAsync(); 186 } 187 188 private void verifyNeverVibrate() { 189 verify(mVibrator, never()).vibrate(anyInt(), anyString(), (long[]) anyObject(), 190 anyInt(), (AudioAttributes) anyObject()); 191 } 192 193 private void verifyVibrate() { 194 verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(), 195 eq(-1), (AudioAttributes) anyObject()); 196 } 197 198 private void verifyVibrateLooped() { 199 verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), (long[]) anyObject(), 200 eq(0), (AudioAttributes) anyObject()); 201 } 202 203 private void verifyStopVibrate() { 204 verify(mVibrator, times(1)).cancel(); 205 } 206 207 private void verifyNeverStopVibrate() throws RemoteException { 208 verify(mVibrator, never()).cancel(); 209 } 210 211 @SmallTest 212 public void testBeep() throws Exception { 213 NotificationRecord r = getBeepyNotification(); 214 215 mService.buzzBeepBlinkLocked(r); 216 217 verifyBeepLooped(); 218 verifyNeverVibrate(); 219 } 220 221 // 222 // Tests 223 // 224 225 @SmallTest 226 public void testBeepInsistently() throws Exception { 227 NotificationRecord r = getInsistentBeepyNotification(); 228 229 mService.buzzBeepBlinkLocked(r); 230 231 verifyBeep(); 232 } 233 234 @SmallTest 235 public void testNoInterruptionForMin() throws Exception { 236 NotificationRecord r = getBeepyNotification(); 237 r.setImportance(Ranking.IMPORTANCE_MIN, "foo"); 238 239 mService.buzzBeepBlinkLocked(r); 240 241 verifyNeverBeep(); 242 verifyNeverVibrate(); 243 } 244 245 @SmallTest 246 public void testNoInterruptionForIntercepted() throws Exception { 247 NotificationRecord r = getBeepyNotification(); 248 r.setIntercepted(true); 249 250 mService.buzzBeepBlinkLocked(r); 251 252 verifyNeverBeep(); 253 verifyNeverVibrate(); 254 } 255 256 @SmallTest 257 public void testBeepTwice() throws Exception { 258 NotificationRecord r = getBeepyNotification(); 259 260 // set up internal state 261 mService.buzzBeepBlinkLocked(r); 262 Mockito.reset(mRingtonePlayer); 263 264 // update should beep 265 r.isUpdate = true; 266 mService.buzzBeepBlinkLocked(r); 267 verifyBeepLooped(); 268 } 269 270 @SmallTest 271 public void testHonorAlertOnlyOnceForBeep() throws Exception { 272 NotificationRecord r = getBeepyNotification(); 273 NotificationRecord s = getBeepyOnceNotification(); 274 s.isUpdate = true; 275 276 // set up internal state 277 mService.buzzBeepBlinkLocked(r); 278 Mockito.reset(mRingtonePlayer); 279 280 // update should not beep 281 mService.buzzBeepBlinkLocked(s); 282 verifyNeverBeep(); 283 } 284 285 @SmallTest 286 public void testNoisyUpdateDoesNotCancelAudio() throws Exception { 287 NotificationRecord r = getBeepyNotification(); 288 289 mService.buzzBeepBlinkLocked(r); 290 r.isUpdate = true; 291 mService.buzzBeepBlinkLocked(r); 292 293 verifyNeverStopAudio(); 294 } 295 296 @SmallTest 297 public void testNoisyOnceUpdateDoesNotCancelAudio() throws Exception { 298 NotificationRecord r = getBeepyNotification(); 299 NotificationRecord s = getBeepyOnceNotification(); 300 s.isUpdate = true; 301 302 mService.buzzBeepBlinkLocked(r); 303 mService.buzzBeepBlinkLocked(s); 304 305 verifyNeverStopAudio(); 306 } 307 308 @SmallTest 309 public void testQuietUpdateDoesNotCancelAudioFromOther() throws Exception { 310 NotificationRecord r = getBeepyNotification(); 311 NotificationRecord s = getQuietNotification(); 312 s.isUpdate = true; 313 NotificationRecord other = getNoisyOtherNotification(); 314 315 // set up internal state 316 mService.buzzBeepBlinkLocked(r); 317 mService.buzzBeepBlinkLocked(other); // this takes the audio stream 318 Mockito.reset(mRingtonePlayer); 319 320 // should not stop noise, since we no longer own it 321 mService.buzzBeepBlinkLocked(s); // this no longer owns the stream 322 verifyNeverStopAudio(); 323 } 324 325 @SmallTest 326 public void testQuietInterloperDoesNotCancelAudio() throws Exception { 327 NotificationRecord r = getBeepyNotification(); 328 NotificationRecord other = getQuietOtherNotification(); 329 330 // set up internal state 331 mService.buzzBeepBlinkLocked(r); 332 Mockito.reset(mRingtonePlayer); 333 334 // should not stop noise, since it does not own it 335 mService.buzzBeepBlinkLocked(other); 336 verifyNeverStopAudio(); 337 } 338 339 @SmallTest 340 public void testQuietUpdateCancelsAudio() throws Exception { 341 NotificationRecord r = getBeepyNotification(); 342 NotificationRecord s = getQuietNotification(); 343 s.isUpdate = true; 344 345 // set up internal state 346 mService.buzzBeepBlinkLocked(r); 347 Mockito.reset(mRingtonePlayer); 348 349 // quiet update should stop making noise 350 mService.buzzBeepBlinkLocked(s); 351 verifyStopAudio(); 352 } 353 354 @SmallTest 355 public void testQuietOnceUpdateCancelsAudio() throws Exception { 356 NotificationRecord r = getBeepyNotification(); 357 NotificationRecord s = getQuietOnceNotification(); 358 s.isUpdate = true; 359 360 // set up internal state 361 mService.buzzBeepBlinkLocked(r); 362 Mockito.reset(mRingtonePlayer); 363 364 // stop making noise - this is a weird corner case, but quiet should override once 365 mService.buzzBeepBlinkLocked(s); 366 verifyStopAudio(); 367 } 368 369 @SmallTest 370 public void testDemoteSoundToVibrate() throws Exception { 371 NotificationRecord r = getBeepyNotification(); 372 373 // the phone is quiet 374 when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0); 375 when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); 376 377 mService.buzzBeepBlinkLocked(r); 378 379 verifyNeverBeep(); 380 verifyVibrate(); 381 } 382 383 @SmallTest 384 public void testDemotInsistenteSoundToVibrate() throws Exception { 385 NotificationRecord r = getInsistentBeepyNotification(); 386 387 // the phone is quiet 388 when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0); 389 when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); 390 391 mService.buzzBeepBlinkLocked(r); 392 393 verifyVibrateLooped(); 394 } 395 396 @SmallTest 397 public void testVibrate() throws Exception { 398 NotificationRecord r = getBuzzyNotification(); 399 400 mService.buzzBeepBlinkLocked(r); 401 402 verifyNeverBeep(); 403 verifyVibrate(); 404 } 405 406 @SmallTest 407 public void testInsistenteVibrate() throws Exception { 408 NotificationRecord r = getInsistentBuzzyNotification(); 409 410 mService.buzzBeepBlinkLocked(r); 411 verifyVibrateLooped(); 412 } 413 414 @SmallTest 415 public void testVibratTwice() throws Exception { 416 NotificationRecord r = getBuzzyNotification(); 417 418 // set up internal state 419 mService.buzzBeepBlinkLocked(r); 420 Mockito.reset(mVibrator); 421 422 // update should vibrate 423 r.isUpdate = true; 424 mService.buzzBeepBlinkLocked(r); 425 verifyVibrate(); 426 } 427 428 @SmallTest 429 public void testHonorAlertOnlyOnceForBuzz() throws Exception { 430 NotificationRecord r = getBuzzyNotification(); 431 NotificationRecord s = getBuzzyOnceNotification(); 432 s.isUpdate = true; 433 434 // set up internal state 435 mService.buzzBeepBlinkLocked(r); 436 Mockito.reset(mVibrator); 437 438 // update should not beep 439 mService.buzzBeepBlinkLocked(s); 440 verifyNeverVibrate(); 441 } 442 443 @SmallTest 444 public void testNoisyUpdateDoesNotCancelVibrate() throws Exception { 445 NotificationRecord r = getBuzzyNotification(); 446 447 mService.buzzBeepBlinkLocked(r); 448 r.isUpdate = true; 449 mService.buzzBeepBlinkLocked(r); 450 451 verifyNeverStopVibrate(); 452 } 453 454 @SmallTest 455 public void testNoisyOnceUpdateDoesNotCancelVibrate() throws Exception { 456 NotificationRecord r = getBuzzyNotification(); 457 NotificationRecord s = getBuzzyOnceNotification(); 458 s.isUpdate = true; 459 460 mService.buzzBeepBlinkLocked(r); 461 mService.buzzBeepBlinkLocked(s); 462 463 verifyNeverStopVibrate(); 464 } 465 466 @SmallTest 467 public void testQuietUpdateDoesNotCancelVibrateFromOther() throws Exception { 468 NotificationRecord r = getBuzzyNotification(); 469 NotificationRecord s = getQuietNotification(); 470 s.isUpdate = true; 471 NotificationRecord other = getNoisyOtherNotification(); 472 473 // set up internal state 474 mService.buzzBeepBlinkLocked(r); 475 mService.buzzBeepBlinkLocked(other); // this takes the vibrate stream 476 Mockito.reset(mVibrator); 477 478 // should not stop vibrate, since we no longer own it 479 mService.buzzBeepBlinkLocked(s); // this no longer owns the stream 480 verifyNeverStopVibrate(); 481 } 482 483 @SmallTest 484 public void testQuietInterloperDoesNotCancelVibrate() throws Exception { 485 NotificationRecord r = getBuzzyNotification(); 486 NotificationRecord other = getQuietOtherNotification(); 487 488 // set up internal state 489 mService.buzzBeepBlinkLocked(r); 490 Mockito.reset(mVibrator); 491 492 // should not stop noise, since it does not own it 493 mService.buzzBeepBlinkLocked(other); 494 verifyNeverStopVibrate(); 495 } 496 497 @SmallTest 498 public void testQuietUpdateCancelsVibrate() throws Exception { 499 NotificationRecord r = getBuzzyNotification(); 500 NotificationRecord s = getQuietNotification(); 501 s.isUpdate = true; 502 503 // set up internal state 504 mService.buzzBeepBlinkLocked(r); 505 506 // quiet update should stop making noise 507 mService.buzzBeepBlinkLocked(s); 508 verifyStopVibrate(); 509 } 510 511 @SmallTest 512 public void testQuietOnceUpdateCancelsvibrate() throws Exception { 513 NotificationRecord r = getBuzzyNotification(); 514 NotificationRecord s = getQuietOnceNotification(); 515 s.isUpdate = true; 516 517 // set up internal state 518 mService.buzzBeepBlinkLocked(r); 519 Mockito.reset(mVibrator); 520 521 // stop making noise - this is a weird corner case, but quiet should override once 522 mService.buzzBeepBlinkLocked(s); 523 verifyStopVibrate(); 524 } 525 526 @SmallTest 527 public void testQuietUpdateCancelsDemotedVibrate() throws Exception { 528 NotificationRecord r = getBeepyNotification(); 529 NotificationRecord s = getQuietNotification(); 530 531 // the phone is quiet 532 when(mAudioManager.getStreamVolume(anyInt())).thenReturn(0); 533 when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); 534 535 mService.buzzBeepBlinkLocked(r); 536 537 // quiet update should stop making noise 538 mService.buzzBeepBlinkLocked(s); 539 verifyStopVibrate(); 540 } 541} 542