TelecomSystemTest.java revision b3ce510a9392230d003e2b740affab476c78f74f
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                        return mMissedCallNotifier;
354                    }
355                },
356                mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
357                headsetMediaButtonFactory,
358                proximitySensorManagerFactory,
359                inCallWakeLockControllerFactory,
360                new CallAudioManager.AudioServiceFactory() {
361                    @Override
362                    public IAudioService getAudioService() {
363                        return mAudioService;
364                    }
365                },
366                new BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory() {
367                    @Override
368                    public BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
369                            TelecomSystem.SyncRoot lock, CallsManager callsManager,
370                            PhoneAccountRegistrar phoneAccountRegistrar) {
371                        return mBluetoothPhoneServiceImpl;
372                    }
373                },
374                mTimeoutsAdapter,
375                mAsyncRingtonePlayer,
376                mPhoneNumberUtilsAdapter);
377
378        mComponentContextFixture.setTelecomManager(new TelecomManager(
379                mComponentContextFixture.getTestDouble(),
380                mTelecomSystem.getTelecomServiceImpl().getBinder()));
381
382        verify(headsetMediaButtonFactory).create(
383                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
384                any(CallsManager.class),
385                any(TelecomSystem.SyncRoot.class));
386        verify(proximitySensorManagerFactory).create(
387                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
388                any(CallsManager.class));
389        verify(inCallWakeLockControllerFactory).create(
390                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
391                any(CallsManager.class));
392    }
393
394    private void setupConnectionServices() throws Exception {
395        mConnectionServiceFixtureA = new ConnectionServiceFixture();
396        mConnectionServiceFixtureB = new ConnectionServiceFixture();
397
398        mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameA,
399                mConnectionServiceFixtureA.getTestDouble());
400        mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameB,
401                mConnectionServiceFixtureB.getTestDouble());
402
403        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
404        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
405        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
406        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE0);
407        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountE1);
408
409        mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
410                mPhoneAccountA0.getAccountHandle(), Process.myUserHandle());
411    }
412
413    private void setupInCallServices() throws Exception {
414        mComponentContextFixture.putResource(
415                com.android.server.telecom.R.string.ui_default_package,
416                mInCallServiceComponentNameX.getPackageName());
417        mComponentContextFixture.putResource(
418                com.android.server.telecom.R.string.incall_default_class,
419                mInCallServiceComponentNameX.getClassName());
420        mComponentContextFixture.putBooleanResource(
421                com.android.internal.R.bool.config_voice_capable, true);
422
423        mInCallServiceFixtureX = new InCallServiceFixture();
424        mInCallServiceFixtureY = new InCallServiceFixture();
425
426        mComponentContextFixture.addInCallService(mInCallServiceComponentNameX,
427                mInCallServiceFixtureX.getTestDouble());
428        mComponentContextFixture.addInCallService(mInCallServiceComponentNameY,
429                mInCallServiceFixtureY.getTestDouble());
430    }
431
432    /**
433     * Helper method for setting up the fake audio service.
434     * Calls to the fake audio service need to toggle the return
435     * value of AudioManager#isMicrophoneMute.
436     * @return mock of IAudioService
437     */
438    private IAudioService setupAudioService() {
439        IAudioService audioService = mock(IAudioService.class);
440
441        final AudioManager fakeAudioManager =
442                (AudioManager) mComponentContextFixture.getTestDouble()
443                        .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
444
445        try {
446            doAnswer(new Answer() {
447                @Override
448                public Object answer(InvocationOnMock i) {
449                    Object[] args = i.getArguments();
450                    doReturn(args[0]).when(fakeAudioManager).isMicrophoneMute();
451                    return null;
452                }
453            }).when(audioService)
454                    .setMicrophoneMute(any(Boolean.class), any(String.class), any(Integer.class));
455
456        } catch (android.os.RemoteException e) {
457            // Do nothing, leave the faked microphone state as-is
458        }
459        return audioService;
460    }
461
462    protected String startOutgoingPhoneCallWithNoPhoneAccount(String number,
463            ConnectionServiceFixture connectionServiceFixture)
464            throws Exception {
465
466        return startOutgoingPhoneCallPendingCreateConnection(number, null,
467                connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
468    }
469
470    protected IdPair outgoingCallPhoneAccountSelected(PhoneAccountHandle phoneAccountHandle,
471            int startingNumConnections, int startingNumCalls,
472            ConnectionServiceFixture connectionServiceFixture) throws Exception {
473
474        IdPair ids = outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
475                phoneAccountHandle, connectionServiceFixture);
476
477        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
478        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
479        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
480
481        connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
482
483        connectionServiceFixture.sendSetActive(ids.mConnectionId);
484        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
485        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
486
487        return ids;
488    }
489
490    protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
491            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser)
492            throws Exception {
493
494        return startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
495                initiatingUser, VideoProfile.STATE_AUDIO_ONLY);
496    }
497
498    protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
499            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
500            int videoState) throws Exception {
501        int startingNumConnections = connectionServiceFixture.mConnectionById.size();
502        int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
503
504        startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle,
505                connectionServiceFixture, initiatingUser, videoState);
506
507        return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
508                phoneAccountHandle, connectionServiceFixture);
509    }
510
511    protected IdPair triggerEmergencyRedial(PhoneAccountHandle phoneAccountHandle,
512            ConnectionServiceFixture connectionServiceFixture, IdPair emergencyIds)
513            throws Exception {
514        int startingNumConnections = connectionServiceFixture.mConnectionById.size();
515        int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
516
517        // Send the message to disconnect the Emergency call due to an error.
518        // CreateConnectionProcessor should now try the second SIM account
519        connectionServiceFixture.sendSetDisconnected(emergencyIds.mConnectionId,
520                DisconnectCause.ERROR);
521        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
522        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(
523                emergencyIds.mCallId).getState());
524        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(
525                emergencyIds.mCallId).getState());
526
527        return redialingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
528                phoneAccountHandle, connectionServiceFixture);
529    }
530
531    protected IdPair startOutgoingEmergencyCall(String number,
532            PhoneAccountHandle phoneAccountHandle,
533            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
534            int videoState) throws Exception {
535        int startingNumConnections = connectionServiceFixture.mConnectionById.size();
536        int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
537
538        mIsEmergencyCall = true;
539        // Call will not use the ordered broadcaster, since it is an Emergency Call
540        startOutgoingPhoneCallWaitForBroadcaster(number, phoneAccountHandle,
541                connectionServiceFixture, initiatingUser, videoState, true /*isEmergency*/);
542
543        return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
544                phoneAccountHandle, connectionServiceFixture);
545    }
546
547    protected void startOutgoingPhoneCallWaitForBroadcaster(String number,
548            PhoneAccountHandle phoneAccountHandle,
549            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
550            int videoState, boolean isEmergency) throws Exception {
551        reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
552                mInCallServiceFixtureY.getTestDouble());
553
554        assertEquals(mInCallServiceFixtureX.mCallById.size(),
555                mInCallServiceFixtureY.mCallById.size());
556        assertEquals((mInCallServiceFixtureX.mInCallAdapter != null),
557                (mInCallServiceFixtureY.mInCallAdapter != null));
558
559        mNumOutgoingCallsMade++;
560
561        boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
562
563        Intent actionCallIntent = new Intent();
564        actionCallIntent.setData(Uri.parse("tel:" + number));
565        actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
566        if(isEmergency) {
567            actionCallIntent.setAction(Intent.ACTION_CALL_EMERGENCY);
568        } else {
569            actionCallIntent.setAction(Intent.ACTION_CALL);
570        }
571        if (phoneAccountHandle != null) {
572            actionCallIntent.putExtra(
573                    TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
574                    phoneAccountHandle);
575        }
576        if (videoState != VideoProfile.STATE_AUDIO_ONLY) {
577            actionCallIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
578        }
579
580        final UserHandle userHandle = initiatingUser;
581        Context localAppContext = mComponentContextFixture.getTestDouble().getApplicationContext();
582        new UserCallIntentProcessor(localAppContext, userHandle).processIntent(
583                actionCallIntent, null, true /* hasCallAppOp*/);
584        // UserCallIntentProcessor's mContext.sendBroadcastAsUser(...) will call to an empty method
585        // as to not actually try to send an intent to PrimaryCallReceiver. We verify that it was
586        // called correctly in order to continue.
587        verify(localAppContext).sendBroadcastAsUser(actionCallIntent, UserHandle.SYSTEM);
588        mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent);
589
590        if (!hasInCallAdapter) {
591            verify(mInCallServiceFixtureX.getTestDouble())
592                    .setInCallAdapter(
593                            any(IInCallAdapter.class));
594            verify(mInCallServiceFixtureY.getTestDouble())
595                    .setInCallAdapter(
596                            any(IInCallAdapter.class));
597        }
598    }
599
600    protected String startOutgoingPhoneCallPendingCreateConnection(String number,
601            PhoneAccountHandle phoneAccountHandle,
602            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
603            int videoState) throws Exception {
604        startOutgoingPhoneCallWaitForBroadcaster(number,phoneAccountHandle,
605                connectionServiceFixture, initiatingUser, videoState, false /*isEmergency*/);
606
607        ArgumentCaptor<Intent> newOutgoingCallIntent =
608                ArgumentCaptor.forClass(Intent.class);
609        ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver =
610                ArgumentCaptor.forClass(BroadcastReceiver.class);
611
612        verify(mComponentContextFixture.getTestDouble().getApplicationContext(),
613                times(mNumOutgoingCallsMade))
614                .sendOrderedBroadcastAsUser(
615                        newOutgoingCallIntent.capture(),
616                        any(UserHandle.class),
617                        anyString(),
618                        anyInt(),
619                        newOutgoingCallReceiver.capture(),
620                        any(Handler.class),
621                        anyInt(),
622                        anyString(),
623                        any(Bundle.class));
624
625        // Pass on the new outgoing call Intent
626        // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
627        newOutgoingCallReceiver.getValue().setPendingResult(
628                new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
629        newOutgoingCallReceiver.getValue().setResultData(
630                newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER));
631        newOutgoingCallReceiver.getValue().onReceive(mComponentContextFixture.getTestDouble(),
632                newOutgoingCallIntent.getValue());
633
634        return mInCallServiceFixtureX.mLatestCallId;
635    }
636
637    // When Telecom is redialing due to an error, we need to make sure the number of connections
638    // increase, but not the number of Calls in the InCallService.
639    protected IdPair redialingCallCreateConnectionComplete(int startingNumConnections,
640            int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
641            ConnectionServiceFixture connectionServiceFixture) throws Exception {
642
643        assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
644
645        verify(connectionServiceFixture.getTestDouble())
646                .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class),
647                        eq(false)/*isIncoming*/, anyBoolean());
648        // Wait for handleCreateConnectionComplete
649        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
650
651        // Make sure the number of registered InCallService Calls stays the same.
652        assertEquals(startingNumCalls, mInCallServiceFixtureX.mCallById.size());
653        assertEquals(startingNumCalls, mInCallServiceFixtureY.mCallById.size());
654
655        assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
656
657        return new IdPair(connectionServiceFixture.mLatestConnectionId,
658                mInCallServiceFixtureX.mLatestCallId);
659    }
660
661    protected IdPair outgoingCallCreateConnectionComplete(int startingNumConnections,
662            int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
663            ConnectionServiceFixture connectionServiceFixture) throws Exception {
664
665        assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
666
667        verify(connectionServiceFixture.getTestDouble())
668                .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class),
669                        eq(false)/*isIncoming*/, anyBoolean());
670        // Wait for handleCreateConnectionComplete
671        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
672        // Wait for the callback in ConnectionService#onAdapterAttached to execute.
673        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
674
675        assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
676        assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
677
678        assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
679
680        return new IdPair(connectionServiceFixture.mLatestConnectionId,
681                mInCallServiceFixtureX.mLatestCallId);
682    }
683
684    protected IdPair startIncomingPhoneCall(
685            String number,
686            PhoneAccountHandle phoneAccountHandle,
687            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
688        return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
689                connectionServiceFixture);
690    }
691
692    protected IdPair startIncomingPhoneCall(
693            String number,
694            PhoneAccountHandle phoneAccountHandle,
695            int videoState,
696            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
697        reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
698                mInCallServiceFixtureY.getTestDouble());
699
700        assertEquals(mInCallServiceFixtureX.mCallById.size(),
701                mInCallServiceFixtureY.mCallById.size());
702        assertEquals((mInCallServiceFixtureX.mInCallAdapter != null),
703                (mInCallServiceFixtureY.mInCallAdapter != null));
704        final int startingNumConnections = connectionServiceFixture.mConnectionById.size();
705        final int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
706        boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
707        connectionServiceFixture.mConnectionServiceDelegate.mVideoState = videoState;
708
709        Bundle extras = new Bundle();
710        extras.putParcelable(
711                TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
712                Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
713        mTelecomSystem.getTelecomServiceImpl().getBinder()
714                .addNewIncomingCall(phoneAccountHandle, extras);
715
716        verify(connectionServiceFixture.getTestDouble())
717                .createConnection(any(PhoneAccountHandle.class), anyString(),
718                        any(ConnectionRequest.class), eq(true), eq(false));
719
720        for (CallerInfoAsyncQueryFactoryFixture.Request request :
721                mCallerInfoAsyncQueryFactoryFixture.mRequests) {
722            request.reply();
723        }
724
725        IContentProvider blockedNumberProvider =
726                mSpyContext.getContentResolver().acquireProvider(BlockedNumberContract.AUTHORITY);
727        verify(blockedNumberProvider, timeout(TEST_TIMEOUT)).call(
728                anyString(),
729                eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
730                eq(number),
731                isNull(Bundle.class));
732
733        // For the case of incoming calls, Telecom connecting the InCall services and adding the
734        // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
735        // is added, future interactions as triggered by the ConnectionService, through the various
736        // test fixtures, will be synchronous.
737
738        if (!hasInCallAdapter) {
739            verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
740                    .setInCallAdapter(any(IInCallAdapter.class));
741            verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
742                    .setInCallAdapter(any(IInCallAdapter.class));
743        }
744
745        // Give the InCallService time to respond
746
747        assertTrueWithTimeout(new Predicate<Void>() {
748            @Override
749            public boolean apply(Void v) {
750                return mInCallServiceFixtureX.mInCallAdapter != null;
751            }
752        });
753
754        assertTrueWithTimeout(new Predicate<Void>() {
755            @Override
756            public boolean apply(Void v) {
757                return mInCallServiceFixtureY.mInCallAdapter != null;
758            }
759        });
760
761        verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
762                .addCall(any(ParcelableCall.class));
763        verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
764                .addCall(any(ParcelableCall.class));
765
766        // Give the InCallService time to respond
767
768        assertTrueWithTimeout(new Predicate<Void>() {
769            @Override
770            public boolean apply(Void v) {
771                return startingNumConnections + 1 ==
772                        connectionServiceFixture.mConnectionById.size();
773            }
774        });
775        assertTrueWithTimeout(new Predicate<Void>() {
776            @Override
777            public boolean apply(Void v) {
778                return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
779            }
780        });
781        assertTrueWithTimeout(new Predicate<Void>() {
782            @Override
783            public boolean apply(Void v) {
784                return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
785            }
786        });
787
788        assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
789
790        return new IdPair(connectionServiceFixture.mLatestConnectionId,
791                mInCallServiceFixtureX.mLatestCallId);
792    }
793
794    protected IdPair startAndMakeActiveOutgoingCall(
795            String number,
796            PhoneAccountHandle phoneAccountHandle,
797            ConnectionServiceFixture connectionServiceFixture) throws Exception {
798        return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture,
799                VideoProfile.STATE_AUDIO_ONLY);
800    }
801
802    // A simple outgoing call, verifying that the appropriate connection service is contacted,
803    // the proper lifecycle is followed, and both In-Call Services are updated correctly.
804    protected IdPair startAndMakeActiveOutgoingCall(
805            String number,
806            PhoneAccountHandle phoneAccountHandle,
807            ConnectionServiceFixture connectionServiceFixture, int videoState) throws Exception {
808        IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
809                Process.myUserHandle(), videoState);
810
811        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
812        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
813        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
814
815        connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
816
817        connectionServiceFixture.sendSetActive(ids.mConnectionId);
818        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
819        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
820
821        return ids;
822    }
823
824    protected IdPair startAndMakeActiveIncomingCall(
825            String number,
826            PhoneAccountHandle phoneAccountHandle,
827            ConnectionServiceFixture connectionServiceFixture) throws Exception {
828        return startAndMakeActiveIncomingCall(number, phoneAccountHandle, connectionServiceFixture,
829                VideoProfile.STATE_AUDIO_ONLY);
830    }
831
832    // A simple incoming call, similar in scope to the previous test
833    protected IdPair startAndMakeActiveIncomingCall(
834            String number,
835            PhoneAccountHandle phoneAccountHandle,
836            ConnectionServiceFixture connectionServiceFixture,
837            int videoState) throws Exception {
838        IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
839
840        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
841        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
842
843        mInCallServiceFixtureX.mInCallAdapter
844                .answerCall(ids.mCallId, videoState);
845
846        if (!VideoProfile.isVideo(videoState)) {
847            verify(connectionServiceFixture.getTestDouble())
848                    .answer(ids.mConnectionId);
849        } else {
850            verify(connectionServiceFixture.getTestDouble())
851                    .answerVideo(ids.mConnectionId, videoState);
852        }
853
854        connectionServiceFixture.sendSetActive(ids.mConnectionId);
855        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
856        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
857
858        return ids;
859    }
860
861    protected IdPair startAndMakeDialingEmergencyCall(
862            String number,
863            PhoneAccountHandle phoneAccountHandle,
864            ConnectionServiceFixture connectionServiceFixture) throws Exception {
865        IdPair ids = startOutgoingEmergencyCall(number, phoneAccountHandle,
866                connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
867
868        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
869        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
870        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
871
872        return ids;
873    }
874
875    protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
876        int elapsed = 0;
877        while (elapsed < TEST_TIMEOUT) {
878            if (predicate.apply(null)) {
879                return;
880            } else {
881                try {
882                    Thread.sleep(TEST_POLL_INTERVAL);
883                    elapsed += TEST_POLL_INTERVAL;
884                } catch (InterruptedException e) {
885                    fail(e.toString());
886                }
887            }
888        }
889        fail("Timeout in assertTrueWithTimeout");
890    }
891}
892