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