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 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 120 public ProximitySensorManager create(Context context, CallsManager callsManager) { 121 return mProximitySensorManager; 122 } 123 } 124 125 public class InCallWakeLockControllerFactoryF implements InCallWakeLockControllerFactory { 126 @Override 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 137 public void clearMissedCalls(UserHandle userHandle) { 138 139 } 140 141 @Override 142 public void showMissedCallNotification(CallInfo call) { 143 missedCallsNotified.add(call); 144 } 145 146 @Override 147 public void reloadAfterBootComplete(CallerInfoLookupHelper callerInfoLookupHelper, 148 CallInfoFactory callInfoFactory) { } 149 150 @Override 151 public void reloadFromDatabase(CallerInfoLookupHelper callerInfoLookupHelper, 152 CallInfoFactory callInfoFactory, UserHandle userHandle) { } 153 154 @Override 155 public void setCurrentUserHandle(UserHandle userHandle) { 156 157 } 158 } 159 160 MissedCallNotifierFakeImpl mMissedCallNotifier = new MissedCallNotifierFakeImpl(); 161 private class EmergencyNumberUtilsAdapter extends PhoneNumberUtilsAdapterImpl { 162 163 @Override 164 public boolean isLocalEmergencyNumber(Context context, String number) { 165 return mIsEmergencyCall; 166 } 167 168 @Override 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 296 public IdPair(String connectionId, String callId) { 297 this.mConnectionId = connectionId; 298 this.mCallId = callId; 299 } 300 } 301 302 @Override 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 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 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 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 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 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 */ 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 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 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 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 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 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 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 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 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. 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 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 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 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 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. 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 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 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 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 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