TelecomSystemTest.java revision 961694a9750626c067be960170bc95f10b5efcc2
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;
34
35import android.content.BroadcastReceiver;
36import android.content.ComponentName;
37import android.content.Context;
38import android.content.IContentProvider;
39import android.content.Intent;
40import android.media.AudioManager;
41import android.media.IAudioService;
42import android.net.Uri;
43import android.os.Bundle;
44import android.os.Handler;
45import android.os.Process;
46import android.os.UserHandle;
47import android.provider.BlockedNumberContract;
48import android.telecom.Call;
49import android.telecom.ConnectionRequest;
50import android.telecom.ParcelableCall;
51import android.telecom.PhoneAccount;
52import android.telecom.PhoneAccountHandle;
53import android.telecom.TelecomManager;
54import android.telecom.VideoProfile;
55
56import com.android.internal.telecom.IInCallAdapter;
57import com.android.server.telecom.BluetoothPhoneServiceImpl;
58import com.android.server.telecom.CallAudioManager;
59import com.android.server.telecom.CallerInfoAsyncQueryFactory;
60import com.android.server.telecom.CallsManager;
61import com.android.server.telecom.CallsManagerListenerBase;
62import com.android.server.telecom.ContactsAsyncHelper;
63import com.android.server.telecom.HeadsetMediaButton;
64import com.android.server.telecom.HeadsetMediaButtonFactory;
65import com.android.server.telecom.InCallWakeLockController;
66import com.android.server.telecom.InCallWakeLockControllerFactory;
67import com.android.server.telecom.MissedCallNotifier;
68import com.android.server.telecom.PhoneAccountRegistrar;
69import com.android.server.telecom.ProximitySensorManager;
70import com.android.server.telecom.ProximitySensorManagerFactory;
71import com.android.server.telecom.TelecomSystem;
72import com.android.server.telecom.components.UserCallIntentProcessor;
73import com.android.server.telecom.ui.MissedCallNotifierImpl.MissedCallNotifierImplFactory;
74
75import com.google.common.base.Predicate;
76
77import org.mockito.ArgumentCaptor;
78import org.mockito.Mock;
79import org.mockito.invocation.InvocationOnMock;
80import org.mockito.stubbing.Answer;
81
82import java.util.ArrayList;
83import java.util.List;
84
85/**
86 * Implements mocks and functionality required to implement telecom system tests.
87 */
88public class TelecomSystemTest extends TelecomTestCase {
89
90    static final int TEST_POLL_INTERVAL = 10;  // milliseconds
91    static final int TEST_TIMEOUT = 1000;  // milliseconds
92
93    public class HeadsetMediaButtonFactoryF implements HeadsetMediaButtonFactory  {
94        @Override
95        public HeadsetMediaButton create(Context context, CallsManager callsManager,
96                TelecomSystem.SyncRoot lock) {
97            return mHeadsetMediaButton;
98        }
99    }
100
101    public class ProximitySensorManagerFactoryF implements ProximitySensorManagerFactory {
102        @Override
103        public ProximitySensorManager create(Context context, CallsManager callsManager) {
104            return mProximitySensorManager;
105        }
106    }
107
108    public class InCallWakeLockControllerFactoryF implements InCallWakeLockControllerFactory {
109        @Override
110        public InCallWakeLockController create(Context context, CallsManager callsManager) {
111            return mInCallWakeLockController;
112        }
113    }
114
115    public static class MissedCallNotifierFakeImpl extends CallsManagerListenerBase
116            implements MissedCallNotifier {
117        List<com.android.server.telecom.Call> missedCallsNotified = new ArrayList<>();
118
119        @Override
120        public void clearMissedCalls(UserHandle userHandle) {
121
122        }
123
124        @Override
125        public void showMissedCallNotification(com.android.server.telecom.Call call) {
126            missedCallsNotified.add(call);
127        }
128
129        @Override
130        public void reloadFromDatabase(TelecomSystem.SyncRoot lock, CallsManager callsManager,
131                ContactsAsyncHelper contactsAsyncHelper,
132                CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, UserHandle userHandle) {
133
134        }
135
136        @Override
137        public void setCurrentUserHandle(UserHandle userHandle) {
138
139        }
140    }
141
142    MissedCallNotifierFakeImpl mMissedCallNotifier = new MissedCallNotifierFakeImpl();
143    @Mock HeadsetMediaButton mHeadsetMediaButton;
144    @Mock ProximitySensorManager mProximitySensorManager;
145    @Mock InCallWakeLockController mInCallWakeLockController;
146    @Mock BluetoothPhoneServiceImpl mBluetoothPhoneServiceImpl;
147
148    final ComponentName mInCallServiceComponentNameX =
149            new ComponentName(
150                    "incall-service-package-X",
151                    "incall-service-class-X");
152    final ComponentName mInCallServiceComponentNameY =
153            new ComponentName(
154                    "incall-service-package-Y",
155                    "incall-service-class-Y");
156
157    InCallServiceFixture mInCallServiceFixtureX;
158    InCallServiceFixture mInCallServiceFixtureY;
159
160    final ComponentName mConnectionServiceComponentNameA =
161            new ComponentName(
162                    "connection-service-package-A",
163                    "connection-service-class-A");
164    final ComponentName mConnectionServiceComponentNameB =
165            new ComponentName(
166                    "connection-service-package-B",
167                    "connection-service-class-B");
168
169    final PhoneAccount mPhoneAccountA0 =
170            PhoneAccount.builder(
171                    new PhoneAccountHandle(
172                            mConnectionServiceComponentNameA,
173                            "id A 0"),
174                    "Phone account service A ID 0")
175                    .addSupportedUriScheme("tel")
176                    .setCapabilities(
177                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
178                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
179                    .build();
180    final PhoneAccount mPhoneAccountA1 =
181            PhoneAccount.builder(
182                    new PhoneAccountHandle(
183                            mConnectionServiceComponentNameA,
184                            "id A 1"),
185                    "Phone account service A ID 1")
186                    .addSupportedUriScheme("tel")
187                    .setCapabilities(
188                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
189                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
190                    .build();
191    final PhoneAccount mPhoneAccountB0 =
192            PhoneAccount.builder(
193                    new PhoneAccountHandle(
194                            mConnectionServiceComponentNameB,
195                            "id B 0"),
196                    "Phone account service B ID 0")
197                    .addSupportedUriScheme("tel")
198                    .setCapabilities(
199                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
200                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
201                    .build();
202
203    ConnectionServiceFixture mConnectionServiceFixtureA;
204    ConnectionServiceFixture mConnectionServiceFixtureB;
205
206    CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture;
207
208    IAudioService mAudioService;
209
210    TelecomSystem mTelecomSystem;
211
212    Context mSpyContext;
213
214    private int mNumOutgoingCallsMade;
215
216    class IdPair {
217        final String mConnectionId;
218        final String mCallId;
219
220        public IdPair(String connectionId, String callId) {
221            this.mConnectionId = connectionId;
222            this.mCallId = callId;
223        }
224    }
225
226    @Override
227    public void setUp() throws Exception {
228        super.setUp();
229        mSpyContext = mComponentContextFixture.getTestDouble().getApplicationContext();
230        doReturn(mSpyContext).when(mSpyContext).getApplicationContext();
231
232        mNumOutgoingCallsMade = 0;
233
234        // First set up information about the In-Call services in the mock Context, since
235        // Telecom will search for these as soon as it is instantiated
236        setupInCallServices();
237
238        // Next, create the TelecomSystem, our system under test
239        setupTelecomSystem();
240
241        // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the
242        // now-running TelecomSystem
243        setupConnectionServices();
244    }
245
246    @Override
247    public void tearDown() throws Exception {
248        mTelecomSystem = null;
249        super.tearDown();
250    }
251
252    protected ParcelableCall makeConferenceCall() throws Exception {
253        IdPair callId1 = startAndMakeActiveOutgoingCall("650-555-1212",
254                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
255
256        IdPair callId2 = startAndMakeActiveOutgoingCall("650-555-1213",
257                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
258
259        IInCallAdapter inCallAdapter = mInCallServiceFixtureX.getInCallAdapter();
260        inCallAdapter.conference(callId1.mCallId, callId2.mCallId);
261        // Wait for wacky non-deterministic behavior
262        Thread.sleep(200);
263        ParcelableCall call1 = mInCallServiceFixtureX.getCall(callId1.mCallId);
264        ParcelableCall call2 = mInCallServiceFixtureX.getCall(callId2.mCallId);
265        // Check that the two calls end up with a parent in the end
266        assertNotNull(call1.getParentCallId());
267        assertNotNull(call2.getParentCallId());
268        assertEquals(call1.getParentCallId(), call2.getParentCallId());
269
270        // Check to make sure that the parent call made it to the in-call service
271        String parentCallId = call1.getParentCallId();
272        ParcelableCall conferenceCall = mInCallServiceFixtureX.getCall(parentCallId);
273        assertEquals(2, conferenceCall.getChildCallIds().size());
274        assertTrue(conferenceCall.getChildCallIds().contains(callId1.mCallId));
275        assertTrue(conferenceCall.getChildCallIds().contains(callId2.mCallId));
276        return conferenceCall;
277    }
278
279    private void setupTelecomSystem() throws Exception {
280        // Use actual implementations instead of mocking the interface out.
281        HeadsetMediaButtonFactory headsetMediaButtonFactory =
282                spy(new HeadsetMediaButtonFactoryF());
283        ProximitySensorManagerFactory proximitySensorManagerFactory =
284                spy(new ProximitySensorManagerFactoryF());
285        InCallWakeLockControllerFactory inCallWakeLockControllerFactory =
286                spy(new InCallWakeLockControllerFactoryF());
287        mAudioService = setupAudioService();
288
289        mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
290
291        mTelecomSystem = new TelecomSystem(
292                mComponentContextFixture.getTestDouble(),
293                new MissedCallNotifierImplFactory() {
294                    @Override
295                    public MissedCallNotifier makeMissedCallNotifierImpl(Context context,
296                            PhoneAccountRegistrar phoneAccountRegistrar) {
297                        return mMissedCallNotifier;
298                    }
299                },
300                mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
301                headsetMediaButtonFactory,
302                proximitySensorManagerFactory,
303                inCallWakeLockControllerFactory,
304                new CallAudioManager.AudioServiceFactory() {
305                    @Override
306                    public IAudioService getAudioService() {
307                        return mAudioService;
308                    }
309                },
310                new BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory() {
311                    @Override
312                    public BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
313                            TelecomSystem.SyncRoot lock, CallsManager callsManager,
314                            PhoneAccountRegistrar phoneAccountRegistrar) {
315                        return mBluetoothPhoneServiceImpl;
316                    }
317                });
318
319        mComponentContextFixture.setTelecomManager(new TelecomManager(
320                mComponentContextFixture.getTestDouble(),
321                mTelecomSystem.getTelecomServiceImpl().getBinder()));
322
323        verify(headsetMediaButtonFactory).create(
324                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
325                any(CallsManager.class),
326                any(TelecomSystem.SyncRoot.class));
327        verify(proximitySensorManagerFactory).create(
328                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
329                any(CallsManager.class));
330        verify(inCallWakeLockControllerFactory).create(
331                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
332                any(CallsManager.class));
333    }
334
335    private void setupConnectionServices() throws Exception {
336        mConnectionServiceFixtureA = new ConnectionServiceFixture();
337        mConnectionServiceFixtureB = new ConnectionServiceFixture();
338
339        mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameA,
340                mConnectionServiceFixtureA.getTestDouble());
341        mComponentContextFixture.addConnectionService(mConnectionServiceComponentNameB,
342                mConnectionServiceFixtureB.getTestDouble());
343
344        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
345        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
346        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
347
348        mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
349                mPhoneAccountA0.getAccountHandle(), Process.myUserHandle());
350    }
351
352    private void setupInCallServices() throws Exception {
353        mComponentContextFixture.putResource(
354                com.android.server.telecom.R.string.ui_default_package,
355                mInCallServiceComponentNameX.getPackageName());
356        mComponentContextFixture.putResource(
357                com.android.server.telecom.R.string.incall_default_class,
358                mInCallServiceComponentNameX.getClassName());
359        mComponentContextFixture.putBooleanResource(
360                com.android.internal.R.bool.config_voice_capable, true);
361
362        mInCallServiceFixtureX = new InCallServiceFixture();
363        mInCallServiceFixtureY = new InCallServiceFixture();
364
365        mComponentContextFixture.addInCallService(mInCallServiceComponentNameX,
366                mInCallServiceFixtureX.getTestDouble());
367        mComponentContextFixture.addInCallService(mInCallServiceComponentNameY,
368                mInCallServiceFixtureY.getTestDouble());
369    }
370
371    /**
372     * Helper method for setting up the fake audio service.
373     * Calls to the fake audio service need to toggle the return
374     * value of AudioManager#isMicrophoneMute.
375     * @return mock of IAudioService
376     */
377    private IAudioService setupAudioService() {
378        IAudioService audioService = mock(IAudioService.class);
379
380        final AudioManager fakeAudioManager =
381                (AudioManager) mComponentContextFixture.getTestDouble()
382                        .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
383
384        try {
385            doAnswer(new Answer() {
386                @Override
387                public Object answer(InvocationOnMock i) {
388                    Object[] args = i.getArguments();
389                    doReturn(args[0]).when(fakeAudioManager).isMicrophoneMute();
390                    return null;
391                }
392            }).when(audioService)
393                    .setMicrophoneMute(any(Boolean.class), any(String.class), any(Integer.class));
394
395        } catch (android.os.RemoteException e) {
396            // Do nothing, leave the faked microphone state as-is
397        }
398        return audioService;
399    }
400
401    protected String startOutgoingPhoneCallWithNoPhoneAccount(String number,
402            ConnectionServiceFixture connectionServiceFixture)
403            throws Exception {
404
405        return startOutgoingPhoneCallPendingCreateConnection(number, null,
406                connectionServiceFixture, Process.myUserHandle(), VideoProfile.STATE_AUDIO_ONLY);
407    }
408
409    protected IdPair outgoingCallPhoneAccountSelected(PhoneAccountHandle phoneAccountHandle,
410            int startingNumConnections, int startingNumCalls,
411            ConnectionServiceFixture connectionServiceFixture) throws Exception {
412
413        IdPair ids = outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
414                phoneAccountHandle, connectionServiceFixture);
415
416        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
417        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
418        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
419
420        connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
421
422        connectionServiceFixture.sendSetActive(ids.mConnectionId);
423        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
424        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
425
426        return ids;
427    }
428
429    protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
430            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser)
431            throws Exception {
432
433        return startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
434                initiatingUser, VideoProfile.STATE_AUDIO_ONLY);
435    }
436
437    protected IdPair startOutgoingPhoneCall(String number, PhoneAccountHandle phoneAccountHandle,
438            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
439            int videoState) throws Exception {
440        int startingNumConnections = connectionServiceFixture.mConnectionById.size();
441        int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
442
443        startOutgoingPhoneCallPendingCreateConnection(number, phoneAccountHandle,
444                connectionServiceFixture, initiatingUser, videoState);
445
446        return outgoingCallCreateConnectionComplete(startingNumConnections, startingNumCalls,
447                phoneAccountHandle, connectionServiceFixture);
448    }
449
450    protected String startOutgoingPhoneCallPendingCreateConnection(String number,
451            PhoneAccountHandle phoneAccountHandle,
452            ConnectionServiceFixture connectionServiceFixture, UserHandle initiatingUser,
453            int videoState) throws Exception {
454        reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
455                mInCallServiceFixtureY.getTestDouble());
456
457        assertEquals(mInCallServiceFixtureX.mCallById.size(),
458                mInCallServiceFixtureY.mCallById.size());
459        assertEquals((mInCallServiceFixtureX.mInCallAdapter != null),
460                (mInCallServiceFixtureY.mInCallAdapter != null));
461
462        mNumOutgoingCallsMade++;
463
464        boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
465
466        Intent actionCallIntent = new Intent();
467        actionCallIntent.setData(Uri.parse("tel:" + number));
468        actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
469        actionCallIntent.setAction(Intent.ACTION_CALL);
470        if (phoneAccountHandle != null) {
471            actionCallIntent.putExtra(
472                    TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
473                    phoneAccountHandle);
474        }
475        if (videoState != VideoProfile.STATE_AUDIO_ONLY) {
476            actionCallIntent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
477        }
478
479        final UserHandle userHandle = initiatingUser;
480        Context localAppContext = mComponentContextFixture.getTestDouble().getApplicationContext();
481        new UserCallIntentProcessor(localAppContext, userHandle).processIntent(
482                actionCallIntent, null, true /* hasCallAppOp*/);
483        // UserCallIntentProcessor's mContext.sendBroadcastAsUser(...) will call to an empty method
484        // as to not actually try to send an intent to PrimaryCallReceiver. We verify that it was
485        // called correctly in order to continue.
486        verify(localAppContext).sendBroadcastAsUser(actionCallIntent, UserHandle.SYSTEM);
487        mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent);
488
489        if (!hasInCallAdapter) {
490            verify(mInCallServiceFixtureX.getTestDouble())
491                    .setInCallAdapter(
492                            any(IInCallAdapter.class));
493            verify(mInCallServiceFixtureY.getTestDouble())
494                    .setInCallAdapter(
495                            any(IInCallAdapter.class));
496        }
497
498        ArgumentCaptor<Intent> newOutgoingCallIntent =
499                ArgumentCaptor.forClass(Intent.class);
500        ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver =
501                ArgumentCaptor.forClass(BroadcastReceiver.class);
502
503        verify(mComponentContextFixture.getTestDouble().getApplicationContext(),
504                times(mNumOutgoingCallsMade))
505                .sendOrderedBroadcastAsUser(
506                        newOutgoingCallIntent.capture(),
507                        any(UserHandle.class),
508                        anyString(),
509                        anyInt(),
510                        newOutgoingCallReceiver.capture(),
511                        any(Handler.class),
512                        anyInt(),
513                        anyString(),
514                        any(Bundle.class));
515
516        // Pass on the new outgoing call Intent
517        // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
518        newOutgoingCallReceiver.getValue().setPendingResult(
519                new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
520        newOutgoingCallReceiver.getValue().setResultData(
521                newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER));
522        newOutgoingCallReceiver.getValue().onReceive(mComponentContextFixture.getTestDouble(),
523                newOutgoingCallIntent.getValue());
524
525        return mInCallServiceFixtureX.mLatestCallId;
526    }
527
528    protected IdPair outgoingCallCreateConnectionComplete(int startingNumConnections,
529            int startingNumCalls, PhoneAccountHandle phoneAccountHandle,
530            ConnectionServiceFixture connectionServiceFixture) throws Exception {
531
532        assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
533
534        verify(connectionServiceFixture.getTestDouble())
535                .createConnection(eq(phoneAccountHandle), anyString(), any(ConnectionRequest.class),
536                        anyBoolean(), anyBoolean());
537        connectionServiceFixture.sendHandleCreateConnectionComplete(
538                connectionServiceFixture.mLatestConnectionId);
539
540        assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
541        assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
542
543        assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
544
545        return new IdPair(connectionServiceFixture.mLatestConnectionId,
546                mInCallServiceFixtureX.mLatestCallId);
547    }
548
549    protected IdPair startIncomingPhoneCall(
550            String number,
551            PhoneAccountHandle phoneAccountHandle,
552            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
553        return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
554                connectionServiceFixture);
555    }
556
557    protected IdPair startIncomingPhoneCall(
558            String number,
559            PhoneAccountHandle phoneAccountHandle,
560            int videoState,
561            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
562        reset(connectionServiceFixture.getTestDouble(), mInCallServiceFixtureX.getTestDouble(),
563                mInCallServiceFixtureY.getTestDouble());
564
565        assertEquals(mInCallServiceFixtureX.mCallById.size(),
566                mInCallServiceFixtureY.mCallById.size());
567        assertEquals((mInCallServiceFixtureX.mInCallAdapter != null),
568                (mInCallServiceFixtureY.mInCallAdapter != null));
569        final int startingNumConnections = connectionServiceFixture.mConnectionById.size();
570        final int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
571        boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
572        connectionServiceFixture.mConnectionServiceDelegate.mVideoState = videoState;
573
574        Bundle extras = new Bundle();
575        extras.putParcelable(
576                TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
577                Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
578        mTelecomSystem.getTelecomServiceImpl().getBinder()
579                .addNewIncomingCall(phoneAccountHandle, extras);
580
581        verify(connectionServiceFixture.getTestDouble())
582                .createConnection(any(PhoneAccountHandle.class), anyString(),
583                        any(ConnectionRequest.class), eq(true), eq(false));
584
585        for (CallerInfoAsyncQueryFactoryFixture.Request request :
586                mCallerInfoAsyncQueryFactoryFixture.mRequests) {
587            request.reply();
588        }
589
590        IContentProvider blockedNumberProvider =
591                mSpyContext.getContentResolver().acquireProvider(BlockedNumberContract.AUTHORITY);
592        verify(blockedNumberProvider, timeout(TEST_TIMEOUT)).call(
593                anyString(),
594                eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
595                eq(number),
596                isNull(Bundle.class));
597
598        // For the case of incoming calls, Telecom connecting the InCall services and adding the
599        // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
600        // is added, future interactions as triggered by the ConnectionService, through the various
601        // test fixtures, will be synchronous.
602
603        if (!hasInCallAdapter) {
604            verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
605                    .setInCallAdapter(any(IInCallAdapter.class));
606            verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
607                    .setInCallAdapter(any(IInCallAdapter.class));
608        }
609
610        // Give the InCallService time to respond
611
612        assertTrueWithTimeout(new Predicate<Void>() {
613            @Override
614            public boolean apply(Void v) {
615                return mInCallServiceFixtureX.mInCallAdapter != null;
616            }
617        });
618
619        assertTrueWithTimeout(new Predicate<Void>() {
620            @Override
621            public boolean apply(Void v) {
622                return mInCallServiceFixtureY.mInCallAdapter != null;
623            }
624        });
625
626        verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
627                .addCall(any(ParcelableCall.class));
628        verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
629                .addCall(any(ParcelableCall.class));
630
631        // Give the InCallService time to respond
632
633        assertTrueWithTimeout(new Predicate<Void>() {
634            @Override
635            public boolean apply(Void v) {
636                return startingNumConnections + 1 ==
637                        connectionServiceFixture.mConnectionById.size();
638            }
639        });
640        assertTrueWithTimeout(new Predicate<Void>() {
641            @Override
642            public boolean apply(Void v) {
643                return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
644            }
645        });
646        assertTrueWithTimeout(new Predicate<Void>() {
647            @Override
648            public boolean apply(Void v) {
649                return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
650            }
651        });
652
653        assertEquals(mInCallServiceFixtureX.mLatestCallId, mInCallServiceFixtureY.mLatestCallId);
654
655        return new IdPair(connectionServiceFixture.mLatestConnectionId,
656                mInCallServiceFixtureX.mLatestCallId);
657    }
658
659    protected IdPair startAndMakeActiveOutgoingCall(
660            String number,
661            PhoneAccountHandle phoneAccountHandle,
662            ConnectionServiceFixture connectionServiceFixture) throws Exception {
663        return startAndMakeActiveOutgoingCall(number, phoneAccountHandle, connectionServiceFixture,
664                VideoProfile.STATE_AUDIO_ONLY);
665    }
666
667    // A simple outgoing call, verifying that the appropriate connection service is contacted,
668    // the proper lifecycle is followed, and both In-Call Services are updated correctly.
669    protected IdPair startAndMakeActiveOutgoingCall(
670            String number,
671            PhoneAccountHandle phoneAccountHandle,
672            ConnectionServiceFixture connectionServiceFixture, int videoState) throws Exception {
673        IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
674                Process.myUserHandle(), videoState);
675
676        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
677        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
678        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
679
680        connectionServiceFixture.sendSetVideoState(ids.mConnectionId);
681
682        connectionServiceFixture.sendSetActive(ids.mConnectionId);
683        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
684        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
685
686        return ids;
687    }
688
689    protected IdPair startAndMakeActiveIncomingCall(
690            String number,
691            PhoneAccountHandle phoneAccountHandle,
692            ConnectionServiceFixture connectionServiceFixture) throws Exception {
693        return startAndMakeActiveIncomingCall(number, phoneAccountHandle, connectionServiceFixture,
694                VideoProfile.STATE_AUDIO_ONLY);
695    }
696
697    // A simple incoming call, similar in scope to the previous test
698    protected IdPair startAndMakeActiveIncomingCall(
699            String number,
700            PhoneAccountHandle phoneAccountHandle,
701            ConnectionServiceFixture connectionServiceFixture,
702            int videoState) throws Exception {
703        IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
704
705        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
706        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
707
708        mInCallServiceFixtureX.mInCallAdapter
709                .answerCall(ids.mCallId, videoState);
710
711        if (!VideoProfile.isVideo(videoState)) {
712            verify(connectionServiceFixture.getTestDouble())
713                    .answer(ids.mConnectionId);
714        } else {
715            verify(connectionServiceFixture.getTestDouble())
716                    .answerVideo(ids.mConnectionId, videoState);
717        }
718
719        connectionServiceFixture.sendSetActive(ids.mConnectionId);
720        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
721        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
722
723        return ids;
724    }
725
726    protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
727        int elapsed = 0;
728        while (elapsed < TEST_TIMEOUT) {
729            if (predicate.apply(null)) {
730                return;
731            } else {
732                try {
733                    Thread.sleep(TEST_POLL_INTERVAL);
734                    elapsed += TEST_POLL_INTERVAL;
735                } catch (InterruptedException e) {
736                    fail(e.toString());
737                }
738            }
739        }
740        fail("Timeout in assertTrueWithTimeout");
741    }
742}
743