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 17 package android.telecom.cts; 18 19 import static android.telecom.cts.TestUtils.PACKAGE; 20 import static android.telecom.cts.TestUtils.TAG; 21 import static android.telecom.cts.TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS; 22 23 import static org.hamcrest.CoreMatchers.equalTo; 24 import static org.hamcrest.CoreMatchers.not; 25 import static org.junit.Assert.assertNotEquals; 26 import static org.junit.Assert.assertThat; 27 28 import android.app.UiModeManager; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.res.Configuration; 32 import android.database.ContentObserver; 33 import android.database.Cursor; 34 import android.media.AudioManager; 35 import android.net.Uri; 36 import android.os.Bundle; 37 import android.os.Handler; 38 import android.os.HandlerThread; 39 import android.os.Looper; 40 import android.provider.CallLog; 41 import android.telecom.Call; 42 import android.telecom.CallAudioState; 43 import android.telecom.Conference; 44 import android.telecom.Connection; 45 import android.telecom.ConnectionRequest; 46 import android.telecom.InCallService; 47 import android.telecom.PhoneAccount; 48 import android.telecom.PhoneAccountHandle; 49 import android.telecom.TelecomManager; 50 import android.telecom.VideoProfile; 51 import android.telecom.cts.MockInCallService.InCallServiceCallbacks; 52 import android.telephony.PhoneStateListener; 53 import android.telephony.TelephonyManager; 54 import android.telephony.emergency.EmergencyNumber; 55 import android.test.InstrumentationTestCase; 56 import android.text.TextUtils; 57 import android.util.Log; 58 import android.util.Pair; 59 60 import com.android.compatibility.common.util.ShellIdentityUtils; 61 62 import java.util.ArrayList; 63 import java.util.List; 64 import java.util.Objects; 65 import java.util.Random; 66 import java.util.concurrent.CountDownLatch; 67 import java.util.concurrent.Semaphore; 68 import java.util.concurrent.TimeUnit; 69 70 /** 71 * Base class for Telecom CTS tests that require a {@link CtsConnectionService} and 72 * {@link MockInCallService} to verify Telecom functionality. 73 */ 74 public class BaseTelecomTestWithMockServices extends InstrumentationTestCase { 75 76 public static final int FLAG_REGISTER = 0x1; 77 public static final int FLAG_ENABLE = 0x2; 78 public static final int FLAG_SET_DEFAULT = 0x4; 79 80 // Don't accidently use emergency number. 81 private static int sCounter = 5553638; 82 83 public static final String TEST_EMERGENCY_NUMBER = "5553637"; 84 public static final Uri TEST_EMERGENCY_URI = Uri.fromParts("tel", TEST_EMERGENCY_NUMBER, null); 85 86 Context mContext; 87 TelecomManager mTelecomManager; 88 TelephonyManager mTelephonyManager; 89 90 TestUtils.InvokeCounter mOnBringToForegroundCounter; 91 TestUtils.InvokeCounter mOnCallAudioStateChangedCounter; 92 TestUtils.InvokeCounter mOnPostDialWaitCounter; 93 TestUtils.InvokeCounter mOnCannedTextResponsesLoadedCounter; 94 TestUtils.InvokeCounter mOnSilenceRingerCounter; 95 TestUtils.InvokeCounter mOnConnectionEventCounter; 96 TestUtils.InvokeCounter mOnExtrasChangedCounter; 97 TestUtils.InvokeCounter mOnPropertiesChangedCounter; 98 TestUtils.InvokeCounter mOnRttModeChangedCounter; 99 TestUtils.InvokeCounter mOnRttStatusChangedCounter; 100 TestUtils.InvokeCounter mOnRttInitiationFailedCounter; 101 TestUtils.InvokeCounter mOnRttRequestCounter; 102 TestUtils.InvokeCounter mOnHandoverCompleteCounter; 103 TestUtils.InvokeCounter mOnHandoverFailedCounter; 104 TestUtils.InvokeCounter mOnPhoneAccountChangedCounter; 105 Bundle mPreviousExtras; 106 int mPreviousProperties = -1; 107 PhoneAccountHandle mPreviousPhoneAccountHandle = null; 108 109 InCallServiceCallbacks mInCallCallbacks; 110 String mPreviousDefaultDialer = null; 111 PhoneAccountHandle mPreviousDefaultOutgoingAccount = null; 112 boolean mShouldRestoreDefaultOutgoingAccount = false; 113 MockConnectionService connectionService = null; 114 boolean mIsEmergencyCallingSetup = false; 115 116 HandlerThread mPhoneStateListenerThread; 117 Handler mPhoneStateListenerHandler; 118 TestPhoneStateListener mPhoneStateListener; 119 Handler mHandler; 120 121 static class TestPhoneStateListener extends PhoneStateListener { 122 /** Semaphore released for every callback invocation. */ 123 public Semaphore mCallbackSemaphore = new Semaphore(0); 124 125 List<Pair<Integer, String>> mCallStates = new ArrayList<>(); 126 EmergencyNumber mLastOutgoingEmergencyNumber; 127 128 @Override onCallStateChanged(int state, String number)129 public void onCallStateChanged(int state, String number) { 130 Log.i(TAG, "onCallStateChanged: state=" + state + ", number=" + number); 131 mCallStates.add(Pair.create(state, number)); 132 mCallbackSemaphore.release(); 133 } 134 135 @Override onOutgoingEmergencyCall(EmergencyNumber emergencyNumber)136 public void onOutgoingEmergencyCall(EmergencyNumber emergencyNumber) { 137 Log.i(TAG, "onOutgoingEmergencyCall: emergencyNumber=" + emergencyNumber); 138 mLastOutgoingEmergencyNumber = emergencyNumber; 139 mCallbackSemaphore.release(); 140 } 141 } 142 143 boolean mShouldTestTelecom = true; 144 145 @Override setUp()146 protected void setUp() throws Exception { 147 super.setUp(); 148 mContext = getInstrumentation().getContext(); 149 mHandler = new Handler(Looper.getMainLooper()); 150 mShouldTestTelecom = TestUtils.shouldTestTelecom(mContext); 151 if (!mShouldTestTelecom) { 152 return; 153 } 154 155 // Assume we start in normal mode at the start of all Telecom tests; a failure to leave car 156 // mode in any of the tests would cause subsequent test failures. 157 assertUiMode(Configuration.UI_MODE_TYPE_NORMAL); 158 159 mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); 160 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 161 162 mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation()); 163 TestUtils.setDefaultDialer(getInstrumentation(), PACKAGE); 164 setupCallbacks(); 165 166 // PhoneStateListener's public API registers the listener on the calling thread, which must 167 // be a looper thread. So we need to create and register the listener in a custom looper 168 // thread. 169 mPhoneStateListenerThread = new HandlerThread("PhoneStateListenerThread"); 170 mPhoneStateListenerThread.start(); 171 mPhoneStateListenerHandler = new Handler(mPhoneStateListenerThread.getLooper()); 172 final CountDownLatch registeredLatch = new CountDownLatch(1); 173 mPhoneStateListenerHandler.post(new Runnable() { 174 @Override 175 public void run() { 176 mPhoneStateListener = new TestPhoneStateListener(); 177 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, 178 (tm) -> tm.listen(mPhoneStateListener, 179 PhoneStateListener.LISTEN_CALL_STATE | PhoneStateListener 180 .LISTEN_OUTGOING_EMERGENCY_CALL)); 181 registeredLatch.countDown(); 182 } 183 }); 184 registeredLatch.await( 185 TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S, TimeUnit.SECONDS); 186 } 187 188 @Override tearDown()189 protected void tearDown() throws Exception { 190 super.tearDown(); 191 if (!mShouldTestTelecom) { 192 return; 193 } 194 195 final CountDownLatch unregisteredLatch = new CountDownLatch(1); 196 mPhoneStateListenerHandler.post(new Runnable() { 197 @Override 198 public void run() { 199 mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); 200 unregisteredLatch.countDown(); 201 } 202 }); 203 unregisteredLatch.await( 204 TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S, TimeUnit.SECONDS); 205 mPhoneStateListenerThread.quit(); 206 207 cleanupCalls(); 208 if (!TextUtils.isEmpty(mPreviousDefaultDialer)) { 209 TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer); 210 } 211 tearDownConnectionService(TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 212 tearDownEmergencyCalling(); 213 try { 214 assertMockInCallServiceUnbound(); 215 } catch (Throwable t) { 216 // If we haven't unbound, that means there's some dirty state in Telecom that needs 217 // cleaning up. Forcibly unbind and clean up Telecom state so that we don't have a 218 // cascading failure of tests. 219 TestUtils.executeShellCommand(getInstrumentation(), "telecom cleanup-stuck-calls"); 220 throw t; 221 } 222 } 223 setupConnectionService(MockConnectionService connectionService, int flags)224 protected PhoneAccount setupConnectionService(MockConnectionService connectionService, 225 int flags) throws Exception { 226 if (connectionService != null) { 227 this.connectionService = connectionService; 228 } else { 229 // Generate a vanilla mock connection service, if not provided. 230 this.connectionService = new MockConnectionService(); 231 } 232 CtsConnectionService.setUp(this.connectionService); 233 234 if ((flags & FLAG_REGISTER) != 0) { 235 mTelecomManager.registerPhoneAccount(TestUtils.TEST_PHONE_ACCOUNT); 236 } 237 if ((flags & FLAG_ENABLE) != 0) { 238 TestUtils.enablePhoneAccount(getInstrumentation(), TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 239 // Wait till the adb commands have executed and account is enabled in Telecom database. 240 assertPhoneAccountEnabled(TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 241 } 242 243 if ((flags & FLAG_SET_DEFAULT) != 0) { 244 mPreviousDefaultOutgoingAccount = mTelecomManager.getUserSelectedOutgoingPhoneAccount(); 245 mShouldRestoreDefaultOutgoingAccount = true; 246 TestUtils.setDefaultOutgoingPhoneAccount(getInstrumentation(), 247 TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 248 // Wait till the adb commands have executed and the default has changed. 249 assertPhoneAccountIsDefault(TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 250 } 251 252 return TestUtils.TEST_PHONE_ACCOUNT; 253 } 254 tearDownConnectionService(PhoneAccountHandle accountHandle)255 protected void tearDownConnectionService(PhoneAccountHandle accountHandle) throws Exception { 256 if (this.connectionService != null) { 257 assertNumConnections(this.connectionService, 0); 258 } 259 mTelecomManager.unregisterPhoneAccount(accountHandle); 260 CtsConnectionService.tearDown(); 261 assertCtsConnectionServiceUnbound(); 262 if (mShouldRestoreDefaultOutgoingAccount) { 263 TestUtils.setDefaultOutgoingPhoneAccount(getInstrumentation(), 264 mPreviousDefaultOutgoingAccount); 265 } 266 this.connectionService = null; 267 mPreviousDefaultOutgoingAccount = null; 268 mShouldRestoreDefaultOutgoingAccount = false; 269 } 270 setupForEmergencyCalling(String testNumber)271 protected void setupForEmergencyCalling(String testNumber) throws Exception { 272 TestUtils.setSystemDialerOverride(getInstrumentation()); 273 TestUtils.addTestEmergencyNumber(getInstrumentation(), testNumber); 274 TestUtils.setTestEmergencyPhoneAccountPackageFilter(getInstrumentation(), mContext); 275 // Emergency calls require special capabilities. 276 TestUtils.registerEmergencyPhoneAccount(getInstrumentation(), 277 TestUtils.TEST_EMERGENCY_PHONE_ACCOUNT_HANDLE, 278 TestUtils.ACCOUNT_LABEL + "E", "tel:555-EMER"); 279 mIsEmergencyCallingSetup = true; 280 } 281 tearDownEmergencyCalling()282 protected void tearDownEmergencyCalling() throws Exception { 283 if (!mIsEmergencyCallingSetup) return; 284 285 TestUtils.clearSystemDialerOverride(getInstrumentation()); 286 TestUtils.clearTestEmergencyNumbers(getInstrumentation()); 287 TestUtils.clearTestEmergencyPhoneAccountPackageFilter(getInstrumentation()); 288 mTelecomManager.unregisterPhoneAccount(TestUtils.TEST_EMERGENCY_PHONE_ACCOUNT_HANDLE); 289 } 290 startCallTo(Uri address, PhoneAccountHandle accountHandle)291 protected void startCallTo(Uri address, PhoneAccountHandle accountHandle) { 292 final Intent intent = new Intent(Intent.ACTION_CALL, address); 293 if (accountHandle != null) { 294 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle); 295 } 296 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 297 mContext.startActivity(intent); 298 } 299 sleep(long ms)300 void sleep(long ms) { 301 try { 302 Thread.sleep(ms); 303 } catch (InterruptedException e) { 304 } 305 } 306 setupCallbacks()307 private void setupCallbacks() { 308 mInCallCallbacks = new InCallServiceCallbacks() { 309 @Override 310 public void onCallAdded(Call call, int numCalls) { 311 Log.i(TAG, "onCallAdded, Call: " + call + ", Num Calls: " + numCalls); 312 this.lock.release(); 313 mPreviousPhoneAccountHandle = call.getDetails().getAccountHandle(); 314 } 315 @Override 316 public void onCallRemoved(Call call, int numCalls) { 317 Log.i(TAG, "onCallRemoved, Call: " + call + ", Num Calls: " + numCalls); 318 } 319 @Override 320 public void onParentChanged(Call call, Call parent) { 321 Log.i(TAG, "onParentChanged, Call: " + call + ", Parent: " + parent); 322 this.lock.release(); 323 } 324 @Override 325 public void onChildrenChanged(Call call, List<Call> children) { 326 Log.i(TAG, "onChildrenChanged, Call: " + call + "Children: " + children); 327 this.lock.release(); 328 } 329 @Override 330 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) { 331 Log.i(TAG, "onConferenceableCallsChanged, Call: " + call + ", Conferenceables: " + 332 conferenceableCalls); 333 } 334 @Override 335 public void onDetailsChanged(Call call, Call.Details details) { 336 Log.i(TAG, "onDetailsChanged, Call: " + call + ", Details: " + details); 337 if (!areBundlesEqual(mPreviousExtras, details.getExtras())) { 338 mOnExtrasChangedCounter.invoke(call, details); 339 } 340 mPreviousExtras = details.getExtras(); 341 342 if (mPreviousProperties != details.getCallProperties()) { 343 mOnPropertiesChangedCounter.invoke(call, details); 344 Log.i(TAG, "onDetailsChanged; properties changed from " + Call.Details.propertiesToString(mPreviousProperties) + 345 " to " + Call.Details.propertiesToString(details.getCallProperties())); 346 } 347 mPreviousProperties = details.getCallProperties(); 348 349 if (details.getAccountHandle() != null && 350 !details.getAccountHandle().equals(mPreviousPhoneAccountHandle)) { 351 mOnPhoneAccountChangedCounter.invoke(call, details.getAccountHandle()); 352 } 353 mPreviousPhoneAccountHandle = details.getAccountHandle(); 354 } 355 @Override 356 public void onCallDestroyed(Call call) { 357 Log.i(TAG, "onCallDestroyed, Call: " + call); 358 } 359 @Override 360 public void onCallStateChanged(Call call, int newState) { 361 Log.i(TAG, "onCallStateChanged, Call: " + call + ", New State: " + newState); 362 } 363 @Override 364 public void onBringToForeground(boolean showDialpad) { 365 mOnBringToForegroundCounter.invoke(showDialpad); 366 } 367 @Override 368 public void onCallAudioStateChanged(CallAudioState audioState) { 369 Log.i(TAG, "onCallAudioStateChanged, audioState: " + audioState); 370 mOnCallAudioStateChangedCounter.invoke(audioState); 371 } 372 @Override 373 public void onPostDialWait(Call call, String remainingPostDialSequence) { 374 mOnPostDialWaitCounter.invoke(call, remainingPostDialSequence); 375 } 376 @Override 377 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) { 378 mOnCannedTextResponsesLoadedCounter.invoke(call, cannedTextResponses); 379 } 380 @Override 381 public void onConnectionEvent(Call call, String event, Bundle extras) { 382 mOnConnectionEventCounter.invoke(call, event, extras); 383 } 384 385 @Override 386 public void onSilenceRinger() { 387 Log.i(TAG, "onSilenceRinger"); 388 mOnSilenceRingerCounter.invoke(); 389 } 390 391 @Override 392 public void onRttModeChanged(Call call, int mode) { 393 mOnRttModeChangedCounter.invoke(call, mode); 394 } 395 396 @Override 397 public void onRttStatusChanged(Call call, boolean enabled, Call.RttCall rttCall) { 398 mOnRttStatusChangedCounter.invoke(call, enabled, rttCall); 399 } 400 401 @Override 402 public void onRttRequest(Call call, int id) { 403 mOnRttRequestCounter.invoke(call, id); 404 } 405 406 @Override 407 public void onRttInitiationFailure(Call call, int reason) { 408 mOnRttInitiationFailedCounter.invoke(call, reason); 409 } 410 411 @Override 412 public void onHandoverComplete(Call call) { 413 mOnHandoverCompleteCounter.invoke(call); 414 } 415 416 @Override 417 public void onHandoverFailed(Call call, int reason) { 418 mOnHandoverFailedCounter.invoke(call, reason); 419 } 420 }; 421 422 MockInCallService.setCallbacks(mInCallCallbacks); 423 424 // TODO: If more InvokeCounters are added in the future, consider consolidating them into a 425 // single Collection. 426 mOnBringToForegroundCounter = new TestUtils.InvokeCounter("OnBringToForeground"); 427 mOnCallAudioStateChangedCounter = new TestUtils.InvokeCounter("OnCallAudioStateChanged"); 428 mOnPostDialWaitCounter = new TestUtils.InvokeCounter("OnPostDialWait"); 429 mOnCannedTextResponsesLoadedCounter = new TestUtils.InvokeCounter("OnCannedTextResponsesLoaded"); 430 mOnSilenceRingerCounter = new TestUtils.InvokeCounter("OnSilenceRinger"); 431 mOnConnectionEventCounter = new TestUtils.InvokeCounter("OnConnectionEvent"); 432 mOnExtrasChangedCounter = new TestUtils.InvokeCounter("OnDetailsChangedCounter"); 433 mOnPropertiesChangedCounter = new TestUtils.InvokeCounter("OnPropertiesChangedCounter"); 434 mOnRttModeChangedCounter = new TestUtils.InvokeCounter("mOnRttModeChangedCounter"); 435 mOnRttStatusChangedCounter = new TestUtils.InvokeCounter("mOnRttStatusChangedCounter"); 436 mOnRttInitiationFailedCounter = 437 new TestUtils.InvokeCounter("mOnRttInitiationFailedCounter"); 438 mOnRttRequestCounter = new TestUtils.InvokeCounter("mOnRttRequestCounter"); 439 mOnHandoverCompleteCounter = new TestUtils.InvokeCounter("mOnHandoverCompleteCounter"); 440 mOnHandoverFailedCounter = new TestUtils.InvokeCounter("mOnHandoverFailedCounter"); 441 mOnPhoneAccountChangedCounter = new TestUtils.InvokeCounter( 442 "mOnPhoneAccountChangedCounter"); 443 } 444 addAndVerifyNewFailedIncomingCall(Uri incomingHandle, Bundle extras)445 void addAndVerifyNewFailedIncomingCall(Uri incomingHandle, Bundle extras) { 446 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 447 int currentCallCount = 0; 448 if (mInCallCallbacks.getService() != null) { 449 currentCallCount = mInCallCallbacks.getService().getCallCount(); 450 } 451 452 if (extras == null) { 453 extras = new Bundle(); 454 } 455 extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, incomingHandle); 456 mTelecomManager.addNewIncomingCall(TestUtils.TEST_PHONE_ACCOUNT_HANDLE, extras); 457 458 try { 459 if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S, 460 TimeUnit.SECONDS)) { 461 fail("Incoming Connection failure indication did not get called."); 462 } 463 } catch (InterruptedException e) { 464 fail("InterruptedException while waiting for incoming call failure"); 465 } 466 467 assertEquals("ConnectionService did not receive failed connection", 468 1, connectionService.failedConnections.size()); 469 470 assertEquals("Address is not correct for failed connection", 471 connectionService.failedConnections.get(0).getAddress(), incomingHandle); 472 473 assertEquals("InCallService should contain the same number of calls.", 474 currentCallCount, 475 mInCallCallbacks.getService().getCallCount()); 476 } 477 478 /** 479 * Puts Telecom in a state where there is an incoming call provided by the 480 * {@link CtsConnectionService} which can be tested. 481 */ addAndVerifyNewIncomingCall(Uri incomingHandle, Bundle extras)482 void addAndVerifyNewIncomingCall(Uri incomingHandle, Bundle extras) { 483 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 484 int currentCallCount = 0; 485 if (mInCallCallbacks.getService() != null) { 486 currentCallCount = mInCallCallbacks.getService().getCallCount(); 487 } 488 489 if (extras == null) { 490 extras = new Bundle(); 491 } 492 extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, incomingHandle); 493 mTelecomManager.addNewIncomingCall(TestUtils.TEST_PHONE_ACCOUNT_HANDLE, extras); 494 495 try { 496 if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S, 497 TimeUnit.SECONDS)) { 498 fail("No call added to InCallService."); 499 } 500 } catch (InterruptedException e) { 501 Log.i(TAG, "Test interrupted!"); 502 } 503 504 assertEquals("InCallService should contain 1 more call after adding a call.", 505 currentCallCount + 1, 506 mInCallCallbacks.getService().getCallCount()); 507 } 508 509 /** 510 * Puts Telecom in a state where there is an active call provided by the 511 * {@link CtsConnectionService} which can be tested. 512 */ placeAndVerifyCall()513 void placeAndVerifyCall() { 514 placeAndVerifyCall(null); 515 } 516 placeAndVerifyCallByRedirection(boolean wasCancelled)517 void placeAndVerifyCallByRedirection(boolean wasCancelled) { 518 placeAndVerifyCallByRedirection(null, wasCancelled); 519 } 520 521 /** 522 * Puts Telecom in a state where there is an active call provided by the 523 * {@link CtsConnectionService} which can be tested. 524 */ placeAndVerifyCallByRedirection(Bundle extras, boolean wasCancelled)525 void placeAndVerifyCallByRedirection(Bundle extras, boolean wasCancelled) { 526 int currentCallCount = (getInCallService() == null) ? 0 : getInCallService().getCallCount(); 527 int currentConnections = getNumberOfConnections(); 528 // We expect a new connection if it wasn't cancelled. 529 if (!wasCancelled) { 530 currentConnections++; 531 currentCallCount++; 532 } 533 placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY, currentConnections, 534 currentCallCount); 535 // Ensure the new outgoing call broadcast fired for the outgoing call. 536 assertOutgoingCallBroadcastReceived(true); 537 538 // CTS test does not have read call log permission so should not get the phone number. 539 assertNull(NewOutgoingCallBroadcastReceiver.getReceivedNumber()); 540 } 541 542 /** 543 * Puts Telecom in a state where there is an active call provided by the 544 * {@link CtsConnectionService} which can be tested. 545 * 546 * @param videoState the video state of the call. 547 */ placeAndVerifyCall(int videoState)548 void placeAndVerifyCall(int videoState) { 549 placeAndVerifyCall(null, videoState); 550 } 551 552 /** 553 * Puts Telecom in a state where there is an active call provided by the 554 * {@link CtsConnectionService} which can be tested. 555 */ placeAndVerifyCall(Bundle extras)556 void placeAndVerifyCall(Bundle extras) { 557 placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY); 558 } 559 560 /** 561 * Puts Telecom in a state where there is an active call provided by the 562 * {@link CtsConnectionService} which can be tested. 563 */ placeAndVerifyCall(Bundle extras, int videoState)564 void placeAndVerifyCall(Bundle extras, int videoState) { 565 int currentCallCount = (getInCallService() == null) ? 0 : getInCallService().getCallCount(); 566 // We expect placing the call adds a new call/connection. 567 placeAndVerifyCall(extras, videoState, getNumberOfConnections() + 1, currentCallCount + 1); 568 assertOutgoingCallBroadcastReceived(true); 569 570 // CTS test does not have read call log permission so should not get the phone number. 571 assertNull(NewOutgoingCallBroadcastReceiver.getReceivedNumber()); 572 } 573 574 /** 575 * Puts Telecom in a state where there is an active call provided by the 576 * {@link CtsConnectionService} which can be tested. 577 */ placeAndVerifyCall(Bundle extras, int videoState, int expectedConnectionCount, int expectedCallCount)578 void placeAndVerifyCall(Bundle extras, int videoState, int expectedConnectionCount, 579 int expectedCallCount) { 580 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 581 placeNewCallWithPhoneAccount(extras, videoState); 582 583 try { 584 if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S, 585 TimeUnit.SECONDS)) { 586 fail("No call added to InCallService."); 587 } 588 } catch (InterruptedException e) { 589 Log.i(TAG, "Test interrupted!"); 590 } 591 592 // Make sure any procedures to disconnect existing calls (makeRoomForOutgoingCall) 593 // complete successfully 594 TestUtils.waitOnLocalMainLooper(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS); 595 TestUtils.waitOnAllHandlers(getInstrumentation()); 596 597 assertEquals("InCallService should match the expected count.", expectedCallCount, 598 mInCallCallbacks.getService().getCallCount()); 599 600 // The connectionService.lock is released in 601 // MockConnectionService#onCreateOutgoingConnection, however the connection will not 602 // actually be added to the list of connections in the ConnectionService until shortly 603 // afterwards. So there is still a potential for the lock to be released before it would 604 // be seen by calls to ConnectionService#getAllConnections(). 605 // We will wait here until the list of connections includes one more connection to ensure 606 // that placing the call has fully completed. 607 assertCSConnections(expectedConnectionCount); 608 } 609 610 /** 611 * Place an emergency call and verify that it has been setup properly. 612 * 613 * @param supportsHold If telecom supports holding emergency calls, this will expect two 614 * calls. If telecom does not support holding emergency calls, this will expect only the 615 * emergency call to be active. 616 * @return The emergency connection 617 */ placeAndVerifyEmergencyCall(boolean supportsHold)618 public Connection placeAndVerifyEmergencyCall(boolean supportsHold) { 619 Bundle extras = new Bundle(); 620 extras.putParcelable(TestUtils.EXTRA_PHONE_NUMBER, TEST_EMERGENCY_URI); 621 int currentConnectionCount = supportsHold ? 622 getNumberOfConnections() + 1 : getNumberOfConnections(); 623 int currentCallCount = (getInCallService() == null) ? 0 : getInCallService().getCallCount(); 624 currentCallCount = supportsHold ? currentCallCount + 1 : currentCallCount; 625 // The device only supports a max of two calls active at any one time 626 currentCallCount = Math.min(currentCallCount, 2); 627 placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY, currentConnectionCount, 628 currentCallCount); 629 assertOutgoingCallBroadcastReceived(true); 630 Connection connection = verifyConnectionForOutgoingCall(TEST_EMERGENCY_URI); 631 TestUtils.waitOnAllHandlers(getInstrumentation()); 632 return connection; 633 } 634 getNumberOfConnections()635 int getNumberOfConnections() { 636 return CtsConnectionService.getAllConnectionsFromTelecom().size(); 637 } 638 getConnection(Uri address)639 Connection getConnection(Uri address) { 640 return CtsConnectionService.getAllConnectionsFromTelecom().stream() 641 .filter(c -> c.getAddress().equals(address)).findFirst().orElse(null); 642 } 643 verifyConnectionForOutgoingCall()644 MockConnection verifyConnectionForOutgoingCall() { 645 // Assuming only 1 connection present 646 return verifyConnectionForOutgoingCall(0); 647 } 648 verifyConnectionForOutgoingCall(int connectionIndex)649 MockConnection verifyConnectionForOutgoingCall(int connectionIndex) { 650 try { 651 if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 652 TimeUnit.MILLISECONDS)) { 653 fail("No outgoing call connection requested by Telecom"); 654 } 655 } catch (InterruptedException e) { 656 Log.i(TAG, "Test interrupted!"); 657 } 658 659 assertThat("Telecom should create outgoing connection for outgoing call", 660 connectionService.outgoingConnections.size(), not(equalTo(0))); 661 MockConnection connection = connectionService.outgoingConnections.get(connectionIndex); 662 return connection; 663 } 664 verifyConnectionForOutgoingCall(Uri address)665 MockConnection verifyConnectionForOutgoingCall(Uri address) { 666 try { 667 if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 668 TimeUnit.MILLISECONDS)) { 669 fail("No outgoing call connection requested by Telecom"); 670 } 671 } catch (InterruptedException e) { 672 Log.i(TAG, "Test interrupted!"); 673 } 674 675 assertThat("Telecom should create outgoing connection for outgoing call", 676 connectionService.outgoingConnections.size(), not(equalTo(0))); 677 Connection connection = getConnection(address); 678 assertNotNull("Could not find outgoing connection in list of active connections.", 679 connection); 680 if (connection instanceof MockConnection) { 681 if (connectionService.outgoingConnections.contains(connection)) { 682 return (MockConnection) connection; 683 } 684 } 685 return null; 686 } 687 verifyConnectionForIncomingCall()688 MockConnection verifyConnectionForIncomingCall() { 689 // Assuming only 1 connection present 690 return verifyConnectionForIncomingCall(0); 691 } 692 verifyConnectionForIncomingCall(int connectionIndex)693 MockConnection verifyConnectionForIncomingCall(int connectionIndex) { 694 try { 695 if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 696 TimeUnit.MILLISECONDS)) { 697 fail("No outgoing call connection requested by Telecom"); 698 } 699 } catch (InterruptedException e) { 700 Log.i(TAG, "Test interrupted!"); 701 } 702 703 assertThat("Telecom should create incoming connections for incoming calls", 704 connectionService.incomingConnections.size(), not(equalTo(0))); 705 MockConnection connection = connectionService.incomingConnections.get(connectionIndex); 706 setAndVerifyConnectionForIncomingCall(connection); 707 return connection; 708 } 709 setAndVerifyConnectionForIncomingCall(MockConnection connection)710 void setAndVerifyConnectionForIncomingCall(MockConnection connection) { 711 if (connection.getState() == Connection.STATE_ACTIVE) { 712 // If the connection is already active (like if it got picked up immediately), don't 713 // bother with setting it back to ringing. 714 return; 715 } 716 connection.setRinging(); 717 assertConnectionState(connection, Connection.STATE_RINGING); 718 } 719 setAndVerifyConferenceablesForOutgoingConnection(int connectionIndex)720 void setAndVerifyConferenceablesForOutgoingConnection(int connectionIndex) { 721 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 722 // Make all other outgoing connections as conferenceable with this connection. 723 MockConnection connection = connectionService.outgoingConnections.get(connectionIndex); 724 List<Connection> confConnections = 725 new ArrayList<>(connectionService.outgoingConnections.size()); 726 for (Connection c : connectionService.outgoingConnections) { 727 if (c != connection) { 728 confConnections.add(c); 729 } 730 } 731 connection.setConferenceableConnections(confConnections); 732 assertEquals(connection.getConferenceables(), confConnections); 733 } 734 addConferenceCall(Call call1, Call call2)735 void addConferenceCall(Call call1, Call call2) { 736 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 737 int currentConfCallCount = 0; 738 if (mInCallCallbacks.getService() != null) { 739 currentConfCallCount = mInCallCallbacks.getService().getConferenceCallCount(); 740 } 741 // Verify that the calls have each other on their conferenceable list before proceeding 742 List<Call> callConfList = new ArrayList<>(); 743 callConfList.add(call2); 744 assertCallConferenceableList(call1, callConfList); 745 746 callConfList.clear(); 747 callConfList.add(call1); 748 assertCallConferenceableList(call2, callConfList); 749 750 call1.conference(call2); 751 752 /** 753 * We should have 1 onCallAdded, 2 onChildrenChanged and 2 onParentChanged invoked, so 754 * we should have 5 available permits on the incallService lock. 755 */ 756 try { 757 if (!mInCallCallbacks.lock.tryAcquire(5, 3, TimeUnit.SECONDS)) { 758 fail("Conference addition failed."); 759 } 760 } catch (InterruptedException e) { 761 Log.i(TAG, "Test interrupted!"); 762 } 763 764 assertEquals("InCallService should contain 1 more call after adding a conf call.", 765 currentConfCallCount + 1, 766 mInCallCallbacks.getService().getConferenceCallCount()); 767 } 768 splitFromConferenceCall(Call call1)769 void splitFromConferenceCall(Call call1) { 770 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 771 772 call1.splitFromConference(); 773 /** 774 * We should have 1 onChildrenChanged and 1 onParentChanged invoked, so 775 * we should have 2 available permits on the incallService lock. 776 */ 777 try { 778 if (!mInCallCallbacks.lock.tryAcquire(2, 3, TimeUnit.SECONDS)) { 779 fail("Conference split failed"); 780 } 781 } catch (InterruptedException e) { 782 Log.i(TAG, "Test interrupted!"); 783 } 784 } 785 verifyConferenceForOutgoingCall()786 MockConference verifyConferenceForOutgoingCall() { 787 try { 788 if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 789 TimeUnit.MILLISECONDS)) { 790 fail("No outgoing conference requested by Telecom"); 791 } 792 } catch (InterruptedException e) { 793 Log.i(TAG, "Test interrupted!"); 794 } 795 // Return the newly created conference object to the caller 796 MockConference conference = connectionService.conferences.get(0); 797 setAndVerifyConferenceForOutgoingCall(conference); 798 return conference; 799 } 800 verifyAdhocConferenceCall()801 Pair<Conference, ConnectionRequest> verifyAdhocConferenceCall() { 802 try { 803 if (!connectionService.lock.tryAcquire(2, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 804 TimeUnit.MILLISECONDS)) { 805 fail("No conference requested by Telecom"); 806 } 807 } catch (InterruptedException e) { 808 Log.i(TAG, "Test interrupted!"); 809 } 810 return new Pair<>(connectionService.conferences.get(0), 811 connectionService.connectionRequest); 812 } 813 setAndVerifyConferenceForOutgoingCall(MockConference conference)814 void setAndVerifyConferenceForOutgoingCall(MockConference conference) { 815 conference.setActive(); 816 assertConferenceState(conference, Connection.STATE_ACTIVE); 817 } 818 verifyPhoneStateListenerCallbacksForCall(int expectedCallState, String expectedNumber)819 void verifyPhoneStateListenerCallbacksForCall(int expectedCallState, String expectedNumber) 820 throws Exception { 821 assertTrue(mPhoneStateListener.mCallbackSemaphore.tryAcquire( 822 TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S, TimeUnit.SECONDS)); 823 // At this point we can only be sure that we got AN update, but not necessarily the one we 824 // are looking for; wait until we see the state we want before verifying further. 825 waitUntilConditionIsTrueOrTimeout(new Condition() { 826 @Override 827 public Object expected() { 828 return true; 829 } 830 831 @Override 832 public Object actual() { 833 return mPhoneStateListener.mCallStates 834 .stream() 835 .filter(p -> p.first.equals( 836 expectedCallState) 837 && p.second.equals( 838 expectedNumber)) 839 .count() > 0; 840 } 841 }, 842 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 843 "Expected call state " + expectedCallState + " and number " 844 + expectedNumber); 845 846 847 // Get the most recent callback; it is possible that there was an initial state reported due 848 // to the fact that TelephonyManager will sometimes give an initial state back to the caller 849 // when the listener is registered. 850 Pair<Integer, String> callState = mPhoneStateListener.mCallStates.get( 851 mPhoneStateListener.mCallStates.size() - 1); 852 assertEquals(expectedCallState, (int) callState.first); 853 // Note: We do NOT check the phone number here. Due to changes in how the phone state 854 // broadcast is sent, the caller may receive multiple broadcasts, and the number will be 855 // present in one or the other. We waited for a full matching broadcast above so we can 856 // be sure the number was reported as expected. 857 } 858 verifyPhoneStateListenerCallbacksForEmergencyCall(String expectedNumber)859 void verifyPhoneStateListenerCallbacksForEmergencyCall(String expectedNumber) 860 throws Exception { 861 assertTrue(mPhoneStateListener.mCallbackSemaphore.tryAcquire( 862 TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S, TimeUnit.SECONDS)); 863 // At this point we can only be sure that we got AN update, but not necessarily the one we 864 // are looking for; wait until we see the state we want before verifying further. 865 waitUntilConditionIsTrueOrTimeout(new Condition() { 866 @Override 867 public Object expected() { 868 return true; 869 } 870 871 @Override 872 public Object actual() { 873 return mPhoneStateListener 874 .mLastOutgoingEmergencyNumber != null 875 && mPhoneStateListener 876 .mLastOutgoingEmergencyNumber.getNumber() 877 .equals(expectedNumber); 878 } 879 }, 880 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 881 "Expected emergency number: " + expectedNumber); 882 883 assertEquals(mPhoneStateListener.mLastOutgoingEmergencyNumber.getNumber(), 884 expectedNumber); 885 } 886 887 /** 888 * Disconnect the created test call and verify that Telecom has cleared all calls. 889 */ cleanupCalls()890 void cleanupCalls() { 891 if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) { 892 mInCallCallbacks.getService().disconnectAllConferenceCalls(); 893 mInCallCallbacks.getService().disconnectAllCalls(); 894 assertNumConferenceCalls(mInCallCallbacks.getService(), 0); 895 assertNumCalls(mInCallCallbacks.getService(), 0); 896 } 897 } 898 899 /** 900 * Place a new outgoing call via the {@link CtsConnectionService} 901 */ placeNewCallWithPhoneAccount(Bundle extras, int videoState)902 private void placeNewCallWithPhoneAccount(Bundle extras, int videoState) { 903 if (extras == null) { 904 extras = new Bundle(); 905 } 906 if (!extras.containsKey(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE)) { 907 extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, 908 TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 909 } 910 911 if (!VideoProfile.isAudioOnly(videoState)) { 912 extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); 913 } 914 Uri number; 915 if (extras.containsKey(TestUtils.EXTRA_PHONE_NUMBER)) { 916 number = extras.getParcelable(TestUtils.EXTRA_PHONE_NUMBER); 917 } else { 918 number = createTestNumber(); 919 } 920 mTelecomManager.placeCall(number, extras); 921 } 922 923 /** 924 * Create a new number each time for a new test. Telecom has special logic to reuse certain 925 * calls if multiple calls to the same number are placed within a short period of time which 926 * can cause certain tests to fail. 927 */ createTestNumber()928 Uri createTestNumber() { 929 return Uri.fromParts("tel", String.valueOf(++sCounter), null); 930 } 931 932 /** 933 * Creates a new random phone number in the range: 934 * 000-000-0000 935 * to 936 * 999-999-9999 937 * @return Randomized phone number. 938 */ createRandomTestNumber()939 Uri createRandomTestNumber() { 940 return Uri.fromParts("tel", String.format("16%05d", new Random().nextInt(99999)) 941 + String.format("%04d", new Random().nextInt(9999)), null); 942 } 943 getTestNumber()944 public static Uri getTestNumber() { 945 return Uri.fromParts("tel", String.valueOf(sCounter), null); 946 } 947 isLoggedCall(PhoneAccountHandle handle)948 public boolean isLoggedCall(PhoneAccountHandle handle) { 949 PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(handle); 950 Bundle extras = phoneAccount.getExtras(); 951 if (extras == null) { 952 extras = new Bundle(); 953 } 954 boolean isSelfManaged = (phoneAccount.getCapabilities() 955 & PhoneAccount.CAPABILITY_SELF_MANAGED) == PhoneAccount.CAPABILITY_SELF_MANAGED; 956 // Calls are logged if: 957 // 1. They're not self-managed 958 // 2. They're self-managed and are configured to request logging. 959 return (!isSelfManaged 960 || (isSelfManaged 961 && extras.getBoolean(PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS) 962 && (phoneAccount.getSupportedUriSchemes().contains(PhoneAccount.SCHEME_TEL) 963 || phoneAccount.getSupportedUriSchemes().contains(PhoneAccount.SCHEME_SIP)))); 964 } 965 getCallLogEntryLatch()966 public CountDownLatch getCallLogEntryLatch() { 967 CountDownLatch changeLatch = new CountDownLatch(1); 968 mContext.getContentResolver().registerContentObserver( 969 CallLog.Calls.CONTENT_URI, true, 970 new ContentObserver(mHandler) { 971 @Override 972 public void onChange(boolean selfChange, Uri uri) { 973 mContext.getContentResolver().unregisterContentObserver(this); 974 changeLatch.countDown(); 975 super.onChange(selfChange); 976 } 977 }); 978 return changeLatch; 979 } 980 981 verifyCallLogging(CountDownLatch logLatch, boolean isCallLogged, Uri testNumber)982 public void verifyCallLogging(CountDownLatch logLatch, boolean isCallLogged, Uri testNumber) { 983 Cursor logCursor = getLatestCallLogCursorIfMatchesUri(logLatch, isCallLogged, testNumber); 984 if (isCallLogged) { 985 assertNotNull("Call log entry not found for test number", logCursor); 986 } 987 } 988 verifyCallLogging(Uri testNumber, int expectedLogType)989 public void verifyCallLogging(Uri testNumber, int expectedLogType) { 990 CountDownLatch logLatch = getCallLogEntryLatch(); 991 Cursor logCursor = getLatestCallLogCursorIfMatchesUri(logLatch, true /*isCallLogged*/, 992 testNumber); 993 assertNotNull("Call log entry not found for test number", logCursor); 994 int typeIndex = logCursor.getColumnIndex(CallLog.Calls.TYPE); 995 int type = logCursor.getInt(typeIndex); 996 assertEquals("recorded type does not match expected", expectedLogType, type); 997 } 998 getLatestCallLogCursorIfMatchesUri(CountDownLatch latch, boolean newLogExpected, Uri testNumber)999 public Cursor getLatestCallLogCursorIfMatchesUri(CountDownLatch latch, boolean newLogExpected, 1000 Uri testNumber) { 1001 if (newLogExpected) { 1002 // Wait for the content observer to report that we have gotten a new call log entry. 1003 try { 1004 latch.await(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1005 } catch (InterruptedException ie) { 1006 fail("Expected log latch"); 1007 } 1008 } 1009 1010 // Query the latest entry into the call log. 1011 Cursor callsCursor = mContext.getContentResolver().query(CallLog.Calls.CONTENT_URI, null, 1012 null, null, CallLog.Calls._ID + " DESC limit 1;"); 1013 int numberIndex = callsCursor.getColumnIndex(CallLog.Calls.NUMBER); 1014 if (callsCursor.moveToNext()) { 1015 String number = callsCursor.getString(numberIndex); 1016 if (testNumber.getSchemeSpecificPart().equals(number)) { 1017 return callsCursor; 1018 } else { 1019 // Last call log entry doesnt match expected number. 1020 return null; 1021 } 1022 } 1023 // No Calls 1024 return null; 1025 } 1026 assertNumCalls(final MockInCallService inCallService, final int numCalls)1027 void assertNumCalls(final MockInCallService inCallService, final int numCalls) { 1028 waitUntilConditionIsTrueOrTimeout(new Condition() { 1029 @Override 1030 public Object expected() { 1031 return numCalls; 1032 } 1033 @Override 1034 public Object actual() { 1035 return inCallService.getCallCount(); 1036 } 1037 }, 1038 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1039 "InCallService should contain " + numCalls + " calls." 1040 ); 1041 } 1042 assertNumConferenceCalls(final MockInCallService inCallService, final int numCalls)1043 void assertNumConferenceCalls(final MockInCallService inCallService, final int numCalls) { 1044 waitUntilConditionIsTrueOrTimeout(new Condition() { 1045 @Override 1046 public Object expected() { 1047 return numCalls; 1048 } 1049 @Override 1050 public Object actual() { 1051 return inCallService.getConferenceCallCount(); 1052 } 1053 }, 1054 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1055 "InCallService should contain " + numCalls + " conference calls." 1056 ); 1057 } 1058 assertCSConnections(final int numConnections)1059 void assertCSConnections(final int numConnections) { 1060 waitUntilConditionIsTrueOrTimeout(new Condition() { 1061 @Override 1062 public Object expected() { 1063 return numConnections; 1064 } 1065 1066 @Override 1067 public Object actual() { 1068 return CtsConnectionService 1069 .getAllConnectionsFromTelecom() 1070 .size(); 1071 } 1072 }, 1073 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1074 "ConnectionService should contain " + numConnections + " connections." 1075 ); 1076 } 1077 assertNumConnections(final MockConnectionService connService, final int numConnections)1078 void assertNumConnections(final MockConnectionService connService, final int numConnections) { 1079 waitUntilConditionIsTrueOrTimeout(new Condition() { 1080 @Override 1081 public Object expected() { 1082 return numConnections; 1083 } 1084 @Override 1085 public Object actual() { 1086 return connService.getAllConnections().size(); 1087 } 1088 }, 1089 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1090 "ConnectionService should contain " + numConnections + " connections." 1091 ); 1092 } 1093 assertMuteState(final InCallService incallService, final boolean isMuted)1094 void assertMuteState(final InCallService incallService, final boolean isMuted) { 1095 waitUntilConditionIsTrueOrTimeout( 1096 new Condition() { 1097 @Override 1098 public Object expected() { 1099 return isMuted; 1100 } 1101 1102 @Override 1103 public Object actual() { 1104 final CallAudioState state = incallService.getCallAudioState(); 1105 return state == null ? null : state.isMuted(); 1106 } 1107 }, 1108 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1109 "Phone's mute state should be: " + isMuted 1110 ); 1111 } 1112 assertMuteState(final MockConnection connection, final boolean isMuted)1113 void assertMuteState(final MockConnection connection, final boolean isMuted) { 1114 waitUntilConditionIsTrueOrTimeout( 1115 new Condition() { 1116 @Override 1117 public Object expected() { 1118 return isMuted; 1119 } 1120 1121 @Override 1122 public Object actual() { 1123 final CallAudioState state = connection.getCallAudioState(); 1124 return state == null ? null : state.isMuted(); 1125 } 1126 }, 1127 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1128 "Connection's mute state should be: " + isMuted 1129 ); 1130 } 1131 assertAudioRoute(final InCallService incallService, final int route)1132 void assertAudioRoute(final InCallService incallService, final int route) { 1133 waitUntilConditionIsTrueOrTimeout( 1134 new Condition() { 1135 @Override 1136 public Object expected() { 1137 return route; 1138 } 1139 1140 @Override 1141 public Object actual() { 1142 final CallAudioState state = incallService.getCallAudioState(); 1143 return state == null ? null : state.getRoute(); 1144 } 1145 }, 1146 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1147 "Phone's audio route should be: " + route 1148 ); 1149 } 1150 assertNotAudioRoute(final InCallService incallService, final int route)1151 void assertNotAudioRoute(final InCallService incallService, final int route) { 1152 waitUntilConditionIsTrueOrTimeout( 1153 new Condition() { 1154 @Override 1155 public Object expected() { 1156 return new Boolean(true); 1157 } 1158 1159 @Override 1160 public Object actual() { 1161 final CallAudioState state = incallService.getCallAudioState(); 1162 return route != state.getRoute(); 1163 } 1164 }, 1165 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1166 "Phone's audio route should not be: " + route 1167 ); 1168 } 1169 assertAudioRoute(final MockConnection connection, final int route)1170 void assertAudioRoute(final MockConnection connection, final int route) { 1171 waitUntilConditionIsTrueOrTimeout( 1172 new Condition() { 1173 @Override 1174 public Object expected() { 1175 return route; 1176 } 1177 1178 @Override 1179 public Object actual() { 1180 final CallAudioState state = ((Connection) connection).getCallAudioState(); 1181 return state == null ? null : state.getRoute(); 1182 } 1183 }, 1184 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1185 "Connection's audio route should be: " + route 1186 ); 1187 } 1188 assertConnectionState(final Connection connection, final int state)1189 void assertConnectionState(final Connection connection, final int state) { 1190 waitUntilConditionIsTrueOrTimeout( 1191 new Condition() { 1192 @Override 1193 public Object expected() { 1194 return state; 1195 } 1196 1197 @Override 1198 public Object actual() { 1199 return connection.getState(); 1200 } 1201 }, 1202 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1203 "Connection should be in state " + state 1204 ); 1205 } 1206 assertCallState(final Call call, final int state)1207 void assertCallState(final Call call, final int state) { 1208 waitUntilConditionIsTrueOrTimeout( 1209 new Condition() { 1210 @Override 1211 public Object expected() { 1212 return state; 1213 } 1214 1215 @Override 1216 public Object actual() { 1217 return call.getState(); 1218 } 1219 }, 1220 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1221 "Call: " + call + " should be in state " + state 1222 ); 1223 } 1224 assertCallConferenceableList(final Call call, final List<Call> conferenceableList)1225 void assertCallConferenceableList(final Call call, final List<Call> conferenceableList) { 1226 waitUntilConditionIsTrueOrTimeout( 1227 new Condition() { 1228 @Override 1229 public Object expected() { 1230 return conferenceableList; 1231 } 1232 1233 @Override 1234 public Object actual() { 1235 return call.getConferenceableCalls(); 1236 } 1237 }, 1238 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1239 "Call: " + call + " does not have the correct conferenceable call list." 1240 ); 1241 } 1242 assertDtmfString(final MockConnection connection, final String dtmfString)1243 void assertDtmfString(final MockConnection connection, final String dtmfString) { 1244 waitUntilConditionIsTrueOrTimeout(new Condition() { 1245 @Override 1246 public Object expected() { 1247 return dtmfString; 1248 } 1249 1250 @Override 1251 public Object actual() { 1252 return connection.getDtmfString(); 1253 } 1254 }, 1255 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1256 "DTMF string should be equivalent to entered DTMF characters: " + dtmfString 1257 ); 1258 } 1259 assertDtmfString(final MockConference conference, final String dtmfString)1260 void assertDtmfString(final MockConference conference, final String dtmfString) { 1261 waitUntilConditionIsTrueOrTimeout(new Condition() { 1262 @Override 1263 public Object expected() { 1264 return dtmfString; 1265 } 1266 1267 @Override 1268 public Object actual() { 1269 return conference.getDtmfString(); 1270 } 1271 }, 1272 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1273 "DTMF string should be equivalent to entered DTMF characters: " + dtmfString 1274 ); 1275 } 1276 assertCallDisplayName(final Call call, final String name)1277 void assertCallDisplayName(final Call call, final String name) { 1278 waitUntilConditionIsTrueOrTimeout( 1279 new Condition() { 1280 @Override 1281 public Object expected() { 1282 return name; 1283 } 1284 1285 @Override 1286 public Object actual() { 1287 return call.getDetails().getCallerDisplayName(); 1288 } 1289 }, 1290 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1291 "Call should have display name: " + name 1292 ); 1293 } 1294 assertCallConnectTimeChanged(final Call call, final long time)1295 void assertCallConnectTimeChanged(final Call call, final long time) { 1296 waitUntilConditionIsTrueOrTimeout( 1297 new Condition() { 1298 @Override 1299 public Object expected() { 1300 return true; 1301 } 1302 1303 @Override 1304 public Object actual() { 1305 return call.getDetails().getConnectTimeMillis() != time; 1306 } 1307 }, 1308 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1309 "Call have connect time: " + time 1310 ); 1311 } 1312 assertConnectionCallDisplayName(final Connection connection, final String name)1313 void assertConnectionCallDisplayName(final Connection connection, final String name) { 1314 waitUntilConditionIsTrueOrTimeout( 1315 new Condition() { 1316 @Override 1317 public Object expected() { 1318 return name; 1319 } 1320 1321 @Override 1322 public Object actual() { 1323 return connection.getCallerDisplayName(); 1324 } 1325 }, 1326 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1327 "Connection should have display name: " + name 1328 ); 1329 } 1330 assertDisconnectReason(final Connection connection, final String disconnectReason)1331 void assertDisconnectReason(final Connection connection, final String disconnectReason) { 1332 waitUntilConditionIsTrueOrTimeout( 1333 new Condition() { 1334 @Override 1335 public Object expected() { 1336 return disconnectReason; 1337 } 1338 1339 @Override 1340 public Object actual() { 1341 return connection.getDisconnectCause().getReason(); 1342 } 1343 }, 1344 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1345 "Connection should have been disconnected with reason: " + disconnectReason 1346 ); 1347 } 1348 assertConferenceState(final Conference conference, final int state)1349 void assertConferenceState(final Conference conference, final int state) { 1350 waitUntilConditionIsTrueOrTimeout( 1351 new Condition() { 1352 @Override 1353 public Object expected() { 1354 return state; 1355 } 1356 1357 @Override 1358 public Object actual() { 1359 return conference.getState(); 1360 } 1361 }, 1362 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1363 "Conference should be in state " + state 1364 ); 1365 } 1366 1367 assertOutgoingCallBroadcastReceived(boolean received)1368 void assertOutgoingCallBroadcastReceived(boolean received) { 1369 waitUntilConditionIsTrueOrTimeout( 1370 new Condition() { 1371 @Override 1372 public Object expected() { 1373 return received; 1374 } 1375 1376 @Override 1377 public Object actual() { 1378 return NewOutgoingCallBroadcastReceiver 1379 .isNewOutgoingCallBroadcastReceived(); 1380 } 1381 }, 1382 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1383 received ? "Outgoing Call Broadcast should be received" 1384 : "Outgoing Call Broadcast should not be received" 1385 ); 1386 } 1387 assertCallDetailsConstructed(Call mCall, boolean constructed)1388 void assertCallDetailsConstructed(Call mCall, boolean constructed) { 1389 waitUntilConditionIsTrueOrTimeout( 1390 new Condition() { 1391 @Override 1392 public Object expected() { 1393 return constructed; 1394 } 1395 1396 @Override 1397 public Object actual() { 1398 return mCall != null && mCall.getDetails() != null; 1399 } 1400 }, 1401 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1402 constructed ? "Call Details should be constructed" 1403 : "Call Details should not be constructed" 1404 ); 1405 } 1406 assertCallGatewayConstructed(Call mCall, boolean constructed)1407 void assertCallGatewayConstructed(Call mCall, boolean constructed) { 1408 waitUntilConditionIsTrueOrTimeout( 1409 new Condition() { 1410 @Override 1411 public Object expected() { 1412 return constructed; 1413 } 1414 1415 @Override 1416 public Object actual() { 1417 return mCall != null && mCall.getDetails() != null 1418 && mCall.getDetails().getGatewayInfo() != null; 1419 } 1420 }, 1421 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1422 constructed ? "Call Gateway should be constructed" 1423 : "Call Gateway should not be constructed" 1424 ); 1425 } 1426 assertCallNotNull(Call mCall, boolean notNull)1427 void assertCallNotNull(Call mCall, boolean notNull) { 1428 waitUntilConditionIsTrueOrTimeout( 1429 new Condition() { 1430 @Override 1431 public Object expected() { 1432 return notNull; 1433 } 1434 1435 @Override 1436 public Object actual() { 1437 return mCall != null; 1438 } 1439 }, 1440 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1441 notNull ? "Call should not be null" : "Call should be null" 1442 ); 1443 } 1444 1445 /** 1446 * Checks all fields of two PhoneAccounts for equality, with the exception of the enabled state. 1447 * Should only be called after assertPhoneAccountRegistered when it can be guaranteed 1448 * that the PhoneAccount is registered. 1449 * @param expected The expected PhoneAccount. 1450 * @param actual The actual PhoneAccount. 1451 */ assertPhoneAccountEquals(final PhoneAccount expected, final PhoneAccount actual)1452 void assertPhoneAccountEquals(final PhoneAccount expected, 1453 final PhoneAccount actual) { 1454 assertEquals(expected.getAddress(), actual.getAddress()); 1455 assertEquals(expected.getAccountHandle(), actual.getAccountHandle()); 1456 assertEquals(expected.getCapabilities(), actual.getCapabilities()); 1457 assertTrue(areBundlesEqual(expected.getExtras(), actual.getExtras())); 1458 assertEquals(expected.getHighlightColor(), actual.getHighlightColor()); 1459 assertEquals(expected.getIcon(), actual.getIcon()); 1460 assertEquals(expected.getLabel(), actual.getLabel()); 1461 assertEquals(expected.getShortDescription(), actual.getShortDescription()); 1462 assertEquals(expected.getSubscriptionAddress(), actual.getSubscriptionAddress()); 1463 assertEquals(expected.getSupportedUriSchemes(), actual.getSupportedUriSchemes()); 1464 } 1465 assertPhoneAccountRegistered(final PhoneAccountHandle handle)1466 void assertPhoneAccountRegistered(final PhoneAccountHandle handle) { 1467 waitUntilConditionIsTrueOrTimeout( 1468 new Condition() { 1469 @Override 1470 public Object expected() { 1471 return true; 1472 } 1473 1474 @Override 1475 public Object actual() { 1476 return mTelecomManager.getPhoneAccount(handle) != null; 1477 } 1478 }, 1479 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1480 "Phone account registration failed for " + handle 1481 ); 1482 } 1483 assertPhoneAccountEnabled(final PhoneAccountHandle handle)1484 void assertPhoneAccountEnabled(final PhoneAccountHandle handle) { 1485 waitUntilConditionIsTrueOrTimeout( 1486 new Condition() { 1487 @Override 1488 public Object expected() { 1489 return true; 1490 } 1491 1492 @Override 1493 public Object actual() { 1494 PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(handle); 1495 return (phoneAccount != null && phoneAccount.isEnabled()); 1496 } 1497 }, 1498 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1499 "Phone account enable failed for " + handle 1500 ); 1501 } 1502 assertPhoneAccountIsDefault(final PhoneAccountHandle handle)1503 void assertPhoneAccountIsDefault(final PhoneAccountHandle handle) { 1504 waitUntilConditionIsTrueOrTimeout( 1505 new Condition() { 1506 @Override 1507 public Object expected() { 1508 return true; 1509 } 1510 1511 @Override 1512 public Object actual() { 1513 PhoneAccountHandle phoneAccountHandle = 1514 mTelecomManager.getUserSelectedOutgoingPhoneAccount(); 1515 return (phoneAccountHandle != null && phoneAccountHandle.equals(handle)); 1516 } 1517 }, 1518 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1519 "Failed to set default phone account to " + handle 1520 ); 1521 } 1522 assertCtsConnectionServiceUnbound()1523 void assertCtsConnectionServiceUnbound() { 1524 if (CtsConnectionService.isBound()) { 1525 assertTrue("CtsConnectionService not yet unbound!", 1526 CtsConnectionService.waitForUnBinding()); 1527 } 1528 } 1529 assertMockInCallServiceUnbound()1530 void assertMockInCallServiceUnbound() { 1531 waitUntilConditionIsTrueOrTimeout( 1532 new Condition() { 1533 @Override 1534 public Object expected() { 1535 return false; 1536 } 1537 1538 @Override 1539 public Object actual() { 1540 return MockInCallService.isServiceBound(); 1541 } 1542 }, 1543 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1544 "MockInCallService not yet unbound!" 1545 ); 1546 } 1547 assertIsOutgoingCallPermitted(boolean isPermitted, PhoneAccountHandle handle)1548 void assertIsOutgoingCallPermitted(boolean isPermitted, PhoneAccountHandle handle) { 1549 waitUntilConditionIsTrueOrTimeout( 1550 new Condition() { 1551 @Override 1552 public Object expected() { 1553 return isPermitted; 1554 } 1555 1556 @Override 1557 public Object actual() { 1558 return mTelecomManager.isOutgoingCallPermitted(handle); 1559 } 1560 }, 1561 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1562 "Expected isOutgoingCallPermitted to be " + isPermitted 1563 ); 1564 } 1565 assertIsIncomingCallPermitted(boolean isPermitted, PhoneAccountHandle handle)1566 void assertIsIncomingCallPermitted(boolean isPermitted, PhoneAccountHandle handle) { 1567 waitUntilConditionIsTrueOrTimeout( 1568 new Condition() { 1569 @Override 1570 public Object expected() { 1571 return isPermitted; 1572 } 1573 1574 @Override 1575 public Object actual() { 1576 return mTelecomManager.isIncomingCallPermitted(handle); 1577 } 1578 }, 1579 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1580 "Expected isIncomingCallPermitted to be " + isPermitted 1581 ); 1582 } 1583 assertIsInCall(boolean isIncall)1584 void assertIsInCall(boolean isIncall) { 1585 waitUntilConditionIsTrueOrTimeout( 1586 new Condition() { 1587 @Override 1588 public Object expected() { 1589 return isIncall; 1590 } 1591 1592 @Override 1593 public Object actual() { 1594 return mTelecomManager.isInCall(); 1595 } 1596 }, 1597 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1598 "Expected isInCall to be " + isIncall 1599 ); 1600 } 1601 assertIsInManagedCall(boolean isIncall)1602 void assertIsInManagedCall(boolean isIncall) { 1603 waitUntilConditionIsTrueOrTimeout( 1604 new Condition() { 1605 @Override 1606 public Object expected() { 1607 return isIncall; 1608 } 1609 1610 @Override 1611 public Object actual() { 1612 return mTelecomManager.isInManagedCall(); 1613 } 1614 }, 1615 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1616 "Expected isInManagedCall to be " + isIncall 1617 ); 1618 } 1619 1620 /** 1621 * Asserts that a call's properties are as expected. 1622 * 1623 * @param call The call. 1624 * @param properties The expected properties. 1625 */ assertCallProperties(final Call call, final int properties)1626 public void assertCallProperties(final Call call, final int properties) { 1627 waitUntilConditionIsTrueOrTimeout( 1628 new Condition() { 1629 @Override 1630 public Object expected() { 1631 return true; 1632 } 1633 1634 @Override 1635 public Object actual() { 1636 return call.getDetails().hasProperty(properties); 1637 } 1638 }, 1639 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1640 "Call should have properties " + properties 1641 ); 1642 } 1643 1644 /** 1645 * Asserts that a call does not have any of the specified call capability bits specified. 1646 * 1647 * @param call The call. 1648 * @param capabilities The capability or capabilities which are not expected. 1649 */ assertDoesNotHaveCallCapabilities(final Call call, final int capabilities)1650 public void assertDoesNotHaveCallCapabilities(final Call call, final int capabilities) { 1651 waitUntilConditionIsTrueOrTimeout( 1652 new Condition() { 1653 @Override 1654 public Object expected() { 1655 return true; 1656 } 1657 1658 @Override 1659 public Object actual() { 1660 int callCapabilities = call.getDetails().getCallCapabilities(); 1661 return !Call.Details.hasProperty(callCapabilities, capabilities); 1662 } 1663 }, 1664 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1665 "Call should not have capabilities " + capabilities 1666 ); 1667 } 1668 1669 /** 1670 * Asserts that a call does not have any of the specified call property bits specified. 1671 * 1672 * @param call The call. 1673 * @param properties The property or properties which are not expected. 1674 */ assertDoesNotHaveCallProperties(final Call call, final int properties)1675 public void assertDoesNotHaveCallProperties(final Call call, final int properties) { 1676 waitUntilConditionIsTrueOrTimeout( 1677 new Condition() { 1678 @Override 1679 public Object expected() { 1680 return true; 1681 } 1682 1683 @Override 1684 public Object actual() { 1685 return !call.getDetails().hasProperty(properties); 1686 } 1687 }, 1688 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1689 "Call should not have properties " + properties 1690 ); 1691 } 1692 1693 /** 1694 * Asserts that the audio manager reports the specified audio mode. 1695 * 1696 * @param audioManager The audio manager to check. 1697 * @param expectedMode The expected audio mode. 1698 */ assertAudioMode(final AudioManager audioManager, final int expectedMode)1699 public void assertAudioMode(final AudioManager audioManager, final int expectedMode) { 1700 waitUntilConditionIsTrueOrTimeout( 1701 new Condition() { 1702 @Override 1703 public Object expected() { 1704 return true; 1705 } 1706 1707 @Override 1708 public Object actual() { 1709 return audioManager.getMode() == expectedMode; 1710 } 1711 }, 1712 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1713 "Audio mode was expected to be " + expectedMode 1714 ); 1715 } 1716 1717 /** 1718 * Asserts that a call's capabilities are as expected. 1719 * 1720 * @param call The call. 1721 * @param capabilities The expected capabiltiies. 1722 */ assertCallCapabilities(final Call call, final int capabilities)1723 public void assertCallCapabilities(final Call call, final int capabilities) { 1724 waitUntilConditionIsTrueOrTimeout( 1725 new Condition() { 1726 @Override 1727 public Object expected() { 1728 return true; 1729 } 1730 1731 @Override 1732 public Object actual() { 1733 return (call.getDetails().getCallCapabilities() & capabilities) == 1734 capabilities; 1735 } 1736 }, 1737 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1738 "Call should have properties " + capabilities 1739 ); 1740 } 1741 getInCallService()1742 MockInCallService getInCallService() { 1743 return (mInCallCallbacks == null) ? null : mInCallCallbacks.getService(); 1744 } 1745 1746 /** 1747 * Asserts that the {@link UiModeManager} mode matches the specified mode. 1748 * 1749 * @param uiMode The expected ui mode. 1750 */ assertUiMode(final int uiMode)1751 public void assertUiMode(final int uiMode) { 1752 final UiModeManager uiModeManager = mContext.getSystemService(UiModeManager.class); 1753 waitUntilConditionIsTrueOrTimeout( 1754 new Condition() { 1755 @Override 1756 public Object expected() { 1757 return uiMode; 1758 } 1759 1760 @Override 1761 public Object actual() { 1762 return uiModeManager.getCurrentModeType(); 1763 } 1764 }, 1765 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1766 "Expected ui mode " + uiMode 1767 ); 1768 } 1769 waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout, String description)1770 void waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout, 1771 String description) { 1772 final long start = System.currentTimeMillis(); 1773 while (!condition.expected().equals(condition.actual()) 1774 && System.currentTimeMillis() - start < timeout) { 1775 sleep(50); 1776 } 1777 assertEquals(description, condition.expected(), condition.actual()); 1778 } 1779 1780 /** 1781 * Performs some work, and waits for the condition to be met. If the condition is not met in 1782 * each step of the loop, the work is performed again. 1783 * 1784 * @param work The work to perform. 1785 * @param condition The condition. 1786 * @param timeout The timeout. 1787 * @param description Description of the work being performed. 1788 */ doWorkAndWaitUntilConditionIsTrueOrTimeout(Work work, Condition condition, long timeout, String description)1789 void doWorkAndWaitUntilConditionIsTrueOrTimeout(Work work, Condition condition, long timeout, 1790 String description) { 1791 final long start = System.currentTimeMillis(); 1792 work.doWork(); 1793 while (!condition.expected().equals(condition.actual()) 1794 && System.currentTimeMillis() - start < timeout) { 1795 sleep(50); 1796 work.doWork(); 1797 } 1798 assertEquals(description, condition.expected(), condition.actual()); 1799 } 1800 1801 protected interface Condition { expected()1802 Object expected(); actual()1803 Object actual(); 1804 } 1805 1806 protected interface Work { doWork()1807 void doWork(); 1808 } 1809 areBundlesEqual(Bundle extras, Bundle newExtras)1810 public static boolean areBundlesEqual(Bundle extras, Bundle newExtras) { 1811 if (extras == null || newExtras == null) { 1812 return extras == newExtras; 1813 } 1814 1815 if (extras.size() != newExtras.size()) { 1816 return false; 1817 } 1818 1819 for (String key : extras.keySet()) { 1820 if (key != null) { 1821 final Object value = extras.get(key); 1822 final Object newValue = newExtras.get(key); 1823 if (!Objects.equals(value, newValue)) { 1824 return false; 1825 } 1826 } 1827 } 1828 return true; 1829 } 1830 } 1831