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