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