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