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