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