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