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