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