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