BasicCallTests.java revision d1b36695f77189f008e2de82b4391e5e49048c18
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 static org.mockito.Matchers.any;
20import static org.mockito.Matchers.anyInt;
21import static org.mockito.Matchers.anyString;
22import static org.mockito.Matchers.eq;
23import static org.mockito.Matchers.isNull;
24import static org.mockito.Mockito.never;
25import static org.mockito.Mockito.timeout;
26import static org.mockito.Mockito.verify;
27import static org.mockito.Mockito.verifyZeroInteractions;
28import static org.mockito.Mockito.when;
29
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.IContentProvider;
33import android.media.AudioManager;
34import android.net.Uri;
35import android.os.Bundle;
36import android.os.Handler;
37import android.os.Looper;
38import android.os.Process;
39import android.provider.BlockedNumberContract;
40import android.telecom.Call;
41import android.telecom.CallAudioState;
42import android.telecom.Connection;
43import android.telecom.ConnectionRequest;
44import android.telecom.DisconnectCause;
45import android.telecom.Log;
46import android.telecom.ParcelableCall;
47import android.telecom.ParcelableCallAnalytics;
48import android.telecom.PhoneAccount;
49import android.telecom.PhoneAccountHandle;
50import android.telecom.TelecomManager;
51import android.telecom.VideoProfile;
52import android.test.suitebuilder.annotation.LargeTest;
53import android.test.suitebuilder.annotation.MediumTest;
54import android.test.suitebuilder.annotation.SmallTest;
55
56import com.android.internal.telecom.IInCallAdapter;
57import com.android.internal.telephony.CallerInfo;
58import com.android.internal.util.IndentingPrintWriter;
59import com.android.server.telecom.Analytics;
60
61import com.google.common.base.Predicate;
62
63import org.mockito.invocation.InvocationOnMock;
64import org.mockito.stubbing.Answer;
65
66import java.io.StringWriter;
67import java.util.List;
68import java.util.Map;
69import java.util.concurrent.BrokenBarrierException;
70import java.util.concurrent.CountDownLatch;
71import java.util.concurrent.CyclicBarrier;
72import java.util.concurrent.TimeUnit;
73
74import org.mockito.ArgumentCaptor;
75
76import static org.mockito.Matchers.any;
77import static org.mockito.Matchers.anyInt;
78import static org.mockito.Matchers.eq;
79import static org.mockito.Mockito.never;
80import static org.mockito.Mockito.timeout;
81import static org.mockito.Mockito.verify;
82
83/**
84 * Performs various basic call tests in Telecom.
85 */
86public class BasicCallTests extends TelecomSystemTest {
87    private static final String TEST_BUNDLE_KEY = "android.telecom.extra.TEST";
88    private static final String TEST_EVENT = "android.telecom.event.TEST";
89
90    @LargeTest
91    public void testSingleOutgoingCallLocalDisconnect() throws Exception {
92        IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
93                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
94
95        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
96        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
97        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
98
99        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
100        assertEquals(Call.STATE_DISCONNECTED,
101                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
102        assertEquals(Call.STATE_DISCONNECTED,
103                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
104        verifyNoBlockChecks();
105    }
106
107    @LargeTest
108    public void testSingleOutgoingCallRemoteDisconnect() throws Exception {
109        IdPair ids = startAndMakeActiveOutgoingCall("650-555-1212",
110                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
111
112        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
113        assertEquals(Call.STATE_DISCONNECTED,
114                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
115        assertEquals(Call.STATE_DISCONNECTED,
116                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
117        verifyNoBlockChecks();
118    }
119
120    /**
121     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
122     * audio-only call.
123     *
124     * @throws Exception
125     */
126    @LargeTest
127    public void testTelecomManagerAcceptRingingCall() throws Exception {
128        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
129                mConnectionServiceFixtureA);
130
131        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
132        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
133
134        // Use TelecomManager API to answer the ringing call.
135        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
136                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
137        telecomManager.acceptRingingCall();
138
139        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
140                .answer(ids.mConnectionId);
141        mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
142
143        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
144    }
145
146    /**
147     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
148     * video call, which should be answered as video.
149     *
150     * @throws Exception
151     */
152    @LargeTest
153    public void testTelecomManagerAcceptRingingVideoCall() throws Exception {
154        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
155                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
156
157        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
158        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
159
160        // Use TelecomManager API to answer the ringing call; the default expected behavior is to
161        // answer using whatever video state the ringing call requests.
162        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
163                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
164        telecomManager.acceptRingingCall();
165
166        // Answer video API should be called
167        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
168                .answerVideo(eq(ids.mConnectionId), eq(VideoProfile.STATE_BIDIRECTIONAL));
169        mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
170
171        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
172    }
173
174    /**
175     * Tests the {@link TelecomManager#acceptRingingCall(int)} API.  Tests answering a video call
176     * as an audio call.
177     *
178     * @throws Exception
179     */
180    @LargeTest
181    public void testTelecomManagerAcceptRingingVideoCallAsAudio() throws Exception {
182        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
183                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
184
185        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
186        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
187
188        // Use TelecomManager API to answer the ringing call.
189        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
190                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
191        telecomManager.acceptRingingCall(VideoProfile.STATE_AUDIO_ONLY);
192
193        // The generic answer method on the ConnectionService is used to answer audio-only calls.
194        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
195                .answer(eq(ids.mConnectionId));
196        mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
197
198        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
199    }
200
201    /**
202     * Tests the {@link TelecomManager#acceptRingingCall()} API.  Tests simple case of an incoming
203     * video call, where an attempt is made to answer with an invalid video state.
204     *
205     * @throws Exception
206     */
207    @LargeTest
208    public void testTelecomManagerAcceptRingingInvalidVideoState() throws Exception {
209        IdPair ids = startIncomingPhoneCall("650-555-1212", mPhoneAccountA0.getAccountHandle(),
210                VideoProfile.STATE_BIDIRECTIONAL, mConnectionServiceFixtureA);
211
212        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
213        assertEquals(Call.STATE_RINGING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
214
215        // Use TelecomManager API to answer the ringing call; the default expected behavior is to
216        // answer using whatever video state the ringing call requests.
217        TelecomManager telecomManager = (TelecomManager) mComponentContextFixture.getTestDouble()
218                .getApplicationContext().getSystemService(Context.TELECOM_SERVICE);
219        telecomManager.acceptRingingCall(999 /* invalid videostate */);
220
221        // Answer video API should be called
222        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
223                .answerVideo(eq(ids.mConnectionId), eq(VideoProfile.STATE_BIDIRECTIONAL));
224        mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
225        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
226    }
227
228    @LargeTest
229    public void testSingleIncomingCallLocalDisconnect() throws Exception {
230        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
231                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
232        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(ids.mCallId);
233        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
234        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
235
236        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
237        assertEquals(Call.STATE_DISCONNECTED,
238                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
239        assertEquals(Call.STATE_DISCONNECTED,
240                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
241    }
242
243    @LargeTest
244    public void testSingleIncomingCallRemoteDisconnect() throws Exception {
245        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
246                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
247
248        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
249        assertEquals(Call.STATE_DISCONNECTED,
250                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
251        assertEquals(Call.STATE_DISCONNECTED,
252                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
253    }
254
255    @LargeTest
256    public void testOutgoingCallAndSelectPhoneAccount() throws Exception {
257        // Remove default PhoneAccount so that the Call moves into the correct
258        // SELECT_PHONE_ACCOUNT state.
259        mTelecomSystem.getPhoneAccountRegistrar().setUserSelectedOutgoingPhoneAccount(
260                null, Process.myUserHandle());
261        int startingNumConnections = mConnectionServiceFixtureA.mConnectionById.size();
262        int startingNumCalls = mInCallServiceFixtureX.mCallById.size();
263        String callId = startOutgoingPhoneCallWithNoPhoneAccount("650-555-1212",
264                mConnectionServiceFixtureA);
265        assertEquals(Call.STATE_SELECT_PHONE_ACCOUNT,
266                mInCallServiceFixtureX.getCall(callId).getState());
267        assertEquals(Call.STATE_SELECT_PHONE_ACCOUNT,
268                mInCallServiceFixtureY.getCall(callId).getState());
269        mInCallServiceFixtureX.mInCallAdapter.phoneAccountSelected(callId,
270                mPhoneAccountA0.getAccountHandle(), false);
271
272        IdPair ids = outgoingCallPhoneAccountSelected(mPhoneAccountA0.getAccountHandle(),
273                startingNumConnections, startingNumCalls, mConnectionServiceFixtureA);
274
275        mConnectionServiceFixtureA.sendSetDisconnected(ids.mConnectionId, DisconnectCause.LOCAL);
276        assertEquals(Call.STATE_DISCONNECTED,
277                mInCallServiceFixtureX.getCall(ids.mCallId).getState());
278        assertEquals(Call.STATE_DISCONNECTED,
279                mInCallServiceFixtureY.getCall(ids.mCallId).getState());
280    }
281
282    @LargeTest
283    public void testIncomingCallFromContactWithSendToVoicemailIsRejected() throws Exception {
284        Bundle extras = new Bundle();
285        extras.putParcelable(
286                TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
287                Uri.fromParts(PhoneAccount.SCHEME_TEL, "650-555-1212", null));
288        mTelecomSystem.getTelecomServiceImpl().getBinder()
289                .addNewIncomingCall(mPhoneAccountA0.getAccountHandle(), extras);
290
291        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
292        verify(mConnectionServiceFixtureA.getTestDouble())
293                .createConnection(any(PhoneAccountHandle.class), anyString(),
294                        any(ConnectionRequest.class), eq(true), eq(false));
295
296        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
297        assertEquals(1, mCallerInfoAsyncQueryFactoryFixture.mRequests.size());
298        for (CallerInfoAsyncQueryFactoryFixture.Request request :
299                mCallerInfoAsyncQueryFactoryFixture.mRequests) {
300            CallerInfo sendToVoicemailCallerInfo = new CallerInfo();
301            sendToVoicemailCallerInfo.shouldSendToVoicemail = true;
302            request.replyWithCallerInfo(sendToVoicemailCallerInfo);
303        }
304
305        assertTrueWithTimeout(new Predicate<Void>() {
306            @Override
307            public boolean apply(Void aVoid) {
308                return mConnectionServiceFixtureA.mConnectionService.rejectedCallIds.size() == 1;
309            }
310        });
311        assertTrueWithTimeout(new Predicate<Void>() {
312            @Override
313            public boolean apply(Void aVoid) {
314                return mMissedCallNotifier.missedCallsNotified.size() == 1;
315            }
316        });
317
318        verify(mInCallServiceFixtureX.getTestDouble(), never())
319                .setInCallAdapter(any(IInCallAdapter.class));
320        verify(mInCallServiceFixtureY.getTestDouble(), never())
321                .setInCallAdapter(any(IInCallAdapter.class));
322    }
323
324    @LargeTest
325    public void testIncomingCallCallerInfoLookupTimesOutIsAllowed() throws Exception {
326        Bundle extras = new Bundle();
327        extras.putParcelable(
328                TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
329                Uri.fromParts(PhoneAccount.SCHEME_TEL, "650-555-1212", null));
330        mTelecomSystem.getTelecomServiceImpl().getBinder()
331                .addNewIncomingCall(mPhoneAccountA0.getAccountHandle(), extras);
332
333        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
334        verify(mConnectionServiceFixtureA.getTestDouble())
335                .createConnection(any(PhoneAccountHandle.class), anyString(),
336                        any(ConnectionRequest.class), eq(true), eq(false));
337
338        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
339        // Never reply to the caller info lookup.
340        assertEquals(1, mCallerInfoAsyncQueryFactoryFixture.mRequests.size());
341
342        verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
343                .setInCallAdapter(any(IInCallAdapter.class));
344        verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
345                .setInCallAdapter(any(IInCallAdapter.class));
346
347        assertEquals(0, mConnectionServiceFixtureA.mConnectionService.rejectedCallIds.size());
348        assertEquals(0, mMissedCallNotifier.missedCallsNotified.size());
349
350        assertTrueWithTimeout(new Predicate<Void>() {
351            @Override
352            public boolean apply(Void v) {
353                return mInCallServiceFixtureX.mInCallAdapter != null;
354            }
355        });
356
357        verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
358                .addCall(any(ParcelableCall.class));
359        verify(mInCallServiceFixtureY.getTestDouble(), timeout(TEST_TIMEOUT))
360                .addCall(any(ParcelableCall.class));
361
362        disconnectCall(mInCallServiceFixtureX.mLatestCallId,
363                mConnectionServiceFixtureA.mLatestConnectionId);
364    }
365
366    @LargeTest
367    public void testIncomingCallFromBlockedNumberIsRejected() throws Exception {
368        String phoneNumber = "650-555-1212";
369        blockNumber(phoneNumber);
370
371        Bundle extras = new Bundle();
372        extras.putParcelable(
373                TelecomManager.EXTRA_INCOMING_CALL_ADDRESS,
374                Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null));
375        mTelecomSystem.getTelecomServiceImpl().getBinder()
376                .addNewIncomingCall(mPhoneAccountA0.getAccountHandle(), extras);
377
378        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
379        verify(mConnectionServiceFixtureA.getTestDouble())
380                .createConnection(any(PhoneAccountHandle.class), anyString(),
381                        any(ConnectionRequest.class), eq(true), eq(false));
382
383        waitForHandlerAction(new Handler(Looper.getMainLooper()), TEST_TIMEOUT);
384        assertEquals(1, mCallerInfoAsyncQueryFactoryFixture.mRequests.size());
385        for (CallerInfoAsyncQueryFactoryFixture.Request request :
386                mCallerInfoAsyncQueryFactoryFixture.mRequests) {
387            request.reply();
388        }
389
390        assertTrueWithTimeout(new Predicate<Void>() {
391            @Override
392            public boolean apply(Void aVoid) {
393                return mConnectionServiceFixtureA.mConnectionService.rejectedCallIds.size() == 1;
394            }
395        });
396        assertEquals(0, mMissedCallNotifier.missedCallsNotified.size());
397
398        verify(mInCallServiceFixtureX.getTestDouble(), never())
399                .setInCallAdapter(any(IInCallAdapter.class));
400        verify(mInCallServiceFixtureY.getTestDouble(), never())
401                .setInCallAdapter(any(IInCallAdapter.class));
402    }
403
404    @LargeTest
405    public void testIncomingCallBlockCheckTimesoutIsAllowed() throws Exception {
406        final CountDownLatch latch = new CountDownLatch(1);
407        String phoneNumber = "650-555-1212";
408        blockNumberWithAnswer(phoneNumber, new Answer<Bundle>() {
409            @Override
410            public Bundle answer(InvocationOnMock invocation) throws Throwable {
411                latch.await(TEST_TIMEOUT * 2, TimeUnit.MILLISECONDS);
412                Bundle bundle = new Bundle();
413                bundle.putBoolean(BlockedNumberContract.RES_NUMBER_IS_BLOCKED, true);
414                return bundle;
415            }
416        });
417
418        IdPair ids = startAndMakeActiveIncomingCall(
419                phoneNumber, mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
420        latch.countDown();
421
422        assertEquals(0, mConnectionServiceFixtureA.mConnectionService.rejectedCallIds.size());
423        assertEquals(0, mMissedCallNotifier.missedCallsNotified.size());
424        disconnectCall(ids.mCallId, ids.mConnectionId);
425    }
426
427    public void do_testDeadlockOnOutgoingCall() throws Exception {
428        final IdPair ids = startOutgoingPhoneCall("650-555-1212",
429                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA,
430                Process.myUserHandle());
431        rapidFire(
432                new Runnable() {
433                    @Override
434                    public void run() {
435                        while (mCallerInfoAsyncQueryFactoryFixture.mRequests.size() > 0) {
436                            mCallerInfoAsyncQueryFactoryFixture.mRequests.remove(0).reply();
437                        }
438                    }
439                },
440                new Runnable() {
441                    @Override
442                    public void run() {
443                        try {
444                            mConnectionServiceFixtureA.sendSetActive(ids.mConnectionId);
445                        } catch (Exception e) {
446                            Log.e(this, e, "");
447                        }
448                    }
449                });
450    }
451
452    @MediumTest
453    public void testDeadlockOnOutgoingCall() throws Exception {
454        for (int i = 0; i < 100; i++) {
455            BasicCallTests test = new BasicCallTests();
456            test.setContext(getContext());
457            test.setTestContext(getTestContext());
458            test.setName(getName());
459            test.setUp();
460            test.do_testDeadlockOnOutgoingCall();
461            test.tearDown();
462        }
463    }
464
465    @LargeTest
466    public void testIncomingThenOutgoingCalls() throws Exception {
467        // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
468        IdPair incoming = startAndMakeActiveIncomingCall("650-555-2323",
469                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
470        IdPair outgoing = startAndMakeActiveOutgoingCall("650-555-1212",
471                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
472
473        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(incoming.mCallId);
474        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(outgoing.mCallId);
475    }
476
477    @LargeTest
478    public void testOutgoingThenIncomingCalls() throws Exception {
479        // TODO: We have to use the same PhoneAccount for both; see http://b/18461539
480        IdPair outgoing = startAndMakeActiveOutgoingCall("650-555-1212",
481                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
482        IdPair incoming = startAndMakeActiveIncomingCall("650-555-2323",
483                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
484        verify(mConnectionServiceFixtureA.getTestDouble())
485                .hold(outgoing.mConnectionId);
486        mConnectionServiceFixtureA.mConnectionById.get(outgoing.mConnectionId).state =
487                Connection.STATE_HOLDING;
488        mConnectionServiceFixtureA.sendSetOnHold(outgoing.mConnectionId);
489        assertEquals(Call.STATE_HOLDING,
490                mInCallServiceFixtureX.getCall(outgoing.mCallId).getState());
491        assertEquals(Call.STATE_HOLDING,
492                mInCallServiceFixtureY.getCall(outgoing.mCallId).getState());
493
494        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(incoming.mCallId);
495        mInCallServiceFixtureX.mInCallAdapter.disconnectCall(outgoing.mCallId);
496    }
497
498    @LargeTest
499    public void testAudioManagerOperations() throws Exception {
500        AudioManager audioManager = (AudioManager) mComponentContextFixture.getTestDouble()
501                .getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
502
503        IdPair outgoing = startAndMakeActiveOutgoingCall("650-555-1212",
504                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
505
506        verify(audioManager, timeout(TEST_TIMEOUT)).requestAudioFocusForCall(anyInt(), anyInt());
507        verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce())
508                .setMode(AudioManager.MODE_IN_CALL);
509
510        mInCallServiceFixtureX.mInCallAdapter.mute(true);
511        verify(mAudioService, timeout(TEST_TIMEOUT))
512                .setMicrophoneMute(eq(true), any(String.class), any(Integer.class));
513        mInCallServiceFixtureX.mInCallAdapter.mute(false);
514        verify(mAudioService, timeout(TEST_TIMEOUT))
515                .setMicrophoneMute(eq(false), any(String.class), any(Integer.class));
516
517        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
518        verify(audioManager, timeout(TEST_TIMEOUT))
519                .setSpeakerphoneOn(true);
520        mInCallServiceFixtureX.mInCallAdapter.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
521        verify(audioManager, timeout(TEST_TIMEOUT))
522                .setSpeakerphoneOn(false);
523
524        mConnectionServiceFixtureA.
525                sendSetDisconnected(outgoing.mConnectionId, DisconnectCause.REMOTE);
526
527        verify(audioManager, timeout(TEST_TIMEOUT))
528                .abandonAudioFocusForCall();
529        verify(audioManager, timeout(TEST_TIMEOUT).atLeastOnce())
530                .setMode(AudioManager.MODE_NORMAL);
531    }
532
533    private void rapidFire(Runnable... tasks) {
534        final CyclicBarrier barrier = new CyclicBarrier(tasks.length);
535        final CountDownLatch latch = new CountDownLatch(tasks.length);
536        for (int i = 0; i < tasks.length; i++) {
537            final Runnable task = tasks[i];
538            new Thread(new Runnable() {
539                @Override
540                public void run() {
541                    try {
542                        barrier.await();
543                        task.run();
544                    } catch (InterruptedException | BrokenBarrierException e){
545                        Log.e(BasicCallTests.this, e, "Unexpectedly interrupted");
546                    } finally {
547                        latch.countDown();
548                    }
549                }
550            }).start();
551        }
552        try {
553            latch.await();
554        } catch (InterruptedException e) {
555            Log.e(BasicCallTests.this, e, "Unexpectedly interrupted");
556        }
557    }
558
559    @MediumTest
560    public void testBasicConferenceCall() throws Exception {
561        makeConferenceCall();
562    }
563
564    @MediumTest
565    public void testAddCallToConference1() throws Exception {
566        ParcelableCall conferenceCall = makeConferenceCall();
567        IdPair callId3 = startAndMakeActiveOutgoingCall("650-555-1214",
568                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
569        // testAddCallToConference{1,2} differ in the order of arguments to InCallAdapter#conference
570        mInCallServiceFixtureX.getInCallAdapter().conference(
571                conferenceCall.getId(), callId3.mCallId);
572        Thread.sleep(200);
573
574        ParcelableCall call3 = mInCallServiceFixtureX.getCall(callId3.mCallId);
575        ParcelableCall updatedConference = mInCallServiceFixtureX.getCall(conferenceCall.getId());
576        assertEquals(conferenceCall.getId(), call3.getParentCallId());
577        assertEquals(3, updatedConference.getChildCallIds().size());
578        assertTrue(updatedConference.getChildCallIds().contains(callId3.mCallId));
579    }
580
581    @MediumTest
582    public void testAddCallToConference2() throws Exception {
583        ParcelableCall conferenceCall = makeConferenceCall();
584        IdPair callId3 = startAndMakeActiveOutgoingCall("650-555-1214",
585                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
586        mInCallServiceFixtureX.getInCallAdapter()
587                .conference(callId3.mCallId, conferenceCall.getId());
588        Thread.sleep(200);
589
590        ParcelableCall call3 = mInCallServiceFixtureX.getCall(callId3.mCallId);
591        ParcelableCall updatedConference = mInCallServiceFixtureX.getCall(conferenceCall.getId());
592        assertEquals(conferenceCall.getId(), call3.getParentCallId());
593        assertEquals(3, updatedConference.getChildCallIds().size());
594        assertTrue(updatedConference.getChildCallIds().contains(callId3.mCallId));
595    }
596
597    /**
598     * Tests the {@link Call#pullExternalCall()} API.  Verifies that if a call is not an external
599     * call, no pull call request is made to the connection service.
600     *
601     * @throws Exception
602     */
603    @MediumTest
604    public void testPullNonExternalCall() throws Exception {
605        // TODO: Revisit this unit test once telecom support for filtering external calls from
606        // InCall services is implemented.
607        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
608                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
609        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
610
611        // Attempt to pull the call and verify the API call makes it through
612        mInCallServiceFixtureX.mInCallAdapter.pullExternalCall(ids.mCallId);
613        Thread.sleep(TEST_TIMEOUT);
614        verify(mConnectionServiceFixtureA.getTestDouble(), never())
615                .pullExternalCall(ids.mCallId);
616    }
617
618    /**
619     * Tests the {@link Connection#sendConnectionEvent(String)} API.
620     *
621     * @throws Exception
622     */
623    @MediumTest
624    public void testSendConnectionEventNull() throws Exception {
625        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
626                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
627        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
628        mConnectionServiceFixtureA.sendConnectionEvent(ids.mConnectionId, TEST_EVENT, null);
629        verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
630                .onConnectionEvent(ids.mCallId, TEST_EVENT, null);
631    }
632
633    /**
634     * Tests the {@link Connection#sendConnectionEvent(String)} API.
635     *
636     * @throws Exception
637     */
638    @MediumTest
639    public void testSendConnectionEventNotNull() throws Exception {
640        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
641                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
642        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
643
644        Bundle testBundle = new Bundle();
645        testBundle.putString(TEST_BUNDLE_KEY, "TEST");
646
647        ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
648        mConnectionServiceFixtureA.sendConnectionEvent(ids.mConnectionId, TEST_EVENT, testBundle);
649        verify(mInCallServiceFixtureX.getTestDouble(), timeout(TEST_TIMEOUT))
650                .onConnectionEvent(eq(ids.mCallId), eq(TEST_EVENT), bundleArgumentCaptor.capture());
651        assert (bundleArgumentCaptor.getValue().containsKey(TEST_BUNDLE_KEY));
652    }
653
654    /**
655     * Tests the {@link Call#sendCallEvent(String, Bundle)} API.
656     *
657     * @throws Exception
658     */
659    @MediumTest
660    public void testSendCallEventNull() throws Exception {
661        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
662                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
663        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
664
665        mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT, null);
666        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
667                .sendCallEvent(ids.mConnectionId, TEST_EVENT, null);
668    }
669
670    /**
671     * Tests the {@link Call#sendCallEvent(String, Bundle)} API.
672     *
673     * @throws Exception
674     */
675    @MediumTest
676    public void testSendCallEventNonNull() throws Exception {
677        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
678                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
679        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
680
681        Bundle testBundle = new Bundle();
682        testBundle.putString(TEST_BUNDLE_KEY, "TEST");
683
684        ArgumentCaptor<Bundle> bundleArgumentCaptor = ArgumentCaptor.forClass(Bundle.class);
685        mInCallServiceFixtureX.mInCallAdapter.sendCallEvent(ids.mCallId, TEST_EVENT,
686                testBundle);
687        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
688                .sendCallEvent(eq(ids.mConnectionId), eq(TEST_EVENT),
689                        bundleArgumentCaptor.capture());
690        assert (bundleArgumentCaptor.getValue().containsKey(TEST_BUNDLE_KEY));
691    }
692
693    @MediumTest
694    public void testAnalyticsSingleCall() throws Exception {
695        IdPair testCall = startAndMakeActiveIncomingCall(
696                "650-555-1212",
697                mPhoneAccountA0.getAccountHandle(),
698                mConnectionServiceFixtureA);
699        Map<String, Analytics.CallInfoImpl> analyticsMap = Analytics.cloneData();
700
701        assertTrue(analyticsMap.containsKey(testCall.mCallId));
702
703        Analytics.CallInfoImpl callAnalytics = analyticsMap.get(testCall.mCallId);
704        assertTrue(callAnalytics.startTime > 0);
705        assertEquals(0, callAnalytics.endTime);
706        assertEquals(Analytics.INCOMING_DIRECTION, callAnalytics.callDirection);
707        assertFalse(callAnalytics.isInterrupted);
708        assertNull(callAnalytics.callTerminationReason);
709        assertEquals(mConnectionServiceComponentNameA.flattenToShortString(),
710                callAnalytics.connectionService);
711
712        mConnectionServiceFixtureA.
713                sendSetDisconnected(testCall.mConnectionId, DisconnectCause.ERROR);
714
715        analyticsMap = Analytics.cloneData();
716        callAnalytics = analyticsMap.get(testCall.mCallId);
717        assertTrue(callAnalytics.endTime > 0);
718        assertNotNull(callAnalytics.callTerminationReason);
719        assertEquals(DisconnectCause.ERROR, callAnalytics.callTerminationReason.getCode());
720
721        StringWriter sr = new StringWriter();
722        IndentingPrintWriter ip = new IndentingPrintWriter(sr, "    ");
723        Analytics.dump(ip);
724        String dumpResult = sr.toString();
725        String[] expectedFields = {"startTime", "endTime", "direction", "isAdditionalCall",
726                "isInterrupted", "callTechnologies", "callTerminationReason", "connectionService"};
727        for (String field : expectedFields) {
728            assertTrue(dumpResult.contains(field));
729        }
730    }
731
732    @SmallTest
733    public void testAnalyticsDumping() throws Exception {
734        Analytics.reset();
735        IdPair testCall = startAndMakeActiveIncomingCall(
736                "650-555-1212",
737                mPhoneAccountA0.getAccountHandle(),
738                mConnectionServiceFixtureA);
739
740        mConnectionServiceFixtureA.
741                sendSetDisconnected(testCall.mConnectionId, DisconnectCause.ERROR);
742        Analytics.CallInfoImpl expectedAnalytics = Analytics.cloneData().get(testCall.mCallId);
743
744        TelecomManager tm = (TelecomManager) mSpyContext.getSystemService(Context.TELECOM_SERVICE);
745        List<ParcelableCallAnalytics> analyticsList = tm.dumpAnalytics();
746
747        assertEquals(1, analyticsList.size());
748        ParcelableCallAnalytics pCA = analyticsList.get(0);
749
750        assertTrue(Math.abs(expectedAnalytics.startTime - pCA.getStartTimeMillis()) <
751                ParcelableCallAnalytics.MILLIS_IN_5_MINUTES);
752        assertEquals(0, pCA.getStartTimeMillis() % ParcelableCallAnalytics.MILLIS_IN_5_MINUTES);
753        assertTrue(Math.abs((expectedAnalytics.endTime - expectedAnalytics.startTime) -
754                pCA.getCallDurationMillis()) < ParcelableCallAnalytics.MILLIS_IN_1_SECOND);
755        assertEquals(0, pCA.getCallDurationMillis() % ParcelableCallAnalytics.MILLIS_IN_1_SECOND);
756
757        assertEquals(expectedAnalytics.callDirection, pCA.getCallType());
758        assertEquals(expectedAnalytics.isAdditionalCall, pCA.isAdditionalCall());
759        assertEquals(expectedAnalytics.isInterrupted, pCA.isInterrupted());
760        assertEquals(expectedAnalytics.callTechnologies, pCA.getCallTechnologies());
761        assertEquals(expectedAnalytics.callTerminationReason.getCode(),
762                pCA.getCallTerminationCode());
763        assertEquals(expectedAnalytics.connectionService, pCA.getConnectionService());
764    }
765
766    @MediumTest
767    public void testAnalyticsTwoCalls() throws Exception {
768        IdPair testCall1 = startAndMakeActiveIncomingCall(
769                "650-555-1212",
770                mPhoneAccountA0.getAccountHandle(),
771                mConnectionServiceFixtureA);
772        IdPair testCall2 = startAndMakeActiveOutgoingCall(
773                "650-555-1213",
774                mPhoneAccountA0.getAccountHandle(),
775                mConnectionServiceFixtureA);
776
777        Map<String, Analytics.CallInfoImpl> analyticsMap = Analytics.cloneData();
778        assertTrue(analyticsMap.containsKey(testCall1.mCallId));
779        assertTrue(analyticsMap.containsKey(testCall2.mCallId));
780
781        Analytics.CallInfoImpl callAnalytics1 = analyticsMap.get(testCall1.mCallId);
782        Analytics.CallInfoImpl callAnalytics2 = analyticsMap.get(testCall2.mCallId);
783        assertTrue(callAnalytics1.startTime > 0);
784        assertTrue(callAnalytics2.startTime > 0);
785        assertEquals(0, callAnalytics1.endTime);
786        assertEquals(0, callAnalytics2.endTime);
787
788        assertEquals(Analytics.INCOMING_DIRECTION, callAnalytics1.callDirection);
789        assertEquals(Analytics.OUTGOING_DIRECTION, callAnalytics2.callDirection);
790
791        assertTrue(callAnalytics1.isInterrupted);
792        assertTrue(callAnalytics2.isAdditionalCall);
793
794        assertNull(callAnalytics1.callTerminationReason);
795        assertNull(callAnalytics2.callTerminationReason);
796
797        assertEquals(mConnectionServiceComponentNameA.flattenToShortString(),
798                callAnalytics1.connectionService);
799        assertEquals(mConnectionServiceComponentNameA.flattenToShortString(),
800                callAnalytics1.connectionService);
801
802        mConnectionServiceFixtureA.
803                sendSetDisconnected(testCall2.mConnectionId, DisconnectCause.REMOTE);
804        mConnectionServiceFixtureA.
805                sendSetDisconnected(testCall1.mConnectionId, DisconnectCause.ERROR);
806
807        analyticsMap = Analytics.cloneData();
808        callAnalytics1 = analyticsMap.get(testCall1.mCallId);
809        callAnalytics2 = analyticsMap.get(testCall2.mCallId);
810        assertTrue(callAnalytics1.endTime > 0);
811        assertTrue(callAnalytics2.endTime > 0);
812        assertNotNull(callAnalytics1.callTerminationReason);
813        assertNotNull(callAnalytics2.callTerminationReason);
814        assertEquals(DisconnectCause.ERROR, callAnalytics1.callTerminationReason.getCode());
815        assertEquals(DisconnectCause.REMOTE, callAnalytics2.callTerminationReason.getCode());
816    }
817
818    private void blockNumber(String phoneNumber) throws Exception {
819        blockNumberWithAnswer(phoneNumber, new Answer<Bundle>() {
820            @Override
821            public Bundle answer(InvocationOnMock invocation) throws Throwable {
822                Bundle bundle = new Bundle();
823                bundle.putBoolean(BlockedNumberContract.RES_NUMBER_IS_BLOCKED, true);
824                return bundle;
825            }
826        });
827    }
828
829    private void blockNumberWithAnswer(String phoneNumber, Answer answer) throws Exception {
830        when(getBlockedNumberProvider().call(
831                anyString(),
832                eq(BlockedNumberContract.SystemContract.METHOD_SHOULD_SYSTEM_BLOCK_NUMBER),
833                eq(phoneNumber),
834                isNull(Bundle.class))).thenAnswer(answer);
835    }
836
837    private void verifyNoBlockChecks() {
838        verifyZeroInteractions(getBlockedNumberProvider());
839    }
840
841    private IContentProvider getBlockedNumberProvider() {
842        return mSpyContext.getContentResolver().acquireProvider(BlockedNumberContract.AUTHORITY);
843    }
844
845    private void disconnectCall(String callId, String connectionId) throws Exception {
846        mConnectionServiceFixtureA.sendSetDisconnected(connectionId, DisconnectCause.LOCAL);
847        assertEquals(Call.STATE_DISCONNECTED, mInCallServiceFixtureX.getCall(callId).getState());
848        assertEquals(Call.STATE_DISCONNECTED, mInCallServiceFixtureY.getCall(callId).getState());
849    }
850
851    /**
852     * Tests the {@link Call#pullExternalCall()} API.  Ensures that an external call which is
853     * pullable can be pulled.
854     *
855     * @throws Exception
856     */
857    @LargeTest
858    public void testPullExternalCall() throws Exception {
859        // TODO: Revisit this unit test once telecom support for filtering external calls from
860        // InCall services is implemented.
861        mConnectionServiceFixtureA.mConnectionServiceDelegate.mCapabilities =
862                Connection.CAPABILITY_CAN_PULL_CALL;
863        mConnectionServiceFixtureA.mConnectionServiceDelegate.mProperties =
864                Connection.PROPERTY_IS_EXTERNAL_CALL;
865
866        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
867                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
868        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
869
870        // Attempt to pull the call and verify the API call makes it through
871        mInCallServiceFixtureX.mInCallAdapter.pullExternalCall(ids.mCallId);
872        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT))
873                .pullExternalCall(ids.mConnectionId);
874    }
875
876    /**
877     * Tests the {@link Call#pullExternalCall()} API.  Verifies that if an external call is not
878     * marked as pullable that the connection service does not get an API call to pull the external
879     * call.
880     *
881     * @throws Exception
882     */
883    @LargeTest
884    public void testPullNonPullableExternalCall() throws Exception {
885        // TODO: Revisit this unit test once telecom support for filtering external calls from
886        // InCall services is implemented.
887        mConnectionServiceFixtureA.mConnectionServiceDelegate.mProperties =
888                Connection.PROPERTY_IS_EXTERNAL_CALL;
889
890        IdPair ids = startAndMakeActiveIncomingCall("650-555-1212",
891                mPhoneAccountA0.getAccountHandle(), mConnectionServiceFixtureA);
892        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
893
894        // Attempt to pull the call and verify the API call makes it through
895        mInCallServiceFixtureX.mInCallAdapter.pullExternalCall(ids.mCallId);
896        Thread.sleep(TEST_TIMEOUT);
897        verify(mConnectionServiceFixtureA.getTestDouble(), never())
898                .pullExternalCall(ids.mConnectionId);
899    }
900
901    @LargeTest
902    public void testEmergencyCallFailMoveToSecondSim() throws Exception {
903        IdPair ids = startAndMakeDialingEmergencyCall("650-555-1212",
904                mPhoneAccountE0.getAccountHandle(), mConnectionServiceFixtureA);
905        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureX.getCall(ids.mCallId).getState());
906        assertEquals(Call.STATE_DIALING, mInCallServiceFixtureY.getCall(ids.mCallId).getState());
907
908        // The Emergency Call has failed on the default SIM with an ERROR Disconnect Cause. Retry
909        // with the other SIM PhoneAccount
910        IdPair newIds = triggerEmergencyRedial(mPhoneAccountE1.getAccountHandle(),
911                mConnectionServiceFixtureA, ids);
912
913        // Call should be active on the E1 PhoneAccount
914        mConnectionServiceFixtureA.sendSetActive(newIds.mConnectionId);
915        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureX.getCall(newIds.mCallId).getState());
916        assertEquals(Call.STATE_ACTIVE, mInCallServiceFixtureY.getCall(newIds.mCallId).getState());
917        assertEquals(mInCallServiceFixtureX.getCall(ids.mCallId).getAccountHandle(),
918                mPhoneAccountE1.getAccountHandle());
919    }
920}
921