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