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