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