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