MissedCallNotifierImplTest.java revision a3799ae6aafba8ccd6448a7c4337acd3460b49f2
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.Context;
24import android.content.Intent;
25import android.content.pm.ApplicationInfo;
26import android.net.Uri;
27import android.os.UserHandle;
28import android.telecom.PhoneAccount;
29import android.telecom.PhoneAccount.Builder;
30import android.telecom.PhoneAccountHandle;
31import android.telecom.TelecomManager;
32import android.telephony.TelephonyManager;
33import android.test.suitebuilder.annotation.SmallTest;
34
35import com.android.server.telecom.Call;
36import com.android.server.telecom.Constants;
37import com.android.server.telecom.MissedCallNotifier;
38import com.android.server.telecom.PhoneAccountRegistrar;
39import com.android.server.telecom.TelecomBroadcastIntentProcessor;
40import com.android.server.telecom.components.TelecomBroadcastReceiver;
41import com.android.server.telecom.ui.MissedCallNotifierImpl;
42import com.android.server.telecom.ui.MissedCallNotifierImpl.NotificationBuilderFactory;
43
44import org.mockito.ArgumentCaptor;
45import org.mockito.Mock;
46import org.mockito.MockitoAnnotations;
47
48import java.util.Arrays;
49import java.util.HashSet;
50
51import static org.mockito.Matchers.any;
52import static org.mockito.Matchers.eq;
53import static org.mockito.Matchers.isNull;
54import static org.mockito.Mockito.doReturn;
55import static org.mockito.Mockito.mock;
56import static org.mockito.Mockito.never;
57import static org.mockito.Mockito.spy;
58import static org.mockito.Mockito.times;
59import static org.mockito.Mockito.verify;
60import static org.mockito.Mockito.when;
61
62public class MissedCallNotifierImplTest extends TelecomTestCase {
63
64    private static final Uri TEL_CALL_HANDLE = Uri.parse("tel:+11915552620");
65    private static final Uri SIP_CALL_HANDLE = Uri.parse("sip:testaddress@testdomain.com");
66    private static final String CALLER_NAME = "Fake Name";
67    private static final String MISSED_CALL_TITLE = "Missed Call";
68    private static final String MISSED_CALLS_TITLE = "Missed Calls";
69    private static final String MISSED_CALLS_MSG = "%s missed calls";
70    private static final String USER_CALL_ACTIVITY_LABEL = "Phone";
71
72    private static final int REQUEST_ID = 0;
73    private static final long CALL_TIMESTAMP;
74    static {
75         CALL_TIMESTAMP = System.currentTimeMillis() - 60 * 1000 * 5;
76    }
77
78    private static final UserHandle PRIMARY_USER = UserHandle.of(0);
79    private static final UserHandle SECONARY_USER = UserHandle.of(12);
80    private static final int NO_CAPABILITY = 0;
81
82    @Mock
83    private NotificationManager mNotificationManager;
84
85    @Mock
86    private PhoneAccountRegistrar mPhoneAccountRegistrar;
87
88    @Mock
89    private TelecomManager mTelecomManager;
90
91    @Override
92    public void setUp() throws Exception {
93        super.setUp();
94        MockitoAnnotations.initMocks(this);
95
96        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
97        mNotificationManager = (NotificationManager) mContext.getSystemService(
98                Context.NOTIFICATION_SERVICE);
99        TelephonyManager fakeTelephonyManager = (TelephonyManager) mContext.getSystemService(
100                Context.TELEPHONY_SERVICE);
101        when(fakeTelephonyManager.getNetworkCountryIso()).thenReturn("US");
102        doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
103        doReturn("com.android.server.telecom.tests").when(mContext).getPackageName();
104
105        mComponentContextFixture.putResource(R.string.notification_missedCallTitle,
106                MISSED_CALL_TITLE);
107        mComponentContextFixture.putResource(R.string.notification_missedCallsTitle,
108                MISSED_CALLS_TITLE);
109        mComponentContextFixture.putResource(R.string.notification_missedCallsMsg,
110                MISSED_CALLS_MSG);
111        mComponentContextFixture.putResource(R.string.userCallActivityLabel,
112                USER_CALL_ACTIVITY_LABEL);
113        mComponentContextFixture.setTelecomManager(mTelecomManager);
114    }
115
116    @SmallTest
117    public void testCancelNotificationInPrimaryUser() {
118        cancelNotificationTestInternal(PRIMARY_USER);
119    }
120
121    @SmallTest
122    public void testCancelNotificationInSecondaryUser() {
123        cancelNotificationTestInternal(SECONARY_USER);
124    }
125
126    private void cancelNotificationTestInternal(UserHandle userHandle) {
127        Notification.Builder builder1 = makeNotificationBuilder("builder1");
128        Notification.Builder builder2 = makeNotificationBuilder("builder2");
129        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
130                makeNotificationBuilderFactory(builder1, builder1, builder2, builder2);
131
132        MissedCallNotifier missedCallNotifier = makeMissedCallNotifier(fakeBuilderFactory,
133                PRIMARY_USER);
134        PhoneAccount phoneAccount = makePhoneAccount(userHandle, NO_CAPABILITY);
135        Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
136                phoneAccount.getAccountHandle());
137
138        missedCallNotifier.showMissedCallNotification(fakeCall);
139        missedCallNotifier.clearMissedCalls(userHandle);
140        missedCallNotifier.showMissedCallNotification(fakeCall);
141
142        ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(
143                Integer.class);
144        verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class),
145                requestIdCaptor.capture(), any(Notification.class), eq(userHandle));
146        verify(mNotificationManager).cancelAsUser(any(String.class), eq(requestIdCaptor.getValue()),
147                eq(userHandle));
148
149        // Verify that the second call to showMissedCallNotification behaves like it were the first.
150        verify(builder2).setContentText(CALLER_NAME);
151    }
152
153    @SmallTest
154    public void testNotifyMultipleMissedCalls() {
155        Notification.Builder[] builders = new Notification.Builder[4];
156
157        for (int i = 0; i < 4; i++) {
158            builders[i] = makeNotificationBuilder("builder" + Integer.toString(i));
159        }
160
161        PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
162        Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
163                phoneAccount.getAccountHandle());
164
165        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
166                makeNotificationBuilderFactory(builders);
167
168        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
169                mPhoneAccountRegistrar, fakeBuilderFactory);
170
171        missedCallNotifier.showMissedCallNotification(fakeCall);
172        missedCallNotifier.showMissedCallNotification(fakeCall);
173
174        // The following captor is to capture the two notifications that got passed into
175        // notifyAsUser. This distinguishes between the builders used for the full notification
176        // (i.e. the one potentially containing sensitive information, such as phone numbers),
177        // and the builders used for the notifications shown on the lockscreen, which have been
178        // scrubbed of such sensitive info. The notifications which are used as arguments
179        // to notifyAsUser are the versions which contain sensitive information.
180        ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(
181                Notification.class);
182        verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class), eq(1),
183                notificationArgumentCaptor.capture(), eq(PRIMARY_USER));
184        HashSet<String> privateNotifications = new HashSet<>();
185        for (Notification n : notificationArgumentCaptor.getAllValues()) {
186            privateNotifications.add(n.toString());
187        }
188
189        for (int i = 0; i < 4; i++) {
190            Notification.Builder builder = builders[i];
191            verify(builder).setWhen(CALL_TIMESTAMP);
192            if (i >= 2) {
193                // The builders after the first two are for multiple missed calls. The notification
194                // for subsequent missed calls is expected to be different in terms of the text
195                // contents of the notification, and that is verified here.
196                if (privateNotifications.contains(builder.toString())) {
197                    verify(builder).setContentText(String.format(MISSED_CALLS_MSG, 2));
198                    verify(builder).setContentTitle(MISSED_CALLS_TITLE);
199                } else {
200                    verify(builder).setContentText(MISSED_CALLS_TITLE);
201                    verify(builder).setContentTitle(USER_CALL_ACTIVITY_LABEL);
202                }
203                verify(builder, never()).addAction(any(Notification.Action.class));
204            } else {
205                if (privateNotifications.contains(builder.toString())) {
206                    verify(builder).setContentText(CALLER_NAME);
207                    verify(builder).setContentTitle(MISSED_CALL_TITLE);
208                } else {
209                    verify(builder).setContentText(MISSED_CALL_TITLE);
210                    verify(builder).setContentTitle(USER_CALL_ACTIVITY_LABEL);
211                }
212            }
213        }
214    }
215
216    @SmallTest
217    public void testNotifySingleCallInPrimaryUser() {
218        PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
219        notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
220    }
221
222    @SmallTest
223    public void testNotifySingleCallInSecondaryUser() {
224        PhoneAccount phoneAccount = makePhoneAccount(SECONARY_USER, NO_CAPABILITY);
225        notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
226    }
227
228    @SmallTest
229    public void testNotifySingleCallInSecondaryUserWithMultiUserCapability() {
230        PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER,
231                PhoneAccount.CAPABILITY_MULTI_USER);
232        notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
233    }
234
235    @SmallTest
236    public void testNotifySingleCallWhenCurrentUserIsSecondaryUser() {
237        PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
238        notifySingleCallTestInternal(phoneAccount, SECONARY_USER);
239    }
240
241    @SmallTest
242    public void testNotifySingleCall() {
243        PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
244        notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
245    }
246
247    private void notifySingleCallTestInternal(PhoneAccount phoneAccount, UserHandle currentUser) {
248        Notification.Builder builder1 = makeNotificationBuilder("builder1");
249        Notification.Builder builder2 = makeNotificationBuilder("builder2");
250        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
251                makeNotificationBuilderFactory(builder1, builder2);
252
253        MissedCallNotifier missedCallNotifier = makeMissedCallNotifier(fakeBuilderFactory,
254                currentUser);
255
256        Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
257                phoneAccount.getAccountHandle());
258        missedCallNotifier.showMissedCallNotification(fakeCall);
259
260        ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(
261                Notification.class);
262
263        UserHandle expectedUserHandle;
264        if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
265            expectedUserHandle = currentUser;
266        } else {
267            expectedUserHandle = phoneAccount.getAccountHandle().getUserHandle();
268        }
269        verify(mNotificationManager).notifyAsUser(isNull(String.class), eq(1),
270                notificationArgumentCaptor.capture(), eq((expectedUserHandle)));
271
272        Notification.Builder builder;
273        Notification.Builder publicBuilder;
274
275        if (notificationArgumentCaptor.getValue().toString().equals("builder1")) {
276            builder = builder1;
277            publicBuilder = builder2;
278        } else {
279            builder = builder2;
280            publicBuilder = builder1;
281        }
282
283        verify(builder).setWhen(CALL_TIMESTAMP);
284        verify(publicBuilder).setWhen(CALL_TIMESTAMP);
285
286        verify(builder).setContentText(CALLER_NAME);
287        verify(publicBuilder).setContentText(MISSED_CALL_TITLE);
288
289        verify(builder).setContentTitle(MISSED_CALL_TITLE);
290        verify(publicBuilder).setContentTitle(USER_CALL_ACTIVITY_LABEL);
291
292        // Create two intents that correspond to call-back and respond back with SMS, and assert
293        // that these pending intents have in fact been registered.
294        Intent callBackIntent = new Intent(
295                TelecomBroadcastIntentProcessor.ACTION_CALL_BACK_FROM_NOTIFICATION,
296                TEL_CALL_HANDLE,
297                mContext,
298                TelecomBroadcastReceiver.class);
299        Intent smsIntent = new Intent(
300                TelecomBroadcastIntentProcessor.ACTION_SEND_SMS_FROM_NOTIFICATION,
301                Uri.fromParts(Constants.SCHEME_SMSTO, TEL_CALL_HANDLE.getSchemeSpecificPart(), null),
302                mContext,
303                TelecomBroadcastReceiver.class);
304
305        assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
306                callBackIntent, PendingIntent.FLAG_NO_CREATE));
307        assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
308                smsIntent, PendingIntent.FLAG_NO_CREATE));
309    }
310
311    @SmallTest
312    public void testNoSmsBackAfterMissedSipCall() {
313        Notification.Builder builder1 = makeNotificationBuilder("builder1");
314        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
315                makeNotificationBuilderFactory(builder1);
316
317        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
318                mPhoneAccountRegistrar, fakeBuilderFactory);
319        PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
320
321        Call fakeCall =
322                makeFakeCall(SIP_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
323                phoneAccount.getAccountHandle());
324        missedCallNotifier.showMissedCallNotification(fakeCall);
325
326        // Create two intents that correspond to call-back and respond back with SMS, and assert
327        // that in the case of a SIP call, no SMS intent is generated.
328        Intent callBackIntent = new Intent(
329                TelecomBroadcastIntentProcessor.ACTION_CALL_BACK_FROM_NOTIFICATION,
330                SIP_CALL_HANDLE,
331                mContext,
332                TelecomBroadcastReceiver.class);
333        Intent smsIntent = new Intent(
334                TelecomBroadcastIntentProcessor.ACTION_SEND_SMS_FROM_NOTIFICATION,
335                Uri.fromParts(Constants.SCHEME_SMSTO, SIP_CALL_HANDLE.getSchemeSpecificPart(),
336                        null),
337                mContext,
338                TelecomBroadcastReceiver.class);
339
340        assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
341                callBackIntent, PendingIntent.FLAG_NO_CREATE));
342        assertNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
343                smsIntent, PendingIntent.FLAG_NO_CREATE));
344    }
345
346    private Notification.Builder makeNotificationBuilder(String label) {
347        Notification.Builder builder = spy(new Notification.Builder(mContext));
348        Notification notification = mock(Notification.class);
349        when(notification.toString()).thenReturn(label);
350        when(builder.toString()).thenReturn(label);
351        doReturn(notification).when(builder).build();
352        return builder;
353    }
354
355    private Call makeFakeCall(Uri handle, String name, long timestamp,
356            PhoneAccountHandle phoneAccountHandle) {
357        Call fakeCall = mock(Call.class);
358        when(fakeCall.getHandle()).thenReturn(handle);
359        when(fakeCall.getName()).thenReturn(name);
360        when(fakeCall.getCreationTimeMillis()).thenReturn(timestamp);
361        when(fakeCall.getTargetPhoneAccount()).thenReturn(phoneAccountHandle);
362        return fakeCall;
363    }
364
365    private MissedCallNotifierImpl.NotificationBuilderFactory makeNotificationBuilderFactory(
366            Notification.Builder... builders) {
367        MissedCallNotifierImpl.NotificationBuilderFactory builderFactory =
368                mock(MissedCallNotifierImpl.NotificationBuilderFactory.class);
369        when(builderFactory.getBuilder(mContext)).thenReturn(builders[0],
370                Arrays.copyOfRange(builders, 1, builders.length));
371        return builderFactory;
372    }
373
374    private MissedCallNotifier makeMissedCallNotifier(
375            NotificationBuilderFactory fakeBuilderFactory, UserHandle currentUser) {
376        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
377                mPhoneAccountRegistrar, fakeBuilderFactory);
378        missedCallNotifier.setCurrentUserHandle(currentUser);
379        return missedCallNotifier;
380    }
381
382    private PhoneAccount makePhoneAccount(UserHandle userHandle, int capability) {
383        ComponentName componentName = new ComponentName("com.anything", "com.whatever");
384        PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(componentName, "id",
385                userHandle);
386        PhoneAccount.Builder builder = new PhoneAccount.Builder(phoneAccountHandle, "test");
387        builder.setCapabilities(capability);
388        PhoneAccount phoneAccount = builder.build();
389        when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle))
390                .thenReturn(phoneAccount);
391        return phoneAccount;
392    }
393}
394