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.assertThat; 26 27 import android.app.AppOpsManager; 28 import android.app.UiAutomation; 29 import android.app.UiModeManager; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.pm.PackageManager; 33 import android.content.res.Configuration; 34 import android.database.ContentObserver; 35 import android.database.Cursor; 36 import android.location.LocationManager; 37 import android.media.AudioManager; 38 import android.net.Uri; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.IBinder; 43 import android.os.Looper; 44 import android.os.Process; 45 import android.os.RemoteException; 46 import android.os.UserHandle; 47 import android.provider.CallLog; 48 import android.telecom.Call; 49 import android.telecom.CallAudioState; 50 import android.telecom.CallEndpoint; 51 import android.telecom.Conference; 52 import android.telecom.Connection; 53 import android.telecom.ConnectionRequest; 54 import android.telecom.InCallService; 55 import android.telecom.PhoneAccount; 56 import android.telecom.PhoneAccountHandle; 57 import android.telecom.TelecomManager; 58 import android.telecom.VideoProfile; 59 import android.telecom.cts.MockInCallService.InCallServiceCallbacks; 60 import android.telecom.cts.carmodetestapp.ICtsCarModeInCallServiceControl; 61 import android.telephony.CarrierConfigManager; 62 import android.telephony.TelephonyCallback; 63 import android.telephony.TelephonyManager; 64 import android.telephony.emergency.EmergencyNumber; 65 import android.test.InstrumentationTestCase; 66 import android.text.TextUtils; 67 import android.util.Log; 68 import android.util.Pair; 69 70 import androidx.test.InstrumentationRegistry; 71 72 import com.android.compatibility.common.util.ShellIdentityUtils; 73 74 import java.util.ArrayList; 75 import java.util.List; 76 import java.util.Map; 77 import java.util.Objects; 78 import java.util.Random; 79 import java.util.concurrent.CountDownLatch; 80 import java.util.concurrent.LinkedBlockingQueue; 81 import java.util.concurrent.Semaphore; 82 import java.util.concurrent.TimeUnit; 83 import java.util.stream.Collectors; 84 85 /** 86 * Base class for Telecom CTS tests that require a {@link CtsConnectionService} and 87 * {@link MockInCallService} to verify Telecom functionality. 88 */ 89 public class BaseTelecomTestWithMockServices extends InstrumentationTestCase { 90 91 public static final int FLAG_REGISTER = 0x1; 92 public static final int FLAG_ENABLE = 0x2; 93 public static final int FLAG_SET_DEFAULT = 0x4; 94 public static final int FLAG_PHONE_ACCOUNT_HANDLES_CONTENT_SCHEME = 0x8; 95 96 // Don't accidently use emergency number. 97 private static int sCounter = 5553638; 98 99 //Smaller timeout for checking outgoing connection 100 //Since this called after placeAndVerifyCall 101 private static final long WAIT_FOR_OUTGOING_CONNECTION_TIMEOUT_MS = 2000; 102 103 public static final String TEST_EMERGENCY_NUMBER = "5553637"; 104 public static final Uri TEST_EMERGENCY_URI = Uri.fromParts("tel", TEST_EMERGENCY_NUMBER, null); 105 public static final String PKG_NAME = "android.telecom.cts"; 106 public static final String PERMISSION_PROCESS_OUTGOING_CALLS = 107 "android.permission.PROCESS_OUTGOING_CALLS"; 108 public static final String PERMISSION_PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS"; 109 110 public static final String OTT_TEST_EVENT_NAME = "test.oem.event_name"; 111 112 Context mContext; 113 TelecomManager mTelecomManager; 114 TelephonyManager mTelephonyManager; 115 CarrierConfigManager mCarrierConfigManager; 116 LocationManager mLocationManager; 117 UiModeManager mUiModeManager; 118 119 TestUtils.InvokeCounter mOnBringToForegroundCounter; 120 TestUtils.InvokeCounter mOnCallAudioStateChangedCounter; 121 TestUtils.InvokeCounter mOnPostDialWaitCounter; 122 TestUtils.InvokeCounter mOnCannedTextResponsesLoadedCounter; 123 TestUtils.InvokeCounter mOnSilenceRingerCounter; 124 TestUtils.InvokeCounter mOnConnectionEventCounter; 125 TestUtils.InvokeCounter mOnExtrasChangedCounter; 126 TestUtils.InvokeCounter mOnPropertiesChangedCounter; 127 TestUtils.InvokeCounter mOnRttModeChangedCounter; 128 TestUtils.InvokeCounter mOnRttStatusChangedCounter; 129 TestUtils.InvokeCounter mOnRttInitiationFailedCounter; 130 TestUtils.InvokeCounter mOnRttRequestCounter; 131 TestUtils.InvokeCounter mOnHandoverCompleteCounter; 132 TestUtils.InvokeCounter mOnHandoverFailedCounter; 133 TestUtils.InvokeCounter mOnPhoneAccountChangedCounter; 134 TestUtils.InvokeCounter mOnCallEndpointChangedCounter; 135 TestUtils.InvokeCounter mOnAvailableEndpointsChangedCounter; 136 TestUtils.InvokeCounter mOnMuteStateChangedCounter; 137 Bundle mPreviousExtras; 138 int mPreviousProperties = -1; 139 PhoneAccountHandle mPreviousPhoneAccountHandle = null; 140 141 InCallServiceCallbacks mInCallCallbacks; 142 String mPreviousDefaultDialer = null; 143 PhoneAccountHandle mPreviousDefaultOutgoingAccount = null; 144 boolean mShouldRestoreDefaultOutgoingAccount = false; 145 MockConnectionService connectionService = null; 146 boolean mIsEmergencyCallingSetup = false; 147 148 HandlerThread mTelephonyCallbackThread; 149 Handler mTelephonyCallbackHandler; 150 TestTelephonyCallback mTelephonyCallback; 151 TestCallStateListener mTestCallStateListener; 152 Handler mHandler; 153 154 /** 155 * Uses the control interface to disable car mode. 156 * @param expectedUiMode 157 */ disableAndVerifyCarMode(ICtsCarModeInCallServiceControl control, int expectedUiMode)158 protected void disableAndVerifyCarMode(ICtsCarModeInCallServiceControl control, 159 int expectedUiMode) { 160 if (control == null) { 161 return; 162 } 163 try { 164 control.disableCarMode(); 165 } catch (RemoteException re) { 166 fail("Bee-boop; can't control the incall service"); 167 } 168 assertUiMode(expectedUiMode); 169 } 170 disconnectAllCallsAndVerify(ICtsCarModeInCallServiceControl controlBinder)171 protected void disconnectAllCallsAndVerify(ICtsCarModeInCallServiceControl controlBinder) { 172 if (controlBinder == null) { 173 return; 174 } 175 try { 176 controlBinder.disconnectCalls(); 177 } catch (RemoteException re) { 178 fail("Bee-boop; can't control the incall service"); 179 } 180 assertCarModeCallCount(controlBinder, 0); 181 } 182 183 /** 184 * Verify the car mode ICS has an expected call count. 185 * @param expected 186 */ assertCarModeCallCount(ICtsCarModeInCallServiceControl control, int expected)187 protected void assertCarModeCallCount(ICtsCarModeInCallServiceControl control, int expected) { 188 waitUntilConditionIsTrueOrTimeout( 189 new Condition() { 190 @Override 191 public Object expected() { 192 return expected; 193 } 194 195 @Override 196 public Object actual() { 197 int callCount = 0; 198 try { 199 callCount = control.getCallCount(); 200 } catch (RemoteException re) { 201 fail("Bee-boop; can't control the incall service"); 202 } 203 return callCount; 204 } 205 }, 206 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 207 "Expected " + expected + " calls." 208 ); 209 } 210 211 static class TestCallStateListener extends TelephonyCallback 212 implements TelephonyCallback.CallStateListener { 213 214 private CountDownLatch mCountDownLatch = new CountDownLatch(1); 215 private int mLastState = -1; 216 217 @Override onCallStateChanged(int state)218 public void onCallStateChanged(int state) { 219 Log.i(TAG, "onCallStateChanged: state=" + state); 220 mLastState = state; 221 mCountDownLatch.countDown(); 222 mCountDownLatch = new CountDownLatch(1); 223 } 224 getCountDownLatch()225 public CountDownLatch getCountDownLatch() { 226 return mCountDownLatch; 227 } 228 getLastState()229 public int getLastState() { 230 return mLastState; 231 } 232 } 233 234 static class TestTelephonyCallback extends TelephonyCallback implements 235 TelephonyCallback.CallStateListener, 236 TelephonyCallback.OutgoingEmergencyCallListener, 237 TelephonyCallback.EmergencyNumberListListener { 238 /** Semaphore released for every callback invocation. */ 239 public Semaphore mCallbackSemaphore = new Semaphore(0); 240 241 List<Integer> mCallStates = new ArrayList<>(); 242 EmergencyNumber mLastOutgoingEmergencyNumber; 243 244 LinkedBlockingQueue<Map<Integer, List<EmergencyNumber>>> mEmergencyNumberListQueue = 245 new LinkedBlockingQueue<>(2); 246 247 @Override onCallStateChanged(int state)248 public void onCallStateChanged(int state) { 249 Log.i(TAG, "onCallStateChanged: state=" + state); 250 mCallStates.add(state); 251 mCallbackSemaphore.release(); 252 } 253 254 @Override onOutgoingEmergencyCall(EmergencyNumber emergencyNumber, int subscriptionId)255 public void onOutgoingEmergencyCall(EmergencyNumber emergencyNumber, int subscriptionId) { 256 Log.i(TAG, "onOutgoingEmergencyCall: emergencyNumber=" + emergencyNumber); 257 mLastOutgoingEmergencyNumber = emergencyNumber; 258 mCallbackSemaphore.release(); 259 } 260 261 @Override onEmergencyNumberListChanged( Map<Integer, List<EmergencyNumber>> emergencyNumberList)262 public void onEmergencyNumberListChanged( 263 Map<Integer, List<EmergencyNumber>> emergencyNumberList) { 264 Log.i(TAG, "onEmergencyNumberChanged, total size=" + emergencyNumberList.values() 265 .stream().mapToInt(List::size).sum()); 266 mEmergencyNumberListQueue.offer(emergencyNumberList); 267 } 268 waitForEmergencyNumberListUpdate( long timeoutMillis)269 public Map<Integer, List<EmergencyNumber>> waitForEmergencyNumberListUpdate( 270 long timeoutMillis) throws Throwable { 271 return mEmergencyNumberListQueue.poll(timeoutMillis, TimeUnit.MILLISECONDS); 272 } 273 } 274 275 boolean mShouldTestTelecom = true; 276 277 @Override setUp()278 protected void setUp() throws Exception { 279 super.setUp(); 280 mContext = getInstrumentation().getContext(); 281 mHandler = new Handler(Looper.getMainLooper()); 282 mShouldTestTelecom = TestUtils.shouldTestTelecom(mContext); 283 if (!mShouldTestTelecom) { 284 return; 285 } 286 287 // Assume we start in normal mode at the start of all Telecom tests; a failure to leave car 288 // mode in any of the tests would cause subsequent test failures. 289 // For Watch, UI_MODE shouldn't be normal mode. 290 mUiModeManager = mContext.getSystemService(UiModeManager.class); 291 TestUtils.executeShellCommand(getInstrumentation(), "telecom reset-car-mode"); 292 293 if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) { 294 assertUiMode(Configuration.UI_MODE_TYPE_WATCH); 295 } else if (mContext.getPackageManager().hasSystemFeature( 296 PackageManager.FEATURE_AUTOMOTIVE)) { 297 assertUiMode(Configuration.UI_MODE_TYPE_CAR); 298 } else { 299 assertUiMode(Configuration.UI_MODE_TYPE_NORMAL); 300 } 301 302 AppOpsManager aom = mContext.getSystemService(AppOpsManager.class); 303 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(aom, 304 (appOpsMan) -> appOpsMan.setUidMode(AppOpsManager.OPSTR_PROCESS_OUTGOING_CALLS, 305 Process.myUid(), AppOpsManager.MODE_ALLOWED)); 306 307 mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); 308 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 309 mCarrierConfigManager = (CarrierConfigManager) mContext.getSystemService( 310 Context.CARRIER_CONFIG_SERVICE); 311 mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); 312 mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation()); 313 TestUtils.setDefaultDialer(getInstrumentation(), PACKAGE); 314 setupCallbacks(); 315 316 // Register a call state listener. 317 mTestCallStateListener = new TestCallStateListener(); 318 CountDownLatch latch = mTestCallStateListener.getCountDownLatch(); 319 mTelephonyManager.registerTelephonyCallback(r -> r.run(), mTestCallStateListener); 320 latch.await( 321 TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_REGISTERED_TIMEOUT_S, TimeUnit.SECONDS); 322 // Create a new thread for the telephony callback. 323 mTelephonyCallbackThread = new HandlerThread("PhoneStateListenerThread"); 324 mTelephonyCallbackThread.start(); 325 mTelephonyCallbackHandler = new Handler(mTelephonyCallbackThread.getLooper()); 326 327 mTelephonyCallback = new TestTelephonyCallback(); 328 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, 329 (tm) -> tm.registerTelephonyCallback( 330 mTelephonyCallbackHandler::post, 331 mTelephonyCallback)); 332 UiAutomation uiAutomation = 333 InstrumentationRegistry.getInstrumentation().getUiAutomation(); 334 uiAutomation.grantRuntimePermissionAsUser(PKG_NAME, PERMISSION_PROCESS_OUTGOING_CALLS, 335 UserHandle.CURRENT); 336 uiAutomation.grantRuntimePermissionAsUser(PKG_NAME, PERMISSION_PACKAGE_USAGE_STATS, 337 UserHandle.CURRENT); 338 } 339 340 @Override tearDown()341 protected void tearDown() throws Exception { 342 super.tearDown(); 343 if (!mShouldTestTelecom) { 344 return; 345 } 346 unregisterTelephonyCallbacks(); 347 cleanupCalls(); 348 if (!TextUtils.isEmpty(mPreviousDefaultDialer)) { 349 TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer); 350 } 351 tearDownConnectionService(TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 352 tearDownEmergencyCalling(); 353 try { 354 assertMockInCallServiceUnbound(); 355 } catch (Throwable t) { 356 // If we haven't unbound, that means there's some dirty state in Telecom that needs 357 // cleaning up. Forcibly unbind and clean up Telecom state so that we don't have a 358 // cascading failure of tests. 359 TestUtils.executeShellCommand(getInstrumentation(), "telecom cleanup-stuck-calls"); 360 throw t; 361 } 362 UiAutomation uiAutomation = 363 InstrumentationRegistry.getInstrumentation().getUiAutomation(); 364 uiAutomation.revokeRuntimePermissionAsUser(PKG_NAME, PERMISSION_PROCESS_OUTGOING_CALLS, 365 UserHandle.CURRENT); 366 } 367 unregisterTelephonyCallbacks()368 public void unregisterTelephonyCallbacks() { 369 if (mTestCallStateListener != null) { 370 mTelephonyManager.unregisterTelephonyCallback(mTestCallStateListener); 371 } 372 if (mTelephonyCallback != null) { 373 mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback); 374 } 375 if (mTelephonyCallbackThread != null) { 376 mTelephonyCallbackThread.quit(); 377 } 378 } 379 setupConnectionService(MockConnectionService connectionService, int flags)380 protected PhoneAccount setupConnectionService(MockConnectionService connectionService, 381 int flags) throws Exception { 382 Log.i(TAG, "Setting up mock connection service"); 383 try { 384 if (connectionService != null) { 385 this.connectionService = connectionService; 386 } else { 387 // Generate a vanilla mock connection service, if not provided. 388 this.connectionService = new MockConnectionService(); 389 } 390 CtsConnectionService.setUp(this.connectionService); 391 392 if ((flags & FLAG_REGISTER) != 0) { 393 if ((flags & FLAG_PHONE_ACCOUNT_HANDLES_CONTENT_SCHEME) != 0) { 394 mTelecomManager.registerPhoneAccount( 395 TestUtils.TEST_PHONE_ACCOUNT_THAT_HANDLES_CONTENT_SCHEME); 396 } else { 397 mTelecomManager.registerPhoneAccount(TestUtils.TEST_PHONE_ACCOUNT); 398 } 399 } 400 if ((flags & FLAG_ENABLE) != 0) { 401 TestUtils.enablePhoneAccount(getInstrumentation(), 402 TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 403 // Wait till the adb commands have executed and account is enabled in Telecom 404 // database. 405 assertPhoneAccountEnabled(TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 406 } 407 408 if ((flags & FLAG_SET_DEFAULT) != 0) { 409 mPreviousDefaultOutgoingAccount = 410 mTelecomManager.getUserSelectedOutgoingPhoneAccount(); 411 mShouldRestoreDefaultOutgoingAccount = true; 412 TestUtils.setDefaultOutgoingPhoneAccount(getInstrumentation(), 413 TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 414 // Wait till the adb commands have executed and the default has changed. 415 assertPhoneAccountIsDefault(TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 416 } 417 418 } catch (Exception e) { 419 // Clear static cts connection service state: its ok to do this if setUp itself throws. 420 CtsConnectionService.tearDown(); 421 unregisterTelephonyCallbacks(); 422 throw e; 423 } 424 return TestUtils.TEST_PHONE_ACCOUNT; 425 } 426 tearDownConnectionService(PhoneAccountHandle accountHandle)427 protected void tearDownConnectionService(PhoneAccountHandle accountHandle) throws Exception { 428 Log.i(TAG, "Tearing down mock connection service"); 429 if (this.connectionService != null) { 430 assertNumConnections(this.connectionService, 0); 431 } 432 mTelecomManager.unregisterPhoneAccount(accountHandle); 433 CtsConnectionService.tearDown(); 434 assertCtsConnectionServiceUnbound(); 435 if (mShouldRestoreDefaultOutgoingAccount) { 436 TestUtils.setDefaultOutgoingPhoneAccount(getInstrumentation(), 437 mPreviousDefaultOutgoingAccount); 438 } 439 this.connectionService = null; 440 mPreviousDefaultOutgoingAccount = null; 441 mShouldRestoreDefaultOutgoingAccount = false; 442 } 443 setupForEmergencyCalling(String testNumber)444 protected void setupForEmergencyCalling(String testNumber) throws Exception { 445 TestUtils.setSystemDialerOverride(getInstrumentation()); 446 TestUtils.addTestEmergencyNumber(getInstrumentation(), testNumber); 447 TestUtils.setTestEmergencyPhoneAccountPackageFilter(getInstrumentation(), mContext); 448 // Emergency calls require special capabilities. 449 TestUtils.registerEmergencyPhoneAccount(getInstrumentation(), 450 TestUtils.TEST_EMERGENCY_PHONE_ACCOUNT_HANDLE, 451 TestUtils.ACCOUNT_LABEL + "E", "tel:555-EMER"); 452 mIsEmergencyCallingSetup = true; 453 } 454 tearDownEmergencyCalling()455 protected void tearDownEmergencyCalling() throws Exception { 456 if (!mIsEmergencyCallingSetup) return; 457 458 TestUtils.clearSystemDialerOverride(getInstrumentation()); 459 TestUtils.clearTestEmergencyNumbers(getInstrumentation()); 460 TestUtils.clearTestEmergencyPhoneAccountPackageFilter(getInstrumentation()); 461 mTelecomManager.unregisterPhoneAccount(TestUtils.TEST_EMERGENCY_PHONE_ACCOUNT_HANDLE); 462 } 463 startCallTo(Uri address, PhoneAccountHandle accountHandle)464 protected void startCallTo(Uri address, PhoneAccountHandle accountHandle) { 465 final Intent intent = new Intent(Intent.ACTION_CALL, address); 466 if (accountHandle != null) { 467 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle); 468 } 469 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 470 mContext.startActivity(intent); 471 } 472 sleep(long ms)473 void sleep(long ms) { 474 try { 475 Thread.sleep(ms); 476 } catch (InterruptedException e) { 477 } 478 } 479 setupCallbacks()480 private void setupCallbacks() { 481 mInCallCallbacks = new InCallServiceCallbacks() { 482 @Override 483 public void onCallAdded(Call call, int numCalls) { 484 Log.i(TAG, "onCallAdded, Call: " + call + ", Num Calls: " + numCalls); 485 this.lock.release(); 486 mPreviousPhoneAccountHandle = call.getDetails().getAccountHandle(); 487 } 488 @Override 489 public void onCallRemoved(Call call, int numCalls) { 490 Log.i(TAG, "onCallRemoved, Call: " + call + ", Num Calls: " + numCalls); 491 } 492 @Override 493 public void onParentChanged(Call call, Call parent) { 494 Log.i(TAG, "onParentChanged, Call: " + call + ", Parent: " + parent); 495 this.lock.release(); 496 } 497 @Override 498 public void onChildrenChanged(Call call, List<Call> children) { 499 Log.i(TAG, "onChildrenChanged, Call: " + call + "Children: " + children); 500 this.lock.release(); 501 } 502 @Override 503 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) { 504 Log.i(TAG, "onConferenceableCallsChanged, Call: " + call + ", Conferenceables: " + 505 conferenceableCalls); 506 } 507 @Override 508 public void onDetailsChanged(Call call, Call.Details details) { 509 Log.i(TAG, "onDetailsChanged, Call: " + call + ", Details: " + details); 510 if (!areBundlesEqual(mPreviousExtras, details.getExtras())) { 511 mOnExtrasChangedCounter.invoke(call, details); 512 } 513 mPreviousExtras = details.getExtras(); 514 515 if (mPreviousProperties != details.getCallProperties()) { 516 mOnPropertiesChangedCounter.invoke(call, details); 517 Log.i(TAG, "onDetailsChanged; properties changed from " + Call.Details.propertiesToString(mPreviousProperties) + 518 " to " + Call.Details.propertiesToString(details.getCallProperties())); 519 } 520 mPreviousProperties = details.getCallProperties(); 521 522 if (details.getAccountHandle() != null && 523 !details.getAccountHandle().equals(mPreviousPhoneAccountHandle)) { 524 mOnPhoneAccountChangedCounter.invoke(call, details.getAccountHandle()); 525 } 526 mPreviousPhoneAccountHandle = details.getAccountHandle(); 527 } 528 @Override 529 public void onCallDestroyed(Call call) { 530 Log.i(TAG, "onCallDestroyed, Call: " + call); 531 } 532 @Override 533 public void onCallStateChanged(Call call, int newState) { 534 Log.i(TAG, "onCallStateChanged, Call: " + call + ", New State: " + newState); 535 } 536 @Override 537 public void onBringToForeground(boolean showDialpad) { 538 mOnBringToForegroundCounter.invoke(showDialpad); 539 } 540 @Override 541 public void onCallAudioStateChanged(CallAudioState audioState) { 542 Log.i(TAG, "onCallAudioStateChanged, audioState: " + audioState); 543 mOnCallAudioStateChangedCounter.invoke(audioState); 544 } 545 @Override 546 public void onPostDialWait(Call call, String remainingPostDialSequence) { 547 mOnPostDialWaitCounter.invoke(call, remainingPostDialSequence); 548 } 549 @Override 550 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) { 551 mOnCannedTextResponsesLoadedCounter.invoke(call, cannedTextResponses); 552 } 553 @Override 554 public void onConnectionEvent(Call call, String event, Bundle extras) { 555 mOnConnectionEventCounter.invoke(call, event, extras); 556 } 557 558 @Override 559 public void onSilenceRinger() { 560 Log.i(TAG, "onSilenceRinger"); 561 mOnSilenceRingerCounter.invoke(); 562 } 563 564 @Override 565 public void onRttModeChanged(Call call, int mode) { 566 mOnRttModeChangedCounter.invoke(call, mode); 567 } 568 569 @Override 570 public void onRttStatusChanged(Call call, boolean enabled, Call.RttCall rttCall) { 571 mOnRttStatusChangedCounter.invoke(call, enabled, rttCall); 572 } 573 574 @Override 575 public void onRttRequest(Call call, int id) { 576 mOnRttRequestCounter.invoke(call, id); 577 } 578 579 @Override 580 public void onRttInitiationFailure(Call call, int reason) { 581 mOnRttInitiationFailedCounter.invoke(call, reason); 582 } 583 584 @Override 585 public void onHandoverComplete(Call call) { 586 mOnHandoverCompleteCounter.invoke(call); 587 } 588 589 @Override 590 public void onHandoverFailed(Call call, int reason) { 591 mOnHandoverFailedCounter.invoke(call, reason); 592 } 593 594 @Override 595 public void onCallEndpointChanged(CallEndpoint callEndpoint) { 596 Log.i(TAG, "onCallEndpointChanged, callEndpoint: " + callEndpoint); 597 mOnCallEndpointChangedCounter.invoke(callEndpoint); 598 } 599 600 @Override 601 public void onAvailableCallEndpointsChanged(List<CallEndpoint> availableEndpoints) { 602 Log.i(TAG, "onAvailableCallEndpointsChanged"); 603 mOnAvailableEndpointsChangedCounter.invoke(availableEndpoints); 604 } 605 606 @Override 607 public void onMuteStateChanged(boolean isMuted) { 608 Log.i(TAG, "onMuteStateChanged, isMuted: " + isMuted); 609 mOnMuteStateChangedCounter.invoke(isMuted); 610 } 611 }; 612 613 MockInCallService.setCallbacks(mInCallCallbacks); 614 615 // TODO: If more InvokeCounters are added in the future, consider consolidating them into a 616 // single Collection. 617 mOnBringToForegroundCounter = new TestUtils.InvokeCounter("OnBringToForeground"); 618 mOnCallAudioStateChangedCounter = new TestUtils.InvokeCounter("OnCallAudioStateChanged"); 619 mOnPostDialWaitCounter = new TestUtils.InvokeCounter("OnPostDialWait"); 620 mOnCannedTextResponsesLoadedCounter = new TestUtils.InvokeCounter("OnCannedTextResponsesLoaded"); 621 mOnSilenceRingerCounter = new TestUtils.InvokeCounter("OnSilenceRinger"); 622 mOnConnectionEventCounter = new TestUtils.InvokeCounter("OnConnectionEvent"); 623 mOnExtrasChangedCounter = new TestUtils.InvokeCounter("OnDetailsChangedCounter"); 624 mOnPropertiesChangedCounter = new TestUtils.InvokeCounter("OnPropertiesChangedCounter"); 625 mOnRttModeChangedCounter = new TestUtils.InvokeCounter("mOnRttModeChangedCounter"); 626 mOnRttStatusChangedCounter = new TestUtils.InvokeCounter("mOnRttStatusChangedCounter"); 627 mOnRttInitiationFailedCounter = 628 new TestUtils.InvokeCounter("mOnRttInitiationFailedCounter"); 629 mOnRttRequestCounter = new TestUtils.InvokeCounter("mOnRttRequestCounter"); 630 mOnHandoverCompleteCounter = new TestUtils.InvokeCounter("mOnHandoverCompleteCounter"); 631 mOnHandoverFailedCounter = new TestUtils.InvokeCounter("mOnHandoverFailedCounter"); 632 mOnPhoneAccountChangedCounter = new TestUtils.InvokeCounter( 633 "mOnPhoneAccountChangedCounter"); 634 mOnCallEndpointChangedCounter = new TestUtils.InvokeCounter("OnCallEndpointChanged"); 635 mOnAvailableEndpointsChangedCounter = new TestUtils.InvokeCounter( 636 "OnAvailableEndpointsChanged"); 637 mOnMuteStateChangedCounter = new TestUtils.InvokeCounter("OnMuteStateChanged"); 638 } 639 addAndVerifyNewFailedIncomingCall(Uri incomingHandle, Bundle extras)640 void addAndVerifyNewFailedIncomingCall(Uri incomingHandle, Bundle extras) { 641 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 642 int currentCallCount = 0; 643 if (mInCallCallbacks.getService() != null) { 644 currentCallCount = mInCallCallbacks.getService().getCallCount(); 645 } 646 647 if (extras == null) { 648 extras = new Bundle(); 649 } 650 extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, incomingHandle); 651 mTelecomManager.addNewIncomingCall(TestUtils.TEST_PHONE_ACCOUNT_HANDLE, extras); 652 653 if (!connectionService.waitForEvent( 654 MockConnectionService.EVENT_CONNECTION_SERVICE_CREATE_CONNECTION_FAILED)) { 655 fail("Incoming Connection failure indication did not get called."); 656 } 657 658 assertEquals("ConnectionService did not receive failed connection", 659 1, connectionService.failedConnections.size()); 660 661 assertEquals("Address is not correct for failed connection", 662 connectionService.failedConnections.get(0).getAddress(), incomingHandle); 663 664 assertEquals("InCallService should contain the same number of calls.", 665 currentCallCount, 666 mInCallCallbacks.getService().getCallCount()); 667 } 668 669 /** 670 * Puts Telecom in a state where there is an incoming call provided by the 671 * {@link CtsConnectionService} which can be tested. 672 */ addAndVerifyNewIncomingCall(Uri incomingHandle, Bundle extras)673 void addAndVerifyNewIncomingCall(Uri incomingHandle, Bundle extras) { 674 int currentCallCount = addNewIncomingCall(incomingHandle, extras); 675 verifyNewIncomingCall(currentCallCount); 676 } 677 addNewIncomingCall(Uri incomingHandle, Bundle extras)678 int addNewIncomingCall(Uri incomingHandle, Bundle extras) { 679 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 680 int currentCallCount = 0; 681 if (mInCallCallbacks.getService() != null) { 682 currentCallCount = mInCallCallbacks.getService().getCallCount(); 683 } 684 685 if (extras == null) { 686 extras = new Bundle(); 687 } 688 extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, incomingHandle); 689 mTelecomManager.addNewIncomingCall(TestUtils.TEST_PHONE_ACCOUNT_HANDLE, extras); 690 691 return currentCallCount; 692 } 693 verifyNewIncomingCall(int currentCallCount)694 void verifyNewIncomingCall(int currentCallCount) { 695 try { 696 if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S, 697 TimeUnit.SECONDS)) { 698 fail("No call added to InCallService."); 699 } 700 } catch (InterruptedException e) { 701 Log.i(TAG, "Test interrupted!"); 702 } 703 704 assertEquals("InCallService should contain 1 more call after adding a call.", 705 currentCallCount + 1, 706 mInCallCallbacks.getService().getCallCount()); 707 } 708 709 /** 710 * Puts Telecom in a state where there is an active call provided by the 711 * {@link CtsConnectionService} which can be tested. 712 */ placeAndVerifyCall()713 void placeAndVerifyCall() { 714 placeAndVerifyCall(null); 715 } 716 placeAndVerifyCallByRedirection(boolean wasCancelled)717 void placeAndVerifyCallByRedirection(boolean wasCancelled) { 718 placeAndVerifyCallByRedirection(null, wasCancelled); 719 } 720 721 /** 722 * Puts Telecom in a state where there is an active call provided by the 723 * {@link CtsConnectionService} which can be tested. 724 */ placeAndVerifyCallByRedirection(Bundle extras, boolean wasCancelled)725 void placeAndVerifyCallByRedirection(Bundle extras, boolean wasCancelled) { 726 int currentCallCount = (getInCallService() == null) ? 0 : getInCallService().getCallCount(); 727 int currentConnections = getNumberOfConnections(); 728 // We expect a new connection if it wasn't cancelled. 729 if (!wasCancelled) { 730 currentConnections++; 731 currentCallCount++; 732 } 733 placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY, currentCallCount); 734 // The connectionService.lock is released in 735 // MockConnectionService#onCreateOutgoingConnection, however the connection will not 736 // actually be added to the list of connections in the ConnectionService until shortly 737 // afterwards. So there is still a potential for the lock to be released before it would 738 // be seen by calls to ConnectionService#getAllConnections(). 739 // We will wait here until the list of connections includes one more connection to ensure 740 // that placing the call has fully completed. 741 assertCSConnections(currentConnections); 742 743 // Ensure the new outgoing call broadcast fired for the outgoing call. 744 assertOutgoingCallBroadcastReceived(true); 745 746 // CTS test does not have read call log permission so should not get the phone number. 747 assertNull(NewOutgoingCallBroadcastReceiver.getReceivedNumber()); 748 } 749 750 /** 751 * Puts Telecom in a state where there is an active call provided by the 752 * {@link CtsConnectionService} which can be tested. 753 * 754 * @param videoState the video state of the call. 755 */ placeAndVerifyCall(int videoState)756 void placeAndVerifyCall(int videoState) { 757 placeAndVerifyCall(null, videoState); 758 } 759 760 /** 761 * Puts Telecom in a state where there is an active call provided by the 762 * {@link CtsConnectionService} which can be tested. 763 */ placeAndVerifyCall(Bundle extras)764 void placeAndVerifyCall(Bundle extras) { 765 placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY); 766 } 767 768 /** 769 * Puts Telecom in a state where there is an active call provided by the 770 * {@link CtsConnectionService} which can be tested. 771 */ placeAndVerifyCall(Bundle extras, int videoState)772 void placeAndVerifyCall(Bundle extras, int videoState) { 773 int currentCallCount = (getInCallService() == null) ? 0 : getInCallService().getCallCount(); 774 // We expect placing the call adds a new call/connection. 775 int expectedConnections = getNumberOfConnections() + 1; 776 placeAndVerifyCall(extras, videoState, currentCallCount + 1); 777 // The connectionService.lock is released in 778 // MockConnectionService#onCreateOutgoingConnection, however the connection will not 779 // actually be added to the list of connections in the ConnectionService until shortly 780 // afterwards. So there is still a potential for the lock to be released before it would 781 // be seen by calls to ConnectionService#getAllConnections(). 782 // We will wait here until the list of connections includes one more connection to ensure 783 // that placing the call has fully completed. 784 assertCSConnections(expectedConnections); 785 assertOutgoingCallBroadcastReceived(true); 786 787 // CTS test does not have read call log permission so should not get the phone number. 788 assertNull(NewOutgoingCallBroadcastReceiver.getReceivedNumber()); 789 } 790 791 /** 792 * Verifies that a call was not placed 793 */ placeAndVerifyNoCall(Bundle extras)794 void placeAndVerifyNoCall(Bundle extras) { 795 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 796 placeNewCallWithPhoneAccount(extras, 0); 797 798 try { 799 if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S, 800 TimeUnit.SECONDS)) { 801 } 802 } catch (InterruptedException e) { 803 Log.i(TAG, "Test interrupted!"); 804 } 805 806 // Make sure any procedures to disconnect existing calls (makeRoomForOutgoingCall) 807 // complete successfully 808 TestUtils.waitOnLocalMainLooper(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS); 809 TestUtils.waitOnAllHandlers(getInstrumentation()); 810 811 assertNull("Service should be null since call should not have been placed", 812 mInCallCallbacks.getService()); 813 } 814 /** 815 * Puts Telecom in a state where there is an active call provided by the 816 * {@link CtsConnectionService} which can be tested. 817 */ placeAndVerifyCall(Bundle extras, int videoState, int expectedCallCount)818 void placeAndVerifyCall(Bundle extras, int videoState, int expectedCallCount) { 819 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 820 placeNewCallWithPhoneAccount(extras, videoState); 821 822 try { 823 if (!mInCallCallbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S, 824 TimeUnit.SECONDS)) { 825 fail("No call added to InCallService."); 826 } 827 } catch (InterruptedException e) { 828 Log.i(TAG, "Test interrupted!"); 829 } 830 831 // Make sure any procedures to disconnect existing calls (makeRoomForOutgoingCall) 832 // complete successfully 833 TestUtils.waitOnLocalMainLooper(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS); 834 TestUtils.waitOnAllHandlers(getInstrumentation()); 835 836 assertEquals("InCallService should match the expected count.", expectedCallCount, 837 mInCallCallbacks.getService().getCallCount()); 838 } 839 840 /** 841 * Place an emergency call and verify that it has been setup properly. 842 * 843 * @param supportsHold If telecom supports holding emergency calls, this will expect two 844 * calls. If telecom does not support holding emergency calls, this will expect only the 845 * emergency call to be active. 846 * @return The emergency connection 847 */ placeAndVerifyEmergencyCall(boolean supportsHold)848 public Connection placeAndVerifyEmergencyCall(boolean supportsHold) { 849 Bundle extras = new Bundle(); 850 extras.putParcelable(TestUtils.EXTRA_PHONE_NUMBER, TEST_EMERGENCY_URI); 851 // We want to request the active connections vs number of connections because in some cases, 852 // we wait to destroy the underlying connection to prevent race conditions. This will result 853 // in Connections in the DISCONNECTED state. 854 int currentConnectionCount = supportsHold ? 855 getNumberOfActiveConnections() + 1 : getNumberOfActiveConnections(); 856 int currentCallCount = (getInCallService() == null) ? 0 : getInCallService().getCallCount(); 857 currentCallCount = supportsHold ? currentCallCount + 1 : currentCallCount; 858 // The device only supports a max of two calls active at any one time 859 currentCallCount = Math.min(currentCallCount, 2); 860 861 placeAndVerifyCall(extras, VideoProfile.STATE_AUDIO_ONLY, currentCallCount); 862 // The connectionService.lock is released in 863 // MockConnectionService#onCreateOutgoingConnection, however the connection will not 864 // actually be added to the list of connections in the ConnectionService until shortly 865 // afterwards. So there is still a potential for the lock to be released before it would 866 // be seen by calls to ConnectionService#getAllConnections(). 867 // We will wait here until the list of connections includes one more connection to ensure 868 // that placing the call has fully completed. 869 assertActiveCSConnections(currentConnectionCount); 870 871 assertOutgoingCallBroadcastReceived(true); 872 Connection connection = verifyConnectionForOutgoingCall(TEST_EMERGENCY_URI); 873 TestUtils.waitOnAllHandlers(getInstrumentation()); 874 return connection; 875 } 876 getNumberOfConnections()877 int getNumberOfConnections() { 878 return CtsConnectionService.getAllConnectionsFromTelecom().size(); 879 } 880 getNumberOfActiveConnections()881 int getNumberOfActiveConnections() { 882 return CtsConnectionService.getAllConnectionsFromTelecom().stream() 883 .filter(c -> c.getState() != Connection.STATE_DISCONNECTED).collect( 884 Collectors.toSet()).size(); 885 } 886 getConnection(Uri address)887 Connection getConnection(Uri address) { 888 return CtsConnectionService.getAllConnectionsFromTelecom().stream() 889 .filter(c -> c.getAddress().equals(address)).findFirst().orElse(null); 890 } 891 verifyConnectionForOutgoingCall()892 MockConnection verifyConnectionForOutgoingCall() { 893 // Assuming only 1 connection present 894 return verifyConnectionForOutgoingCall(0); 895 } 896 verifyConnectionForOutgoingCall(int connectionIndex)897 MockConnection verifyConnectionForOutgoingCall(int connectionIndex) { 898 try { 899 if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 900 TimeUnit.MILLISECONDS)) { 901 fail("No outgoing call connection requested by Telecom"); 902 } 903 } catch (InterruptedException e) { 904 Log.i(TAG, "Test interrupted!"); 905 } 906 907 assertThat("Telecom should create outgoing connection for outgoing call", 908 connectionService.outgoingConnections.size(), not(equalTo(0))); 909 MockConnection connection = connectionService.outgoingConnections.get(connectionIndex); 910 return connection; 911 } 912 verifyConnectionForOutgoingCall(Uri address)913 MockConnection verifyConnectionForOutgoingCall(Uri address) { 914 if (!connectionService.waitForEvent( 915 MockConnectionService.EVENT_CONNECTION_SERVICE_CREATE_CONNECTION)) { 916 fail("No outgoing call connection requested by Telecom"); 917 } 918 assertThat("Telecom should create outgoing connection for outgoing call", 919 connectionService.outgoingConnections.size(), not(equalTo(0))); 920 921 // There is a subtle race condition in ConnectionService. When onCreateIncomingConnection 922 // or onCreateOutgoingConnection completes, ConnectionService then adds the connection to 923 // the list of tracked connections. It's very possible for the lock to be released and 924 // the connection to have not yet been added to the connection list yet. 925 waitUntilConditionIsTrueOrTimeout(new Condition() { 926 @Override 927 public Object expected() { 928 return true; 929 } 930 931 @Override 932 public Object actual() { 933 return getConnection(address) != null; 934 } 935 }, 936 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 937 "Expected call from number " + address); 938 Connection connection = getConnection(address); 939 940 if (connection instanceof MockConnection) { 941 if (connectionService.outgoingConnections.contains(connection)) { 942 return (MockConnection) connection; 943 } 944 } 945 return null; 946 } 947 verifyNoConnectionForOutgoingCall()948 void verifyNoConnectionForOutgoingCall() { 949 try { 950 if (!connectionService.lock.tryAcquire(WAIT_FOR_OUTGOING_CONNECTION_TIMEOUT_MS, 951 TimeUnit.MILLISECONDS)) { 952 } 953 } catch (InterruptedException e) { 954 Log.i(TAG, "Test interrupted!"); 955 } 956 957 assertThat("Telecom should not create outgoing connection for outgoing call", 958 connectionService.outgoingConnections.size(), equalTo(0)); 959 return; 960 } 961 verifyConnectionForIncomingCall()962 MockConnection verifyConnectionForIncomingCall() { 963 // Assuming only 1 connection present 964 return verifyConnectionForIncomingCall(0); 965 } 966 verifyConnectionForIncomingCall(int connectionIndex)967 MockConnection verifyConnectionForIncomingCall(int connectionIndex) { 968 try { 969 if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 970 TimeUnit.MILLISECONDS)) { 971 fail("No outgoing call connection requested by Telecom"); 972 } 973 } catch (InterruptedException e) { 974 Log.i(TAG, "Test interrupted!"); 975 } 976 977 assertThat("Telecom should create incoming connections for incoming calls", 978 connectionService.incomingConnections.size(), not(equalTo(0))); 979 MockConnection connection = connectionService.incomingConnections.get(connectionIndex); 980 setAndVerifyConnectionForIncomingCall(connection); 981 return connection; 982 } 983 verifyConference(int permit)984 MockConference verifyConference(int permit) { 985 try { 986 if (!connectionService.lock.tryAcquire(permit, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 987 TimeUnit.MILLISECONDS)) { 988 fail("No conference requested by Telecom"); 989 } 990 } catch (InterruptedException e) { 991 Log.i(TAG, "Test interrupted!"); 992 } 993 return connectionService.conferences.get(0); 994 } 995 setAndVerifyConnectionForIncomingCall(MockConnection connection)996 void setAndVerifyConnectionForIncomingCall(MockConnection connection) { 997 if (connection.getState() == Connection.STATE_ACTIVE) { 998 // If the connection is already active (like if it got picked up immediately), don't 999 // bother with setting it back to ringing. 1000 return; 1001 } 1002 connection.setRinging(); 1003 assertConnectionState(connection, Connection.STATE_RINGING); 1004 } 1005 setAndVerifyConferenceablesForOutgoingConnection(int connectionIndex)1006 void setAndVerifyConferenceablesForOutgoingConnection(int connectionIndex) { 1007 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 1008 // Make all other outgoing connections as conferenceable with this connection. 1009 MockConnection connection = connectionService.outgoingConnections.get(connectionIndex); 1010 List<Connection> confConnections = 1011 new ArrayList<>(connectionService.outgoingConnections.size()); 1012 for (Connection c : connectionService.outgoingConnections) { 1013 if (c != connection) { 1014 confConnections.add(c); 1015 } 1016 } 1017 connection.setConferenceableConnections(confConnections); 1018 assertEquals(connection.getConferenceables(), confConnections); 1019 } 1020 addConferenceCall(Call call1, Call call2)1021 void addConferenceCall(Call call1, Call call2) { 1022 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 1023 int currentConfCallCount = 0; 1024 if (mInCallCallbacks.getService() != null) { 1025 currentConfCallCount = mInCallCallbacks.getService().getConferenceCallCount(); 1026 } 1027 // Verify that the calls have each other on their conferenceable list before proceeding 1028 List<Call> callConfList = new ArrayList<>(); 1029 callConfList.add(call2); 1030 assertCallConferenceableList(call1, callConfList); 1031 1032 callConfList.clear(); 1033 callConfList.add(call1); 1034 assertCallConferenceableList(call2, callConfList); 1035 1036 call1.conference(call2); 1037 1038 /** 1039 * We should have 1 onCallAdded, 2 onChildrenChanged and 2 onParentChanged invoked, so 1040 * we should have 5 available permits on the incallService lock. 1041 */ 1042 try { 1043 if (!mInCallCallbacks.lock.tryAcquire(5, 3, TimeUnit.SECONDS)) { 1044 fail("Conference addition failed."); 1045 } 1046 } catch (InterruptedException e) { 1047 Log.i(TAG, "Test interrupted!"); 1048 } 1049 1050 assertEquals("InCallService should contain 1 more call after adding a conf call.", 1051 currentConfCallCount + 1, 1052 mInCallCallbacks.getService().getConferenceCallCount()); 1053 } 1054 splitFromConferenceCall(Call call1)1055 void splitFromConferenceCall(Call call1) { 1056 assertEquals("Lock should have no permits!", 0, mInCallCallbacks.lock.availablePermits()); 1057 1058 call1.splitFromConference(); 1059 /** 1060 * We should have 1 onChildrenChanged and 1 onParentChanged invoked, so 1061 * we should have 2 available permits on the incallService lock. 1062 */ 1063 try { 1064 if (!mInCallCallbacks.lock.tryAcquire(2, 3, TimeUnit.SECONDS)) { 1065 fail("Conference split failed"); 1066 } 1067 } catch (InterruptedException e) { 1068 Log.i(TAG, "Test interrupted!"); 1069 } 1070 } 1071 verifyConferenceForOutgoingCall()1072 MockConference verifyConferenceForOutgoingCall() { 1073 try { 1074 if (!connectionService.lock.tryAcquire(TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1075 TimeUnit.MILLISECONDS)) { 1076 fail("No outgoing conference requested by Telecom"); 1077 } 1078 } catch (InterruptedException e) { 1079 Log.i(TAG, "Test interrupted!"); 1080 } 1081 // Return the newly created conference object to the caller 1082 MockConference conference = connectionService.conferences.get(0); 1083 setAndVerifyConferenceForOutgoingCall(conference); 1084 return conference; 1085 } 1086 verifyAdhocConferenceCall()1087 Pair<Conference, ConnectionRequest> verifyAdhocConferenceCall() { 1088 try { 1089 if (!connectionService.lock.tryAcquire(2, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1090 TimeUnit.MILLISECONDS)) { 1091 fail("No conference requested by Telecom"); 1092 } 1093 } catch (InterruptedException e) { 1094 Log.i(TAG, "Test interrupted!"); 1095 } 1096 return new Pair<>(connectionService.conferences.get(0), 1097 connectionService.connectionRequest); 1098 } 1099 setAndVerifyConferenceForOutgoingCall(MockConference conference)1100 void setAndVerifyConferenceForOutgoingCall(MockConference conference) { 1101 conference.setActive(); 1102 assertConferenceState(conference, Connection.STATE_ACTIVE); 1103 } 1104 verifyCallStateListener(int expectedCallState)1105 void verifyCallStateListener(int expectedCallState) throws InterruptedException { 1106 mTestCallStateListener.getCountDownLatch().await( 1107 TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S, TimeUnit.SECONDS); 1108 assertEquals(expectedCallState, mTestCallStateListener.getLastState()); 1109 } 1110 verifyPhoneStateListenerCallbacksForCall(int expectedCallState, String expectedNumber)1111 void verifyPhoneStateListenerCallbacksForCall(int expectedCallState, String expectedNumber) 1112 throws Exception { 1113 assertTrue(mTelephonyCallback.mCallbackSemaphore.tryAcquire( 1114 TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S, TimeUnit.SECONDS)); 1115 // At this point we can only be sure that we got AN update, but not necessarily the one we 1116 // are looking for; wait until we see the state we want before verifying further. 1117 waitUntilConditionIsTrueOrTimeout(new Condition() { 1118 @Override 1119 public Object expected() { 1120 return true; 1121 } 1122 1123 @Override 1124 public Object actual() { 1125 return mTelephonyCallback.mCallStates 1126 .stream() 1127 .filter(p -> p == expectedCallState) 1128 .count() > 0; 1129 } 1130 }, 1131 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1132 "Expected call state " + expectedCallState + " and number " 1133 + expectedNumber); 1134 1135 1136 // Get the most recent callback; it is possible that there was an initial state reported due 1137 // to the fact that TelephonyManager will sometimes give an initial state back to the caller 1138 // when the listener is registered. 1139 int callState = mTelephonyCallback.mCallStates.get( 1140 mTelephonyCallback.mCallStates.size() - 1); 1141 assertEquals(expectedCallState, callState); 1142 // Note: We do NOT check the phone number here. Due to changes in how the phone state 1143 // broadcast is sent, the caller may receive multiple broadcasts, and the number will be 1144 // present in one or the other. We waited for a full matching broadcast above so we can 1145 // be sure the number was reported as expected. 1146 } 1147 verifyPhoneStateListenerCallbacksForEmergencyCall(String expectedNumber)1148 void verifyPhoneStateListenerCallbacksForEmergencyCall(String expectedNumber) 1149 throws Exception { 1150 assertTrue(mTelephonyCallback.mCallbackSemaphore.tryAcquire( 1151 TestUtils.WAIT_FOR_PHONE_STATE_LISTENER_CALLBACK_TIMEOUT_S, TimeUnit.SECONDS)); 1152 // At this point we can only be sure that we got AN update, but not necessarily the one we 1153 // are looking for; wait until we see the state we want before verifying further. 1154 waitUntilConditionIsTrueOrTimeout(new Condition() { 1155 @Override 1156 public Object expected() { 1157 return true; 1158 } 1159 1160 @Override 1161 public Object actual() { 1162 return mTelephonyCallback 1163 .mLastOutgoingEmergencyNumber != null 1164 && mTelephonyCallback 1165 .mLastOutgoingEmergencyNumber.getNumber() 1166 .equals(expectedNumber); 1167 } 1168 }, 1169 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1170 "Expected emergency number: " + expectedNumber); 1171 1172 assertEquals(mTelephonyCallback.mLastOutgoingEmergencyNumber.getNumber(), 1173 expectedNumber); 1174 } 1175 1176 /** 1177 * Disconnect the created test call and verify that Telecom has cleared all calls. 1178 */ cleanupCalls()1179 void cleanupCalls() { 1180 if (mInCallCallbacks != null && mInCallCallbacks.getService() != null) { 1181 mInCallCallbacks.getService().disconnectAllConferenceCalls(); 1182 mInCallCallbacks.getService().disconnectAllCalls(); 1183 assertNumConferenceCalls(mInCallCallbacks.getService(), 0); 1184 assertNumCalls(mInCallCallbacks.getService(), 0); 1185 } 1186 } 1187 1188 /** 1189 * Place a new outgoing call via the {@link CtsConnectionService} 1190 */ placeNewCallWithPhoneAccount(Bundle extras, int videoState)1191 private void placeNewCallWithPhoneAccount(Bundle extras, int videoState) { 1192 if (extras == null) { 1193 extras = new Bundle(); 1194 } 1195 if (!extras.containsKey(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE)) { 1196 extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, 1197 TestUtils.TEST_PHONE_ACCOUNT_HANDLE); 1198 } 1199 1200 if (!VideoProfile.isAudioOnly(videoState)) { 1201 extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); 1202 } 1203 Uri number; 1204 if (extras.containsKey(TestUtils.EXTRA_PHONE_NUMBER)) { 1205 number = extras.getParcelable(TestUtils.EXTRA_PHONE_NUMBER); 1206 } else { 1207 number = createTestNumber(); 1208 } 1209 mTelecomManager.placeCall(number, extras); 1210 } 1211 1212 /** 1213 * Create a new number each time for a new test. Telecom has special logic to reuse certain 1214 * calls if multiple calls to the same number are placed within a short period of time which 1215 * can cause certain tests to fail. 1216 */ createTestNumber()1217 Uri createTestNumber() { 1218 return Uri.fromParts("tel", String.valueOf(++sCounter), null); 1219 } 1220 1221 /** 1222 * Creates a new random phone number in the range: 1223 * 000-000-0000 1224 * to 1225 * 999-999-9999 1226 * @return Randomized phone number. 1227 */ createRandomTestNumber()1228 Uri createRandomTestNumber() { 1229 return Uri.fromParts("tel", String.format("16%05d", new Random().nextInt(99999)) 1230 + String.format("%04d", new Random().nextInt(9999)), null); 1231 } 1232 getTestNumber()1233 public static Uri getTestNumber() { 1234 return Uri.fromParts("tel", String.valueOf(sCounter), null); 1235 } 1236 isLoggedCall(PhoneAccountHandle handle)1237 public boolean isLoggedCall(PhoneAccountHandle handle) { 1238 PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(handle); 1239 Bundle extras = phoneAccount.getExtras(); 1240 if (extras == null) { 1241 extras = new Bundle(); 1242 } 1243 boolean isSelfManaged = (phoneAccount.getCapabilities() 1244 & PhoneAccount.CAPABILITY_SELF_MANAGED) == PhoneAccount.CAPABILITY_SELF_MANAGED; 1245 // Calls are logged if: 1246 // 1. They're not self-managed 1247 // 2. They're self-managed and are configured to request logging. 1248 return (!isSelfManaged 1249 || (isSelfManaged 1250 && extras.getBoolean(PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS) 1251 && (phoneAccount.getSupportedUriSchemes().contains(PhoneAccount.SCHEME_TEL) 1252 || phoneAccount.getSupportedUriSchemes().contains(PhoneAccount.SCHEME_SIP)))); 1253 } 1254 getCallLogEntryLatch()1255 public CountDownLatch getCallLogEntryLatch() { 1256 CountDownLatch changeLatch = new CountDownLatch(1); 1257 mContext.getContentResolver().registerContentObserver( 1258 CallLog.Calls.CONTENT_URI, true, 1259 new ContentObserver(mHandler) { 1260 @Override 1261 public void onChange(boolean selfChange, Uri uri) { 1262 mContext.getContentResolver().unregisterContentObserver(this); 1263 changeLatch.countDown(); 1264 super.onChange(selfChange); 1265 } 1266 }); 1267 return changeLatch; 1268 } 1269 verifyCallLogging( Uri testNumber, int expectedLogType, PhoneAccountHandle handle)1270 public void verifyCallLogging( 1271 Uri testNumber, int expectedLogType, PhoneAccountHandle handle) { 1272 CountDownLatch logLatch = getCallLogEntryLatch(); 1273 Cursor logCursor = getLatestCallLogCursorIfMatchesUri(logLatch, true /*isCallLogged*/, 1274 testNumber); 1275 assertNotNull("Call log entry not found for test number", logCursor); 1276 1277 int typeIndex = logCursor.getColumnIndex(CallLog.Calls.TYPE); 1278 int type = logCursor.getInt(typeIndex); 1279 assertEquals("recorded type does not match expected", expectedLogType, type); 1280 1281 int phoneAccountIdIndex = logCursor.getColumnIndex(CallLog.Calls.PHONE_ACCOUNT_ID); 1282 String phoneAccountId = logCursor.getString(phoneAccountIdIndex); 1283 assertEquals("recorded account ID does not match expected", 1284 handle.getId(), phoneAccountId); 1285 1286 int phoneAccountComponentNameIndex = logCursor.getColumnIndex( 1287 CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME); 1288 String phoneAccountComponentName = logCursor.getString(phoneAccountComponentNameIndex); 1289 assertEquals("recorded account component name does not match expected", 1290 handle.getComponentName().flattenToString(), phoneAccountComponentName); 1291 } 1292 getLatestCallLogCursorIfMatchesUri(CountDownLatch latch, boolean newLogExpected, Uri testNumber)1293 public Cursor getLatestCallLogCursorIfMatchesUri(CountDownLatch latch, boolean newLogExpected, 1294 Uri testNumber) { 1295 if (newLogExpected) { 1296 // Wait for the content observer to report that we have gotten a new call log entry. 1297 try { 1298 latch.await(WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, TimeUnit.MILLISECONDS); 1299 } catch (InterruptedException ie) { 1300 fail("Expected log latch"); 1301 } 1302 } 1303 1304 // Query the latest entry into the call log. 1305 Cursor callsCursor = mContext.getContentResolver().query(CallLog.Calls.CONTENT_URI, null, 1306 null, null, CallLog.Calls._ID + " DESC limit 1;"); 1307 int numberIndex = callsCursor.getColumnIndex(CallLog.Calls.NUMBER); 1308 if (callsCursor.moveToNext()) { 1309 String number = callsCursor.getString(numberIndex); 1310 if (testNumber.getSchemeSpecificPart().equals(number)) { 1311 return callsCursor; 1312 } else { 1313 // Last call log entry doesnt match expected number. 1314 return null; 1315 } 1316 } 1317 // No Calls 1318 return null; 1319 } 1320 1321 /** 1322 * @return A test Parcelable with some basic values as well as a Binder that just responds with 1323 * the same thing that was sent to it. 1324 */ createTestParcelable()1325 TestParcelable createTestParcelable() { 1326 return new TestParcelable(42, "a test string", 1327 new ITestInterface.Stub() { 1328 @Override 1329 public String testLoopback(String testString) { 1330 return testString; 1331 } 1332 }); 1333 } 1334 1335 /** 1336 * Send a relatively complex Bundle that will go through Telecom as a call or connection event. 1337 * @param parcelable TestParcelable created with {@link #createTestParcelable()} 1338 */ 1339 Bundle createTestBundle(TestParcelable parcelable) { 1340 Bundle bundle = new Bundle(); 1341 bundle.putInt(TestParcelable.VAL_1_KEY, parcelable.mVal1); 1342 bundle.putString(TestParcelable.VAL_2_KEY, parcelable.mVal2); 1343 bundle.putBinder(TestParcelable.VAL_3_KEY, parcelable.mVal3); 1344 parcelable.copyIntoBundle(bundle); 1345 return bundle; 1346 } 1347 1348 /** 1349 * Verify the bundle created in {@link #createTestBundle} is correct. 1350 * @param receivedBundle The Bundle that was received 1351 * @param originalParcelable The original Parcelable created as part of 1352 * {@link #createTestBundle} 1353 */ 1354 void verifyTestBundle(Bundle receivedBundle, TestParcelable originalParcelable) { 1355 assertNotNull(receivedBundle); 1356 // We have to set the classloader here to the classloader of the test app or we will not 1357 // be able to unparcel. 1358 receivedBundle.setClassLoader(mContext.getClassLoader()); 1359 assertEquals(originalParcelable.mVal1, receivedBundle.getInt(TestParcelable.VAL_1_KEY)); 1360 assertEquals(originalParcelable.mVal2, receivedBundle.getString(TestParcelable.VAL_2_KEY)); 1361 IBinder testBinder = receivedBundle.getBinder(TestParcelable.VAL_3_KEY); 1362 assertNotNull(testBinder); 1363 assertEquals(originalParcelable.mVal3, testBinder); 1364 TestParcelable resultParcelable = null; 1365 try { 1366 resultParcelable = TestParcelable.getFromBundle(receivedBundle); 1367 } catch (Exception e) { 1368 fail("could not retrieve parcelable: " + e); 1369 } 1370 assertNotNull(resultParcelable); 1371 assertEquals(originalParcelable, resultParcelable); 1372 // Test Binder references that were received work properly 1373 try { 1374 ITestInterface testInterface = ITestInterface.Stub.asInterface(testBinder); 1375 assertEquals("testString", testInterface.testLoopback("testString")); 1376 testInterface = ITestInterface.Stub.asInterface(resultParcelable.mVal3); 1377 assertEquals("testString", testInterface.testLoopback("testString")); 1378 } catch (RemoteException e) { 1379 // this should not happen since it is accessing the local process 1380 fail("could not test IBinder due to Exception: " + e); 1381 } 1382 } 1383 1384 void assertNumCalls(final MockInCallService inCallService, final int numCalls) { 1385 waitUntilConditionIsTrueOrTimeout(new Condition() { 1386 @Override 1387 public Object expected() { 1388 return numCalls; 1389 } 1390 @Override 1391 public Object actual() { 1392 return inCallService.getCallCount(); 1393 } 1394 }, 1395 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1396 "InCallService should contain " + numCalls + " calls." 1397 ); 1398 } 1399 1400 void assertNumCalls_OrICSUnbound(final MockInCallService inCallService, final int numCalls) { 1401 waitUntilConditionIsTrueOrTimeout(new Condition() { 1402 @Override 1403 public Object expected() { 1404 return true; 1405 } 1406 1407 @Override 1408 public Object actual() { 1409 return inCallService == null || numCalls == inCallService.getCallCount(); 1410 } 1411 }, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, "InCallService should contain " + numCalls 1412 + " calls or the ICS should be unbound (meaning the call is destroyed)." 1413 ); 1414 } 1415 1416 void assertNumConferenceCalls(final MockInCallService inCallService, final int numCalls) { 1417 waitUntilConditionIsTrueOrTimeout(new Condition() { 1418 @Override 1419 public Object expected() { 1420 return numCalls; 1421 } 1422 @Override 1423 public Object actual() { 1424 return inCallService.getConferenceCallCount(); 1425 } 1426 }, 1427 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1428 "InCallService should contain " + numCalls + " conference calls." 1429 ); 1430 } 1431 1432 void assertActiveCSConnections(final int numConnections) { 1433 waitUntilConditionIsTrueOrTimeout(new Condition() { 1434 @Override 1435 public Object expected() { 1436 return numConnections; 1437 } 1438 1439 @Override 1440 public Object actual() { 1441 return getNumberOfActiveConnections(); 1442 } 1443 }, 1444 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1445 "ConnectionService should contain " + numConnections + " connections." 1446 ); 1447 } 1448 1449 void assertCSConnections(final int numConnections) { 1450 waitUntilConditionIsTrueOrTimeout(new Condition() { 1451 @Override 1452 public Object expected() { 1453 return numConnections; 1454 } 1455 1456 @Override 1457 public Object actual() { 1458 return CtsConnectionService 1459 .getAllConnectionsFromTelecom() 1460 .size(); 1461 } 1462 }, 1463 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1464 "ConnectionService should contain " + numConnections + " connections." 1465 ); 1466 } 1467 1468 void assertNumConnections(final MockConnectionService connService, final int numConnections) { 1469 waitUntilConditionIsTrueOrTimeout(new Condition() { 1470 @Override 1471 public Object expected() { 1472 return numConnections; 1473 } 1474 @Override 1475 public Object actual() { 1476 return connService.getAllConnections().size(); 1477 } 1478 }, 1479 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1480 "ConnectionService should contain " + numConnections + " connections." 1481 ); 1482 } 1483 1484 void assertMuteState(final InCallService incallService, final boolean isMuted) { 1485 waitUntilConditionIsTrueOrTimeout( 1486 new Condition() { 1487 @Override 1488 public Object expected() { 1489 return isMuted; 1490 } 1491 1492 @Override 1493 public Object actual() { 1494 final CallAudioState state = incallService.getCallAudioState(); 1495 return state == null ? null : state.isMuted(); 1496 } 1497 }, 1498 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1499 "Phone's mute state should be: " + isMuted 1500 ); 1501 } 1502 1503 void assertMuteState(final Connection connection, final boolean isMuted) { 1504 waitUntilConditionIsTrueOrTimeout( 1505 new Condition() { 1506 @Override 1507 public Object expected() { 1508 return isMuted; 1509 } 1510 1511 @Override 1512 public Object actual() { 1513 final CallAudioState state = connection.getCallAudioState(); 1514 return state == null ? null : state.isMuted(); 1515 } 1516 }, 1517 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1518 "Connection's mute state should be: " + isMuted 1519 ); 1520 } 1521 1522 /** 1523 * Asserts that a call video state is as expected. 1524 * 1525 * @param call The call. 1526 * @param videoState The expected video state. 1527 */ 1528 void assertVideoState(final Call call, final int videoState) { 1529 waitUntilConditionIsTrueOrTimeout( 1530 new Condition() { 1531 @Override 1532 public Object expected() { 1533 return videoState; 1534 } 1535 1536 @Override 1537 public Object actual() { 1538 return call.getDetails().getVideoState(); 1539 } 1540 }, 1541 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1542 "Call should be in videoState " + videoState 1543 ); 1544 } 1545 1546 void assertAudioRoute(final InCallService incallService, final int route) { 1547 waitUntilConditionIsTrueOrTimeout( 1548 new Condition() { 1549 @Override 1550 public Object expected() { 1551 return route; 1552 } 1553 1554 @Override 1555 public Object actual() { 1556 final CallAudioState state = incallService.getCallAudioState(); 1557 return state == null ? null : state.getRoute(); 1558 } 1559 }, 1560 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1561 "Phone's audio route should be: " + route 1562 ); 1563 } 1564 1565 void assertNotAudioRoute(final InCallService incallService, final int route) { 1566 waitUntilConditionIsTrueOrTimeout( 1567 new Condition() { 1568 @Override 1569 public Object expected() { 1570 return new Boolean(true); 1571 } 1572 1573 @Override 1574 public Object actual() { 1575 final CallAudioState state = incallService.getCallAudioState(); 1576 return route != state.getRoute(); 1577 } 1578 }, 1579 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1580 "Phone's audio route should not be: " + route 1581 ); 1582 } 1583 1584 void assertAudioRoute(final MockConnection connection, final int route) { 1585 waitUntilConditionIsTrueOrTimeout( 1586 new Condition() { 1587 @Override 1588 public Object expected() { 1589 return route; 1590 } 1591 1592 @Override 1593 public Object actual() { 1594 final CallAudioState state = ((Connection) connection).getCallAudioState(); 1595 return state == null ? null : state.getRoute(); 1596 } 1597 }, 1598 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1599 "Connection's audio route should be: " + route 1600 ); 1601 } 1602 1603 void assertConnectionState(final Connection connection, final int state) { 1604 waitUntilConditionIsTrueOrTimeout( 1605 new Condition() { 1606 @Override 1607 public Object expected() { 1608 return state; 1609 } 1610 1611 @Override 1612 public Object actual() { 1613 return connection.getState(); 1614 } 1615 }, 1616 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1617 "Connection should be in state " + state 1618 ); 1619 } 1620 1621 void assertCallState(final Call call, final int state) { 1622 waitUntilConditionIsTrueOrTimeout( 1623 new Condition() { 1624 @Override 1625 public Object expected() { 1626 return true; 1627 } 1628 1629 @Override 1630 public Object actual() { 1631 return call.getState() == state && call.getDetails().getState() == state; 1632 } 1633 }, 1634 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1635 "Expected state: " + state + ", callState=" + call.getState() + ", detailState=" 1636 + call.getDetails().getState() 1637 ); 1638 } 1639 1640 void assertCallConferenceableList(final Call call, final List<Call> conferenceableList) { 1641 waitUntilConditionIsTrueOrTimeout( 1642 new Condition() { 1643 @Override 1644 public Object expected() { 1645 return conferenceableList; 1646 } 1647 1648 @Override 1649 public Object actual() { 1650 return call.getConferenceableCalls(); 1651 } 1652 }, 1653 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1654 "Call: " + call + " does not have the correct conferenceable call list." 1655 ); 1656 } 1657 1658 void assertDtmfString(final MockConnection connection, final String dtmfString) { 1659 waitUntilConditionIsTrueOrTimeout(new Condition() { 1660 @Override 1661 public Object expected() { 1662 return dtmfString; 1663 } 1664 1665 @Override 1666 public Object actual() { 1667 return connection.getDtmfString(); 1668 } 1669 }, 1670 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1671 "DTMF string should be equivalent to entered DTMF characters: " + dtmfString 1672 ); 1673 } 1674 1675 void assertDtmfString(final MockConference conference, final String dtmfString) { 1676 waitUntilConditionIsTrueOrTimeout(new Condition() { 1677 @Override 1678 public Object expected() { 1679 return dtmfString; 1680 } 1681 1682 @Override 1683 public Object actual() { 1684 return conference.getDtmfString(); 1685 } 1686 }, 1687 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1688 "DTMF string should be equivalent to entered DTMF characters: " + dtmfString 1689 ); 1690 } 1691 1692 void assertCallDisplayName(final Call call, final String name) { 1693 waitUntilConditionIsTrueOrTimeout( 1694 new Condition() { 1695 @Override 1696 public Object expected() { 1697 return name; 1698 } 1699 1700 @Override 1701 public Object actual() { 1702 return call.getDetails().getCallerDisplayName(); 1703 } 1704 }, 1705 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1706 "Call should have display name: " + name 1707 ); 1708 } 1709 1710 void assertCallHandle(final Call call, final Uri expectedHandle) { 1711 waitUntilConditionIsTrueOrTimeout( 1712 new Condition() { 1713 @Override 1714 public Object expected() { 1715 return expectedHandle; 1716 } 1717 1718 @Override 1719 public Object actual() { 1720 return call.getDetails().getHandle(); 1721 } 1722 }, 1723 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1724 "Call should have handle name: " + expectedHandle 1725 ); 1726 } 1727 1728 void assertCallConnectTimeChanged(final Call call, final long time) { 1729 waitUntilConditionIsTrueOrTimeout( 1730 new Condition() { 1731 @Override 1732 public Object expected() { 1733 return true; 1734 } 1735 1736 @Override 1737 public Object actual() { 1738 return call.getDetails().getConnectTimeMillis() != time; 1739 } 1740 }, 1741 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1742 "Call have connect time: " + time 1743 ); 1744 } 1745 1746 void assertConnectionCallDisplayName(final Connection connection, final String name) { 1747 waitUntilConditionIsTrueOrTimeout( 1748 new Condition() { 1749 @Override 1750 public Object expected() { 1751 return name; 1752 } 1753 1754 @Override 1755 public Object actual() { 1756 return connection.getCallerDisplayName(); 1757 } 1758 }, 1759 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1760 "Connection should have display name: " + name 1761 ); 1762 } 1763 1764 void assertDisconnectReason(final Connection connection, final String disconnectReason) { 1765 waitUntilConditionIsTrueOrTimeout( 1766 new Condition() { 1767 @Override 1768 public Object expected() { 1769 return disconnectReason; 1770 } 1771 1772 @Override 1773 public Object actual() { 1774 return connection.getDisconnectCause().getReason(); 1775 } 1776 }, 1777 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1778 "Connection should have been disconnected with reason: " + disconnectReason 1779 ); 1780 } 1781 1782 void assertConferenceState(final Conference conference, final int state) { 1783 waitUntilConditionIsTrueOrTimeout( 1784 new Condition() { 1785 @Override 1786 public Object expected() { 1787 return state; 1788 } 1789 1790 @Override 1791 public Object actual() { 1792 return conference.getState(); 1793 } 1794 }, 1795 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1796 "Conference should be in state " + state 1797 ); 1798 } 1799 1800 1801 void assertOutgoingCallBroadcastReceived(boolean received) { 1802 waitUntilConditionIsTrueOrTimeout( 1803 new Condition() { 1804 @Override 1805 public Object expected() { 1806 return received; 1807 } 1808 1809 @Override 1810 public Object actual() { 1811 return NewOutgoingCallBroadcastReceiver 1812 .isNewOutgoingCallBroadcastReceived(); 1813 } 1814 }, 1815 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1816 received ? "Outgoing Call Broadcast should be received" 1817 : "Outgoing Call Broadcast should not be received" 1818 ); 1819 } 1820 1821 void assertCallDetailsConstructed(Call mCall, boolean constructed) { 1822 waitUntilConditionIsTrueOrTimeout( 1823 new Condition() { 1824 @Override 1825 public Object expected() { 1826 return constructed; 1827 } 1828 1829 @Override 1830 public Object actual() { 1831 return mCall != null && mCall.getDetails() != null; 1832 } 1833 }, 1834 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1835 constructed ? "Call Details should be constructed" 1836 : "Call Details should not be constructed" 1837 ); 1838 } 1839 1840 void assertCallGatewayConstructed(Call mCall, boolean constructed) { 1841 waitUntilConditionIsTrueOrTimeout( 1842 new Condition() { 1843 @Override 1844 public Object expected() { 1845 return constructed; 1846 } 1847 1848 @Override 1849 public Object actual() { 1850 return mCall != null && mCall.getDetails() != null 1851 && mCall.getDetails().getGatewayInfo() != null; 1852 } 1853 }, 1854 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1855 constructed ? "Call Gateway should be constructed" 1856 : "Call Gateway should not be constructed" 1857 ); 1858 } 1859 1860 void assertCallNotNull(Call mCall, boolean notNull) { 1861 waitUntilConditionIsTrueOrTimeout( 1862 new Condition() { 1863 @Override 1864 public Object expected() { 1865 return notNull; 1866 } 1867 1868 @Override 1869 public Object actual() { 1870 return mCall != null; 1871 } 1872 }, 1873 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1874 notNull ? "Call should not be null" : "Call should be null" 1875 ); 1876 } 1877 1878 /** 1879 * Checks all fields of two PhoneAccounts for equality, with the exception of the enabled state. 1880 * Should only be called after assertPhoneAccountRegistered when it can be guaranteed 1881 * that the PhoneAccount is registered. 1882 * @param expected The expected PhoneAccount. 1883 * @param actual The actual PhoneAccount. 1884 */ 1885 void assertPhoneAccountEquals(final PhoneAccount expected, 1886 final PhoneAccount actual) { 1887 assertEquals(expected.getAddress(), actual.getAddress()); 1888 assertEquals(expected.getAccountHandle(), actual.getAccountHandle()); 1889 assertEquals(expected.getCapabilities(), actual.getCapabilities()); 1890 assertTrue(areBundlesEqual(expected.getExtras(), actual.getExtras())); 1891 assertEquals(expected.getHighlightColor(), actual.getHighlightColor()); 1892 assertEquals(expected.getIcon(), actual.getIcon()); 1893 assertEquals(expected.getLabel(), actual.getLabel()); 1894 assertEquals(expected.getShortDescription(), actual.getShortDescription()); 1895 assertEquals(expected.getSubscriptionAddress(), actual.getSubscriptionAddress()); 1896 assertEquals(expected.getSupportedUriSchemes(), actual.getSupportedUriSchemes()); 1897 } 1898 1899 void assertPhoneAccountRegistered(final PhoneAccountHandle handle) { 1900 waitUntilConditionIsTrueOrTimeout( 1901 new Condition() { 1902 @Override 1903 public Object expected() { 1904 return true; 1905 } 1906 1907 @Override 1908 public Object actual() { 1909 return mTelecomManager.getPhoneAccount(handle) != null; 1910 } 1911 }, 1912 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1913 "Phone account registration failed for " + handle 1914 ); 1915 } 1916 1917 void assertPhoneAccountEnabled(final PhoneAccountHandle handle) { 1918 waitUntilConditionIsTrueOrTimeout( 1919 new Condition() { 1920 @Override 1921 public Object expected() { 1922 return true; 1923 } 1924 1925 @Override 1926 public Object actual() { 1927 PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(handle); 1928 return (phoneAccount != null && phoneAccount.isEnabled()); 1929 } 1930 }, 1931 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1932 "Phone account enable failed for " + handle 1933 ); 1934 } 1935 1936 void assertPhoneAccountIsDefault(final PhoneAccountHandle handle) { 1937 waitUntilConditionIsTrueOrTimeout( 1938 new Condition() { 1939 @Override 1940 public Object expected() { 1941 return true; 1942 } 1943 1944 @Override 1945 public Object actual() { 1946 PhoneAccountHandle phoneAccountHandle = 1947 mTelecomManager.getUserSelectedOutgoingPhoneAccount(); 1948 return (phoneAccountHandle != null && phoneAccountHandle.equals(handle)); 1949 } 1950 }, 1951 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1952 "Failed to set default phone account to " + handle 1953 ); 1954 } 1955 1956 void assertCtsConnectionServiceUnbound() { 1957 if (CtsConnectionService.isBound()) { 1958 assertTrue("CtsConnectionService not yet unbound!", 1959 CtsConnectionService.waitForUnBinding()); 1960 } 1961 } 1962 1963 void assertMockInCallServiceUnbound() { 1964 waitUntilConditionIsTrueOrTimeout( 1965 new Condition() { 1966 @Override 1967 public Object expected() { 1968 return false; 1969 } 1970 1971 @Override 1972 public Object actual() { 1973 return MockInCallService.isServiceBound(); 1974 } 1975 }, 1976 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1977 "MockInCallService not yet unbound!" 1978 ); 1979 } 1980 1981 void assertIsOutgoingCallPermitted(boolean isPermitted, PhoneAccountHandle handle) { 1982 waitUntilConditionIsTrueOrTimeout( 1983 new Condition() { 1984 @Override 1985 public Object expected() { 1986 return isPermitted; 1987 } 1988 1989 @Override 1990 public Object actual() { 1991 return mTelecomManager.isOutgoingCallPermitted(handle); 1992 } 1993 }, 1994 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 1995 "Expected isOutgoingCallPermitted to be " + isPermitted 1996 ); 1997 } 1998 1999 void assertIsIncomingCallPermitted(boolean isPermitted, PhoneAccountHandle handle) { 2000 waitUntilConditionIsTrueOrTimeout( 2001 new Condition() { 2002 @Override 2003 public Object expected() { 2004 return isPermitted; 2005 } 2006 2007 @Override 2008 public Object actual() { 2009 return mTelecomManager.isIncomingCallPermitted(handle); 2010 } 2011 }, 2012 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2013 "Expected isIncomingCallPermitted to be " + isPermitted 2014 ); 2015 } 2016 2017 void assertIsInCall(boolean isIncall) { 2018 waitUntilConditionIsTrueOrTimeout( 2019 new Condition() { 2020 @Override 2021 public Object expected() { 2022 return isIncall; 2023 } 2024 2025 @Override 2026 public Object actual() { 2027 return mTelecomManager.isInCall(); 2028 } 2029 }, 2030 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2031 "Expected isInCall to be " + isIncall 2032 ); 2033 } 2034 2035 void assertIsInManagedCall(boolean isIncall) { 2036 waitUntilConditionIsTrueOrTimeout( 2037 new Condition() { 2038 @Override 2039 public Object expected() { 2040 return isIncall; 2041 } 2042 2043 @Override 2044 public Object actual() { 2045 return mTelecomManager.isInManagedCall(); 2046 } 2047 }, 2048 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2049 "Expected isInManagedCall to be " + isIncall 2050 ); 2051 } 2052 2053 /** 2054 * Asserts that a call's properties are as expected. 2055 * 2056 * @param call The call. 2057 * @param properties The expected properties. 2058 */ 2059 public void assertCallProperties(final Call call, final int properties) { 2060 waitUntilConditionIsTrueOrTimeout( 2061 new Condition() { 2062 @Override 2063 public Object expected() { 2064 return true; 2065 } 2066 2067 @Override 2068 public Object actual() { 2069 return call.getDetails().hasProperty(properties); 2070 } 2071 }, 2072 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2073 "Call should have properties " + properties 2074 ); 2075 } 2076 2077 /** 2078 * Asserts that a call does not have any of the specified call capability bits specified. 2079 * 2080 * @param call The call. 2081 * @param capabilities The capability or capabilities which are not expected. 2082 */ 2083 public void assertDoesNotHaveCallCapabilities(final Call call, final int capabilities) { 2084 waitUntilConditionIsTrueOrTimeout( 2085 new Condition() { 2086 @Override 2087 public Object expected() { 2088 return true; 2089 } 2090 2091 @Override 2092 public Object actual() { 2093 int callCapabilities = call.getDetails().getCallCapabilities(); 2094 return !Call.Details.hasProperty(callCapabilities, capabilities); 2095 } 2096 }, 2097 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2098 "Call should not have capabilities " + capabilities 2099 ); 2100 } 2101 2102 /** 2103 * Asserts that a call does not have any of the specified call property bits specified. 2104 * 2105 * @param call The call. 2106 * @param properties The property or properties which are not expected. 2107 */ 2108 public void assertDoesNotHaveCallProperties(final Call call, final int properties) { 2109 waitUntilConditionIsTrueOrTimeout( 2110 new Condition() { 2111 @Override 2112 public Object expected() { 2113 return true; 2114 } 2115 2116 @Override 2117 public Object actual() { 2118 return !call.getDetails().hasProperty(properties); 2119 } 2120 }, 2121 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2122 "Call should not have properties " + properties 2123 ); 2124 } 2125 2126 /** 2127 * Asserts that the audio manager reports the specified audio mode. 2128 * 2129 * @param audioManager The audio manager to check. 2130 * @param expectedMode The expected audio mode. 2131 */ 2132 public void assertAudioMode(final AudioManager audioManager, final int expectedMode) { 2133 waitUntilConditionIsTrueOrTimeout( 2134 new Condition() { 2135 @Override 2136 public Object expected() { 2137 return true; 2138 } 2139 2140 @Override 2141 public Object actual() { 2142 return audioManager.getMode() == expectedMode; 2143 } 2144 }, 2145 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2146 "Audio mode was expected to be " + expectedMode 2147 ); 2148 } 2149 2150 /** 2151 * Asserts that a call's capabilities are as expected. 2152 * 2153 * @param call The call. 2154 * @param capabilities The expected capabiltiies. 2155 */ 2156 public void assertCallCapabilities(final Call call, final int capabilities) { 2157 waitUntilConditionIsTrueOrTimeout( 2158 new Condition() { 2159 @Override 2160 public Object expected() { 2161 return true; 2162 } 2163 2164 @Override 2165 public Object actual() { 2166 return (call.getDetails().getCallCapabilities() & capabilities) == 2167 capabilities; 2168 } 2169 }, 2170 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2171 "Call should have properties " + capabilities 2172 ); 2173 } 2174 2175 MockInCallService getInCallService() { 2176 return (mInCallCallbacks == null) ? null : mInCallCallbacks.getService(); 2177 } 2178 2179 public void waitOnInCallService() { 2180 waitUntilConditionIsTrueOrTimeout(new Condition() { 2181 @Override 2182 public Object expected() { 2183 return true; 2184 } 2185 2186 @Override 2187 public Object actual() { 2188 return mInCallCallbacks.getService() != null; 2189 } 2190 }, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, "MockInCallService failed to get Call"); 2191 } 2192 2193 /** 2194 * Asserts that the {@link UiModeManager} mode matches the specified mode. 2195 * 2196 * @param uiMode The expected ui mode. 2197 */ 2198 public void assertUiMode(final int uiMode) { 2199 waitUntilConditionIsTrueOrTimeout( 2200 new Condition() { 2201 @Override 2202 public Object expected() { 2203 return uiMode; 2204 } 2205 2206 @Override 2207 public Object actual() { 2208 return mUiModeManager.getCurrentModeType(); 2209 } 2210 }, 2211 TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2212 "Expected ui mode " + uiMode 2213 ); 2214 } 2215 void assertEndpointType(final InCallService incallService, final int type) { 2216 waitUntilConditionIsTrueOrTimeout( 2217 new Condition() { 2218 @Override 2219 public Object expected() { 2220 return type; 2221 } 2222 2223 @Override 2224 public Object actual() { 2225 final CallEndpoint endpoint = incallService.getCurrentCallEndpoint(); 2226 return endpoint == null ? null : endpoint.getEndpointType(); 2227 } 2228 }, 2229 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2230 "Phone's call endpoint type should be: " + type 2231 ); 2232 } 2233 2234 void assertEndpointType(final MockConnection connection, final int type) { 2235 waitUntilConditionIsTrueOrTimeout( 2236 new Condition() { 2237 @Override 2238 public Object expected() { 2239 return type; 2240 } 2241 2242 @Override 2243 public Object actual() { 2244 final CallEndpoint endpoint = 2245 ((Connection) connection).getCurrentCallEndpoint(); 2246 return endpoint == null ? null : endpoint.getEndpointType(); 2247 } 2248 }, 2249 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2250 "Connection's call endpoint type should be: " + type 2251 ); 2252 } 2253 2254 void assertMuteEndpoint(final MockInCallService incallService, final boolean isMuted) { 2255 waitUntilConditionIsTrueOrTimeout( 2256 new Condition() { 2257 @Override 2258 public Object expected() { 2259 return isMuted; 2260 } 2261 2262 @Override 2263 public Object actual() { 2264 return incallService.getEndpointMuteState(); 2265 } 2266 }, 2267 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2268 "Phone's mute state should be: " + isMuted 2269 ); 2270 } 2271 2272 void assertMuteEndpoint(final MockConnection connection, final boolean isMuted) { 2273 waitUntilConditionIsTrueOrTimeout( 2274 new Condition() { 2275 @Override 2276 public Object expected() { 2277 return isMuted; 2278 } 2279 2280 @Override 2281 public Object actual() { 2282 return connection.getEndpointMuteState(); 2283 } 2284 }, 2285 WAIT_FOR_STATE_CHANGE_TIMEOUT_MS, 2286 "Connection's mute state should be: " + isMuted 2287 ); 2288 } 2289 2290 void waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout, 2291 String description) { 2292 final long start = System.currentTimeMillis(); 2293 while (!Objects.equals(condition.expected(), condition.actual()) 2294 && System.currentTimeMillis() - start < timeout) { 2295 sleep(50); 2296 } 2297 assertEquals(description, condition.expected(), condition.actual()); 2298 } 2299 2300 /** 2301 * Performs some work, and waits for the condition to be met. If the condition is not met in 2302 * each step of the loop, the work is performed again. 2303 * 2304 * @param work The work to perform. 2305 * @param condition The condition. 2306 * @param timeout The timeout. 2307 * @param description Description of the work being performed. 2308 */ 2309 void doWorkAndWaitUntilConditionIsTrueOrTimeout(Work work, Condition condition, long timeout, 2310 String description) { 2311 final long start = System.currentTimeMillis(); 2312 work.doWork(); 2313 while (!condition.expected().equals(condition.actual()) 2314 && System.currentTimeMillis() - start < timeout) { 2315 sleep(50); 2316 work.doWork(); 2317 } 2318 assertEquals(description, condition.expected(), condition.actual()); 2319 } 2320 2321 protected interface Condition { 2322 Object expected(); 2323 Object actual(); 2324 } 2325 2326 protected interface Work { 2327 void doWork(); 2328 } 2329 2330 public static boolean areBundlesEqual(Bundle extras, Bundle newExtras) { 2331 if (extras == null || newExtras == null) { 2332 return extras == newExtras; 2333 } 2334 2335 if (extras.size() != newExtras.size()) { 2336 return false; 2337 } 2338 2339 for (String key : extras.keySet()) { 2340 if (key != null) { 2341 final Object value = extras.get(key); 2342 final Object newValue = newExtras.get(key); 2343 if (!Objects.equals(value, newValue)) { 2344 return false; 2345 } 2346 } 2347 } 2348 return true; 2349 } 2350 } 2351