MissedCallNotifierImplTest.java revision 37585d6e3ec68b9f83e565888d53ef586a0867f3
1/* 2 * Copyright (C) 2015 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 17package com.android.server.telecom.tests; 18 19import android.app.Notification; 20import android.app.NotificationManager; 21import android.app.PendingIntent; 22import android.content.ComponentName; 23import android.content.ContentProvider; 24import android.content.Context; 25import android.content.IContentProvider; 26import android.content.Intent; 27import android.content.pm.ApplicationInfo; 28import android.database.Cursor; 29import android.net.Uri; 30import android.os.Bundle; 31import android.os.Handler; 32import android.os.ICancellationSignal; 33import android.os.Looper; 34import android.os.UserHandle; 35import android.provider.CallLog; 36import android.telecom.PhoneAccount; 37import android.telecom.PhoneAccountHandle; 38import android.telecom.TelecomManager; 39import android.telephony.TelephonyManager; 40import android.test.suitebuilder.annotation.SmallTest; 41 42import com.android.internal.telephony.CallerInfo; 43import com.android.server.telecom.CallerInfoLookupHelper; 44import com.android.server.telecom.Constants; 45import com.android.server.telecom.DefaultDialerCache; 46import com.android.server.telecom.MissedCallNotifier; 47import com.android.server.telecom.PhoneAccountRegistrar; 48import com.android.server.telecom.TelecomBroadcastIntentProcessor; 49import com.android.server.telecom.TelecomSystem; 50import com.android.server.telecom.components.TelecomBroadcastReceiver; 51import com.android.server.telecom.ui.MissedCallNotifierImpl; 52import com.android.server.telecom.ui.MissedCallNotifierImpl.NotificationBuilderFactory; 53 54import org.mockito.ArgumentCaptor; 55import org.mockito.Mock; 56import org.mockito.MockitoAnnotations; 57 58import java.util.Arrays; 59import java.util.HashSet; 60import java.util.LinkedList; 61import java.util.List; 62 63import static org.mockito.Matchers.any; 64import static org.mockito.Matchers.anyString; 65import static org.mockito.Matchers.eq; 66import static org.mockito.Matchers.isNull; 67import static org.mockito.Mockito.doReturn; 68import static org.mockito.Mockito.mock; 69import static org.mockito.Mockito.never; 70import static org.mockito.Mockito.spy; 71import static org.mockito.Mockito.timeout; 72import static org.mockito.Mockito.times; 73import static org.mockito.Mockito.verify; 74import static org.mockito.Mockito.when; 75 76public class MissedCallNotifierImplTest extends TelecomTestCase { 77 78 private static class MockMissedCallCursorBuilder { 79 private class CallLogRow { 80 String number; 81 int presentation; 82 long date; 83 84 public CallLogRow(String number, int presentation, long date) { 85 this.number = number; 86 this.presentation = presentation; 87 this.date = date; 88 } 89 } 90 91 private List<CallLogRow> mRows; 92 93 MockMissedCallCursorBuilder() { 94 mRows = new LinkedList<>(); 95 mRows.add(null); 96 } 97 98 public MockMissedCallCursorBuilder addEntry(String number, int presentation, long date) { 99 mRows.add(new CallLogRow(number, presentation, date)); 100 return this; 101 } 102 103 public Cursor build() { 104 Cursor c = mock(Cursor.class); 105 when(c.moveToNext()).thenAnswer(unused -> { 106 mRows.remove(0); 107 return mRows.size() > 0; 108 }); 109 when(c.getString(MissedCallNotifierImpl.CALL_LOG_COLUMN_NUMBER)) 110 .thenAnswer(unused -> mRows.get(0).number); 111 when(c.getInt(MissedCallNotifierImpl.CALL_LOG_COLUMN_NUMBER_PRESENTATION)) 112 .thenAnswer(unused -> mRows.get(0).presentation); 113 when(c.getLong(MissedCallNotifierImpl.CALL_LOG_COLUMN_DATE)) 114 .thenAnswer(unused -> mRows.get(0).date); 115 return c; 116 } 117 } 118 119 private static final Uri TEL_CALL_HANDLE = Uri.parse("tel:+11915552620"); 120 private static final Uri SIP_CALL_HANDLE = Uri.parse("sip:testaddress@testdomain.com"); 121 private static final String CALLER_NAME = "Fake Name"; 122 private static final String MISSED_CALL_TITLE = "Missed Call"; 123 private static final String MISSED_CALLS_TITLE = "Missed Calls"; 124 private static final String MISSED_CALLS_MSG = "%s missed calls"; 125 private static final String USER_CALL_ACTIVITY_LABEL = "Phone"; 126 127 private static final int REQUEST_ID = 0; 128 private static final long CALL_TIMESTAMP; 129 static { 130 CALL_TIMESTAMP = System.currentTimeMillis() - 60 * 1000 * 5; 131 } 132 133 private static final UserHandle PRIMARY_USER = UserHandle.of(0); 134 private static final UserHandle SECONARY_USER = UserHandle.of(12); 135 private static final int NO_CAPABILITY = 0; 136 private static final int TEST_TIMEOUT = 1000; 137 138 @Mock 139 private NotificationManager mNotificationManager; 140 141 @Mock 142 private PhoneAccountRegistrar mPhoneAccountRegistrar; 143 144 @Mock 145 private TelecomManager mTelecomManager; 146 147 @Mock TelecomSystem mTelecomSystem; 148 @Mock private DefaultDialerCache mDefaultDialerCache; 149 150 @Override 151 public void setUp() throws Exception { 152 super.setUp(); 153 MockitoAnnotations.initMocks(this); 154 155 mContext = mComponentContextFixture.getTestDouble().getApplicationContext(); 156 mNotificationManager = (NotificationManager) mContext.getSystemService( 157 Context.NOTIFICATION_SERVICE); 158 TelephonyManager fakeTelephonyManager = (TelephonyManager) mContext.getSystemService( 159 Context.TELEPHONY_SERVICE); 160 when(fakeTelephonyManager.getNetworkCountryIso()).thenReturn("US"); 161 doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo(); 162 doReturn("com.android.server.telecom.tests").when(mContext).getPackageName(); 163 164 mComponentContextFixture.putResource(R.string.notification_missedCallTitle, 165 MISSED_CALL_TITLE); 166 mComponentContextFixture.putResource(R.string.notification_missedCallsTitle, 167 MISSED_CALLS_TITLE); 168 mComponentContextFixture.putResource(R.string.notification_missedCallsMsg, 169 MISSED_CALLS_MSG); 170 mComponentContextFixture.putResource(R.string.userCallActivityLabel, 171 USER_CALL_ACTIVITY_LABEL); 172 mComponentContextFixture.setTelecomManager(mTelecomManager); 173 } 174 175 @Override 176 public void tearDown() throws Exception { 177 TelecomSystem.setInstance(null); 178 when(mTelecomSystem.isBootComplete()).thenReturn(false); 179 } 180 181 @SmallTest 182 public void testCancelNotificationInPrimaryUser() { 183 cancelNotificationTestInternal(PRIMARY_USER); 184 } 185 186 @SmallTest 187 public void testCancelNotificationInSecondaryUser() { 188 cancelNotificationTestInternal(SECONARY_USER); 189 } 190 191 private void cancelNotificationTestInternal(UserHandle userHandle) { 192 Notification.Builder builder1 = makeNotificationBuilder("builder1"); 193 Notification.Builder builder2 = makeNotificationBuilder("builder2"); 194 MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory = 195 makeNotificationBuilderFactory(builder1, builder1, builder2, builder2); 196 197 MissedCallNotifier missedCallNotifier = makeMissedCallNotifier(fakeBuilderFactory, 198 PRIMARY_USER); 199 PhoneAccount phoneAccount = makePhoneAccount(userHandle, NO_CAPABILITY); 200 MissedCallNotifier.CallInfo fakeCall = makeFakeCallInfo(TEL_CALL_HANDLE, CALLER_NAME, 201 CALL_TIMESTAMP, phoneAccount.getAccountHandle()); 202 203 missedCallNotifier.showMissedCallNotification(fakeCall); 204 missedCallNotifier.clearMissedCalls(userHandle); 205 missedCallNotifier.showMissedCallNotification(fakeCall); 206 207 ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass( 208 Integer.class); 209 verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class), 210 requestIdCaptor.capture(), any(Notification.class), eq(userHandle)); 211 verify(mNotificationManager).cancelAsUser(any(String.class), eq(requestIdCaptor.getValue()), 212 eq(userHandle)); 213 214 // Verify that the second call to showMissedCallNotification behaves like it were the first. 215 verify(builder2).setContentText(CALLER_NAME); 216 } 217 218 @SmallTest 219 public void testNotifyMultipleMissedCalls() { 220 Notification.Builder[] builders = new Notification.Builder[4]; 221 222 for (int i = 0; i < 4; i++) { 223 builders[i] = makeNotificationBuilder("builder" + Integer.toString(i)); 224 } 225 226 PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY); 227 MissedCallNotifier.CallInfo fakeCall = makeFakeCallInfo(TEL_CALL_HANDLE, CALLER_NAME, 228 CALL_TIMESTAMP, phoneAccount.getAccountHandle()); 229 230 MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory = 231 makeNotificationBuilderFactory(builders); 232 233 MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext, 234 mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory); 235 236 missedCallNotifier.showMissedCallNotification(fakeCall); 237 missedCallNotifier.showMissedCallNotification(fakeCall); 238 239 // The following captor is to capture the two notifications that got passed into 240 // notifyAsUser. This distinguishes between the builders used for the full notification 241 // (i.e. the one potentially containing sensitive information, such as phone numbers), 242 // and the builders used for the notifications shown on the lockscreen, which have been 243 // scrubbed of such sensitive info. The notifications which are used as arguments 244 // to notifyAsUser are the versions which contain sensitive information. 245 ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass( 246 Notification.class); 247 verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class), eq(1), 248 notificationArgumentCaptor.capture(), eq(PRIMARY_USER)); 249 HashSet<String> privateNotifications = new HashSet<>(); 250 for (Notification n : notificationArgumentCaptor.getAllValues()) { 251 privateNotifications.add(n.toString()); 252 } 253 254 for (int i = 0; i < 4; i++) { 255 Notification.Builder builder = builders[i]; 256 verify(builder).setWhen(CALL_TIMESTAMP); 257 if (i >= 2) { 258 // The builders after the first two are for multiple missed calls. The notification 259 // for subsequent missed calls is expected to be different in terms of the text 260 // contents of the notification, and that is verified here. 261 if (privateNotifications.contains(builder.toString())) { 262 verify(builder).setContentText(String.format(MISSED_CALLS_MSG, 2)); 263 verify(builder).setContentTitle(MISSED_CALLS_TITLE); 264 } else { 265 verify(builder).setContentText(MISSED_CALLS_TITLE); 266 verify(builder).setContentTitle(USER_CALL_ACTIVITY_LABEL); 267 } 268 verify(builder, never()).addAction(any(Notification.Action.class)); 269 } else { 270 if (privateNotifications.contains(builder.toString())) { 271 verify(builder).setContentText(CALLER_NAME); 272 verify(builder).setContentTitle(MISSED_CALL_TITLE); 273 } else { 274 verify(builder).setContentText(MISSED_CALL_TITLE); 275 verify(builder).setContentTitle(USER_CALL_ACTIVITY_LABEL); 276 } 277 } 278 } 279 } 280 281 @SmallTest 282 public void testNotifySingleCallInPrimaryUser() { 283 PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY); 284 notifySingleCallTestInternal(phoneAccount, PRIMARY_USER); 285 } 286 287 @SmallTest 288 public void testNotifySingleCallInSecondaryUser() { 289 PhoneAccount phoneAccount = makePhoneAccount(SECONARY_USER, NO_CAPABILITY); 290 notifySingleCallTestInternal(phoneAccount, PRIMARY_USER); 291 } 292 293 @SmallTest 294 public void testNotifySingleCallInSecondaryUserWithMultiUserCapability() { 295 PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, 296 PhoneAccount.CAPABILITY_MULTI_USER); 297 notifySingleCallTestInternal(phoneAccount, PRIMARY_USER); 298 } 299 300 @SmallTest 301 public void testNotifySingleCallWhenCurrentUserIsSecondaryUser() { 302 PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY); 303 notifySingleCallTestInternal(phoneAccount, SECONARY_USER); 304 } 305 306 @SmallTest 307 public void testNotifySingleCall() { 308 PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY); 309 notifySingleCallTestInternal(phoneAccount, PRIMARY_USER); 310 } 311 312 private void notifySingleCallTestInternal(PhoneAccount phoneAccount, UserHandle currentUser) { 313 Notification.Builder builder1 = makeNotificationBuilder("builder1"); 314 Notification.Builder builder2 = makeNotificationBuilder("builder2"); 315 MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory = 316 makeNotificationBuilderFactory(builder1, builder2); 317 318 MissedCallNotifier missedCallNotifier = makeMissedCallNotifier(fakeBuilderFactory, 319 currentUser); 320 321 MissedCallNotifier.CallInfo fakeCall = makeFakeCallInfo(TEL_CALL_HANDLE, CALLER_NAME, 322 CALL_TIMESTAMP, phoneAccount.getAccountHandle()); 323 missedCallNotifier.showMissedCallNotification(fakeCall); 324 325 ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass( 326 Notification.class); 327 328 UserHandle expectedUserHandle; 329 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) { 330 expectedUserHandle = currentUser; 331 } else { 332 expectedUserHandle = phoneAccount.getAccountHandle().getUserHandle(); 333 } 334 verify(mNotificationManager).notifyAsUser(isNull(String.class), eq(1), 335 notificationArgumentCaptor.capture(), eq((expectedUserHandle))); 336 337 Notification.Builder builder; 338 Notification.Builder publicBuilder; 339 340 if (notificationArgumentCaptor.getValue().toString().equals("builder1")) { 341 builder = builder1; 342 publicBuilder = builder2; 343 } else { 344 builder = builder2; 345 publicBuilder = builder1; 346 } 347 348 verify(builder).setWhen(CALL_TIMESTAMP); 349 verify(publicBuilder).setWhen(CALL_TIMESTAMP); 350 351 verify(builder).setContentText(CALLER_NAME); 352 verify(publicBuilder).setContentText(MISSED_CALL_TITLE); 353 354 verify(builder).setContentTitle(MISSED_CALL_TITLE); 355 verify(publicBuilder).setContentTitle(USER_CALL_ACTIVITY_LABEL); 356 357 // Create two intents that correspond to call-back and respond back with SMS, and assert 358 // that these pending intents have in fact been registered. 359 Intent callBackIntent = new Intent( 360 TelecomBroadcastIntentProcessor.ACTION_CALL_BACK_FROM_NOTIFICATION, 361 TEL_CALL_HANDLE, 362 mContext, 363 TelecomBroadcastReceiver.class); 364 Intent smsIntent = new Intent( 365 TelecomBroadcastIntentProcessor.ACTION_SEND_SMS_FROM_NOTIFICATION, 366 Uri.fromParts(Constants.SCHEME_SMSTO, TEL_CALL_HANDLE.getSchemeSpecificPart(), null), 367 mContext, 368 TelecomBroadcastReceiver.class); 369 370 assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID, 371 callBackIntent, PendingIntent.FLAG_NO_CREATE)); 372 assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID, 373 smsIntent, PendingIntent.FLAG_NO_CREATE)); 374 } 375 376 @SmallTest 377 public void testNoSmsBackAfterMissedSipCall() { 378 Notification.Builder builder1 = makeNotificationBuilder("builder1"); 379 MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory = 380 makeNotificationBuilderFactory(builder1); 381 382 MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext, 383 mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory); 384 PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY); 385 386 MissedCallNotifier.CallInfo fakeCall = 387 makeFakeCallInfo(SIP_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP, 388 phoneAccount.getAccountHandle()); 389 missedCallNotifier.showMissedCallNotification(fakeCall); 390 391 // Create two intents that correspond to call-back and respond back with SMS, and assert 392 // that in the case of a SIP call, no SMS intent is generated. 393 Intent callBackIntent = new Intent( 394 TelecomBroadcastIntentProcessor.ACTION_CALL_BACK_FROM_NOTIFICATION, 395 SIP_CALL_HANDLE, 396 mContext, 397 TelecomBroadcastReceiver.class); 398 Intent smsIntent = new Intent( 399 TelecomBroadcastIntentProcessor.ACTION_SEND_SMS_FROM_NOTIFICATION, 400 Uri.fromParts(Constants.SCHEME_SMSTO, SIP_CALL_HANDLE.getSchemeSpecificPart(), 401 null), 402 mContext, 403 TelecomBroadcastReceiver.class); 404 405 assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID, 406 callBackIntent, PendingIntent.FLAG_NO_CREATE)); 407 assertNull(PendingIntent.getBroadcast(mContext, REQUEST_ID, 408 smsIntent, PendingIntent.FLAG_NO_CREATE)); 409 } 410 411 @SmallTest 412 public void testLoadOneCallFromDb() throws Exception { 413 CallerInfoLookupHelper mockCallerInfoLookupHelper = mock(CallerInfoLookupHelper.class); 414 MissedCallNotifier.CallInfoFactory mockCallInfoFactory = 415 mock(MissedCallNotifier.CallInfoFactory.class); 416 417 Uri queryUri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, 418 PRIMARY_USER.getIdentifier()); 419 IContentProvider cp = getContentProviderForUser(PRIMARY_USER.getIdentifier()); 420 421 Cursor mockMissedCallsCursor = new MockMissedCallCursorBuilder() 422 .addEntry(TEL_CALL_HANDLE.getSchemeSpecificPart(), 423 CallLog.Calls.PRESENTATION_ALLOWED, CALL_TIMESTAMP) 424 .build(); 425 426 when(cp.query(anyString(), eq(queryUri), any(String[].class), 427 any(Bundle.class), any(ICancellationSignal.class))) 428 .thenReturn(mockMissedCallsCursor); 429 430 PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY); 431 MissedCallNotifier.CallInfo fakeCallInfo = makeFakeCallInfo(TEL_CALL_HANDLE, 432 CALLER_NAME, CALL_TIMESTAMP, phoneAccount.getAccountHandle()); 433 when(mockCallInfoFactory.makeCallInfo(any(CallerInfo.class), 434 any(PhoneAccountHandle.class), any(Uri.class), eq(CALL_TIMESTAMP))) 435 .thenReturn(fakeCallInfo); 436 437 Notification.Builder builder1 = makeNotificationBuilder("builder1"); 438 MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory = 439 makeNotificationBuilderFactory(builder1); 440 441 MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext, 442 mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory); 443 444 // AsyncQueryHandler used in reloadFromDatabase interacts poorly with the below 445 // timeout-verify, so run this in a new handler to mitigate that. 446 Handler h = new Handler(Looper.getMainLooper()); 447 h.post(() -> missedCallNotifier.reloadFromDatabase( 448 mockCallerInfoLookupHelper, mockCallInfoFactory, PRIMARY_USER)); 449 waitForHandlerAction(h, TEST_TIMEOUT); 450 451 // TelecomSystem.getInstance returns null in this test, so we expect that nothing will 452 // happen. 453 verify(mockCallerInfoLookupHelper, never()).startLookup(any(Uri.class), 454 any(CallerInfoLookupHelper.OnQueryCompleteListener.class)); 455 // Simulate a boot-complete 456 TelecomSystem.setInstance(mTelecomSystem); 457 when(mTelecomSystem.isBootComplete()).thenReturn(true); 458 h.post(() -> missedCallNotifier.reloadAfterBootComplete(mockCallerInfoLookupHelper, 459 mockCallInfoFactory)); 460 waitForHandlerAction(h, TEST_TIMEOUT); 461 462 Uri escapedHandle = Uri.fromParts(PhoneAccount.SCHEME_TEL, 463 TEL_CALL_HANDLE.getSchemeSpecificPart(), null); 464 ArgumentCaptor<CallerInfoLookupHelper.OnQueryCompleteListener> listenerCaptor = 465 ArgumentCaptor.forClass(CallerInfoLookupHelper.OnQueryCompleteListener.class); 466 verify(mockCallerInfoLookupHelper, timeout(TEST_TIMEOUT)).startLookup(eq(escapedHandle), 467 listenerCaptor.capture()); 468 469 CallerInfo ci = new CallerInfo(); 470 listenerCaptor.getValue().onCallerInfoQueryComplete(escapedHandle, ci); 471 verify(mockCallInfoFactory).makeCallInfo(eq(ci), isNull(PhoneAccountHandle.class), 472 eq(escapedHandle), eq(CALL_TIMESTAMP)); 473 } 474 475 @SmallTest 476 public void testLoadTwoCallsFromDb() throws Exception { 477 TelecomSystem.setInstance(mTelecomSystem); 478 when(mTelecomSystem.isBootComplete()).thenReturn(true); 479 CallerInfoLookupHelper mockCallerInfoLookupHelper = mock(CallerInfoLookupHelper.class); 480 MissedCallNotifier.CallInfoFactory mockCallInfoFactory = 481 mock(MissedCallNotifier.CallInfoFactory.class); 482 483 Cursor mockMissedCallsCursor = new MockMissedCallCursorBuilder() 484 .addEntry(TEL_CALL_HANDLE.getSchemeSpecificPart(), 485 CallLog.Calls.PRESENTATION_ALLOWED, CALL_TIMESTAMP) 486 .addEntry(SIP_CALL_HANDLE.getSchemeSpecificPart(), 487 CallLog.Calls.PRESENTATION_ALLOWED, CALL_TIMESTAMP) 488 .build(); 489 490 Uri queryUri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI, 491 PRIMARY_USER.getIdentifier()); 492 IContentProvider cp = getContentProviderForUser(PRIMARY_USER.getIdentifier()); 493 494 when(cp.query(anyString(), eq(queryUri), any(String[].class), 495 any(Bundle.class), any(ICancellationSignal.class))) 496 .thenReturn(mockMissedCallsCursor); 497 498 PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY); 499 MissedCallNotifier.CallInfo fakeCallInfo = makeFakeCallInfo(TEL_CALL_HANDLE, 500 CALLER_NAME, CALL_TIMESTAMP, phoneAccount.getAccountHandle()); 501 when(mockCallInfoFactory.makeCallInfo(any(CallerInfo.class), 502 any(PhoneAccountHandle.class), any(Uri.class), eq(CALL_TIMESTAMP))) 503 .thenReturn(fakeCallInfo); 504 505 Notification.Builder builder1 = makeNotificationBuilder("builder1"); 506 MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory = 507 makeNotificationBuilderFactory(builder1); 508 509 MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext, 510 mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory); 511 512 // AsyncQueryHandler used in reloadFromDatabase interacts poorly with the below 513 // timeout-verify, so run this in a new handler to mitigate that. 514 Handler h = new Handler(Looper.getMainLooper()); 515 h.post(() -> missedCallNotifier.reloadFromDatabase( 516 mockCallerInfoLookupHelper, mockCallInfoFactory, PRIMARY_USER)); 517 waitForHandlerAction(h, TEST_TIMEOUT); 518 519 Uri escapedTelHandle = Uri.fromParts(PhoneAccount.SCHEME_TEL, 520 TEL_CALL_HANDLE.getSchemeSpecificPart(), null); 521 Uri escapedSipHandle = Uri.fromParts(PhoneAccount.SCHEME_SIP, 522 SIP_CALL_HANDLE.getSchemeSpecificPart(), null); 523 524 ArgumentCaptor<CallerInfoLookupHelper.OnQueryCompleteListener> listenerCaptor = 525 ArgumentCaptor.forClass(CallerInfoLookupHelper.OnQueryCompleteListener.class); 526 verify(mockCallerInfoLookupHelper, timeout(TEST_TIMEOUT)).startLookup(eq(escapedTelHandle), 527 listenerCaptor.capture()); 528 verify(mockCallerInfoLookupHelper, timeout(TEST_TIMEOUT)).startLookup(eq(escapedSipHandle), 529 listenerCaptor.capture()); 530 531 CallerInfo ci = new CallerInfo(); 532 listenerCaptor.getAllValues().get(0).onCallerInfoQueryComplete(escapedTelHandle, ci); 533 listenerCaptor.getAllValues().get(1).onCallerInfoQueryComplete(escapedSipHandle, ci); 534 535 // Verify that two notifications were generated, both with the same id. 536 verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class), eq(1), 537 any(Notification.class), eq(PRIMARY_USER)); 538 } 539 540 private Notification.Builder makeNotificationBuilder(String label) { 541 Notification.Builder builder = spy(new Notification.Builder(mContext)); 542 Notification notification = mock(Notification.class); 543 when(notification.toString()).thenReturn(label); 544 when(builder.toString()).thenReturn(label); 545 doReturn(notification).when(builder).build(); 546 return builder; 547 } 548 549 private MissedCallNotifier.CallInfo makeFakeCallInfo(Uri handle, String name, long timestamp, 550 PhoneAccountHandle phoneAccountHandle) { 551 MissedCallNotifier.CallInfo fakeCall = mock(MissedCallNotifier.CallInfo.class); 552 when(fakeCall.getHandle()).thenReturn(handle); 553 when(fakeCall.getHandleSchemeSpecificPart()).thenReturn(handle.getSchemeSpecificPart()); 554 when(fakeCall.getName()).thenReturn(name); 555 when(fakeCall.getCreationTimeMillis()).thenReturn(timestamp); 556 when(fakeCall.getPhoneAccountHandle()).thenReturn(phoneAccountHandle); 557 return fakeCall; 558 } 559 560 private MissedCallNotifierImpl.NotificationBuilderFactory makeNotificationBuilderFactory( 561 Notification.Builder... builders) { 562 MissedCallNotifierImpl.NotificationBuilderFactory builderFactory = 563 mock(MissedCallNotifierImpl.NotificationBuilderFactory.class); 564 when(builderFactory.getBuilder(mContext)).thenReturn(builders[0], 565 Arrays.copyOfRange(builders, 1, builders.length)); 566 return builderFactory; 567 } 568 569 private MissedCallNotifier makeMissedCallNotifier( 570 NotificationBuilderFactory fakeBuilderFactory, UserHandle currentUser) { 571 MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext, 572 mPhoneAccountRegistrar, mDefaultDialerCache, fakeBuilderFactory); 573 missedCallNotifier.setCurrentUserHandle(currentUser); 574 return missedCallNotifier; 575 } 576 577 private PhoneAccount makePhoneAccount(UserHandle userHandle, int capability) { 578 ComponentName componentName = new ComponentName("com.anything", "com.whatever"); 579 PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(componentName, "id", 580 userHandle); 581 PhoneAccount.Builder builder = new PhoneAccount.Builder(phoneAccountHandle, "test"); 582 builder.setCapabilities(capability); 583 PhoneAccount phoneAccount = builder.build(); 584 when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle)) 585 .thenReturn(phoneAccount); 586 return phoneAccount; 587 } 588 589 private IContentProvider getContentProviderForUser(int userId) { 590 return mContext.getContentResolver().acquireProvider(userId + "@call_log"); 591 } 592 593} 594