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 20 import static org.mockito.ArgumentMatchers.nullable; 21 import static org.mockito.Matchers.any; 22 import static org.mockito.Matchers.anyBoolean; 23 import static org.mockito.Matchers.anyInt; 24 import static org.mockito.Matchers.anyString; 25 import static org.mockito.Matchers.eq; 26 import static org.mockito.Matchers.isNull; 27 import static org.mockito.Mockito.doAnswer; 28 import static org.mockito.Mockito.doNothing; 29 import static org.mockito.Mockito.doReturn; 30 import static org.mockito.Mockito.mock; 31 import static org.mockito.Mockito.reset; 32 import static org.mockito.Mockito.spy; 33 import static org.mockito.Mockito.timeout; 34 import static org.mockito.Mockito.times; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.when; 37 38 import android.app.NotificationManager; 39 import android.content.BroadcastReceiver; 40 import android.content.ComponentName; 41 import android.content.ContentResolver; 42 import android.content.Context; 43 import android.content.IContentProvider; 44 import android.content.Intent; 45 import android.media.AudioManager; 46 import android.media.IAudioService; 47 import android.net.Uri; 48 import android.os.Bundle; 49 import android.os.Handler; 50 import android.os.Looper; 51 import android.os.Process; 52 import android.os.UserHandle; 53 import android.provider.BlockedNumberContract; 54 import android.telecom.Call; 55 import android.telecom.ConnectionRequest; 56 import android.telecom.DisconnectCause; 57 import android.telecom.ParcelableCall; 58 import android.telecom.PhoneAccount; 59 import android.telecom.PhoneAccountHandle; 60 import android.telecom.TelecomManager; 61 import android.telecom.VideoProfile; 62 63 import com.android.internal.telecom.IInCallAdapter; 64 import com.android.server.telecom.AsyncRingtonePlayer; 65 import com.android.server.telecom.BluetoothPhoneServiceImpl; 66 import com.android.server.telecom.CallAudioManager; 67 import com.android.server.telecom.CallAudioRouteStateMachine; 68 import com.android.server.telecom.CallerInfoAsyncQueryFactory; 69 import com.android.server.telecom.CallerInfoLookupHelper; 70 import com.android.server.telecom.CallsManager; 71 import com.android.server.telecom.CallsManagerListenerBase; 72 import com.android.server.telecom.DefaultDialerCache; 73 import com.android.server.telecom.HeadsetMediaButton; 74 import com.android.server.telecom.HeadsetMediaButtonFactory; 75 import com.android.server.telecom.InCallWakeLockController; 76 import com.android.server.telecom.InCallWakeLockControllerFactory; 77 import com.android.server.telecom.MissedCallNotifier; 78 import com.android.server.telecom.PhoneAccountRegistrar; 79 import com.android.server.telecom.PhoneNumberUtilsAdapter; 80 import com.android.server.telecom.PhoneNumberUtilsAdapterImpl; 81 import com.android.server.telecom.ProximitySensorManager; 82 import com.android.server.telecom.ProximitySensorManagerFactory; 83 import com.android.server.telecom.TelecomSystem; 84 import com.android.server.telecom.Timeouts; 85 import com.android.server.telecom.callfiltering.AsyncBlockCheckFilter; 86 import com.android.server.telecom.components.UserCallIntentProcessor; 87 import com.android.server.telecom.ui.IncomingCallNotifier; 88 import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory; 89 90 import com.google.common.base.Predicate; 91 92 import org.mockito.ArgumentCaptor; 93 import org.mockito.Mock; 94 import org.mockito.invocation.InvocationOnMock; 95 import org.mockito.stubbing.Answer; 96 97 import java.util.ArrayList; 98 import java.util.List; 99 import java.util.concurrent.CountDownLatch; 100 import java.util.concurrent.TimeUnit; 101 102 /** 103 * Implements mocks and functionality required to implement telecom system tests. 104 */ 105 public class TelecomSystemTest extends TelecomTestCase { 106 107 static final int TEST_POLL_INTERVAL = 10; // milliseconds 108 static final int TEST_TIMEOUT = 1000; // milliseconds 109 110 public class HeadsetMediaButtonFactoryF implements HeadsetMediaButtonFactory { 111 @Override create(Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock)112 public HeadsetMediaButton create(Context context, CallsManager callsManager, 113 TelecomSystem.SyncRoot lock) { 114 return mHeadsetMediaButton; 115 } 116 } 117 118 public class ProximitySensorManagerFactoryF implements ProximitySensorManagerFactory { 119 @Override create(Context context, CallsManager callsManager)120 public ProximitySensorManager create(Context context, CallsManager callsManager) { 121 return mProximitySensorManager; 122 } 123 } 124 125 public class InCallWakeLockControllerFactoryF implements InCallWakeLockControllerFactory { 126 @Override create(Context context, CallsManager callsManager)127 public InCallWakeLockController create(Context context, CallsManager callsManager) { 128 return mInCallWakeLockController; 129 } 130 } 131 132 public static class MissedCallNotifierFakeImpl extends CallsManagerListenerBase 133 implements MissedCallNotifier { 134 List<CallInfo> missedCallsNotified = new ArrayList<>(); 135 136 @Override clearMissedCalls(UserHandle userHandle)137 public void clearMissedCalls(UserHandle userHandle) { 138 139 } 140 141 @Override showMissedCallNotification(CallInfo call)142 public void showMissedCallNotification(CallInfo call) { 143 missedCallsNotified.add(call); 144 } 145 146 @Override reloadAfterBootComplete(CallerInfoLookupHelper callerInfoLookupHelper, CallInfoFactory callInfoFactory)147 public void reloadAfterBootComplete(CallerInfoLookupHelper callerInfoLookupHelper, 148 CallInfoFactory callInfoFactory) { } 149 150 @Override reloadFromDatabase(CallerInfoLookupHelper callerInfoLookupHelper, CallInfoFactory callInfoFactory, UserHandle userHandle)151 public void reloadFromDatabase(CallerInfoLookupHelper callerInfoLookupHelper, 152 CallInfoFactory callInfoFactory, UserHandle userHandle) { } 153 154 @Override setCurrentUserHandle(UserHandle userHandle)155 public void setCurrentUserHandle(UserHandle userHandle) { 156 157 } 158 } 159 160 MissedCallNotifierFakeImpl mMissedCallNotifier = new MissedCallNotifierFakeImpl(); 161 private class EmergencyNumberUtilsAdapter extends PhoneNumberUtilsAdapterImpl { 162 163 @Override isLocalEmergencyNumber(Context context, String number)164 public boolean isLocalEmergencyNumber(Context context, String number) { 165 return mIsEmergencyCall; 166 } 167 168 @Override isPotentialLocalEmergencyNumber(Context context, String number)169 public boolean isPotentialLocalEmergencyNumber(Context context, String number) { 170 return mIsEmergencyCall; 171 } 172 } 173 PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter = new EmergencyNumberUtilsAdapter(); 174 175 @Mock HeadsetMediaButton mHeadsetMediaButton; 176 @Mock ProximitySensorManager mProximitySensorManager; 177 @Mock InCallWakeLockController mInCallWakeLockController; 178 @Mock BluetoothPhoneServiceImpl mBluetoothPhoneServiceImpl; 179 @Mock AsyncRingtonePlayer mAsyncRingtonePlayer; 180 @Mock IncomingCallNotifier mIncomingCallNotifier; 181 182 final ComponentName mInCallServiceComponentNameX = 183 new ComponentName( 184 "incall-service-package-X", 185 "incall-service-class-X"); 186 final ComponentName mInCallServiceComponentNameY = 187 new ComponentName( 188 "incall-service-package-Y", 189 "incall-service-class-Y"); 190 191 InCallServiceFixture mInCallServiceFixtureX; 192 InCallServiceFixture mInCallServiceFixtureY; 193 194 final ComponentName mConnectionServiceComponentNameA = 195 new ComponentName( 196 "connection-service-package-A", 197 "connection-service-class-A"); 198 final ComponentName mConnectionServiceComponentNameB = 199 new ComponentName( 200 "connection-service-package-B", 201 "connection-service-class-B"); 202 203 final PhoneAccount mPhoneAccountA0 = 204 PhoneAccount.builder( 205 new PhoneAccountHandle( 206 mConnectionServiceComponentNameA, 207 "id A 0"), 208 "Phone account service A ID 0") 209 .addSupportedUriScheme("tel") 210 .setCapabilities( 211 PhoneAccount.CAPABILITY_CALL_PROVIDER | 212 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION | 213 PhoneAccount.CAPABILITY_VIDEO_CALLING) 214 .build(); 215 final PhoneAccount mPhoneAccountA1 = 216 PhoneAccount.builder( 217 new PhoneAccountHandle( 218 mConnectionServiceComponentNameA, 219 "id A 1"), 220 "Phone account service A ID 1") 221 .addSupportedUriScheme("tel") 222 .setCapabilities( 223 PhoneAccount.CAPABILITY_CALL_PROVIDER | 224 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION | 225 PhoneAccount.CAPABILITY_VIDEO_CALLING) 226 .build(); 227 final PhoneAccount mPhoneAccountA2 = 228 PhoneAccount.builder( 229 new PhoneAccountHandle( 230 mConnectionServiceComponentNameA, 231 "id A 2"), 232 "Phone account service A ID 2") 233 .addSupportedUriScheme("tel") 234 .setCapabilities( 235 PhoneAccount.CAPABILITY_CALL_PROVIDER | 236 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) 237 .build(); 238 final PhoneAccount mPhoneAccountB0 = 239 PhoneAccount.builder( 240 new PhoneAccountHandle( 241 mConnectionServiceComponentNameB, 242 "id B 0"), 243 "Phone account service B ID 0") 244 .addSupportedUriScheme("tel") 245 .setCapabilities( 246 PhoneAccount.CAPABILITY_CALL_PROVIDER | 247 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION | 248 PhoneAccount.CAPABILITY_VIDEO_CALLING) 249 .build(); 250 final PhoneAccount mPhoneAccountE0 = 251 PhoneAccount.builder( 252 new PhoneAccountHandle( 253 mConnectionServiceComponentNameA, 254 "id E 0"), 255 "Phone account service E ID 0") 256 .addSupportedUriScheme("tel") 257 .setCapabilities( 258 PhoneAccount.CAPABILITY_CALL_PROVIDER | 259 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION | 260 PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) 261 .build(); 262 263 final PhoneAccount mPhoneAccountE1 = 264 PhoneAccount.builder( 265 new PhoneAccountHandle( 266 mConnectionServiceComponentNameA, 267 "id E 1"), 268 "Phone account service E ID 1") 269 .addSupportedUriScheme("tel") 270 .setCapabilities( 271 PhoneAccount.CAPABILITY_CALL_PROVIDER | 272 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION | 273 PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) 274 .build(); 275 276 ConnectionServiceFixture mConnectionServiceFixtureA; 277 ConnectionServiceFixture mConnectionServiceFixtureB; 278 Timeouts.Adapter mTimeoutsAdapter; 279 280 CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture; 281 282 IAudioService mAudioService; 283 284 TelecomSystem mTelecomSystem; 285 286 Context mSpyContext; 287 288 private int mNumOutgoingCallsMade; 289 290 private boolean mIsEmergencyCall; 291 292 class IdPair { 293 final String mConnectionId; 294 final String mCallId; 295 IdPair(String connectionId, String callId)296 public IdPair(String connectionId, String callId) { 297 this.mConnectionId = connectionId; 298 this.mCallId = callId; 299 } 300 } 301 302 @Override setUp()303 public void setUp() throws Exception { 304 super.setUp(); 305 mSpyContext = mComponentContextFixture.getTestDouble().getApplicationContext(); 306 doReturn(mSpyContext).when(mSpyContext).getApplicationContext(); 307 doNothing().when(mSpyContext).sendBroadcastAsUser(any(), any(), any()); 308 309 mNumOutgoingCallsMade = 0; 310 311 mIsEmergencyCall = false; 312 313 // First set up information about the In-Call services in the mock Context, since 314 // Telecom will search for these as soon as it is instantiated 315 setupInCallServices(); 316 317 // Next, create the TelecomSystem, our system under test 318 setupTelecomSystem(); 319 320 // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the 321 // now-running TelecomSystem 322 setupConnectionServices(); 323 } 324 325 @Override tearDown()326 public void tearDown() throws Exception { 327 mTelecomSystem.getCallsManager().getCallAudioManager() 328 .getCallAudioRouteStateMachine().quitNow(); 329 mTelecomSystem.getCallsManager().getCallAudioManager() 330 .getCallAudioModeStateMachine().quitNow(); 331 mTelecomSystem = null; 332 super.tearDown(); 333 } 334 makeConferenceCall()335 protected ParcelableCall makeConferenceCall() throws Exception { 336 IdPair callId1 = startAndMakeActiveOutgoingCall("650-555-1212", 337 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA); 338 339 IdPair callId2 = startAndMakeActiveOutgoingCall("650-555-1213", 340 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA); 341 342 IInCallAdapter inCallAdapter = mInCallServiceFixtureX.getInCallAdapter(); 343 inCallAdapter.conference(callId1.mCallId, callId2.mCallId); 344 // Wait for wacky non-deterministic behavior 345 Thread.sleep(200); 346 ParcelableCall call1 = mInCallServiceFixtureX.getCall(callId1.mCallId); 347 ParcelableCall call2 = mInCallServiceFixtureX.getCall(callId2.mCallId); 348 // Check that the two calls end up with a parent in the end 349 assertNotNull(call1.getParentCallId()); 350 assertNotNull(call2.getParentCallId()); 351 assertEquals(call1.getParentCallId(), call2.getParentCallId()); 352 353 // Check to make sure that the parent call made it to the in-call service 354 String parentCallId = call1.getParentCallId(); 355 ParcelableCall conferenceCall = mInCallServiceFixtureX.getCall(parentCallId); 356 assertEquals(2, conferenceCall.getChildCallIds().size()); 357 assertTrue(conferenceCall.getChildCallIds().contains(callId1.mCallId)); 358 assertTrue(conferenceCall.getChildCallIds().contains(callId2.mCallId)); 359 return conferenceCall; 360 } 361 setupTelecomSystem()362 private void setupTelecomSystem() throws Exception { 363 // Use actual implementations instead of mocking the interface out. 364 HeadsetMediaButtonFactory headsetMediaButtonFactory = 365 spy(new HeadsetMediaButtonFactoryF()); 366 ProximitySensorManagerFactory proximitySensorManagerFactory = 367 spy(new ProximitySensorManagerFactoryF()); 368 InCallWakeLockControllerFactory inCallWakeLockControllerFactory = 369 spy(new InCallWakeLockControllerFactoryF()); 370 mAudioService = setupAudioService(); 371 372 mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture(); 373 374 mTimeoutsAdapter = mock(Timeouts.Adapter.class); 375 when(mTimeoutsAdapter.getCallScreeningTimeoutMillis(any(ContentResolver.class))) 376 .thenReturn(TEST_TIMEOUT / 5L); 377 mIncomingCallNotifier = mock(IncomingCallNotifier.class); 378 379 mTelecomSystem = new TelecomSystem( 380 mComponentContextFixture.getTestDouble(), 381 new MissedCallNotifierImplFactory() { 382 @Override 383 public MissedCallNotifier makeMissedCallNotifierImpl(Context context, 384 PhoneAccountRegistrar phoneAccountRegistrar, 385 DefaultDialerCache defaultDialerCache) { 386 return mMissedCallNotifier; 387 } 388 }, 389 mCallerInfoAsyncQueryFactoryFixture.getTestDouble(), 390 headsetMediaButtonFactory, 391 proximitySensorManagerFactory, 392 inCallWakeLockControllerFactory, 393 new CallAudioManager.AudioServiceFactory() { 394 @Override 395 public IAudioService getAudioService() { 396 return mAudioService; 397 } 398 }, 399 new BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory() { 400 @Override 401 public BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context, 402 TelecomSystem.SyncRoot lock, CallsManager callsManager, 403 PhoneAccountRegistrar phoneAccountRegistrar) { 404 return mBluetoothPhoneServiceImpl; 405 } 406 }, 407 mTimeoutsAdapter, 408 mAsyncRingtonePlayer, 409 mPhoneNumberUtilsAdapter, 410 mIncomingCallNotifier); 411 412 mComponentContextFixture.setTelecomManager(new TelecomManager( 413 mComponentContextFixture.getTestDouble(), 414 mTelecomSystem.getTelecomServiceImpl().getBinder())); 415 416 verify(headsetMediaButtonFactory).create( 417 eq(mComponentContextFixture.getTestDouble().getApplicationContext()), 418 any(CallsManager.class), 419 any(TelecomSystem.SyncRoot.class)); 420 verify(proximitySensorManagerFactory).create( 421 eq(mComponentContextFixture.getTestDouble().getApplicationContext()), 422 any(CallsManager.class)); 423 verify(inCallWakeLockControllerFactory).create( 424 eq(mComponentContextFixture.getTestDouble().getApplicationContext()), 425 any(CallsManager.class)); 426 } 427 setupConnectionServices()428 private void setupConnectionServices() throws Exception { 429 mConnectionServiceFixtureA = new ConnectionServiceFixture(); 430 mConnectionServiceFixtureB = new ConnectionServiceFixture(); 431 432 mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameA, 433 mConnectionServiceFixtureA.getTestDouble()); 434 mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameB, 435 mConnectionServiceFixtureB.getTestDouble()); 436 437 mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0); 438 mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1); 439 mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA2); 440 mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0); 441 mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE0); 442 mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE1); 443 444 mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount( 445 mPhoneAccountA0.getAccountHandle(), Process.myUserHandle()); 446 } 447 setupInCallServices()448 private void setupInCallServices() throws Exception { 449 mComponentContextFixture.putResource( 450 com.android.server.telecom.R.string.ui_default_package, 451 mInCallServiceComponentNameX.getPackageName()); 452 mComponentContextFixture.putResource( 453 com.android.server.telecom.R.string.incall_default_class, 454 mInCallServiceComponentNameX.getClassName()); 455 mComponentContextFixture.putBooleanResource( 456 com.android.internal.R.bool.config_voice_capable, true); 457 458 mInCallServiceFixtureX = new InCallServiceFixture(); 459 mInCallServiceFixtureY = new InCallServiceFixture(); 460 461 mComponentContextFixture.addInCallService(mInCallServiceComponentNameX, 462 mInCallServiceFixtureX.getTestDouble()); 463 mComponentContextFixture.addInCallService(mInCallServiceComponentNameY, 464 mInCallServiceFixtureY.getTestDouble()); 465 } 466 467 /** 468 * Helper method for setting up the fake audio service. 469 * Calls to the fake audio service need to toggle the return 470 * value of AudioManager#isMicrophoneMute. 471 * @return mock of IAudioService 472 */ setupAudioService()473 private IAudioService setupAudioService() { 474 IAudioService audioService = mock(IAudioService.class); 475 476 final AudioManager fakeAudioManager = 477 (AudioManager) mComponentContextFixture.getTestDouble() 478 .getApplicationContext().getSystemService(Context.AUDIO_SERVICE); 479 480 try { 481 doAnswer(new Answer() { 482 @Override 483 public Object answer(InvocationOnMock i) { 484 Object[] args = i.getArguments(); 485 doReturn(args[0]).when(fakeAudioManager).isMicrophoneMute(); 486 return null; 487 } 488 }).when(audioService) 489 .setMicrophoneMute(any(Boolean.class), any(String.class), any(Integer.class)); 490 491 } catch (android.os.RemoteException e) { 492 // Do nothing, leave the faked microphone state as-is 493 } 494 return audioService; 495 } 496 startOutgoingPhoneCallWithNoPhoneAccount(String number, ConnectionServiceFixture connectionServiceFixture)497 protected String startOutgoingPhoneCallWithNoPhoneAccount(String number, 498 ConnectionServiceFixture connectionServiceFixture) 499 throws Exception { 500 501 return startOutgoingPhoneCallPendingCreateConnection(number, null, 502 connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY); 503 } 504 outgoingCallPhoneAccountSelected(PhoneAccountHandle phoneAccountHandle, int startingNumConnections, int startingNumCalls, ConnectionServiceFixture connectionServiceFixture)505 protected IdPair outgoingCallPhoneAccountSelected(PhoneAccountHandle phoneAccountHandle, 506 int startingNumConnections, int startingNumCalls, 507 ConnectionServiceFixture connectionServiceFixture) throws Exception { 508 509 IdPair ids = outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls, 510 phoneAccountHandle, connectionServiceFixture); 511 512 connectionServiceFixture.sendSetDialing(ids.mConnectionId); 513 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 514 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 515 516 connectionServiceFixture.sendSetVideoState(ids.mConnectionId); 517 518 connectionServiceFixture.sendSetActive(ids.mConnectionId); 519 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 520 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 521 522 return ids; 523 } 524 startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser)525 protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle, 526 ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser) 527 throws Exception { 528 529 return startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture, 530 initiatingUser, VideoProfile.STATE_AUDIO_ONLY); 531 } 532 startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, int videoState)533 protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle, 534 ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, 535 int videoState) throws Exception { 536 int startingNumConnections = connectionServiceFixture.mConnectionById.size(); 537 int startingNumCalls = mInCallServiceFixtureX.mCallById.size(); 538 539 startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle, 540 connectionServiceFixture, initiatingUser, videoState); 541 542 return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls, 543 phoneAccountHandle, connectionServiceFixture); 544 } 545 triggerEmergencyRedial(PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, IdPair emergencyIds)546 protected IdPair triggerEmergencyRedial(PhoneAccountHandle phoneAccountHandle, 547 ConnectionServiceFixture connectionServiceFixture, IdPair emergencyIds) 548 throws Exception { 549 int startingNumConnections = connectionServiceFixture.mConnectionById.size(); 550 int startingNumCalls = mInCallServiceFixtureX.mCallById.size(); 551 552 // Send the message to disconnect the Emergency call due to an error. 553 // CreateConnectionProcessor should now try the second SIM account 554 connectionServiceFixture.sendSetDisconnected(emergencyIds.mConnectionId, 555 DisconnectCause.ERROR); 556 waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT); 557 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall( 558 emergencyIds.mCallId).getState()); 559 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall( 560 emergencyIds.mCallId).getState()); 561 562 return redialingCallCreateConnectionComplete(startingNumConnections, startingNumCalls, 563 phoneAccountHandle, connectionServiceFixture); 564 } 565 startOutgoingEmergencyCall(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, int videoState)566 protected IdPair startOutgoingEmergencyCall(String number, 567 PhoneAccountHandle phoneAccountHandle, 568 ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, 569 int videoState) throws Exception { 570 int startingNumConnections = connectionServiceFixture.mConnectionById.size(); 571 int startingNumCalls = mInCallServiceFixtureX.mCallById.size(); 572 573 mIsEmergencyCall = true; 574 // Call will not use the ordered broadcaster, since it is an Emergency Call 575 startOutgoingPhoneCallWaitForBroadcaster(number, phoneAccountHandle, 576 connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/); 577 578 return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls, 579 phoneAccountHandle, connectionServiceFixture); 580 } 581 startOutgoingPhoneCallWaitForBroadcaster(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, int videoState, boolean isEmergency)582 protected void startOutgoingPhoneCallWaitForBroadcaster(String number, 583 PhoneAccountHandle phoneAccountHandle, 584 ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, 585 int videoState, boolean isEmergency) throws Exception { 586 reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(), 587 mInCallServiceFixtureY.getTestDouble()); 588 589 assertEquals(mInCallServiceFixtureX.mCallById.size(), 590 mInCallServiceFixtureY.mCallById.size()); 591 assertEquals((mInCallServiceFixtureX.mInCallAdapter != null), 592 (mInCallServiceFixtureY.mInCallAdapter != null)); 593 594 mNumOutgoingCallsMade++; 595 596 boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null; 597 598 Intent actionCallIntent = new Intent(); 599 actionCallIntent.setData(Uri.parse("tel:" + number)); 600 actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number); 601 if(isEmergency) { 602 actionCallIntent.setAction(Intent.ACTION_CALL_EMERGENCY); 603 } else { 604 actionCallIntent.setAction(Intent.ACTION_CALL); 605 } 606 if (phoneAccountHandle != null) { 607 actionCallIntent.putExtra( 608 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, 609 phoneAccountHandle); 610 } 611 if (videoState != VideoProfile.STATE_AUDIO_ONLY) { 612 actionCallIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); 613 } 614 615 final UserHandle userHandle = initiatingUser; 616 Context localAppContext = mComponentContextFixture.getTestDouble().getApplicationContext(); 617 new UserCallIntentProcessor(localAppContext, userHandle).processIntent( 618 actionCallIntent, null, true /* hasCallAppOp*/); 619 // UserCallIntentProcessor's mContext.sendBroadcastAsUser(...) will call to an empty method 620 // as to not actually try to send an intent to PrimaryCallReceiver. We verify that it was 621 // called correctly in order to continue. 622 verify(localAppContext).sendBroadcastAsUser(actionCallIntent, UserHandle.SYSTEM); 623 mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent); 624 // Wait for handler to start CallerInfo lookup. 625 waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT); 626 // Send the CallerInfo lookup reply. 627 mCallerInfoAsyncQueryFactoryFixture.mRequests.forEach( 628 CallerInfoAsyncQueryFactoryFixture.Request::reply); 629 630 if (!hasInCallAdapter) { 631 verify(mInCallServiceFixtureX.getTestDouble()) 632 .setInCallAdapter( 633 any(IInCallAdapter.class)); 634 verify(mInCallServiceFixtureY.getTestDouble()) 635 .setInCallAdapter( 636 any(IInCallAdapter.class)); 637 } 638 } 639 startOutgoingPhoneCallPendingCreateConnection(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, int videoState)640 protected String startOutgoingPhoneCallPendingCreateConnection(String number, 641 PhoneAccountHandle phoneAccountHandle, 642 ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, 643 int videoState) throws Exception { 644 startOutgoingPhoneCallWaitForBroadcaster(number,phoneAccountHandle, 645 connectionServiceFixture, initiatingUser, videoState, false /*isEmergency*/); 646 647 ArgumentCaptor<Intent> newOutgoingCallIntent = 648 ArgumentCaptor.forClass(Intent.class); 649 ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver = 650 ArgumentCaptor.forClass(BroadcastReceiver.class); 651 652 verify(mComponentContextFixture.getTestDouble().getApplicationContext(), 653 times(mNumOutgoingCallsMade)) 654 .sendOrderedBroadcastAsUser( 655 newOutgoingCallIntent.capture(), 656 any(UserHandle.class), 657 anyString(), 658 anyInt(), 659 newOutgoingCallReceiver.capture(), 660 nullable(Handler.class), 661 anyInt(), 662 anyString(), 663 nullable(Bundle.class)); 664 665 // Pass on the new outgoing call Intent 666 // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive() 667 newOutgoingCallReceiver.getValue().setPendingResult( 668 new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0)); 669 newOutgoingCallReceiver.getValue().setResultData( 670 newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER)); 671 newOutgoingCallReceiver.getValue().onReceive(mComponentContextFixture.getTestDouble(), 672 newOutgoingCallIntent.getValue()); 673 674 return mInCallServiceFixtureX.mLatestCallId; 675 } 676 677 // When Telecom is redialing due to an error, we need to make sure the number of connections 678 // increase, but not the number of Calls in the InCallService. redialingCallCreateConnectionComplete(int startingNumConnections, int startingNumCalls, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)679 protected IdPair redialingCallCreateConnectionComplete(int startingNumConnections, 680 int startingNumCalls, PhoneAccountHandle phoneAccountHandle, 681 ConnectionServiceFixture connectionServiceFixture) throws Exception { 682 683 assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size()); 684 685 verify(connectionServiceFixture.getTestDouble()) 686 .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class), 687 eq(false)/*isIncoming*/, anyBoolean(), any()); 688 // Wait for handleCreateConnectionComplete 689 waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT); 690 691 // Make sure the number of registered InCallService Calls stays the same. 692 assertEquals(startingNumCalls, mInCallServiceFixtureX.mCallById.size()); 693 assertEquals(startingNumCalls, mInCallServiceFixtureY.mCallById.size()); 694 695 assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId); 696 697 return new IdPair(connectionServiceFixture.mLatestConnectionId, 698 mInCallServiceFixtureX.mLatestCallId); 699 } 700 outgoingCallCreateConnectionComplete(int startingNumConnections, int startingNumCalls, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)701 protected IdPair outgoingCallCreateConnectionComplete(int startingNumConnections, 702 int startingNumCalls, PhoneAccountHandle phoneAccountHandle, 703 ConnectionServiceFixture connectionServiceFixture) throws Exception { 704 705 assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size()); 706 707 verify(connectionServiceFixture.getTestDouble()) 708 .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class), 709 eq(false)/*isIncoming*/, anyBoolean(), any()); 710 // Wait for handleCreateConnectionComplete 711 waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT); 712 // Wait for the callback in ConnectionService#onAdapterAttached to execute. 713 waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT); 714 715 // Ensure callback to CS on successful creation happened. 716 verify(connectionServiceFixture.getTestDouble(), timeout(TEST_TIMEOUT)) 717 .createConnectionComplete(anyString(), any()); 718 719 assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size()); 720 assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size()); 721 722 assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId); 723 724 return new IdPair(connectionServiceFixture.mLatestConnectionId, 725 mInCallServiceFixtureX.mLatestCallId); 726 } 727 startIncomingPhoneCall( String number, PhoneAccountHandle phoneAccountHandle, final ConnectionServiceFixture connectionServiceFixture)728 protected IdPair startIncomingPhoneCall( 729 String number, 730 PhoneAccountHandle phoneAccountHandle, 731 final ConnectionServiceFixture connectionServiceFixture) throws Exception { 732 return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY, 733 connectionServiceFixture); 734 } 735 startIncomingPhoneCall( String number, PhoneAccountHandle phoneAccountHandle, int videoState, final ConnectionServiceFixture connectionServiceFixture)736 protected IdPair startIncomingPhoneCall( 737 String number, 738 PhoneAccountHandle phoneAccountHandle, 739 int videoState, 740 final ConnectionServiceFixture connectionServiceFixture) throws Exception { 741 reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(), 742 mInCallServiceFixtureY.getTestDouble()); 743 744 assertEquals(mInCallServiceFixtureX.mCallById.size(), 745 mInCallServiceFixtureY.mCallById.size()); 746 assertEquals((mInCallServiceFixtureX.mInCallAdapter != null), 747 (mInCallServiceFixtureY.mInCallAdapter != null)); 748 final int startingNumConnections = connectionServiceFixture.mConnectionById.size(); 749 final int startingNumCalls = mInCallServiceFixtureX.mCallById.size(); 750 boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null; 751 connectionServiceFixture.mConnectionServiceDelegate.mVideoState = videoState; 752 753 Bundle extras = new Bundle(); 754 extras.putParcelable( 755 TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, 756 Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null)); 757 mTelecomSystem.getTelecomServiceImpl().getBinder() 758 .addNewIncomingCall(phoneAccountHandle, extras); 759 760 verify(connectionServiceFixture.getTestDouble()) 761 .createConnection(any(PhoneAccountHandle.class), anyString(), 762 any(ConnectionRequest.class), eq(true), eq(false), any()); 763 764 // Wait for the handler to start the CallerInfo lookup 765 waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT); 766 767 // Ensure callback to CS on successful creation happened. 768 verify(connectionServiceFixture.getTestDouble(), timeout(TEST_TIMEOUT)) 769 .createConnectionComplete(anyString(), any()); 770 771 772 // Process the CallerInfo lookup reply 773 mCallerInfoAsyncQueryFactoryFixture.mRequests.forEach( 774 CallerInfoAsyncQueryFactoryFixture.Request::reply); 775 776 IContentProvider blockedNumberProvider = 777 mSpyContext.getContentResolver().acquireProvider(BlockedNumberContract.AUTHORITY); 778 verify(blockedNumberProvider, timeout(TEST_TIMEOUT)).call( 779 anyString(), 780 eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER), 781 eq(number), 782 isNull(Bundle.class)); 783 784 // For the case of incoming calls, Telecom connecting the InCall services and adding the 785 // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call 786 // is added, future interactions as triggered by the ConnectionService, through the various 787 // test fixtures, will be synchronous. 788 789 if (!hasInCallAdapter) { 790 verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT)) 791 .setInCallAdapter(any(IInCallAdapter.class)); 792 verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT)) 793 .setInCallAdapter(any(IInCallAdapter.class)); 794 } 795 796 // Give the InCallService time to respond 797 798 assertTrueWithTimeout(new Predicate<Void>() { 799 @Override 800 public boolean apply(Void v) { 801 return mInCallServiceFixtureX.mInCallAdapter != null; 802 } 803 }); 804 805 assertTrueWithTimeout(new Predicate<Void>() { 806 @Override 807 public boolean apply(Void v) { 808 return mInCallServiceFixtureY.mInCallAdapter != null; 809 } 810 }); 811 812 verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT)) 813 .addCall(any(ParcelableCall.class)); 814 verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT)) 815 .addCall(any(ParcelableCall.class)); 816 817 // Give the InCallService time to respond 818 819 assertTrueWithTimeout(new Predicate<Void>() { 820 @Override 821 public boolean apply(Void v) { 822 return startingNumConnections + 1 == 823 connectionServiceFixture.mConnectionById.size(); 824 } 825 }); 826 assertTrueWithTimeout(new Predicate<Void>() { 827 @Override 828 public boolean apply(Void v) { 829 return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size(); 830 } 831 }); 832 assertTrueWithTimeout(new Predicate<Void>() { 833 @Override 834 public boolean apply(Void v) { 835 return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size(); 836 } 837 }); 838 839 assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId); 840 841 return new IdPair(connectionServiceFixture.mLatestConnectionId, 842 mInCallServiceFixtureX.mLatestCallId); 843 } 844 startAndMakeActiveOutgoingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)845 protected IdPair startAndMakeActiveOutgoingCall( 846 String number, 847 PhoneAccountHandle phoneAccountHandle, 848 ConnectionServiceFixture connectionServiceFixture) throws Exception { 849 return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture, 850 VideoProfile.STATE_AUDIO_ONLY); 851 } 852 853 // A simple outgoing call, verifying that the appropriate connection service is contacted, 854 // the proper lifecycle is followed, and both In-Call Services are updated correctly. startAndMakeActiveOutgoingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, int videoState)855 protected IdPair startAndMakeActiveOutgoingCall( 856 String number, 857 PhoneAccountHandle phoneAccountHandle, 858 ConnectionServiceFixture connectionServiceFixture, int videoState) throws Exception { 859 IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture, 860 Process.myUserHandle(), videoState); 861 862 connectionServiceFixture.sendSetDialing(ids.mConnectionId); 863 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 864 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 865 866 connectionServiceFixture.sendSetVideoState(ids.mConnectionId); 867 868 connectionServiceFixture.sendSetActive(ids.mConnectionId); 869 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 870 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 871 872 return ids; 873 } 874 startAndMakeActiveIncomingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)875 protected IdPair startAndMakeActiveIncomingCall( 876 String number, 877 PhoneAccountHandle phoneAccountHandle, 878 ConnectionServiceFixture connectionServiceFixture) throws Exception { 879 return startAndMakeActiveIncomingCall(number, phoneAccountHandle, connectionServiceFixture, 880 VideoProfile.STATE_AUDIO_ONLY); 881 } 882 883 // A simple incoming call, similar in scope to the previous test startAndMakeActiveIncomingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, int videoState)884 protected IdPair startAndMakeActiveIncomingCall( 885 String number, 886 PhoneAccountHandle phoneAccountHandle, 887 ConnectionServiceFixture connectionServiceFixture, 888 int videoState) throws Exception { 889 IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture); 890 891 assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 892 assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 893 894 mInCallServiceFixtureX.mInCallAdapter 895 .answerCall(ids.mCallId, videoState); 896 897 if (!VideoProfile.isVideo(videoState)) { 898 verify(connectionServiceFixture.getTestDouble()) 899 .answer(eq(ids.mConnectionId), any()); 900 } else { 901 verify(connectionServiceFixture.getTestDouble()) 902 .answerVideo(eq(ids.mConnectionId), eq(videoState), any()); 903 } 904 905 connectionServiceFixture.sendSetActive(ids.mConnectionId); 906 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 907 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 908 909 return ids; 910 } 911 startAndMakeDialingEmergencyCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)912 protected IdPair startAndMakeDialingEmergencyCall( 913 String number, 914 PhoneAccountHandle phoneAccountHandle, 915 ConnectionServiceFixture connectionServiceFixture) throws Exception { 916 IdPair ids = startOutgoingEmergencyCall(number, phoneAccountHandle, 917 connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY); 918 919 connectionServiceFixture.sendSetDialing(ids.mConnectionId); 920 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 921 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 922 923 return ids; 924 } 925 assertTrueWithTimeout(Predicate<Void> predicate)926 protected static void assertTrueWithTimeout(Predicate<Void> predicate) { 927 int elapsed = 0; 928 while (elapsed < TEST_TIMEOUT) { 929 if (predicate.apply(null)) { 930 return; 931 } else { 932 try { 933 Thread.sleep(TEST_POLL_INTERVAL); 934 elapsed += TEST_POLL_INTERVAL; 935 } catch (InterruptedException e) { 936 fail(e.toString()); 937 } 938 } 939 } 940 fail("Timeout in assertTrueWithTimeout"); 941 } 942 } 943