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