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