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