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