TelecomSystemTest.java revision b3979ee8e636820cc5e68f26562bc02987e5d489
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        reset(
414                connectionServiceFixture.getTestDouble(),
415                mInCallServiceFixtureX.getTestDouble(),
416                mInCallServiceFixtureY.getTestDouble());
417
418        assertEquals(
419                mInCallServiceFixtureX.mCallById.size(),
420                mInCallServiceFixtureY.mCallById.size());
421        assertEquals(
422                (mInCallServiceFixtureX.mInCallAdapter != null),
423                (mInCallServiceFixtureY.mInCallAdapter != null));
424
425        final int startingNumConnections = connectionServiceFixture.mConnectionById.size();
426        final int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
427        boolean hasInCallAdapter = mInCallServiceFixtureX.mInCallAdapter != null;
428
429        Bundle extras = new Bundle();
430        extras.putParcelable(
431                TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
432                Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null));
433        mTelecomSystem.getTelecomServiceImpl().getBinder()
434                .addNewIncomingCall(phoneAccountHandle, extras);
435
436        verify(connectionServiceFixture.getTestDouble()).createConnection(
437                any(PhoneAccountHandle.class),
438                anyString(),
439                any(ConnectionRequest.class),
440                eq(true),
441                eq(false));
442
443        connectionServiceFixture.sendHandleCreateConnectionComplete(
444                connectionServiceFixture.mLatestConnectionId);
445        connectionServiceFixture.sendSetRinging(
446                connectionServiceFixture.mLatestConnectionId);
447
448        // For the case of incoming calls, Telecom connecting the InCall services and adding the
449        // Call is triggered by the async completion of the CallerInfoAsyncQuery. Once the Call
450        // is added, future interactions as triggered by the ConnectionService, through the various
451        // test fixtures, will be synchronous.
452
453        if (!hasInCallAdapter) {
454            verify(
455                    mInCallServiceFixtureX.getTestDouble(),
456                    timeout(TEST_TIMEOUT))
457                    .setInCallAdapter(
458                            any(IInCallAdapter.class));
459            verify(
460                    mInCallServiceFixtureY.getTestDouble(),
461                    timeout(TEST_TIMEOUT))
462                    .setInCallAdapter(
463                            any(IInCallAdapter.class));
464        }
465
466        // Give the InCallService time to respond
467
468        assertTrueWithTimeout(new Predicate<Void>() {
469            @Override
470            public boolean apply(Void v) {
471                return mInCallServiceFixtureX.mInCallAdapter != null;
472            }
473        });
474
475        assertTrueWithTimeout(new Predicate<Void>() {
476            @Override
477            public boolean apply(Void v) {
478                return mInCallServiceFixtureY.mInCallAdapter != null;
479            }
480        });
481
482        verify(
483                mInCallServiceFixtureX.getTestDouble(),
484                timeout(TEST_TIMEOUT))
485                .addCall(
486                        any(ParcelableCall.class));
487        verify(
488                mInCallServiceFixtureY.getTestDouble(),
489                timeout(TEST_TIMEOUT))
490                .addCall(
491                        any(ParcelableCall.class));
492
493        // Give the InCallService time to respond
494
495        assertTrueWithTimeout(new Predicate<Void>() {
496            @Override
497            public boolean apply(Void v) {
498                return startingNumConnections + 1 ==
499                        connectionServiceFixture.mConnectionById.size();
500            }
501        });
502        assertTrueWithTimeout(new Predicate<Void>() {
503            @Override
504            public boolean apply(Void v) {
505                return startingNumCalls + 1 == mInCallServiceFixtureX.mCallById.size();
506            }
507        });
508        assertTrueWithTimeout(new Predicate<Void>() {
509            @Override
510            public boolean apply(Void v) {
511                return startingNumCalls + 1 == mInCallServiceFixtureY.mCallById.size();
512            }
513        });
514
515        assertEquals(
516                mInCallServiceFixtureX.mLatestCallId,
517                mInCallServiceFixtureY.mLatestCallId);
518
519        return new IdPair(
520                connectionServiceFixture.mLatestConnectionId,
521                mInCallServiceFixtureX.mLatestCallId);
522    }
523
524    private void rapidFire(Runnable... tasks) {
525        final CyclicBarrier barrier = new CyclicBarrier(tasks.length);
526        final CountDownLatch latch = new CountDownLatch(tasks.length);
527        for (int i = 0; i < tasks.length; i++) {
528            final Runnable task = tasks[i];
529            new Thread(new Runnable() {
530                @Override
531                public void run() {
532                    try {
533                        barrier.await();
534                        task.run();
535                    } catch (InterruptedException | BrokenBarrierException e){
536                        Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
537                    } finally {
538                        latch.countDown();
539                    }
540                }
541            }).start();
542        }
543        try {
544            latch.await();
545        } catch (InterruptedException e) {
546            Log.e(TelecomSystemTest.this, e, "Unexpectedly interrupted");
547        }
548    }
549
550    // A simple outgoing call, verifying that the appropriate connection service is contacted,
551    // the proper lifecycle is followed, and both In-Call Services are updated correctly.
552    private IdPair startAndMakeActiveOutgoingCall(
553            String number,
554            PhoneAccountHandle phoneAccountHandle,
555            ConnectionServiceFixture connectionServiceFixture) throws Exception {
556        IdPair ids = startOutgoingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
557
558        connectionServiceFixture.sendSetDialing(ids.mConnectionId);
559        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
560        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
561
562        connectionServiceFixture.sendSetActive(ids.mConnectionId);
563        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
564        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
565
566        return ids;
567    }
568
569    public void testSingleOutgoingCallLocalDisconnect() throws Exception {
570        IdPair ids = startAndMakeActiveOutgoingCall(
571                "650-555-1212",
572                mPhoneAccountA0.getAccountHandle(),
573                mConnectionServiceFixtureA);
574
575        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
576        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
577        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
578
579        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
580        assertEquals(Call.STATE_DISCONNECTED,
581                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
582        assertEquals(Call.STATE_DISCONNECTED,
583                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
584    }
585
586    public void testSingleOutgoingCallRemoteDisconnect() throws Exception {
587        IdPair ids = startAndMakeActiveOutgoingCall(
588                "650-555-1212",
589                mPhoneAccountA0.getAccountHandle(),
590                mConnectionServiceFixtureA);
591
592        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
593        assertEquals(Call.STATE_DISCONNECTED,
594                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
595        assertEquals(Call.STATE_DISCONNECTED,
596                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
597    }
598
599    // A simple incoming call, similar in scope to the previous test
600    private IdPair startAndMakeActiveIncomingCall(
601            String number,
602            PhoneAccountHandle phoneAccountHandle,
603            ConnectionServiceFixture connectionServiceFixture) throws Exception {
604        IdPair ids = startIncomingPhoneCall(number, phoneAccountHandle, connectionServiceFixture);
605
606        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
607        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
608
609        mInCallServiceFixtureX.mInCallAdapter
610                .answerCall(ids.mCallId, VideoProfile.STATE_AUDIO_ONLY);
611
612        verify(connectionServiceFixture.getTestDouble())
613                .answer(ids.mConnectionId);
614
615        connectionServiceFixture.sendSetActive(ids.mConnectionId);
616        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
617        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
618
619        return ids;
620    }
621
622    public void testSingleIncomingCallLocalDisconnect() throws Exception {
623        IdPair ids = startAndMakeActiveIncomingCall(
624                "650-555-1212",
625                mPhoneAccountA0.getAccountHandle(),
626                mConnectionServiceFixtureA);
627
628        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
629        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
630        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
631
632        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
633        assertEquals(Call.STATE_DISCONNECTED,
634                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
635        assertEquals(Call.STATE_DISCONNECTED,
636                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
637    }
638
639    public void testSingleIncomingCallRemoteDisconnect() throws Exception {
640        IdPair ids = startAndMakeActiveIncomingCall(
641                "650-555-1212",
642                mPhoneAccountA0.getAccountHandle(),
643                mConnectionServiceFixtureA);
644
645        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
646        assertEquals(Call.STATE_DISCONNECTED,
647                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
648        assertEquals(Call.STATE_DISCONNECTED,
649                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
650    }
651
652    public void do_testDeadlockOnOutgoingCall() throws Exception {
653        final IdPair ids = startOutgoingPhoneCall(
654                "650-555-1212",
655                mPhoneAccountA0.getAccountHandle(),
656                mConnectionServiceFixtureA);
657        rapidFire(
658                new Runnable() {
659                    @Override
660                    public void run() {
661                        while (mCallerInfoAsyncQueryFactoryFixture.mRequests.size() > 0) {
662                            mCallerInfoAsyncQueryFactoryFixture.mRequests.remove(0).reply();
663                        }
664                    }
665                },
666                new Runnable() {
667                    @Override
668                    public void run() {
669                        try {
670                            mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
671                        } catch (Exception e) {
672                            Log.e(this, e, "");
673                        }
674                    }
675                });
676    }
677
678    public void testDeadlockOnOutgoingCall() throws Exception {
679        for (int i = 0; i < 100; i++) {
680            TelecomSystemTest test = new TelecomSystemTest();
681            test.setContext(getContext());
682            test.setTestContext(getTestContext());
683            test.setName(getName());
684            test.setUp();
685            test.do_testDeadlockOnOutgoingCall();
686            test.tearDown();
687        }
688    }
689
690    public void testIncomingThenOutgoingCalls() throws Exception {
691        // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
692        IdPair incoming = startAndMakeActiveIncomingCall(
693                "650-555-2323",
694                mPhoneAccountA0.getAccountHandle(),
695                mConnectionServiceFixtureA);
696        IdPair outgoing = startAndMakeActiveOutgoingCall(
697                "650-555-1212",
698                mPhoneAccountA0.getAccountHandle(),
699                mConnectionServiceFixtureA);
700    }
701
702    public void testOutgoingThenIncomingCalls() throws Exception {
703        // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
704        IdPair outgoing = startAndMakeActiveOutgoingCall(
705                "650-555-1212",
706                mPhoneAccountA0.getAccountHandle(),
707                mConnectionServiceFixtureA);
708        IdPair incoming = startAndMakeActiveIncomingCall(
709                "650-555-2323",
710                mPhoneAccountA0.getAccountHandle(),
711                mConnectionServiceFixtureA);
712        verify(mConnectionServiceFixtureA.getTestDouble())
713                .hold(outgoing.mConnectionId);
714        mConnectionServiceFixtureA.mConnectionById.get(outgoing.mConnectionId).state =
715                Connection.STATE_HOLDING;
716        mConnectionServiceFixtureA.sendSetOnHold(outgoing.mConnectionId);
717        assertEquals(
718                Call.STATE_HOLDING,
719                mInCallServiceFixtureX.getCall(outgoing.mCallId).getState());
720        assertEquals(
721                Call.STATE_HOLDING,
722                mInCallServiceFixtureY.getCall(outgoing.mCallId).getState());
723    }
724
725    public void testAudioManagerOperations() throws Exception {
726        AudioManager audioManager = (AudioManager) mComponentContextFixture.getTestDouble()
727                .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
728
729        IdPair outgoing = startAndMakeActiveOutgoingCall(
730                "650-555-1212",
731                mPhoneAccountA0.getAccountHandle(),
732                mConnectionServiceFixtureA);
733
734        verify(audioManager, timeout(TEST_TIMEOUT))
735                .requestAudioFocusForCall(anyInt(), anyInt());
736        verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce())
737                .setMode(AudioManager.MODE_IN_CALL);
738
739        mInCallServiceFixtureX.mInCallAdapter.mute(true);
740        verify(mAudioService, timeout(TEST_TIMEOUT))
741                .setMicrophoneMute(eq(true), any(String.class), any(Integer.class));
742        mInCallServiceFixtureX.mInCallAdapter.mute(false);
743        verify(mAudioService, timeout(TEST_TIMEOUT))
744                .setMicrophoneMute(eq(false), any(String.class), any(Integer.class));
745
746        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
747        verify(audioManager, timeout(TEST_TIMEOUT))
748                .setSpeakerphoneOn(true);
749        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
750        verify(audioManager, timeout(TEST_TIMEOUT))
751                .setSpeakerphoneOn(false);
752
753        mConnectionServiceFixtureA.
754                sendSetDisconnected(outgoing.mConnectionId, DisconnectCause.REMOTE);
755
756        verify(audioManager, timeout(TEST_TIMEOUT))
757                .abandonAudioFocusForCall();
758        verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce())
759                .setMode(AudioManager.MODE_NORMAL);
760    }
761
762    protected static void assertTrueWithTimeout(Predicate<Void> predicate) {
763        int elapsed = 0;
764        while (elapsed < TEST_TIMEOUT) {
765            if (predicate.apply(null)) {
766                return;
767            } else {
768                try {
769                    Thread.sleep(TEST_POLL_INTERVAL);
770                    elapsed += TEST_POLL_INTERVAL;
771                } catch (InterruptedException e) {
772                    fail(e.toString());
773                }
774            }
775        }
776        fail("Timeout in assertTrueWithTimeout");
777    }
778}
779