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