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