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