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