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