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