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