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