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