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