Home | History | Annotate | Download | only in tests
      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 com.android.server.telecom.tests;
     18 
     19 
     20 import static org.mockito.Matchers.any;
     21 import static org.mockito.Matchers.anyBoolean;
     22 import static org.mockito.Matchers.anyInt;
     23 import static org.mockito.Matchers.anyString;
     24 import static org.mockito.Matchers.eq;
     25 import static org.mockito.Matchers.isNull;
     26 import static org.mockito.Mockito.doAnswer;
     27 import static org.mockito.Mockito.doReturn;
     28 import static org.mockito.Mockito.mock;
     29 import static org.mockito.Mockito.reset;
     30 import static org.mockito.Mockito.spy;
     31 import static org.mockito.Mockito.timeout;
     32 import static org.mockito.Mockito.times;
     33 import static org.mockito.Mockito.verify;
     34 import static org.mockito.Mockito.when;
     35 
     36 import android.content.BroadcastReceiver;
     37 import android.content.ComponentName;
     38 import android.content.ContentResolver;
     39 import android.content.Context;
     40 import android.content.IContentProvider;
     41 import android.content.Intent;
     42 import android.media.AudioManager;
     43 import android.media.IAudioService;
     44 import android.net.Uri;
     45 import android.os.Bundle;
     46 import android.os.Handler;
     47 import android.os.Looper;
     48 import android.os.Process;
     49 import android.os.UserHandle;
     50 import android.provider.BlockedNumberContract;
     51 import android.telecom.Call;
     52 import android.telecom.ConnectionRequest;
     53 import android.telecom.DisconnectCause;
     54 import android.telecom.ParcelableCall;
     55 import android.telecom.PhoneAccount;
     56 import android.telecom.PhoneAccountHandle;
     57 import android.telecom.TelecomManager;
     58 import android.telecom.VideoProfile;
     59 
     60 import com.android.internal.telecom.IInCallAdapter;
     61 import com.android.server.telecom.AsyncRingtonePlayer;
     62 import com.android.server.telecom.BluetoothPhoneServiceImpl;
     63 import com.android.server.telecom.CallAudioManager;
     64 import com.android.server.telecom.CallerInfoAsyncQueryFactory;
     65 import com.android.server.telecom.CallsManager;
     66 import com.android.server.telecom.CallsManagerListenerBase;
     67 import com.android.server.telecom.ContactsAsyncHelper;
     68 import com.android.server.telecom.HeadsetMediaButton;
     69 import com.android.server.telecom.HeadsetMediaButtonFactory;
     70 import com.android.server.telecom.InCallWakeLockController;
     71 import com.android.server.telecom.InCallWakeLockControllerFactory;
     72 import com.android.server.telecom.MissedCallNotifier;
     73 import com.android.server.telecom.PhoneAccountRegistrar;
     74 import com.android.server.telecom.PhoneNumberUtilsAdapter;
     75 import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
     76 import com.android.server.telecom.ProximitySensorManager;
     77 import com.android.server.telecom.ProximitySensorManagerFactory;
     78 import com.android.server.telecom.TelecomSystem;
     79 import com.android.server.telecom.Timeouts;
     80 import com.android.server.telecom.components.UserCallIntentProcessor;
     81 import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
     82 
     83 import com.google.common.base.Predicate;
     84 
     85 import org.mockito.ArgumentCaptor;
     86 import org.mockito.Mock;
     87 import org.mockito.invocation.InvocationOnMock;
     88 import org.mockito.stubbing.Answer;
     89 
     90 import java.util.ArrayList;
     91 import java.util.List;
     92 
     93 /**
     94  * Implements mocks and functionality required to implement telecom system tests.
     95  */
     96 public class TelecomSystemTest extends TelecomTestCase {
     97 
     98     static final int TEST_POLL_INTERVAL = 10;  // milliseconds
     99     static final int TEST_TIMEOUT = 1000;  // milliseconds
    100 
    101     public class HeadsetMediaButtonFactoryF implements HeadsetMediaButtonFactory  {
    102         @Override
    103         public HeadsetMediaButton create(Context context, CallsManager callsManager,
    104                 TelecomSystem.SyncRoot lock) {
    105             return mHeadsetMediaButton;
    106         }
    107     }
    108 
    109     public class ProximitySensorManagerFactoryF implements ProximitySensorManagerFactory {
    110         @Override
    111         public ProximitySensorManager create(Context context, CallsManager callsManager) {
    112             return mProximitySensorManager;
    113         }
    114     }
    115 
    116     public class InCallWakeLockControllerFactoryF implements InCallWakeLockControllerFactory {
    117         @Override
    118         public InCallWakeLockController create(Context context, CallsManager callsManager) {
    119             return mInCallWakeLockController;
    120         }
    121     }
    122 
    123     public static class MissedCallNotifierFakeImpl extends CallsManagerListenerBase
    124             implements MissedCallNotifier {
    125         List<com.android.server.telecom.Call> missedCallsNotified = new ArrayList<>();
    126 
    127         @Override
    128         public void clearMissedCalls(UserHandle userHandle) {
    129 
    130         }
    131 
    132         @Override
    133         public void showMissedCallNotification(com.android.server.telecom.Call call) {
    134             missedCallsNotified.add(call);
    135         }
    136 
    137         @Override
    138         public void reloadFromDatabase(TelecomSystem.SyncRoot lock, CallsManager callsManager,
    139                 ContactsAsyncHelper contactsAsyncHelper,
    140                 CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, UserHandle userHandle) {
    141 
    142         }
    143 
    144         @Override
    145         public void setCurrentUserHandle(UserHandle userHandle) {
    146 
    147         }
    148     }
    149 
    150     MissedCallNotifierFakeImpl mMissedCallNotifier = new MissedCallNotifierFakeImpl();
    151     private class EmergencyNumberUtilsAdapter extends PhoneNumberUtilsAdapterImpl {
    152 
    153         @Override
    154         public boolean isLocalEmergencyNumber(Context context, String number) {
    155             return mIsEmergencyCall;
    156         }
    157 
    158         @Override
    159         public boolean isPotentialLocalEmergencyNumber(Context context, String number) {
    160             return mIsEmergencyCall;
    161         }
    162     }
    163     PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter = new EmergencyNumberUtilsAdapter();
    164     @Mock HeadsetMediaButton mHeadsetMediaButton;
    165     @Mock ProximitySensorManager mProximitySensorManager;
    166     @Mock InCallWakeLockController mInCallWakeLockController;
    167     @Mock BluetoothPhoneServiceImpl mBluetoothPhoneServiceImpl;
    168     @Mock AsyncRingtonePlayer mAsyncRingtonePlayer;
    169 
    170     final ComponentName mInCallServiceComponentNameX =
    171             new ComponentName(
    172                     "incall-service-package-X",
    173                     "incall-service-class-X");
    174     final ComponentName mInCallServiceComponentNameY =
    175             new ComponentName(
    176                     "incall-service-package-Y",
    177                     "incall-service-class-Y");
    178 
    179     InCallServiceFixture mInCallServiceFixtureX;
    180     InCallServiceFixture mInCallServiceFixtureY;
    181 
    182     final ComponentName mConnectionServiceComponentNameA =
    183             new ComponentName(
    184                     "connection-service-package-A",
    185                     "connection-service-class-A");
    186     final ComponentName mConnectionServiceComponentNameB =
    187             new ComponentName(
    188                     "connection-service-package-B",
    189                     "connection-service-class-B");
    190 
    191     final PhoneAccount mPhoneAccountA0 =
    192             PhoneAccount.builder(
    193                     new PhoneAccountHandle(
    194                             mConnectionServiceComponentNameA,
    195                             "id A 0"),
    196                     "Phone account service A ID 0")
    197                     .addSupportedUriScheme("tel")
    198                     .setCapabilities(
    199                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
    200                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
    201                     .build();
    202     final PhoneAccount mPhoneAccountA1 =
    203             PhoneAccount.builder(
    204                     new PhoneAccountHandle(
    205                             mConnectionServiceComponentNameA,
    206                             "id A 1"),
    207                     "Phone account service A ID 1")
    208                     .addSupportedUriScheme("tel")
    209                     .setCapabilities(
    210                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
    211                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
    212                     .build();
    213     final PhoneAccount mPhoneAccountB0 =
    214             PhoneAccount.builder(
    215                     new PhoneAccountHandle(
    216                             mConnectionServiceComponentNameB,
    217                             "id B 0"),
    218                     "Phone account service B ID 0")
    219                     .addSupportedUriScheme("tel")
    220                     .setCapabilities(
    221                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
    222                             PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
    223                     .build();
    224     final PhoneAccount mPhoneAccountE0 =
    225             PhoneAccount.builder(
    226                     new PhoneAccountHandle(
    227                             mConnectionServiceComponentNameA,
    228                             "id E 0"),
    229                     "Phone account service E ID 0")
    230                     .addSupportedUriScheme("tel")
    231                     .setCapabilities(
    232                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
    233                                     PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
    234                                     PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
    235                     .build();
    236 
    237     final PhoneAccount mPhoneAccountE1 =
    238             PhoneAccount.builder(
    239                     new PhoneAccountHandle(
    240                             mConnectionServiceComponentNameA,
    241                             "id E 1"),
    242                     "Phone account service E ID 1")
    243                     .addSupportedUriScheme("tel")
    244                     .setCapabilities(
    245                             PhoneAccount.CAPABILITY_CALL_PROVIDER |
    246                                     PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
    247                                     PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
    248                     .build();
    249 
    250     ConnectionServiceFixture mConnectionServiceFixtureA;
    251     ConnectionServiceFixture mConnectionServiceFixtureB;
    252     Timeouts.Adapter mTimeoutsAdapter;
    253 
    254     CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture;
    255 
    256     IAudioService mAudioService;
    257 
    258     TelecomSystem mTelecomSystem;
    259 
    260     Context mSpyContext;
    261 
    262     private int mNumOutgoingCallsMade;
    263 
    264     private boolean mIsEmergencyCall;
    265 
    266     class IdPair {
    267         final String mConnectionId;
    268         final String mCallId;
    269 
    270         public IdPair(String connectionId, String callId) {
    271             this.mConnectionId = connectionId;
    272             this.mCallId = callId;
    273         }
    274     }
    275 
    276     @Override
    277     public void setUp() throws Exception {
    278         super.setUp();
    279         mSpyContext = mComponentContextFixture.getTestDouble().getApplicationContext();
    280         doReturn(mSpyContext).when(mSpyContext).getApplicationContext();
    281 
    282         mNumOutgoingCallsMade = 0;
    283 
    284         mIsEmergencyCall = false;
    285 
    286         // First set up information about the In-Call services in the mock Context, since
    287         // Telecom will search for these as soon as it is instantiated
    288         setupInCallServices();
    289 
    290         // Next, create the TelecomSystem, our system under test
    291         setupTelecomSystem();
    292 
    293         // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the
    294         // now-running TelecomSystem
    295         setupConnectionServices();
    296     }
    297 
    298     @Override
    299     public void tearDown() throws Exception {
    300         mTelecomSystem = null;
    301         super.tearDown();
    302     }
    303 
    304     protected ParcelableCall makeConferenceCall() throws Exception {
    305         IdPair callId1 = startAndMakeActiveOutgoingCall("650-555-1212",
    306                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
    307 
    308         IdPair callId2 = startAndMakeActiveOutgoingCall("650-555-1213",
    309                 mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
    310 
    311         IInCallAdapter inCallAdapter = mInCallServiceFixtureX.getInCallAdapter();
    312         inCallAdapter.conference(callId1.mCallId, callId2.mCallId);
    313         // Wait for wacky non-deterministic behavior
    314         Thread.sleep(200);
    315         ParcelableCall call1 = mInCallServiceFixtureX.getCall(callId1.mCallId);
    316         ParcelableCall call2 = mInCallServiceFixtureX.getCall(callId2.mCallId);
    317         // Check that the two calls end up with a parent in the end
    318         assertNotNull(call1.getParentCallId());
    319         assertNotNull(call2.getParentCallId());
    320         assertEquals(call1.getParentCallId(), call2.getParentCallId());
    321 
    322         // Check to make sure that the parent call made it to the in-call service
    323         String parentCallId = call1.getParentCallId();
    324         ParcelableCall conferenceCall = mInCallServiceFixtureX.getCall(parentCallId);
    325         assertEquals(2, conferenceCall.getChildCallIds().size());
    326         assertTrue(conferenceCall.getChildCallIds().contains(callId1.mCallId));
    327         assertTrue(conferenceCall.getChildCallIds().contains(callId2.mCallId));
    328         return conferenceCall;
    329     }
    330 
    331     private void setupTelecomSystem() throws Exception {
    332         // Use actual implementations instead of mocking the interface out.
    333         HeadsetMediaButtonFactory headsetMediaButtonFactory =
    334                 spy(new HeadsetMediaButtonFactoryF());
    335         ProximitySensorManagerFactory proximitySensorManagerFactory =
    336                 spy(new ProximitySensorManagerFactoryF());
    337         InCallWakeLockControllerFactory inCallWakeLockControllerFactory =
    338                 spy(new InCallWakeLockControllerFactoryF());
    339         mAudioService = setupAudioService();
    340 
    341         mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
    342 
    343         mTimeoutsAdapter = mock(Timeouts.Adapter.class);
    344         when(mTimeoutsAdapter.getCallScreeningTimeoutMillis(any(ContentResolver.class)))
    345                 .thenReturn(TEST_TIMEOUT / 10L);
    346 
    347         mTelecomSystem = new TelecomSystem(
    348                 mComponentContextFixture.getTestDouble(),
    349                 new MissedCallNotifierImplFactory() {
    350                     @Override
    351                     public MissedCallNotifier makeMissedCallNotifierImpl(Context context,
    352                             PhoneAccountRegistrar phoneAccountRegistrar,
    353                             PhoneNumberUtilsAdapter phoneNumberUtilsAdapter) {
    354                         return mMissedCallNotifier;
    355                     }
    356                 },
    357                 mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
    358                 headsetMediaButtonFactory,
    359                 proximitySensorManagerFactory,
    360                 inCallWakeLockControllerFactory,
    361                 new CallAudioManager.AudioServiceFactory() {
    362                     @Override
    363                     public IAudioService getAudioService() {
    364                         return mAudioService;
    365                     }
    366                 },
    367                 new BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory() {
    368                     @Override
    369                     public BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
    370                             TelecomSystem.SyncRoot lock, CallsManager callsManager,
    371                             PhoneAccountRegistrar phoneAccountRegistrar) {
    372                         return mBluetoothPhoneServiceImpl;
    373                     }
    374                 },
    375                 mTimeoutsAdapter,
    376                 mAsyncRingtonePlayer,
    377                 mPhoneNumberUtilsAdapter);
    378 
    379         mComponentContextFixture.setTelecomManager(new TelecomManager(
    380                 mComponentContextFixture.getTestDouble(),
    381                 mTelecomSystem.getTelecomServiceImpl().getBinder()));
    382 
    383         verify(headsetMediaButtonFactory).create(
    384                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
    385                 any(CallsManager.class),
    386                 any(TelecomSystem.SyncRoot.class));
    387         verify(proximitySensorManagerFactory).create(
    388                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
    389                 any(CallsManager.class));
    390         verify(inCallWakeLockControllerFactory).create(
    391                 eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
    392                 any(CallsManager.class));
    393     }
    394 
    395     private void setupConnectionServices() throws Exception {
    396         mConnectionServiceFixtureA = new ConnectionServiceFixture();
    397         mConnectionServiceFixtureB = new ConnectionServiceFixture();
    398 
    399         mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameA,
    400                 mConnectionServiceFixtureA.getTestDouble());
    401         mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameB,
    402                 mConnectionServiceFixtureB.getTestDouble());
    403 
    404         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
    405         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
    406         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
    407         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE0);
    408         mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE1);
    409 
    410         mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
    411                 mPhoneAccountA0.getAccountHandle(), Process.myUserHandle());
    412     }
    413 
    414     private void setupInCallServices() throws Exception {
    415         mComponentContextFixture.putResource(
    416                 com.android.server.telecom.R.string.ui_default_package,
    417                 mInCallServiceComponentNameX.getPackageName());
    418         mComponentContextFixture.putResource(
    419                 com.android.server.telecom.R.string.incall_default_class,
    420                 mInCallServiceComponentNameX.getClassName());
    421         mComponentContextFixture.putBooleanResource(
    422                 com.android.internal.R.bool.config_voice_capable, true);
    423 
    424         mInCallServiceFixtureX = new InCallServiceFixture();
    425         mInCallServiceFixtureY = new InCallServiceFixture();
    426 
    427         mComponentContextFixture.addInCallService(mInCallServiceComponentNameX,
    428                 mInCallServiceFixtureX.getTestDouble());
    429         mComponentContextFixture.addInCallService(mInCallServiceComponentNameY,
    430                 mInCallServiceFixtureY.getTestDouble());
    431     }
    432 
    433     /**
    434      * Helper method for setting up the fake audio service.
    435      * Calls to the fake audio service need to toggle the return
    436      * value of AudioManager#isMicrophoneMute.
    437      * @return mock of IAudioService
    438      */
    439     private IAudioService setupAudioService() {
    440         IAudioService audioService = mock(IAudioService.class);
    441 
    442         final AudioManager fakeAudioManager =
    443                 (AudioManager) mComponentContextFixture.getTestDouble()
    444                         .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
    445 
    446         try {
    447             doAnswer(new Answer() {
    448                 @Override
    449                 public Object answer(InvocationOnMock i) {
    450                     Object[] args = i.getArguments();
    451                     doReturn(args[0]).when(fakeAudioManager).isMicrophoneMute();
    452                     return null;
    453                 }
    454             }).when(audioService)
    455                     .setMicrophoneMute(any(Boolean.class), any(String.class), any(Integer.class));
    456 
    457         } catch (android.os.RemoteException e) {
    458             // Do nothing, leave the faked microphone state as-is
    459         }
    460         return audioService;
    461     }
    462 
    463     protected String startOutgoingPhoneCallWithNoPhoneAccount(String number,
    464             ConnectionServiceFixture connectionServiceFixture)
    465             throws Exception {
    466 
    467         return startOutgoingPhoneCallPendingCreateConnection(number, null,
    468                 connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
    469     }
    470 
    471     protected IdPair outgoingCallPhoneAccountSelected(PhoneAccountHandle phoneAccountHandle,
    472             int startingNumConnections, int startingNumCalls,
    473             ConnectionServiceFixture connectionServiceFixture) throws Exception {
    474 
    475         IdPair ids = outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
    476                 phoneAccountHandle, connectionServiceFixture);
    477 
    478         connectionServiceFixture.sendSetDialing(ids.mConnectionId);
    479         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    480         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    481 
    482         connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
    483 
    484         connectionServiceFixture.sendSetActive(ids.mConnectionId);
    485         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    486         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    487 
    488         return ids;
    489     }
    490 
    491     protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
    492             ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser)
    493             throws Exception {
    494 
    495         return startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
    496                 initiatingUser, VideoProfile.STATE_AUDIO_ONLY);
    497     }
    498 
    499     protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
    500             ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
    501             int videoState) throws Exception {
    502         int startingNumConnections = connectionServiceFixture.mConnectionById.size();
    503         int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
    504 
    505         startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle,
    506                 connectionServiceFixture, initiatingUser, videoState);
    507 
    508         return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
    509                 phoneAccountHandle, connectionServiceFixture);
    510     }
    511 
    512     protected IdPair triggerEmergencyRedial(PhoneAccountHandle phoneAccountHandle,
    513             ConnectionServiceFixture connectionServiceFixture, IdPair emergencyIds)
    514             throws Exception {
    515         int startingNumConnections = connectionServiceFixture.mConnectionById.size();
    516         int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
    517 
    518         // Send the message to disconnect the Emergency call due to an error.
    519         // CreateConnectionProcessor should now try the second SIM account
    520         connectionServiceFixture.sendSetDisconnected(emergencyIds.mConnectionId,
    521                 DisconnectCause.ERROR);
    522         waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
    523         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(
    524                 emergencyIds.mCallId).getState());
    525         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(
    526                 emergencyIds.mCallId).getState());
    527 
    528         return redialingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
    529                 phoneAccountHandle, connectionServiceFixture);
    530     }
    531 
    532     protected IdPair startOutgoingEmergencyCall(String number,
    533             PhoneAccountHandle phoneAccountHandle,
    534             ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
    535             int videoState) throws Exception {
    536         int startingNumConnections = connectionServiceFixture.mConnectionById.size();
    537         int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
    538 
    539         mIsEmergencyCall = true;
    540         // Call will not use the ordered broadcaster, since it is an Emergency Call
    541         startOutgoingPhoneCallWaitForBroadcaster(number, phoneAccountHandle,
    542                 connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/);
    543 
    544         return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
    545                 phoneAccountHandle, connectionServiceFixture);
    546     }
    547 
    548     protected void startOutgoingPhoneCallWaitForBroadcaster(String number,
    549             PhoneAccountHandle phoneAccountHandle,
    550             ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
    551             int videoState, boolean isEmergency) throws Exception {
    552         reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
    553                 mInCallServiceFixtureY.getTestDouble());
    554 
    555         assertEquals(mInCallServiceFixtureX.mCallById.size(),
    556                 mInCallServiceFixtureY.mCallById.size());
    557         assertEquals((mInCallServiceFixtureX.mInCallAdapter != null),
    558                 (mInCallServiceFixtureY.mInCallAdapter != null));
    559 
    560         mNumOutgoingCallsMade++;
    561 
    562         boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
    563 
    564         Intent actionCallIntent = new Intent();
    565         actionCallIntent.setData(Uri.parse("tel:" + number));
    566         actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
    567         if(isEmergency) {
    568             actionCallIntent.setAction(Intent.ACTION_CALL_EMERGENCY);
    569         } else {
    570             actionCallIntent.setAction(Intent.ACTION_CALL);
    571         }
    572         if (phoneAccountHandle != null) {
    573             actionCallIntent.putExtra(
    574                     TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
    575                     phoneAccountHandle);
    576         }
    577         if (videoState != VideoProfile.STATE_AUDIO_ONLY) {
    578             actionCallIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
    579         }
    580 
    581         final UserHandle userHandle = initiatingUser;
    582         Context localAppContext = mComponentContextFixture.getTestDouble().getApplicationContext();
    583         new UserCallIntentProcessor(localAppContext, userHandle).processIntent(
    584                 actionCallIntent, null, true /* hasCallAppOp*/);
    585         // UserCallIntentProcessor's mContext.sendBroadcastAsUser(...) will call to an empty method
    586         // as to not actually try to send an intent to PrimaryCallReceiver. We verify that it was
    587         // called correctly in order to continue.
    588         verify(localAppContext).sendBroadcastAsUser(actionCallIntent, UserHandle.SYSTEM);
    589         mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent);
    590 
    591         if (!hasInCallAdapter) {
    592             verify(mInCallServiceFixtureX.getTestDouble())
    593                     .setInCallAdapter(
    594                             any(IInCallAdapter.class));
    595             verify(mInCallServiceFixtureY.getTestDouble())
    596                     .setInCallAdapter(
    597                             any(IInCallAdapter.class));
    598         }
    599     }
    600 
    601     protected String startOutgoingPhoneCallPendingCreateConnection(String number,
    602             PhoneAccountHandle phoneAccountHandle,
    603             ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
    604             int videoState) throws Exception {
    605         startOutgoingPhoneCallWaitForBroadcaster(number,phoneAccountHandle,
    606                 connectionServiceFixture, initiatingUser, videoState, false /*isEmergency*/);
    607 
    608         ArgumentCaptor<Intent> newOutgoingCallIntent =
    609                 ArgumentCaptor.forClass(Intent.class);
    610         ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver =
    611                 ArgumentCaptor.forClass(BroadcastReceiver.class);
    612 
    613         verify(mComponentContextFixture.getTestDouble().getApplicationContext(),
    614                 times(mNumOutgoingCallsMade))
    615                 .sendOrderedBroadcastAsUser(
    616                         newOutgoingCallIntent.capture(),
    617                         any(UserHandle.class),
    618                         anyString(),
    619                         anyInt(),
    620                         newOutgoingCallReceiver.capture(),
    621                         any(Handler.class),
    622                         anyInt(),
    623                         anyString(),
    624                         any(Bundle.class));
    625 
    626         // Pass on the new outgoing call Intent
    627         // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
    628         newOutgoingCallReceiver.getValue().setPendingResult(
    629                 new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
    630         newOutgoingCallReceiver.getValue().setResultData(
    631                 newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER));
    632         newOutgoingCallReceiver.getValue().onReceive(mComponentContextFixture.getTestDouble(),
    633                 newOutgoingCallIntent.getValue());
    634 
    635         return mInCallServiceFixtureX.mLatestCallId;
    636     }
    637 
    638     // When Telecom is redialing due to an error, we need to make sure the number of connections
    639     // increase, but not the number of Calls in the InCallService.
    640     protected IdPair redialingCallCreateConnectionComplete(int startingNumConnections,
    641             int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
    642             ConnectionServiceFixture connectionServiceFixture) throws Exception {
    643 
    644         assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
    645 
    646         verify(connectionServiceFixture.getTestDouble())
    647                 .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class),
    648                         eq(false)/*isIncoming*/, anyBoolean());
    649         // Wait for handleCreateConnectionComplete
    650         waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
    651 
    652         // Make sure the number of registered InCallService Calls stays the same.
    653         assertEquals(startingNumCalls, mInCallServiceFixtureX.mCallById.size());
    654         assertEquals(startingNumCalls, mInCallServiceFixtureY.mCallById.size());
    655 
    656         assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
    657 
    658         return new IdPair(connectionServiceFixture.mLatestConnectionId,
    659                 mInCallServiceFixtureX.mLatestCallId);
    660     }
    661 
    662     protected IdPair outgoingCallCreateConnectionComplete(int startingNumConnections,
    663             int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
    664             ConnectionServiceFixture connectionServiceFixture) throws Exception {
    665 
    666         assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
    667 
    668         verify(connectionServiceFixture.getTestDouble())
    669                 .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class),
    670                         eq(false)/*isIncoming*/, anyBoolean());
    671         // Wait for handleCreateConnectionComplete
    672         waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
    673         // Wait for the callback in ConnectionService#onAdapterAttached to execute.
    674         waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
    675 
    676         assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
    677         assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
    678 
    679         assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
    680 
    681         return new IdPair(connectionServiceFixture.mLatestConnectionId,
    682                 mInCallServiceFixtureX.mLatestCallId);
    683     }
    684 
    685     protected IdPair startIncomingPhoneCall(
    686             String number,
    687             PhoneAccountHandle phoneAccountHandle,
    688             final ConnectionServiceFixture connectionServiceFixture) throws Exception {
    689         return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
    690                 connectionServiceFixture);
    691     }
    692 
    693     protected IdPair startIncomingPhoneCall(
    694             String number,
    695             PhoneAccountHandle phoneAccountHandle,
    696             int videoState,
    697             final ConnectionServiceFixture connectionServiceFixture) throws Exception {
    698         reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
    699                 mInCallServiceFixtureY.getTestDouble());
    700 
    701         assertEquals(mInCallServiceFixtureX.mCallById.size(),
    702                 mInCallServiceFixtureY.mCallById.size());
    703         assertEquals((mInCallServiceFixtureX.mInCallAdapter != null),
    704                 (mInCallServiceFixtureY.mInCallAdapter != null));
    705         final int startingNumConnections = connectionServiceFixture.mConnectionById.size();
    706         final int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
    707         boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
    708         connectionServiceFixture.mConnectionServiceDelegate.mVideoState = videoState;
    709 
    710         Bundle extras = new Bundle();
    711         extras.putParcelable(
    712                 TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
    713                 Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
    714         mTelecomSystem.getTelecomServiceImpl().getBinder()
    715                 .addNewIncomingCall(phoneAccountHandle, extras);
    716 
    717         verify(connectionServiceFixture.getTestDouble())
    718                 .createConnection(any(PhoneAccountHandle.class), anyString(),
    719                         any(ConnectionRequest.class), eq(true), eq(false));
    720 
    721         for (CallerInfoAsyncQueryFactoryFixture.Request request :
    722                 mCallerInfoAsyncQueryFactoryFixture.mRequests) {
    723             request.reply();
    724         }
    725 
    726         IContentProvider blockedNumberProvider =
    727                 mSpyContext.getContentResolver().acquireProvider(BlockedNumberContract.AUTHORITY);
    728         verify(blockedNumberProvider, timeout(TEST_TIMEOUT)).call(
    729                 anyString(),
    730                 eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
    731                 eq(number),
    732                 isNull(Bundle.class));
    733 
    734         // For the case of incoming calls, Telecom connecting the InCall services and adding the
    735         // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
    736         // is added, future interactions as triggered by the ConnectionService, through the various
    737         // test fixtures, will be synchronous.
    738 
    739         if (!hasInCallAdapter) {
    740             verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
    741                     .setInCallAdapter(any(IInCallAdapter.class));
    742             verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
    743                     .setInCallAdapter(any(IInCallAdapter.class));
    744         }
    745 
    746         // Give the InCallService time to respond
    747 
    748         assertTrueWithTimeout(new Predicate<Void>() {
    749             @Override
    750             public boolean apply(Void v) {
    751                 return mInCallServiceFixtureX.mInCallAdapter != null;
    752             }
    753         });
    754 
    755         assertTrueWithTimeout(new Predicate<Void>() {
    756             @Override
    757             public boolean apply(Void v) {
    758                 return mInCallServiceFixtureY.mInCallAdapter != null;
    759             }
    760         });
    761 
    762         verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
    763                 .addCall(any(ParcelableCall.class));
    764         verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
    765                 .addCall(any(ParcelableCall.class));
    766 
    767         // Give the InCallService time to respond
    768 
    769         assertTrueWithTimeout(new Predicate<Void>() {
    770             @Override
    771             public boolean apply(Void v) {
    772                 return startingNumConnections + 1 ==
    773                         connectionServiceFixture.mConnectionById.size();
    774             }
    775         });
    776         assertTrueWithTimeout(new Predicate<Void>() {
    777             @Override
    778             public boolean apply(Void v) {
    779                 return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
    780             }
    781         });
    782         assertTrueWithTimeout(new Predicate<Void>() {
    783             @Override
    784             public boolean apply(Void v) {
    785                 return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
    786             }
    787         });
    788 
    789         assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
    790 
    791         return new IdPair(connectionServiceFixture.mLatestConnectionId,
    792                 mInCallServiceFixtureX.mLatestCallId);
    793     }
    794 
    795     protected IdPair startAndMakeActiveOutgoingCall(
    796             String number,
    797             PhoneAccountHandle phoneAccountHandle,
    798             ConnectionServiceFixture connectionServiceFixture) throws Exception {
    799         return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture,
    800                 VideoProfile.STATE_AUDIO_ONLY);
    801     }
    802 
    803     // A simple outgoing call, verifying that the appropriate connection service is contacted,
    804     // the proper lifecycle is followed, and both In-Call Services are updated correctly.
    805     protected IdPair startAndMakeActiveOutgoingCall(
    806             String number,
    807             PhoneAccountHandle phoneAccountHandle,
    808             ConnectionServiceFixture connectionServiceFixture, int videoState) throws Exception {
    809         IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
    810                 Process.myUserHandle(), videoState);
    811 
    812         connectionServiceFixture.sendSetDialing(ids.mConnectionId);
    813         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    814         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    815 
    816         connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
    817 
    818         connectionServiceFixture.sendSetActive(ids.mConnectionId);
    819         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    820         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    821 
    822         return ids;
    823     }
    824 
    825     protected IdPair startAndMakeActiveIncomingCall(
    826             String number,
    827             PhoneAccountHandle phoneAccountHandle,
    828             ConnectionServiceFixture connectionServiceFixture) throws Exception {
    829         return startAndMakeActiveIncomingCall(number, phoneAccountHandle, connectionServiceFixture,
    830                 VideoProfile.STATE_AUDIO_ONLY);
    831     }
    832 
    833     // A simple incoming call, similar in scope to the previous test
    834     protected IdPair startAndMakeActiveIncomingCall(
    835             String number,
    836             PhoneAccountHandle phoneAccountHandle,
    837             ConnectionServiceFixture connectionServiceFixture,
    838             int videoState) throws Exception {
    839         IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
    840 
    841         assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    842         assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    843 
    844         mInCallServiceFixtureX.mInCallAdapter
    845                 .answerCall(ids.mCallId, videoState);
    846 
    847         if (!VideoProfile.isVideo(videoState)) {
    848             verify(connectionServiceFixture.getTestDouble())
    849                     .answer(ids.mConnectionId);
    850         } else {
    851             verify(connectionServiceFixture.getTestDouble())
    852                     .answerVideo(ids.mConnectionId, videoState);
    853         }
    854 
    855         connectionServiceFixture.sendSetActive(ids.mConnectionId);
    856         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    857         assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    858 
    859         return ids;
    860     }
    861 
    862     protected IdPair startAndMakeDialingEmergencyCall(
    863             String number,
    864             PhoneAccountHandle phoneAccountHandle,
    865             ConnectionServiceFixture connectionServiceFixture) throws Exception {
    866         IdPair ids = startOutgoingEmergencyCall(number, phoneAccountHandle,
    867                 connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
    868 
    869         connectionServiceFixture.sendSetDialing(ids.mConnectionId);
    870         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
    871         assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
    872 
    873         return ids;
    874     }
    875 
    876     protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
    877         int elapsed = 0;
    878         while (elapsed < TEST_TIMEOUT) {
    879             if (predicate.apply(null)) {
    880                 return;
    881             } else {
    882                 try {
    883                     Thread.sleep(TEST_POLL_INTERVAL);
    884                     elapsed += TEST_POLL_INTERVAL;
    885                 } catch (InterruptedException e) {
    886                     fail(e.toString());
    887                 }
    888             }
    889         }
    890         fail("Timeout in assertTrueWithTimeout");
    891     }
    892 }
    893