TelecomSystemTest.java revision 4a3e2fd2b783c9f667a719237ef527ebe686fe32
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.Mockito.doAnswer;
26import static org.mockito.Mockito.doReturn;
27import static org.mockito.Mockito.mock;
28import static org.mockito.Mockito.reset;
29import static org.mockito.Mockito.spy;
30import static org.mockito.Mockito.timeout;
31import static org.mockito.Mockito.times;
32import static org.mockito.Mockito.verify;
33
34import android.content.BroadcastReceiver;
35import android.content.ComponentName;
36import android.content.Context;
37import android.content.Intent;
38import android.media.AudioManager;
39import android.media.IAudioService;
40import android.net.Uri;
41import android.os.Bundle;
42import android.os.Handler;
43import android.os.Process;
44import android.os.UserHandle;
45import android.telecom.Call;
46import android.telecom.CallAudioState;
47import android.telecom.Connection;
48import android.telecom.ConnectionRequest;
49import android.telecom.DisconnectCause;
50import android.telecom.ParcelableCall;
51import android.telecom.PhoneAccount;
52import android.telecom.PhoneAccountHandle;
53import android.telecom.TelecomManager;
54import android.telecom.VideoProfile;
55
56import com.android.internal.telecom.IInCallAdapter;
57import com.android.server.telecom.BluetoothPhoneServiceImpl;
58import com.android.server.telecom.CallAudioManager;
59import com.android.server.telecom.CallIntentProcessor;
60import com.android.server.telecom.CallerInfoAsyncQueryFactory;
61import com.android.server.telecom.CallsManager;
62import com.android.server.telecom.CallsManagerListenerBase;
63import com.android.server.telecom.ContactsAsyncHelper;
64import com.android.server.telecom.HeadsetMediaButton;
65import com.android.server.telecom.HeadsetMediaButtonFactory;
66import com.android.server.telecom.InCallWakeLockController;
67import com.android.server.telecom.InCallWakeLockControllerFactory;
68import com.android.server.telecom.Log;
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.components.UserCallIntentProcessor;
75
76import com.google.common.base.Predicate;
77
78import org.mockito.ArgumentCaptor;
79import org.mockito.Mock;
80import org.mockito.invocation.InvocationOnMock;
81import org.mockito.stubbing.Answer;
82
83import java.util.concurrent.BrokenBarrierException;
84import java.util.concurrent.CountDownLatch;
85import java.util.concurrent.CyclicBarrier;
86
87public class TelecomSystemTest extends TelecomTestCase {
88
89    static final int TEST_POLL_INTERVAL = 10;  // milliseconds
90    static final int TEST_TIMEOUT = 1000;  // milliseconds
91
92    public class HeadsetMediaButtonFactoryF implements HeadsetMediaButtonFactory  {
93        @Override
94        public HeadsetMediaButton create(Context context, CallsManager callsManager,
95                TelecomSystem.SyncRoot lock) {
96            return mHeadsetMediaButton;
97        }
98    }
99
100    public class ProximitySensorManagerFactoryF implements ProximitySensorManagerFactory {
101        @Override
102        public ProximitySensorManager create(Context context, CallsManager callsManager) {
103            return mProximitySensorManager;
104        }
105    }
106
107    public class InCallWakeLockControllerFactoryF implements InCallWakeLockControllerFactory {
108        @Override
109        public InCallWakeLockController create(Context context, CallsManager callsManager) {
110            return mInCallWakeLockController;
111        }
112    }
113
114    public static class MissedCallNotifierFakeImpl extends CallsManagerListenerBase
115            implements MissedCallNotifier {
116        @Override
117        public void clearMissedCalls() {
118
119        }
120
121        @Override
122        public void showMissedCallNotification(com.android.server.telecom.Call call) {
123
124        }
125
126        @Override
127        public void updateOnStartup(TelecomSystem.SyncRoot lock, CallsManager callsManager,
128                ContactsAsyncHelper contactsAsyncHelper,
129                CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory) {
130
131        }
132    }
133
134    MissedCallNotifier mMissedCallNotifier = new MissedCallNotifierFakeImpl();
135    @Mock HeadsetMediaButton mHeadsetMediaButton;
136    @Mock ProximitySensorManager mProximitySensorManager;
137    @Mock InCallWakeLockController mInCallWakeLockController;
138    @Mock BluetoothPhoneServiceImpl mBluetoothPhoneServiceImpl;
139
140    final ComponentName mInCallServiceComponentNameX =
141            new ComponentName(
142                    "incall-service-package-X",
143                    "incall-service-class-X");
144    final ComponentName mInCallServiceComponentNameY =
145            new ComponentName(
146                    "incall-service-package-Y",
147                    "incall-service-class-Y");
148
149    InCallServiceFixture mInCallServiceFixtureX;
150    InCallServiceFixture mInCallServiceFixtureY;
151
152    final ComponentName mConnectionServiceComponentNameA =
153            new ComponentName(
154                    "connection-service-package-A",
155                    "connection-service-class-A");
156    final ComponentName mConnectionServiceComponentNameB =
157            new ComponentName(
158                    "connection-service-package-B",
159                    "connection-service-class-B");
160
161    final PhoneAccount mPhoneAccountA0 =
162            PhoneAccount.builder(
163                    new PhoneAccountHandle(
164                            mConnectionServiceComponentNameA,
165                            "id A 0"),
166                    "Phone account service A ID 0")
167                    .addSupportedUriScheme("tel")
168                    .setCapabilities(
169                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
170                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
171                    .build();
172    final PhoneAccount mPhoneAccountA1 =
173            PhoneAccount.builder(
174                    new PhoneAccountHandle(
175                            mConnectionServiceComponentNameA,
176                            "id A 1"),
177                    "Phone account service A ID 1")
178                    .addSupportedUriScheme("tel")
179                    .setCapabilities(
180                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
181                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
182                    .build();
183    final PhoneAccount mPhoneAccountB0 =
184            PhoneAccount.builder(
185                    new PhoneAccountHandle(
186                            mConnectionServiceComponentNameB,
187                            "id B 0"),
188                    "Phone account service B ID 0")
189                    .addSupportedUriScheme("tel")
190                    .setCapabilities(
191                            PhoneAccount.CAPABILITY_CALL_PROVIDER |
192                            PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION)
193                    .build();
194
195    ConnectionServiceFixture mConnectionServiceFixtureA;
196    ConnectionServiceFixture mConnectionServiceFixtureB;
197
198    CallerInfoAsyncQueryFactoryFixture mCallerInfoAsyncQueryFactoryFixture;
199
200    IAudioService mAudioService;
201
202    TelecomSystem mTelecomSystem;
203
204    private int mNumOutgoingCallsMade;
205
206    class IdPair {
207        final String mConnectionId;
208        final String mCallId;
209
210        public IdPair(String connectionId, String callId) {
211            this.mConnectionId = connectionId;
212            this.mCallId = callId;
213        }
214    }
215
216    @Override
217    public void setUp() throws Exception {
218        super.setUp();
219        mNumOutgoingCallsMade = 0;
220
221        // First set up information about the In-Call services in the mock Context, since
222        // Telecom will search for these as soon as it is instantiated
223        setupInCallServices();
224
225        // Next, create the TelecomSystem, our system under test
226        setupTelecomSystem();
227
228        // Finally, register the ConnectionServices with the PhoneAccountRegistrar of the
229        // now-running TelecomSystem
230        setupConnectionServices();
231    }
232
233    @Override
234    public void tearDown() throws Exception {
235        mTelecomSystem = null;
236        super.tearDown();
237    }
238
239    private void setupTelecomSystem() throws Exception {
240        // Use actual implementations instead of mocking the interface out.
241        HeadsetMediaButtonFactory headsetMediaButtonFactory =
242                spy(new HeadsetMediaButtonFactoryF());
243        ProximitySensorManagerFactory proximitySensorManagerFactory =
244                spy(new ProximitySensorManagerFactoryF());
245        InCallWakeLockControllerFactory inCallWakeLockControllerFactory =
246                spy(new InCallWakeLockControllerFactoryF());
247        mAudioService = setupAudioService();
248
249        mCallerInfoAsyncQueryFactoryFixture = new CallerInfoAsyncQueryFactoryFixture();
250
251        mTelecomSystem = new TelecomSystem(
252                mComponentContextFixture.getTestDouble(),
253                mMissedCallNotifier,
254                mCallerInfoAsyncQueryFactoryFixture.getTestDouble(),
255                headsetMediaButtonFactory,
256                proximitySensorManagerFactory,
257                inCallWakeLockControllerFactory,
258                new CallAudioManager.AudioServiceFactory() {
259                    @Override
260                    public IAudioService getAudioService() {
261                        return mAudioService;
262                    }
263                },
264                new BluetoothPhoneServiceImpl.BluetoothPhoneServiceImplFactory() {
265                    @Override
266                    public BluetoothPhoneServiceImpl makeBluetoothPhoneServiceImpl(Context context,
267                            TelecomSystem.SyncRoot lock, CallsManager callsManager,
268                            PhoneAccountRegistrar phoneAccountRegistrar) {
269                        return mBluetoothPhoneServiceImpl;
270                    }
271                });
272
273        mComponentContextFixture.setTelecomManager(new TelecomManager(
274                mComponentContextFixture.getTestDouble(),
275                mTelecomSystem.getTelecomServiceImpl().getBinder()));
276
277        verify(headsetMediaButtonFactory).create(
278                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
279                any(CallsManager.class),
280                any(TelecomSystem.SyncRoot.class));
281        verify(proximitySensorManagerFactory).create(
282                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
283                any(CallsManager.class));
284        verify(inCallWakeLockControllerFactory).create(
285                eq(mComponentContextFixture.getTestDouble().getApplicationContext()),
286                any(CallsManager.class));
287    }
288
289    private void setupConnectionServices() throws Exception {
290        mConnectionServiceFixtureA = new ConnectionServiceFixture();
291        mConnectionServiceFixtureB = new ConnectionServiceFixture();
292
293        mComponentContextFixture.addConnectionService(
294                mConnectionServiceComponentNameA,
295                mConnectionServiceFixtureA.getTestDouble());
296        mComponentContextFixture.addConnectionService(
297                mConnectionServiceComponentNameB,
298                mConnectionServiceFixtureB.getTestDouble());
299
300        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA0);
301        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountA1);
302        mTelecomSystem.getPhoneAccountRegistrar().registerPhoneAccount(mPhoneAccountB0);
303
304        mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
305                mPhoneAccountA0.getAccountHandle(), Process.myUserHandle());
306    }
307
308    private void setupInCallServices() throws Exception {
309        mComponentContextFixture.putResource(
310                com.android.server.telecom.R.string.ui_default_package,
311                mInCallServiceComponentNameX.getPackageName());
312        mComponentContextFixture.putResource(
313                com.android.server.telecom.R.string.incall_default_class,
314                mInCallServiceComponentNameX.getClassName());
315
316        mInCallServiceFixtureX = new InCallServiceFixture();
317        mInCallServiceFixtureY = new InCallServiceFixture();
318
319        mComponentContextFixture.addInCallService(
320                mInCallServiceComponentNameX,
321                mInCallServiceFixtureX.getTestDouble());
322        mComponentContextFixture.addInCallService(
323                mInCallServiceComponentNameY,
324                mInCallServiceFixtureY.getTestDouble());
325    }
326
327    /**
328     * Helper method for setting up the fake audio service.
329     * Calls to the fake audio service need to toggle the return
330     * value of AudioManager#isMicrophoneMute.
331     * @return mock of IAudioService
332     */
333    private IAudioService setupAudioService() {
334        IAudioService audioService = mock(IAudioService.class);
335
336        final AudioManager fakeAudioManager =
337                (AudioManager) mComponentContextFixture.getTestDouble()
338                        .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
339
340        try {
341            doAnswer(new Answer() {
342                @Override
343                public Object answer(InvocationOnMock i) {
344                    Object[] args = i.getArguments();
345                    doReturn(args[0]).when(fakeAudioManager).isMicrophoneMute();
346                    return null;
347                }
348            }).when(audioService)
349                    .setMicrophoneMute(any(Boolean.class), any(String.class), any(Integer.class));
350
351        } catch (android.os.RemoteException e) {
352            // Do nothing, leave the faked microphone state as-is
353        }
354        return audioService;
355    }
356
357    private IdPair startOutgoingPhoneCall(
358            String number,
359            PhoneAccountHandle phoneAccountHandle,
360            ConnectionServiceFixture connectionServiceFixture,
361            UserHandle initiatingUser) throws Exception {
362        reset(
363                connectionServiceFixture.getTestDouble(),
364                mInCallServiceFixtureX.getTestDouble(),
365                mInCallServiceFixtureY.getTestDouble());
366
367        assertEquals(
368                mInCallServiceFixtureX.mCallById.size(),
369                mInCallServiceFixtureY.mCallById.size());
370        assertEquals(
371                (mInCallServiceFixtureX.mInCallAdapter != null),
372                (mInCallServiceFixtureY.mInCallAdapter != null));
373
374        mNumOutgoingCallsMade++;
375        int startingNumConnections = connectionServiceFixture.mConnectionById.size();
376        int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
377        boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
378
379        Intent actionCallIntent = new Intent();
380        actionCallIntent.setData(Uri.parse("tel:" + number));
381        actionCallIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
382        actionCallIntent.setAction(Intent.ACTION_CALL);
383        if (phoneAccountHandle != null) {
384            actionCallIntent.putExtra(
385                    TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE,
386                    phoneAccountHandle);
387        }
388
389        final UserHandle userHandle = initiatingUser;
390        new UserCallIntentProcessor(mContext, userHandle)
391                .processIntent(actionCallIntent, null, true /* hasCallAppOp*/);
392        mTelecomSystem.getCallIntentProcessor().processIntent(actionCallIntent);
393
394        assertEquals(userHandle,
395                actionCallIntent.getParcelableExtra(CallIntentProcessor.KEY_INITIATING_USER));
396
397        if (!hasInCallAdapter) {
398            verify(mInCallServiceFixtureX.getTestDouble())
399                    .setInCallAdapter(
400                            any(IInCallAdapter.class));
401            verify(mInCallServiceFixtureY.getTestDouble())
402                    .setInCallAdapter(
403                            any(IInCallAdapter.class));
404        }
405
406        ArgumentCaptor<Intent> newOutgoingCallIntent =
407                ArgumentCaptor.forClass(Intent.class);
408        ArgumentCaptor<BroadcastReceiver> newOutgoingCallReceiver =
409                ArgumentCaptor.forClass(BroadcastReceiver.class);
410
411        verify(mComponentContextFixture.getTestDouble().getApplicationContext(),
412                times(mNumOutgoingCallsMade))
413                .sendOrderedBroadcastAsUser(
414                        newOutgoingCallIntent.capture(),
415                        any(UserHandle.class),
416                        anyString(),
417                        anyInt(),
418                        newOutgoingCallReceiver.capture(),
419                        any(Handler.class),
420                        anyInt(),
421                        anyString(),
422                        any(Bundle.class));
423
424        // Pass on the new outgoing call Intent
425        // Set a dummy PendingResult so the BroadcastReceiver agrees to accept onReceive()
426        newOutgoingCallReceiver.getValue().setPendingResult(
427                new BroadcastReceiver.PendingResult(0, "", null, 0, true, false, null, 0, 0));
428        newOutgoingCallReceiver.getValue().setResultData(
429                newOutgoingCallIntent.getValue().getStringExtra(Intent.EXTRA_PHONE_NUMBER));
430        newOutgoingCallReceiver.getValue().onReceive(
431                mComponentContextFixture.getTestDouble(),
432                newOutgoingCallIntent.getValue());
433
434        assertEquals(startingNumConnections + 1, connectionServiceFixture.mConnectionById.size());
435
436        verify(connectionServiceFixture.getTestDouble()).createConnection(
437                eq(phoneAccountHandle),
438                anyString(),
439                any(ConnectionRequest.class),
440                anyBoolean(),
441                anyBoolean());
442
443        connectionServiceFixture.sendHandleCreateConnectionComplete(
444                connectionServiceFixture.mLatestConnectionId);
445
446        assertEquals(startingNumCalls + 1, mInCallServiceFixtureX.mCallById.size());
447        assertEquals(startingNumCalls + 1, mInCallServiceFixtureY.mCallById.size());
448
449        assertEquals(
450                mInCallServiceFixtureX.mLatestCallId,
451                mInCallServiceFixtureY.mLatestCallId);
452
453        return new IdPair(
454                connectionServiceFixture.mLatestConnectionId,
455                mInCallServiceFixtureX.mLatestCallId);
456    }
457
458    private IdPair startIncomingPhoneCall(
459            String number,
460            PhoneAccountHandle phoneAccountHandle,
461            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
462        return startIncomingPhoneCall(number, phoneAccountHandle, VideoProfile.STATE_AUDIO_ONLY,
463                connectionServiceFixture);
464    }
465
466    private IdPair startIncomingPhoneCall(
467            String number,
468            PhoneAccountHandle phoneAccountHandle,
469            int videoState,
470            final ConnectionServiceFixture connectionServiceFixture) throws Exception {
471        reset(
472                connectionServiceFixture.getTestDouble(),
473                mInCallServiceFixtureX.getTestDouble(),
474                mInCallServiceFixtureY.getTestDouble());
475
476        assertEquals(
477                mInCallServiceFixtureX.mCallById.size(),
478                mInCallServiceFixtureY.mCallById.size());
479        assertEquals(
480                (mInCallServiceFixtureX.mInCallAdapter != null),
481                (mInCallServiceFixtureY.mInCallAdapter != null));
482
483        final int startingNumConnections = connectionServiceFixture.mConnectionById.size();
484        final int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
485        boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
486
487        Bundle extras = new Bundle();
488        extras.putParcelable(
489                TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
490                Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
491        mTelecomSystem.getTelecomServiceImpl().getBinder()
492                .addNewIncomingCall(phoneAccountHandle, extras);
493
494        verify(connectionServiceFixture.getTestDouble()).createConnection(
495                any(PhoneAccountHandle.class),
496                anyString(),
497                any(ConnectionRequest.class),
498                eq(true),
499                eq(false));
500
501        mConnectionServiceFixtureA.mConnectionById.get(
502                connectionServiceFixture.mLatestConnectionId).videoState = videoState;
503
504        connectionServiceFixture.sendHandleCreateConnectionComplete(
505                connectionServiceFixture.mLatestConnectionId);
506        connectionServiceFixture.sendSetRinging(
507                connectionServiceFixture.mLatestConnectionId);
508        connectionServiceFixture.sendSetVideoState(
509                connectionServiceFixture.mLatestConnectionId);
510
511        // For the case of incoming calls, Telecom connecting the InCall services and adding the
512        // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
513        // is added, future interactions as triggered by the ConnectionService, through the various
514        // test fixtures, will be synchronous.
515
516        if (!hasInCallAdapter) {
517            verify(
518                    mInCallServiceFixtureX.getTestDouble(),
519                    timeout(TEST_TIMEOUT))
520                    .setInCallAdapter(
521                            any(IInCallAdapter.class));
522            verify(
523                    mInCallServiceFixtureY.getTestDouble(),
524                    timeout(TEST_TIMEOUT))
525                    .setInCallAdapter(
526                            any(IInCallAdapter.class));
527        }
528
529        // Give the InCallService time to respond
530
531        assertTrueWithTimeout(new Predicate<Void>() {
532            @Override
533            public boolean apply(Void v) {
534                return mInCallServiceFixtureX.mInCallAdapter != null;
535            }
536        });
537
538        assertTrueWithTimeout(new Predicate<Void>() {
539            @Override
540            public boolean apply(Void v) {
541                return mInCallServiceFixtureY.mInCallAdapter != null;
542            }
543        });
544
545        verify(
546                mInCallServiceFixtureX.getTestDouble(),
547                timeout(TEST_TIMEOUT))
548                .addCall(
549                        any(ParcelableCall.class));
550        verify(
551                mInCallServiceFixtureY.getTestDouble(),
552                timeout(TEST_TIMEOUT))
553                .addCall(
554                        any(ParcelableCall.class));
555
556        // Give the InCallService time to respond
557
558        assertTrueWithTimeout(new Predicate<Void>() {
559            @Override
560            public boolean apply(Void v) {
561                return startingNumConnections + 1 ==
562                        connectionServiceFixture.mConnectionById.size();
563            }
564        });
565        assertTrueWithTimeout(new Predicate<Void>() {
566            @Override
567            public boolean apply(Void v) {
568                return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
569            }
570        });
571        assertTrueWithTimeout(new Predicate<Void>() {
572            @Override
573            public boolean apply(Void v) {
574                return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
575            }
576        });
577
578        assertEquals(
579                mInCallServiceFixtureX.mLatestCallId,
580                mInCallServiceFixtureY.mLatestCallId);
581
582        return new IdPair(
583                connectionServiceFixture.mLatestConnectionId,
584                mInCallServiceFixtureX.mLatestCallId);
585    }
586
587    private void rapidFire(Runnable... tasks) {
588        final CyclicBarrier barrier = new CyclicBarrier(tasks.length);
589        final CountDownLatch latch = new CountDownLatch(tasks.length);
590        for (int i = 0; i < tasks.length; i++) {
591            final Runnable task = tasks[i];
592            new Thread(new Runnable() {
593                @Override
594                public void run() {
595                    try {
596                        barrier.await();
597                        task.run();
598                    } catch (InterruptedException | BrokenBarrierException e){
599                        Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
600                    } finally {
601                        latch.countDown();
602                    }
603                }
604            }).start();
605        }
606        try {
607            latch.await();
608        } catch (InterruptedException e) {
609            Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
610        }
611    }
612
613    // A simple outgoing call, verifying that the appropriate connection service is contacted,
614    // the proper lifecycle is followed, and both In-Call Services are updated correctly.
615    private IdPair startAndMakeActiveOutgoingCall(
616            String number,
617            PhoneAccountHandle phoneAccountHandle,
618            ConnectionServiceFixture connectionServiceFixture) throws Exception {
619        IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture,
620                Process.myUserHandle());
621
622        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
623        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
624        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
625
626        connectionServiceFixture.sendSetActive(ids.mConnectionId);
627        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
628        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
629
630        return ids;
631    }
632
633    public void testBasicConferenceCall() throws Exception {
634        makeConferenceCall();
635    }
636
637    public void testAddCallToConference1() throws Exception {
638        ParcelableCall conferenceCall = makeConferenceCall();
639        IdPair callId3 = startAndMakeActiveOutgoingCall(
640                "650-555-1214",
641                mPhoneAccountA0.getAccountHandle(),
642                mConnectionServiceFixtureA);
643        // testAddCallToConference{1,2} differ in the order of arguments to InCallAdapter#conference
644        mInCallServiceFixtureX.getInCallAdapter().conference(
645                conferenceCall.getId(), callId3.mCallId);
646        Thread.sleep(200);
647
648        ParcelableCall call3 = mInCallServiceFixtureX.getCall(callId3.mCallId);
649        ParcelableCall updatedConference = mInCallServiceFixtureX.getCall(conferenceCall.getId());
650        assertEquals(conferenceCall.getId(), call3.getParentCallId());
651        assertEquals(3, updatedConference.getChildCallIds().size());
652        assertTrue(updatedConference.getChildCallIds().contains(callId3.mCallId));
653    }
654
655    public void testAddCallToConference2() throws Exception {
656        ParcelableCall conferenceCall = makeConferenceCall();
657        IdPair callId3 = startAndMakeActiveOutgoingCall(
658                "650-555-1214",
659                mPhoneAccountA0.getAccountHandle(),
660                mConnectionServiceFixtureA);
661        mInCallServiceFixtureX.getInCallAdapter().conference(
662                callId3.mCallId, conferenceCall.getId());
663        Thread.sleep(200);
664
665        ParcelableCall call3 = mInCallServiceFixtureX.getCall(callId3.mCallId);
666        ParcelableCall updatedConference = mInCallServiceFixtureX.getCall(conferenceCall.getId());
667        assertEquals(conferenceCall.getId(), call3.getParentCallId());
668        assertEquals(3, updatedConference.getChildCallIds().size());
669        assertTrue(updatedConference.getChildCallIds().contains(callId3.mCallId));
670    }
671
672    private ParcelableCall makeConferenceCall() throws Exception {
673        IdPair callId1 = startAndMakeActiveOutgoingCall(
674                "650-555-1212",
675                mPhoneAccountA0.getAccountHandle(),
676                mConnectionServiceFixtureA);
677
678        IdPair callId2 = startAndMakeActiveOutgoingCall(
679                "650-555-1213",
680                mPhoneAccountA0.getAccountHandle(),
681                mConnectionServiceFixtureA);
682
683        IInCallAdapter inCallAdapter = mInCallServiceFixtureX.getInCallAdapter();
684        inCallAdapter.conference(callId1.mCallId, callId2.mCallId);
685        // Wait for wacky non-deterministic behavior
686        Thread.sleep(200);
687        ParcelableCall call1 = mInCallServiceFixtureX.getCall(callId1.mCallId);
688        ParcelableCall call2 = mInCallServiceFixtureX.getCall(callId2.mCallId);
689        // Check that the two calls end up with a parent in the end
690        assertNotNull(call1.getParentCallId());
691        assertNotNull(call2.getParentCallId());
692        assertEquals(call1.getParentCallId(), call2.getParentCallId());
693
694        // Check to make sure that the parent call made it to the in-call service
695        String parentCallId = call1.getParentCallId();
696        ParcelableCall conferenceCall = mInCallServiceFixtureX.getCall(parentCallId);
697        assertEquals(2, conferenceCall.getChildCallIds().size());
698        assertTrue(conferenceCall.getChildCallIds().contains(callId1.mCallId));
699        assertTrue(conferenceCall.getChildCallIds().contains(callId2.mCallId));
700        return conferenceCall;
701    }
702
703    public void testSingleOutgoingCallLocalDisconnect() throws Exception {
704        IdPair ids = startAndMakeActiveOutgoingCall(
705                "650-555-1212",
706                mPhoneAccountA0.getAccountHandle(),
707                mConnectionServiceFixtureA);
708
709        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
710        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
711        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
712
713        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
714        assertEquals(Call.STATE_DISCONNECTED,
715                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
716        assertEquals(Call.STATE_DISCONNECTED,
717                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
718    }
719
720    public void testSingleOutgoingCallRemoteDisconnect() throws Exception {
721        IdPair ids = startAndMakeActiveOutgoingCall(
722                "650-555-1212",
723                mPhoneAccountA0.getAccountHandle(),
724                mConnectionServiceFixtureA);
725
726        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
727        assertEquals(Call.STATE_DISCONNECTED,
728                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
729        assertEquals(Call.STATE_DISCONNECTED,
730                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
731    }
732
733    /**
734     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
735     * audio-only call.
736     *
737     * @throws Exception
738     */
739    public void testTelecomManagerAcceptRingingCall() throws Exception {
740        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
741                mConnectionServiceFixtureA);
742
743        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
744        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
745
746        // Use TelecomManager API to answer the ringing call.
747        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
748                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
749        telecomManager.acceptRingingCall();
750
751        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
752                .answer(ids.mCallId);
753    }
754
755    /**
756     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
757     * video call, which should be answered as video.
758     *
759     * @throws Exception
760     */
761    public void testTelecomManagerAcceptRingingVideoCall() throws Exception {
762        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
763                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
764
765        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
766        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
767
768        // Use TelecomManager API to answer the ringing call; the default expected behavior is to
769        // answer using whatever video state the ringing call requests.
770        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
771                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
772        telecomManager.acceptRingingCall();
773
774        // Answer video API should be called
775        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
776                .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
777    }
778
779    /**
780     * Tests the {@link TelecomManager#acceptRingingCall(int)} API.  Tests answering a video call
781     * as an audio call.
782     *
783     * @throws Exception
784     */
785    public void testTelecomManagerAcceptRingingVideoCallAsAudio() throws Exception {
786        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
787                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
788
789        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
790        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
791
792        // Use TelecomManager API to answer the ringing call.
793        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
794                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
795        telecomManager.acceptRingingCall(VideoProfile.STATE_AUDIO_ONLY);
796
797        // The generic answer method on the ConnectionService is used to answer audio-only calls.
798        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
799                .answer(eq(ids.mCallId));
800    }
801
802    /**
803     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
804     * video call, where an attempt is made to answer with an invalid video state.
805     *
806     * @throws Exception
807     */
808    public void testTelecomManagerAcceptRingingInvalidVideoState() throws Exception {
809        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
810                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
811
812        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
813        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
814
815        // Use TelecomManager API to answer the ringing call; the default expected behavior is to
816        // answer using whatever video state the ringing call requests.
817        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
818                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
819        telecomManager.acceptRingingCall(999 /* invalid videostate */);
820
821        // Answer video API should be called
822        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
823                .answerVideo(eq(ids.mCallId), eq(VideoProfile.STATE_BIDIRECTIONAL));
824    }
825
826    // A simple incoming call, similar in scope to the previous test
827    private IdPair startAndMakeActiveIncomingCall(
828            String number,
829            PhoneAccountHandle phoneAccountHandle,
830            ConnectionServiceFixture connectionServiceFixture) throws Exception {
831        IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
832
833        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
834        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
835
836        mInCallServiceFixtureX.mInCallAdapter
837                .answerCall(ids.mCallId, VideoProfile.STATE_AUDIO_ONLY);
838
839        verify(connectionServiceFixture.getTestDouble())
840                .answer(ids.mConnectionId);
841
842        connectionServiceFixture.sendSetActive(ids.mConnectionId);
843        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
844        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
845
846        return ids;
847    }
848
849    public void testSingleIncomingCallLocalDisconnect() throws Exception {
850        IdPair ids = startAndMakeActiveIncomingCall(
851                "650-555-1212",
852                mPhoneAccountA0.getAccountHandle(),
853                mConnectionServiceFixtureA);
854
855        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
856        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
857        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
858
859        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
860        assertEquals(Call.STATE_DISCONNECTED,
861                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
862        assertEquals(Call.STATE_DISCONNECTED,
863                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
864    }
865
866    public void testSingleIncomingCallRemoteDisconnect() throws Exception {
867        IdPair ids = startAndMakeActiveIncomingCall(
868                "650-555-1212",
869                mPhoneAccountA0.getAccountHandle(),
870                mConnectionServiceFixtureA);
871
872        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
873        assertEquals(Call.STATE_DISCONNECTED,
874                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
875        assertEquals(Call.STATE_DISCONNECTED,
876                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
877    }
878
879    public void do_testDeadlockOnOutgoingCall() throws Exception {
880        final IdPair ids = startOutgoingPhoneCall(
881                "650-555-1212",
882                mPhoneAccountA0.getAccountHandle(),
883                mConnectionServiceFixtureA,
884                Process.myUserHandle());
885        rapidFire(
886                new Runnable() {
887                    @Override
888                    public void run() {
889                        while (mCallerInfoAsyncQueryFactoryFixture.mRequests.size() > 0) {
890                            mCallerInfoAsyncQueryFactoryFixture.mRequests.remove(0).reply();
891                        }
892                    }
893                },
894                new Runnable() {
895                    @Override
896                    public void run() {
897                        try {
898                            mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
899                        } catch (Exception e) {
900                            Log.e(this, e, "");
901                        }
902                    }
903                });
904    }
905
906    public void testDeadlockOnOutgoingCall() throws Exception {
907        for (int i = 0; i < 100; i++) {
908            TelecomSystemTest test = new TelecomSystemTest();
909            test.setContext(getContext());
910            test.setTestContext(getTestContext());
911            test.setName(getName());
912            test.setUp();
913            test.do_testDeadlockOnOutgoingCall();
914            test.tearDown();
915        }
916    }
917
918    public void testIncomingThenOutgoingCalls() throws Exception {
919        // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
920        IdPair incoming = startAndMakeActiveIncomingCall(
921                "650-555-2323",
922                mPhoneAccountA0.getAccountHandle(),
923                mConnectionServiceFixtureA);
924        IdPair outgoing = startAndMakeActiveOutgoingCall(
925                "650-555-1212",
926                mPhoneAccountA0.getAccountHandle(),
927                mConnectionServiceFixtureA);
928    }
929
930    public void testOutgoingThenIncomingCalls() throws Exception {
931        // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
932        IdPair outgoing = startAndMakeActiveOutgoingCall(
933                "650-555-1212",
934                mPhoneAccountA0.getAccountHandle(),
935                mConnectionServiceFixtureA);
936        IdPair incoming = startAndMakeActiveIncomingCall(
937                "650-555-2323",
938                mPhoneAccountA0.getAccountHandle(),
939                mConnectionServiceFixtureA);
940        verify(mConnectionServiceFixtureA.getTestDouble())
941                .hold(outgoing.mConnectionId);
942        mConnectionServiceFixtureA.mConnectionById.get(outgoing.mConnectionId).state =
943                Connection.STATE_HOLDING;
944        mConnectionServiceFixtureA.sendSetOnHold(outgoing.mConnectionId);
945        assertEquals(
946                Call.STATE_HOLDING,
947                mInCallServiceFixtureX.getCall(outgoing.mCallId).getState());
948        assertEquals(
949                Call.STATE_HOLDING,
950                mInCallServiceFixtureY.getCall(outgoing.mCallId).getState());
951    }
952
953    public void testAudioManagerOperations() throws Exception {
954        AudioManager audioManager = (AudioManager) mComponentContextFixture.getTestDouble()
955                .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
956
957        IdPair outgoing = startAndMakeActiveOutgoingCall(
958                "650-555-1212",
959                mPhoneAccountA0.getAccountHandle(),
960                mConnectionServiceFixtureA);
961
962        verify(audioManager, timeout(TEST_TIMEOUT))
963                .requestAudioFocusForCall(anyInt(), anyInt());
964        verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce())
965                .setMode(AudioManager.MODE_IN_CALL);
966
967        mInCallServiceFixtureX.mInCallAdapter.mute(true);
968        verify(mAudioService, timeout(TEST_TIMEOUT))
969                .setMicrophoneMute(eq(true), any(String.class), any(Integer.class));
970        mInCallServiceFixtureX.mInCallAdapter.mute(false);
971        verify(mAudioService, timeout(TEST_TIMEOUT))
972                .setMicrophoneMute(eq(false), any(String.class), any(Integer.class));
973
974        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
975        verify(audioManager, timeout(TEST_TIMEOUT))
976                .setSpeakerphoneOn(true);
977        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
978        verify(audioManager, timeout(TEST_TIMEOUT))
979                .setSpeakerphoneOn(false);
980
981        mConnectionServiceFixtureA.
982                sendSetDisconnected(outgoing.mConnectionId, DisconnectCause.REMOTE);
983
984        verify(audioManager, timeout(TEST_TIMEOUT))
985                .abandonAudioFocusForCall();
986        verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce())
987                .setMode(AudioManager.MODE_NORMAL);
988    }
989
990    protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
991        int elapsed = 0;
992        while (elapsed < TEST_TIMEOUT) {
993            if (predicate.apply(null)) {
994                return;
995            } else {
996                try {
997                    Thread.sleep(TEST_POLL_INTERVAL);
998                    elapsed += TEST_POLL_INTERVAL;
999                } catch (InterruptedException e) {
1000                    fail(e.toString());
1001                }
1002            }
1003        }
1004        fail("Timeout in assertTrueWithTimeout");
1005    }
1006}
1007