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