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