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