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