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