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