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