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