TelecomSystemTest.java revision b3979ee8e636820cc5e68f26562bc02987e5d489
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 reset( 414 connectionServiceFixture.getTestDouble(), 415 mInCallServiceFixtureX.getTestDouble(), 416 mInCallServiceFixtureY.getTestDouble()); 417 418 assertEquals( 419 mInCallServiceFixtureX.mCallById.size(), 420 mInCallServiceFixtureY.mCallById.size()); 421 assertEquals( 422 (mInCallServiceFixtureX.mInCallAdapter != null), 423 (mInCallServiceFixtureY.mInCallAdapter != null)); 424 425 final int startingNumConnections = connectionServiceFixture.mConnectionById.size(); 426 final int startingNumCalls = mInCallServiceFixtureX.mCallById.size(); 427 boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null; 428 429 Bundle extras = new Bundle(); 430 extras.putParcelable( 431 TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, 432 Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null)); 433 mTelecomSystem.getTelecomServiceImpl().getBinder() 434 .addNewIncomingCall(phoneAccountHandle, extras); 435 436 verify(connectionServiceFixture.getTestDouble()).createConnection( 437 any(PhoneAccountHandle.class), 438 anyString(), 439 any(ConnectionRequest.class), 440 eq(true), 441 eq(false)); 442 443 connectionServiceFixture.sendHandleCreateConnectionComplete( 444 connectionServiceFixture.mLatestConnectionId); 445 connectionServiceFixture.sendSetRinging( 446 connectionServiceFixture.mLatestConnectionId); 447 448 // For the case of incoming calls, Telecom connecting the InCall services and adding the 449 // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call 450 // is added, future interactions as triggered by the ConnectionService, through the various 451 // test fixtures, will be synchronous. 452 453 if (!hasInCallAdapter) { 454 verify( 455 mInCallServiceFixtureX.getTestDouble(), 456 timeout(TEST_TIMEOUT)) 457 .setInCallAdapter( 458 any(IInCallAdapter.class)); 459 verify( 460 mInCallServiceFixtureY.getTestDouble(), 461 timeout(TEST_TIMEOUT)) 462 .setInCallAdapter( 463 any(IInCallAdapter.class)); 464 } 465 466 // Give the InCallService time to respond 467 468 assertTrueWithTimeout(new Predicate<Void>() { 469 @Override 470 public boolean apply(Void v) { 471 return mInCallServiceFixtureX.mInCallAdapter != null; 472 } 473 }); 474 475 assertTrueWithTimeout(new Predicate<Void>() { 476 @Override 477 public boolean apply(Void v) { 478 return mInCallServiceFixtureY.mInCallAdapter != null; 479 } 480 }); 481 482 verify( 483 mInCallServiceFixtureX.getTestDouble(), 484 timeout(TEST_TIMEOUT)) 485 .addCall( 486 any(ParcelableCall.class)); 487 verify( 488 mInCallServiceFixtureY.getTestDouble(), 489 timeout(TEST_TIMEOUT)) 490 .addCall( 491 any(ParcelableCall.class)); 492 493 // Give the InCallService time to respond 494 495 assertTrueWithTimeout(new Predicate<Void>() { 496 @Override 497 public boolean apply(Void v) { 498 return startingNumConnections + 1 == 499 connectionServiceFixture.mConnectionById.size(); 500 } 501 }); 502 assertTrueWithTimeout(new Predicate<Void>() { 503 @Override 504 public boolean apply(Void v) { 505 return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size(); 506 } 507 }); 508 assertTrueWithTimeout(new Predicate<Void>() { 509 @Override 510 public boolean apply(Void v) { 511 return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size(); 512 } 513 }); 514 515 assertEquals( 516 mInCallServiceFixtureX.mLatestCallId, 517 mInCallServiceFixtureY.mLatestCallId); 518 519 return new IdPair( 520 connectionServiceFixture.mLatestConnectionId, 521 mInCallServiceFixtureX.mLatestCallId); 522 } 523 524 private void rapidFire(Runnable... tasks) { 525 final CyclicBarrier barrier = new CyclicBarrier(tasks.length); 526 final CountDownLatch latch = new CountDownLatch(tasks.length); 527 for (int i = 0; i < tasks.length; i++) { 528 final Runnable task = tasks[i]; 529 new Thread(new Runnable() { 530 @Override 531 public void run() { 532 try { 533 barrier.await(); 534 task.run(); 535 } catch (InterruptedException | BrokenBarrierException e){ 536 Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted"); 537 } finally { 538 latch.countDown(); 539 } 540 } 541 }).start(); 542 } 543 try { 544 latch.await(); 545 } catch (InterruptedException e) { 546 Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted"); 547 } 548 } 549 550 // A simple outgoing call, verifying that the appropriate connection service is contacted, 551 // the proper lifecycle is followed, and both In-Call Services are updated correctly. 552 private IdPair startAndMakeActiveOutgoingCall( 553 String number, 554 PhoneAccountHandle phoneAccountHandle, 555 ConnectionServiceFixture connectionServiceFixture) throws Exception { 556 IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture); 557 558 connectionServiceFixture.sendSetDialing(ids.mConnectionId); 559 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 560 assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 561 562 connectionServiceFixture.sendSetActive(ids.mConnectionId); 563 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 564 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 565 566 return ids; 567 } 568 569 public void testSingleOutgoingCallLocalDisconnect() throws Exception { 570 IdPair ids = startAndMakeActiveOutgoingCall( 571 "650-555-1212", 572 mPhoneAccountA0.getAccountHandle(), 573 mConnectionServiceFixtureA); 574 575 mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId); 576 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 577 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 578 579 mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL); 580 assertEquals(Call.STATE_DISCONNECTED, 581 mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 582 assertEquals(Call.STATE_DISCONNECTED, 583 mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 584 } 585 586 public void testSingleOutgoingCallRemoteDisconnect() throws Exception { 587 IdPair ids = startAndMakeActiveOutgoingCall( 588 "650-555-1212", 589 mPhoneAccountA0.getAccountHandle(), 590 mConnectionServiceFixtureA); 591 592 mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL); 593 assertEquals(Call.STATE_DISCONNECTED, 594 mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 595 assertEquals(Call.STATE_DISCONNECTED, 596 mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 597 } 598 599 // A simple incoming call, similar in scope to the previous test 600 private IdPair startAndMakeActiveIncomingCall( 601 String number, 602 PhoneAccountHandle phoneAccountHandle, 603 ConnectionServiceFixture connectionServiceFixture) throws Exception { 604 IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture); 605 606 assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 607 assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 608 609 mInCallServiceFixtureX.mInCallAdapter 610 .answerCall(ids.mCallId, VideoProfile.STATE_AUDIO_ONLY); 611 612 verify(connectionServiceFixture.getTestDouble()) 613 .answer(ids.mConnectionId); 614 615 connectionServiceFixture.sendSetActive(ids.mConnectionId); 616 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 617 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 618 619 return ids; 620 } 621 622 public void testSingleIncomingCallLocalDisconnect() throws Exception { 623 IdPair ids = startAndMakeActiveIncomingCall( 624 "650-555-1212", 625 mPhoneAccountA0.getAccountHandle(), 626 mConnectionServiceFixtureA); 627 628 mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId); 629 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 630 assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 631 632 mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL); 633 assertEquals(Call.STATE_DISCONNECTED, 634 mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 635 assertEquals(Call.STATE_DISCONNECTED, 636 mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 637 } 638 639 public void testSingleIncomingCallRemoteDisconnect() throws Exception { 640 IdPair ids = startAndMakeActiveIncomingCall( 641 "650-555-1212", 642 mPhoneAccountA0.getAccountHandle(), 643 mConnectionServiceFixtureA); 644 645 mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL); 646 assertEquals(Call.STATE_DISCONNECTED, 647 mInCallServiceFixtureX.getCall(ids.mCallId).getState()); 648 assertEquals(Call.STATE_DISCONNECTED, 649 mInCallServiceFixtureY.getCall(ids.mCallId).getState()); 650 } 651 652 public void do_testDeadlockOnOutgoingCall() throws Exception { 653 final IdPair ids = startOutgoingPhoneCall( 654 "650-555-1212", 655 mPhoneAccountA0.getAccountHandle(), 656 mConnectionServiceFixtureA); 657 rapidFire( 658 new Runnable() { 659 @Override 660 public void run() { 661 while (mCallerInfoAsyncQueryFactoryFixture.mRequests.size() > 0) { 662 mCallerInfoAsyncQueryFactoryFixture.mRequests.remove(0).reply(); 663 } 664 } 665 }, 666 new Runnable() { 667 @Override 668 public void run() { 669 try { 670 mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId); 671 } catch (Exception e) { 672 Log.e(this, e, ""); 673 } 674 } 675 }); 676 } 677 678 public void testDeadlockOnOutgoingCall() throws Exception { 679 for (int i = 0; i < 100; i++) { 680 TelecomSystemTest test = new TelecomSystemTest(); 681 test.setContext(getContext()); 682 test.setTestContext(getTestContext()); 683 test.setName(getName()); 684 test.setUp(); 685 test.do_testDeadlockOnOutgoingCall(); 686 test.tearDown(); 687 } 688 } 689 690 public void testIncomingThenOutgoingCalls() throws Exception { 691 // TODO: We have to use the same PhoneAccount for both; see http://b/18461539 692 IdPair incoming = startAndMakeActiveIncomingCall( 693 "650-555-2323", 694 mPhoneAccountA0.getAccountHandle(), 695 mConnectionServiceFixtureA); 696 IdPair outgoing = startAndMakeActiveOutgoingCall( 697 "650-555-1212", 698 mPhoneAccountA0.getAccountHandle(), 699 mConnectionServiceFixtureA); 700 } 701 702 public void testOutgoingThenIncomingCalls() throws Exception { 703 // TODO: We have to use the same PhoneAccount for both; see http://b/18461539 704 IdPair outgoing = startAndMakeActiveOutgoingCall( 705 "650-555-1212", 706 mPhoneAccountA0.getAccountHandle(), 707 mConnectionServiceFixtureA); 708 IdPair incoming = startAndMakeActiveIncomingCall( 709 "650-555-2323", 710 mPhoneAccountA0.getAccountHandle(), 711 mConnectionServiceFixtureA); 712 verify(mConnectionServiceFixtureA.getTestDouble()) 713 .hold(outgoing.mConnectionId); 714 mConnectionServiceFixtureA.mConnectionById.get(outgoing.mConnectionId).state = 715 Connection.STATE_HOLDING; 716 mConnectionServiceFixtureA.sendSetOnHold(outgoing.mConnectionId); 717 assertEquals( 718 Call.STATE_HOLDING, 719 mInCallServiceFixtureX.getCall(outgoing.mCallId).getState()); 720 assertEquals( 721 Call.STATE_HOLDING, 722 mInCallServiceFixtureY.getCall(outgoing.mCallId).getState()); 723 } 724 725 public void testAudioManagerOperations() throws Exception { 726 AudioManager audioManager = (AudioManager) mComponentContextFixture.getTestDouble() 727 .getApplicationContext().getSystemService(Context.AUDIO_SERVICE); 728 729 IdPair outgoing = startAndMakeActiveOutgoingCall( 730 "650-555-1212", 731 mPhoneAccountA0.getAccountHandle(), 732 mConnectionServiceFixtureA); 733 734 verify(audioManager, timeout(TEST_TIMEOUT)) 735 .requestAudioFocusForCall(anyInt(), anyInt()); 736 verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce()) 737 .setMode(AudioManager.MODE_IN_CALL); 738 739 mInCallServiceFixtureX.mInCallAdapter.mute(true); 740 verify(mAudioService, timeout(TEST_TIMEOUT)) 741 .setMicrophoneMute(eq(true), any(String.class), any(Integer.class)); 742 mInCallServiceFixtureX.mInCallAdapter.mute(false); 743 verify(mAudioService, timeout(TEST_TIMEOUT)) 744 .setMicrophoneMute(eq(false), any(String.class), any(Integer.class)); 745 746 mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER); 747 verify(audioManager, timeout(TEST_TIMEOUT)) 748 .setSpeakerphoneOn(true); 749 mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE); 750 verify(audioManager, timeout(TEST_TIMEOUT)) 751 .setSpeakerphoneOn(false); 752 753 mConnectionServiceFixtureA. 754 sendSetDisconnected(outgoing.mConnectionId, DisconnectCause.REMOTE); 755 756 verify(audioManager, timeout(TEST_TIMEOUT)) 757 .abandonAudioFocusForCall(); 758 verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce()) 759 .setMode(AudioManager.MODE_NORMAL); 760 } 761 762 protected static void assertTrueWithTimeout(Predicate<Void> predicate) { 763 int elapsed = 0; 764 while (elapsed < TEST_TIMEOUT) { 765 if (predicate.apply(null)) { 766 return; 767 } else { 768 try { 769 Thread.sleep(TEST_POLL_INTERVAL); 770 elapsed += TEST_POLL_INTERVAL; 771 } catch (InterruptedException e) { 772 fail(e.toString()); 773 } 774 } 775 } 776 fail("Timeout in assertTrueWithTimeout"); 777 } 778} 779