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