blob: 24c59ffc578250191567c9d8fd57b85208cced2f [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.telecom.tests;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.net.Uri;
import android.os.UserHandle;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccount.Builder;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.server.telecom.Call;
import com.android.server.telecom.Constants;
import com.android.server.telecom.MissedCallNotifier;
import com.android.server.telecom.PhoneAccountRegistrar;
import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
import com.android.server.telecom.TelecomBroadcastIntentProcessor;
import com.android.server.telecom.components.TelecomBroadcastReceiver;
import com.android.server.telecom.ui.MissedCallNotifierImpl;
import com.android.server.telecom.ui.MissedCallNotifierImpl.NotificationBuilderFactory;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
import java.util.HashSet;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class MissedCallNotifierImplTest extends TelecomTestCase {
private static final Uri TEL_CALL_HANDLE = Uri.parse("tel:+11915552620");
private static final Uri SIP_CALL_HANDLE = Uri.parse("sip:testaddress@testdomain.com");
private static final String CALLER_NAME = "Fake Name";
private static final String MISSED_CALL_TITLE = "Missed Call";
private static final String MISSED_CALLS_TITLE = "Missed Calls";
private static final String MISSED_CALLS_MSG = "%s missed calls";
private static final String USER_CALL_ACTIVITY_LABEL = "Phone";
private static final int REQUEST_ID = 0;
private static final long CALL_TIMESTAMP;
static {
CALL_TIMESTAMP = System.currentTimeMillis() - 60 * 1000 * 5;
}
private static final UserHandle PRIMARY_USER = UserHandle.of(0);
private static final UserHandle SECONARY_USER = UserHandle.of(12);
private static final int NO_CAPABILITY = 0;
@Mock
private NotificationManager mNotificationManager;
@Mock
private PhoneAccountRegistrar mPhoneAccountRegistrar;
@Mock
private TelecomManager mTelecomManager;
@Override
public void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
mNotificationManager = (NotificationManager) mContext.getSystemService(
Context.NOTIFICATION_SERVICE);
TelephonyManager fakeTelephonyManager = (TelephonyManager) mContext.getSystemService(
Context.TELEPHONY_SERVICE);
when(fakeTelephonyManager.getNetworkCountryIso()).thenReturn("US");
doReturn(new ApplicationInfo()).when(mContext).getApplicationInfo();
doReturn("com.android.server.telecom.tests").when(mContext).getPackageName();
mComponentContextFixture.putResource(R.string.notification_missedCallTitle,
MISSED_CALL_TITLE);
mComponentContextFixture.putResource(R.string.notification_missedCallsTitle,
MISSED_CALLS_TITLE);
mComponentContextFixture.putResource(R.string.notification_missedCallsMsg,
MISSED_CALLS_MSG);
mComponentContextFixture.putResource(R.string.userCallActivityLabel,
USER_CALL_ACTIVITY_LABEL);
mComponentContextFixture.setTelecomManager(mTelecomManager);
}
@SmallTest
public void testCancelNotificationInPrimaryUser() {
cancelNotificationTestInternal(PRIMARY_USER);
}
@SmallTest
public void testCancelNotificationInSecondaryUser() {
cancelNotificationTestInternal(SECONARY_USER);
}
private void cancelNotificationTestInternal(UserHandle userHandle) {
Notification.Builder builder1 = makeNotificationBuilder("builder1");
Notification.Builder builder2 = makeNotificationBuilder("builder2");
MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
makeNotificationBuilderFactory(builder1, builder1, builder2, builder2);
MissedCallNotifier missedCallNotifier = makeMissedCallNotifier(fakeBuilderFactory,
PRIMARY_USER);
PhoneAccount phoneAccount = makePhoneAccount(userHandle, NO_CAPABILITY);
Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
phoneAccount.getAccountHandle());
missedCallNotifier.showMissedCallNotification(fakeCall);
missedCallNotifier.clearMissedCalls(userHandle);
missedCallNotifier.showMissedCallNotification(fakeCall);
ArgumentCaptor<Integer> requestIdCaptor = ArgumentCaptor.forClass(
Integer.class);
verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class),
requestIdCaptor.capture(), any(Notification.class), eq(userHandle));
verify(mNotificationManager).cancelAsUser(any(String.class), eq(requestIdCaptor.getValue()),
eq(userHandle));
// Verify that the second call to showMissedCallNotification behaves like it were the first.
verify(builder2).setContentText(CALLER_NAME);
}
@SmallTest
public void testNotifyMultipleMissedCalls() {
Notification.Builder[] builders = new Notification.Builder[4];
for (int i = 0; i < 4; i++) {
builders[i] = makeNotificationBuilder("builder" + Integer.toString(i));
}
PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
phoneAccount.getAccountHandle());
MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
makeNotificationBuilderFactory(builders);
MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
mPhoneAccountRegistrar, new PhoneNumberUtilsAdapterImpl(), fakeBuilderFactory);
missedCallNotifier.showMissedCallNotification(fakeCall);
missedCallNotifier.showMissedCallNotification(fakeCall);
// The following captor is to capture the two notifications that got passed into
// notifyAsUser. This distinguishes between the builders used for the full notification
// (i.e. the one potentially containing sensitive information, such as phone numbers),
// and the builders used for the notifications shown on the lockscreen, which have been
// scrubbed of such sensitive info. The notifications which are used as arguments
// to notifyAsUser are the versions which contain sensitive information.
ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(
Notification.class);
verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class), eq(1),
notificationArgumentCaptor.capture(), eq(PRIMARY_USER));
HashSet<String> privateNotifications = new HashSet<>();
for (Notification n : notificationArgumentCaptor.getAllValues()) {
privateNotifications.add(n.toString());
}
for (int i = 0; i < 4; i++) {
Notification.Builder builder = builders[i];
verify(builder).setWhen(CALL_TIMESTAMP);
if (i >= 2) {
// The builders after the first two are for multiple missed calls. The notification
// for subsequent missed calls is expected to be different in terms of the text
// contents of the notification, and that is verified here.
if (privateNotifications.contains(builder.toString())) {
verify(builder).setContentText(String.format(MISSED_CALLS_MSG, 2));
verify(builder).setContentTitle(MISSED_CALLS_TITLE);
} else {
verify(builder).setContentText(MISSED_CALLS_TITLE);
verify(builder).setContentTitle(USER_CALL_ACTIVITY_LABEL);
}
verify(builder, never()).addAction(any(Notification.Action.class));
} else {
if (privateNotifications.contains(builder.toString())) {
verify(builder).setContentText(CALLER_NAME);
verify(builder).setContentTitle(MISSED_CALL_TITLE);
} else {
verify(builder).setContentText(MISSED_CALL_TITLE);
verify(builder).setContentTitle(USER_CALL_ACTIVITY_LABEL);
}
}
}
}
@SmallTest
public void testNotifySingleCallInPrimaryUser() {
PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
}
@SmallTest
public void testNotifySingleCallInSecondaryUser() {
PhoneAccount phoneAccount = makePhoneAccount(SECONARY_USER, NO_CAPABILITY);
notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
}
@SmallTest
public void testNotifySingleCallInSecondaryUserWithMultiUserCapability() {
PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER,
PhoneAccount.CAPABILITY_MULTI_USER);
notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
}
@SmallTest
public void testNotifySingleCallWhenCurrentUserIsSecondaryUser() {
PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
notifySingleCallTestInternal(phoneAccount, SECONARY_USER);
}
@SmallTest
public void testNotifySingleCall() {
PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
notifySingleCallTestInternal(phoneAccount, PRIMARY_USER);
}
private void notifySingleCallTestInternal(PhoneAccount phoneAccount, UserHandle currentUser) {
Notification.Builder builder1 = makeNotificationBuilder("builder1");
Notification.Builder builder2 = makeNotificationBuilder("builder2");
MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
makeNotificationBuilderFactory(builder1, builder2);
MissedCallNotifier missedCallNotifier = makeMissedCallNotifier(fakeBuilderFactory,
currentUser);
Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
phoneAccount.getAccountHandle());
missedCallNotifier.showMissedCallNotification(fakeCall);
ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(
Notification.class);
UserHandle expectedUserHandle;
if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
expectedUserHandle = currentUser;
} else {
expectedUserHandle = phoneAccount.getAccountHandle().getUserHandle();
}
verify(mNotificationManager).notifyAsUser(isNull(String.class), eq(1),
notificationArgumentCaptor.capture(), eq((expectedUserHandle)));
Notification.Builder builder;
Notification.Builder publicBuilder;
if (notificationArgumentCaptor.getValue().toString().equals("builder1")) {
builder = builder1;
publicBuilder = builder2;
} else {
builder = builder2;
publicBuilder = builder1;
}
verify(builder).setWhen(CALL_TIMESTAMP);
verify(publicBuilder).setWhen(CALL_TIMESTAMP);
verify(builder).setContentText(CALLER_NAME);
verify(publicBuilder).setContentText(MISSED_CALL_TITLE);
verify(builder).setContentTitle(MISSED_CALL_TITLE);
verify(publicBuilder).setContentTitle(USER_CALL_ACTIVITY_LABEL);
// Create two intents that correspond to call-back and respond back with SMS, and assert
// that these pending intents have in fact been registered.
Intent callBackIntent = new Intent(
TelecomBroadcastIntentProcessor.ACTION_CALL_BACK_FROM_NOTIFICATION,
TEL_CALL_HANDLE,
mContext,
TelecomBroadcastReceiver.class);
Intent smsIntent = new Intent(
TelecomBroadcastIntentProcessor.ACTION_SEND_SMS_FROM_NOTIFICATION,
Uri.fromParts(Constants.SCHEME_SMSTO, TEL_CALL_HANDLE.getSchemeSpecificPart(), null),
mContext,
TelecomBroadcastReceiver.class);
assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
callBackIntent, PendingIntent.FLAG_NO_CREATE));
assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
smsIntent, PendingIntent.FLAG_NO_CREATE));
}
@SmallTest
public void testNoSmsBackAfterMissedSipCall() {
Notification.Builder builder1 = makeNotificationBuilder("builder1");
MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
makeNotificationBuilderFactory(builder1);
MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
mPhoneAccountRegistrar, new PhoneNumberUtilsAdapterImpl(), fakeBuilderFactory);
PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
Call fakeCall =
makeFakeCall(SIP_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
phoneAccount.getAccountHandle());
missedCallNotifier.showMissedCallNotification(fakeCall);
// Create two intents that correspond to call-back and respond back with SMS, and assert
// that in the case of a SIP call, no SMS intent is generated.
Intent callBackIntent = new Intent(
TelecomBroadcastIntentProcessor.ACTION_CALL_BACK_FROM_NOTIFICATION,
SIP_CALL_HANDLE,
mContext,
TelecomBroadcastReceiver.class);
Intent smsIntent = new Intent(
TelecomBroadcastIntentProcessor.ACTION_SEND_SMS_FROM_NOTIFICATION,
Uri.fromParts(Constants.SCHEME_SMSTO, SIP_CALL_HANDLE.getSchemeSpecificPart(),
null),
mContext,
TelecomBroadcastReceiver.class);
assertNotNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
callBackIntent, PendingIntent.FLAG_NO_CREATE));
assertNull(PendingIntent.getBroadcast(mContext, REQUEST_ID,
smsIntent, PendingIntent.FLAG_NO_CREATE));
}
private Notification.Builder makeNotificationBuilder(String label) {
Notification.Builder builder = spy(new Notification.Builder(mContext));
Notification notification = mock(Notification.class);
when(notification.toString()).thenReturn(label);
when(builder.toString()).thenReturn(label);
doReturn(notification).when(builder).build();
return builder;
}
private Call makeFakeCall(Uri handle, String name, long timestamp,
PhoneAccountHandle phoneAccountHandle) {
Call fakeCall = mock(Call.class);
when(fakeCall.getHandle()).thenReturn(handle);
when(fakeCall.getName()).thenReturn(name);
when(fakeCall.getCreationTimeMillis()).thenReturn(timestamp);
when(fakeCall.getTargetPhoneAccount()).thenReturn(phoneAccountHandle);
return fakeCall;
}
private MissedCallNotifierImpl.NotificationBuilderFactory makeNotificationBuilderFactory(
Notification.Builder... builders) {
MissedCallNotifierImpl.NotificationBuilderFactory builderFactory =
mock(MissedCallNotifierImpl.NotificationBuilderFactory.class);
when(builderFactory.getBuilder(mContext)).thenReturn(builders[0],
Arrays.copyOfRange(builders, 1, builders.length));
return builderFactory;
}
private MissedCallNotifier makeMissedCallNotifier(
NotificationBuilderFactory fakeBuilderFactory, UserHandle currentUser) {
MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
mPhoneAccountRegistrar, new PhoneNumberUtilsAdapterImpl(), fakeBuilderFactory);
missedCallNotifier.setCurrentUserHandle(currentUser);
return missedCallNotifier;
}
private PhoneAccount makePhoneAccount(UserHandle userHandle, int capability) {
ComponentName componentName = new ComponentName("com.anything", "com.whatever");
PhoneAccountHandle phoneAccountHandle = new PhoneAccountHandle(componentName, "id",
userHandle);
PhoneAccount.Builder builder = new PhoneAccount.Builder(phoneAccountHandle, "test");
builder.setCapabilities(capability);
PhoneAccount phoneAccount = builder.build();
when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle))
.thenReturn(phoneAccount);
return phoneAccount;
}
}