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