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