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