TelecomSystemTest.java revision d7b70ca78c1ba41d5c0a931a6d55f60c19301394
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.CallerInfoLookupHelper;
65import com.android.server.telecom.CallsManager;
66import com.android.server.telecom.CallsManagerListenerBase;
67import com.android.server.telecom.HeadsetMediaButton;
68import com.android.server.telecom.HeadsetMediaButtonFactory;
69import com.android.server.telecom.InCallWakeLockController;
70import com.android.server.telecom.InCallWakeLockControllerFactory;
71import com.android.server.telecom.MissedCallNotifier;
72import com.android.server.telecom.PhoneAccountRegistrar;
73import com.android.server.telecom.PhoneNumberUtilsAdapter;
74import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
75import com.android.server.telecom.ProximitySensorManager;
76import com.android.server.telecom.ProximitySensorManagerFactory;
77import com.android.server.telecom.TelecomSystem;
78import com.android.server.telecom.Timeouts;
79import com.android.server.telecom.components.UserCallIntentProcessor;
80import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
81
82import com.google.common.base.Predicate;
83
84import org.mockito.ArgumentCaptor;
85import org.mockito.Mock;
86import org.mockito.invocation.InvocationOnMock;
87import org.mockito.stubbing.Answer;
88
89import java.util.ArrayList;
90import java.util.List;
91
92/**
93 * Implements mocks and functionality required to implement telecom system tests.
94 */
95public class TelecomSystemTest extends TelecomTestCase {
96
97    static final int TEST_POLL_INTERVAL = 10;  // milliseconds
98    static final int TEST_TIMEOUT = 1000;  // milliseconds
99
100    public class HeadsetMediaButtonFactoryF implements HeadsetMediaButtonFactory  {
101        @Override
102        public HeadsetMediaButton create(Context context, CallsManager callsManager,
103                TelecomSystem.SyncRoot lock) {
104            return mHeadsetMediaButton;
105        }
106    }
107
108    public class ProximitySensorManagerFactoryF implements ProximitySensorManagerFactory {
109        @Override
110        public ProximitySensorManager create(Context context, CallsManager callsManager) {
111            return mProximitySensorManager;
112        }
113    }
114
115    public class InCallWakeLockControllerFactoryF implements InCallWakeLockControllerFactory {
116        @Override
117        public InCallWakeLockController create(Context context, CallsManager callsManager) {
118            return mInCallWakeLockController;
119        }
120    }
121
122    public static class MissedCallNotifierFakeImpl extends CallsManagerListenerBase
123            implements MissedCallNotifier {
124        List<CallInfo> missedCallsNotified = new ArrayList<>();
125
126        @Override
127        public void clearMissedCalls(UserHandle userHandle) {
128
129        }
130
131        @Override
132        public void showMissedCallNotification(CallInfo call) {
133            missedCallsNotified.add(call);
134        }
135
136        @Override
137        public void reloadAfterBootComplete(CallerInfoLookupHelper callerInfoLookupHelper,
138                CallInfoFactory callInfoFactory) { }
139
140        @Override
141        public void reloadFromDatabase(CallerInfoLookupHelper callerInfoLookupHelper,
142                CallInfoFactory callInfoFactory, UserHandle userHandle) { }
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                                    PhoneAccount.CAPABILITY_VIDEO_CALLING)
202                    .build();
203    final PhoneAccount mPhoneAccountA1 =
204            PhoneAccount.builder(
205                    new PhoneAccountHandle(
206                            mConnectionServiceComponentNameA,
207                            "id A 1"),
208                    "Phone account service A ID 1")
209                    .addSupportedUriScheme("tel")
210                    .setCapabilities(
211                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
212                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
213                    .build();
214    final PhoneAccount mPhoneAccountB0 =
215            PhoneAccount.builder(
216                    new PhoneAccountHandle(
217                            mConnectionServiceComponentNameB,
218                            "id B 0"),
219                    "Phone account service B ID 0")
220                    .addSupportedUriScheme("tel")
221                    .setCapabilities(
222                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
223                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
224                    .build();
225    final PhoneAccount mPhoneAccountE0 =
226            PhoneAccount.builder(
227                    new PhoneAccountHandle(
228                            mConnectionServiceComponentNameA,
229                            "id E 0"),
230                    "Phone account service E ID 0")
231                    .addSupportedUriScheme("tel")
232                    .setCapabilities(
233                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
234                                    PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
235                                    PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
236                    .build();
237
238    final PhoneAccount mPhoneAccountE1 =
239            PhoneAccount.builder(
240                    new PhoneAccountHandle(
241                            mConnectionServiceComponentNameA,
242                            "id E 1"),
243                    "Phone account service E ID 1")
244                    .addSupportedUriScheme("tel")
245                    .setCapabilities(
246                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
247                                    PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
248                                    PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS)
249                    .build();
250
251    ConnectionServiceFixture mConnectionServiceFixtureA;
252    ConnectionServiceFixture mConnectionServiceFixtureB;
253    Timeouts.Adapter mTimeoutsAdapter;
254
255    CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture;
256
257    IAudioService mAudioService;
258
259    TelecomSystem mTelecomSystem;
260
261    Context mSpyContext;
262
263    private int mNumOutgoingCallsMade;
264
265    private boolean mIsEmergencyCall;
266
267    class IdPair {
268        final String mConnectionId;
269        final String mCallId;
270
271        public IdPair(String connectionId, String callId) {
272            this.mConnectionId = connectionId;
273            this.mCallId = callId;
274        }
275    }
276
277    @Override
278    public void setUp() throws Exception {
279        super.setUp();
280        mSpyContext = mComponentContextFixture.getTestDouble().getApplicationContext();
281        doReturn(mSpyContext).when(mSpyContext).getApplicationContext();
282
283        mNumOutgoingCallsMade = 0;
284
285        mIsEmergencyCall = false;
286
287        // First set up information about the In-Call services in the mock Context, since
288        // Telecom will search for these as soon as it is instantiated
289        setupInCallServices();
290
291        // Next, create the TelecomSystem, our system under test
292        setupTelecomSystem();
293
294        // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the
295        // now-running TelecomSystem
296        setupConnectionServices();
297    }
298
299    @Override
300    public void tearDown() throws Exception {
301        mTelecomSystem = null;
302        super.tearDown();
303    }
304
305    protected ParcelableCall makeConferenceCall() throws Exception {
306        IdPair callId1 = startAndMakeActiveOutgoingCall("650-555-1212",
307                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
308
309        IdPair callId2 = startAndMakeActiveOutgoingCall("650-555-1213",
310                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
311
312        IInCallAdapter inCallAdapter = mInCallServiceFixtureX.getInCallAdapter();
313        inCallAdapter.conference(callId1.mCallId, callId2.mCallId);
314        // Wait for wacky non-deterministic behavior
315        Thread.sleep(200);
316        ParcelableCall call1 = mInCallServiceFixtureX.getCall(callId1.mCallId);
317        ParcelableCall call2 = mInCallServiceFixtureX.getCall(callId2.mCallId);
318        // Check that the two calls end up with a parent in the end
319        assertNotNull(call1.getParentCallId());
320        assertNotNull(call2.getParentCallId());
321        assertEquals(call1.getParentCallId(), call2.getParentCallId());
322
323        // Check to make sure that the parent call made it to the in-call service
324        String parentCallId = call1.getParentCallId();
325        ParcelableCall conferenceCall = mInCallServiceFixtureX.getCall(parentCallId);
326        assertEquals(2, conferenceCall.getChildCallIds().size());
327        assertTrue(conferenceCall.getChildCallIds().contains(callId1.mCallId));
328        assertTrue(conferenceCall.getChildCallIds().contains(callId2.mCallId));
329        return conferenceCall;
330    }
331
332    private void setupTelecomSystem() throws Exception {
333        // Use actual implementations instead of mocking the interface out.
334        HeadsetMediaButtonFactory headsetMediaButtonFactory =
335                spy(new HeadsetMediaButtonFactoryF());
336        ProximitySensorManagerFactory proximitySensorManagerFactory =
337                spy(new ProximitySensorManagerFactoryF());
338        InCallWakeLockControllerFactory inCallWakeLockControllerFactory =
339                spy(new InCallWakeLockControllerFactoryF());
340        mAudioService = setupAudioService();
341
342        mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
343
344        mTimeoutsAdapter = mock(Timeouts.Adapter.class);
345        when(mTimeoutsAdapter.getCallScreeningTimeoutMillis(any(ContentResolver.class)))
346                .thenReturn(TEST_TIMEOUT / 5L);
347
348        mTelecomSystem = new TelecomSystem(
349                mComponentContextFixture.getTestDouble(),
350                new MissedCallNotifierImplFactory() {
351                    @Override
352                    public MissedCallNotifier makeMissedCallNotifierImpl(Context context,
353                            PhoneAccountRegistrar phoneAccountRegistrar) {
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(), any());
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(), any());
671        // Wait for handleCreateConnectionComplete
672        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
673        // Wait for the callback in ConnectionService#onAdapterAttached to execute.
674        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
675
676        assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
677        assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
678
679        assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
680
681        return new IdPair(connectionServiceFixture.mLatestConnectionId,
682                mInCallServiceFixtureX.mLatestCallId);
683    }
684
685    protected IdPair startIncomingPhoneCall(
686            String number,
687            PhoneAccountHandle phoneAccountHandle,
688            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
689        return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
690                connectionServiceFixture);
691    }
692
693    protected IdPair startIncomingPhoneCall(
694            String number,
695            PhoneAccountHandle phoneAccountHandle,
696            int videoState,
697            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
698        reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
699                mInCallServiceFixtureY.getTestDouble());
700
701        assertEquals(mInCallServiceFixtureX.mCallById.size(),
702                mInCallServiceFixtureY.mCallById.size());
703        assertEquals((mInCallServiceFixtureX.mInCallAdapter != null),
704                (mInCallServiceFixtureY.mInCallAdapter != null));
705        final int startingNumConnections = connectionServiceFixture.mConnectionById.size();
706        final int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
707        boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
708        connectionServiceFixture.mConnectionServiceDelegate.mVideoState = videoState;
709
710        Bundle extras = new Bundle();
711        extras.putParcelable(
712                TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
713                Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
714        mTelecomSystem.getTelecomServiceImpl().getBinder()
715                .addNewIncomingCall(phoneAccountHandle, extras);
716
717        verify(connectionServiceFixture.getTestDouble())
718                .createConnection(any(PhoneAccountHandle.class), anyString(),
719                        any(ConnectionRequest.class), eq(true), eq(false), any());
720
721        for (CallerInfoAsyncQueryFactoryFixture.Request request :
722                mCallerInfoAsyncQueryFactoryFixture.mRequests) {
723            request.reply();
724        }
725
726        IContentProvider blockedNumberProvider =
727                mSpyContext.getContentResolver().acquireProvider(BlockedNumberContract.AUTHORITY);
728        verify(blockedNumberProvider, timeout(TEST_TIMEOUT)).call(
729                anyString(),
730                eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
731                eq(number),
732                isNull(Bundle.class));
733
734        // For the case of incoming calls, Telecom connecting the InCall services and adding the
735        // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
736        // is added, future interactions as triggered by the ConnectionService, through the various
737        // test fixtures, will be synchronous.
738
739        if (!hasInCallAdapter) {
740            verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
741                    .setInCallAdapter(any(IInCallAdapter.class));
742            verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
743                    .setInCallAdapter(any(IInCallAdapter.class));
744        }
745
746        // Give the InCallService time to respond
747
748        assertTrueWithTimeout(new Predicate<Void>() {
749            @Override
750            public boolean apply(Void v) {
751                return mInCallServiceFixtureX.mInCallAdapter != null;
752            }
753        });
754
755        assertTrueWithTimeout(new Predicate<Void>() {
756            @Override
757            public boolean apply(Void v) {
758                return mInCallServiceFixtureY.mInCallAdapter != null;
759            }
760        });
761
762        verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
763                .addCall(any(ParcelableCall.class));
764        verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
765                .addCall(any(ParcelableCall.class));
766
767        // Give the InCallService time to respond
768
769        assertTrueWithTimeout(new Predicate<Void>() {
770            @Override
771            public boolean apply(Void v) {
772                return startingNumConnections + 1 ==
773                        connectionServiceFixture.mConnectionById.size();
774            }
775        });
776        assertTrueWithTimeout(new Predicate<Void>() {
777            @Override
778            public boolean apply(Void v) {
779                return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
780            }
781        });
782        assertTrueWithTimeout(new Predicate<Void>() {
783            @Override
784            public boolean apply(Void v) {
785                return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
786            }
787        });
788
789        assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
790
791        return new IdPair(connectionServiceFixture.mLatestConnectionId,
792                mInCallServiceFixtureX.mLatestCallId);
793    }
794
795    protected IdPair startAndMakeActiveOutgoingCall(
796            String number,
797            PhoneAccountHandle phoneAccountHandle,
798            ConnectionServiceFixture connectionServiceFixture) throws Exception {
799        return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture,
800                VideoProfile.STATE_AUDIO_ONLY);
801    }
802
803    // A simple outgoing call, verifying that the appropriate connection service is contacted,
804    // the proper lifecycle is followed, and both In-Call Services are updated correctly.
805    protected IdPair startAndMakeActiveOutgoingCall(
806            String number,
807            PhoneAccountHandle phoneAccountHandle,
808            ConnectionServiceFixture connectionServiceFixture, int videoState) throws Exception {
809        IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
810                Process.myUserHandle(), videoState);
811
812        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
813        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
814        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
815
816        connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
817
818        connectionServiceFixture.sendSetActive(ids.mConnectionId);
819        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
820        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
821
822        return ids;
823    }
824
825    protected IdPair startAndMakeActiveIncomingCall(
826            String number,
827            PhoneAccountHandle phoneAccountHandle,
828            ConnectionServiceFixture connectionServiceFixture) throws Exception {
829        return startAndMakeActiveIncomingCall(number, phoneAccountHandle, connectionServiceFixture,
830                VideoProfile.STATE_AUDIO_ONLY);
831    }
832
833    // A simple incoming call, similar in scope to the previous test
834    protected IdPair startAndMakeActiveIncomingCall(
835            String number,
836            PhoneAccountHandle phoneAccountHandle,
837            ConnectionServiceFixture connectionServiceFixture,
838            int videoState) throws Exception {
839        IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
840
841        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
842        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
843
844        mInCallServiceFixtureX.mInCallAdapter
845                .answerCall(ids.mCallId, videoState);
846
847        if (!VideoProfile.isVideo(videoState)) {
848            verify(connectionServiceFixture.getTestDouble())
849                    .answer(eq(ids.mConnectionId), any());
850        } else {
851            verify(connectionServiceFixture.getTestDouble())
852                    .answerVideo(eq(ids.mConnectionId), eq(videoState), any());
853        }
854
855        connectionServiceFixture.sendSetActive(ids.mConnectionId);
856        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
857        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
858
859        return ids;
860    }
861
862    protected IdPair startAndMakeDialingEmergencyCall(
863            String number,
864            PhoneAccountHandle phoneAccountHandle,
865            ConnectionServiceFixture connectionServiceFixture) throws Exception {
866        IdPair ids = startOutgoingEmergencyCall(number, phoneAccountHandle,
867                connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
868
869        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
870        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
871        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
872
873        return ids;
874    }
875
876    protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
877        int elapsed = 0;
878        while (elapsed < TEST_TIMEOUT) {
879            if (predicate.apply(null)) {
880                return;
881            } else {
882                try {
883                    Thread.sleep(TEST_POLL_INTERVAL);
884                    elapsed += TEST_POLL_INTERVAL;
885                } catch (InterruptedException e) {
886                    fail(e.toString());
887                }
888            }
889        }
890        fail("Timeout in assertTrueWithTimeout");
891    }
892}
893