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