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