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