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