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