1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.telecom.tests; 18 19 20 import static org.mockito.Matchers.any; 21 import static org.mockito.Matchers.anyBoolean; 22 import static org.mockito.Matchers.anyInt; 23 import static org.mockito.Matchers.anyString; 24 import static org.mockito.Matchers.eq; 25 import static org.mockito.Matchers.isNull; 26 import static org.mockito.Mockito.doAnswer; 27 import static org.mockito.Mockito.doReturn; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.reset; 30 import static org.mockito.Mockito.spy; 31 import static org.mockito.Mockito.timeout; 32 import static org.mockito.Mockito.times; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.when; 35 36 import android.content.BroadcastReceiver; 37 import android.content.ComponentName; 38 import android.content.ContentResolver; 39 import android.content.Context; 40 import android.content.IContentProvider; 41 import android.content.Intent; 42 import android.media.AudioManager; 43 import android.media.IAudioService; 44 import android.net.Uri; 45 import android.os.Bundle; 46 import android.os.Handler; 47 import android.os.Process; 48 import android.os.UserHandle; 49 import android.provider.BlockedNumberContract; 50 import android.telecom.Call; 51 import android.telecom.ConnectionRequest; 52 import android.telecom.ParcelableCall; 53 import android.telecom.PhoneAccount; 54 import android.telecom.PhoneAccountHandle; 55 import android.telecom.TelecomManager; 56 import android.telecom.VideoProfile; 57 58 import com.android.internal.telecom.IInCallAdapter; 59 import com.android.server.telecom.AsyncRingtonePlayer; 60 import com.android.server.telecom.BluetoothPhoneServiceImpl; 61 import com.android.server.telecom.CallAudioManager; 62 import com.android.server.telecom.CallerInfoAsyncQueryFactory; 63 import com.android.server.telecom.CallsManager; 64 import com.android.server.telecom.CallsManagerListenerBase; 65 import com.android.server.telecom.ContactsAsyncHelper; 66 import com.android.server.telecom.HeadsetMediaButton; 67 import com.android.server.telecom.HeadsetMediaButtonFactory; 68 import com.android.server.telecom.InCallWakeLockController; 69 import com.android.server.telecom.InCallWakeLockControllerFactory; 70 import com.android.server.telecom.MissedCallNotifier; 71 import com.android.server.telecom.PhoneAccountRegistrar; 72 import com.android.server.telecom.ProximitySensorManager; 73 import com.android.server.telecom.ProximitySensorManagerFactory; 74 import com.android.server.telecom.TelecomSystem; 75 import com.android.server.telecom.Timeouts; 76 import com.android.server.telecom.components.UserCallIntentProcessor; 77 import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory; 78 79 import com.google.common.base.Predicate; 80 81 import org.mockito.ArgumentCaptor; 82 import org.mockito.Mock; 83 import org.mockito.invocation.InvocationOnMock; 84 import org.mockito.stubbing.Answer; 85 86 import java.util.ArrayList; 87 import java.util.List; 88 89 /** 90 * Implements mocks and functionality required to implement telecom system tests. 91 */ 92 public 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 create(Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock)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 create(Context context, CallsManager callsManager)107 public ProximitySensorManager create(Context context, CallsManager callsManager) { 108 return mProximitySensorManager; 109 } 110 } 111 112 public class InCallWakeLockControllerFactoryF implements InCallWakeLockControllerFactory { 113 @Override create(Context context, CallsManager callsManager)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 clearMissedCalls(UserHandle userHandle)124 public void clearMissedCalls(UserHandle userHandle) { 125 126 } 127 128 @Override showMissedCallNotification(com.android.server.telecom.Call call)129 public void showMissedCallNotification(com.android.server.telecom.Call call) { 130 missedCallsNotified.add(call); 131 } 132 133 @Override reloadFromDatabase(TelecomSystem.SyncRoot lock, CallsManager callsManager, ContactsAsyncHelper contactsAsyncHelper, CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, UserHandle userHandle)134 public void reloadFromDatabase(TelecomSystem.SyncRoot lock, CallsManager callsManager, 135 ContactsAsyncHelper contactsAsyncHelper, 136 CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, UserHandle userHandle) { 137 138 } 139 140 @Override setCurrentUserHandle(UserHandle userHandle)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 IdPair(String connectionId, String callId)226 public IdPair(String connectionId, String callId) { 227 this.mConnectionId = connectionId; 228 this.mCallId = callId; 229 } 230 } 231 232 @Override setUp()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 tearDown()253 public void tearDown() throws Exception { 254 mTelecomSystem = null; 255 super.tearDown(); 256 } 257 makeConferenceCall()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 setupTelecomSystem()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 setupConnectionServices()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 setupInCallServices()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 */ setupAudioService()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 startOutgoingPhoneCallWithNoPhoneAccount(String number, ConnectionServiceFixture connectionServiceFixture)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 outgoingCallPhoneAccountSelected(PhoneAccountHandle phoneAccountHandle, int startingNumConnections, int startingNumCalls, ConnectionServiceFixture connectionServiceFixture)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 startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser)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 startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, int videoState)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 startOutgoingPhoneCallPendingCreateConnection(String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser, int videoState)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 outgoingCallCreateConnectionComplete(int startingNumConnections, int startingNumCalls, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)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 startIncomingPhoneCall( String number, PhoneAccountHandle phoneAccountHandle, final ConnectionServiceFixture connectionServiceFixture)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 startIncomingPhoneCall( String number, PhoneAccountHandle phoneAccountHandle, int videoState, final ConnectionServiceFixture connectionServiceFixture)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 startAndMakeActiveOutgoingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)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. startAndMakeActiveOutgoingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, int videoState)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 startAndMakeActiveIncomingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture)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 startAndMakeActiveIncomingCall( String number, PhoneAccountHandle phoneAccountHandle, ConnectionServiceFixture connectionServiceFixture, int videoState)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 assertTrueWithTimeout(Predicate<Void> predicate)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