BluetoothPhoneServiceTest.java revision d7a57f03a21e507a6577c2229354e589658e8efe
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 android.bluetooth.BluetoothAdapter;
20import android.content.ComponentName;
21import android.content.Intent;
22import android.graphics.drawable.Icon;
23import android.net.Uri;
24import android.os.Binder;
25import android.os.Debug;
26import android.telecom.Connection;
27import android.telecom.GatewayInfo;
28import android.telecom.PhoneAccount;
29import android.telecom.PhoneAccountHandle;
30import android.telecom.TelecomManager;
31import android.telephony.PhoneNumberUtils;
32import android.telephony.TelephonyManager;
33import android.test.suitebuilder.annotation.MediumTest;
34import android.test.suitebuilder.annotation.SmallTest;
35
36import com.android.server.telecom.BluetoothAdapterProxy;
37import com.android.server.telecom.BluetoothHeadsetProxy;
38import com.android.server.telecom.BluetoothPhoneServiceImpl;
39import com.android.server.telecom.Call;
40import com.android.server.telecom.CallState;
41import com.android.server.telecom.CallsManager;
42import com.android.server.telecom.PhoneAccountRegistrar;
43import com.android.server.telecom.TelecomSystem;
44
45import org.mockito.Mock;
46import org.mockito.MockitoAnnotations;
47
48import java.util.ArrayList;
49import java.util.LinkedList;
50
51import static org.mockito.ArgumentMatchers.nullable;
52import static org.mockito.Matchers.any;
53import static org.mockito.Matchers.anyBoolean;
54import static org.mockito.Matchers.anyChar;
55import static org.mockito.Matchers.anyInt;
56import static org.mockito.Matchers.anyString;
57import static org.mockito.Matchers.eq;
58import static org.mockito.Matchers.isNull;
59import static org.mockito.Mockito.doNothing;
60import static org.mockito.Mockito.doReturn;
61import static org.mockito.Mockito.mock;
62import static org.mockito.Mockito.never;
63import static org.mockito.Mockito.times;
64import static org.mockito.Mockito.when;
65import static org.mockito.Mockito.verify;
66
67public class BluetoothPhoneServiceTest extends TelecomTestCase {
68
69    private static final int TEST_DTMF_TONE = 0;
70    private static final String TEST_ACCOUNT_ADDRESS = "//foo.com/";
71    private static final int TEST_ACCOUNT_INDEX = 0;
72
73    // match up with BluetoothPhoneServiceImpl
74    private static final int CALL_STATE_ACTIVE = 0;
75    private static final int CALL_STATE_HELD = 1;
76    private static final int CALL_STATE_DIALING = 2;
77    private static final int CALL_STATE_ALERTING = 3;
78    private static final int CALL_STATE_INCOMING = 4;
79    private static final int CALL_STATE_WAITING = 5;
80    private static final int CALL_STATE_IDLE = 6;
81    // Terminate all held or set UDUB("busy") to a waiting call
82    private static final int CHLD_TYPE_RELEASEHELD = 0;
83    // Terminate all active calls and accepts a waiting/held call
84    private static final int CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD = 1;
85    // Hold all active calls and accepts a waiting/held call
86    private static final int CHLD_TYPE_HOLDACTIVE_ACCEPTHELD = 2;
87    // Add all held calls to a conference
88    private static final int CHLD_TYPE_ADDHELDTOCONF = 3;
89
90    private BluetoothPhoneServiceImpl mBluetoothPhoneService;
91    private final TelecomSystem.SyncRoot mLock = new TelecomSystem.SyncRoot() {
92    };
93
94    @Mock CallsManager mMockCallsManager;
95    @Mock PhoneAccountRegistrar mMockPhoneAccountRegistrar;
96    @Mock BluetoothHeadsetProxy mMockBluetoothHeadset;
97
98    @Override
99    public void setUp() throws Exception {
100        super.setUp();
101        MockitoAnnotations.initMocks(this);
102        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();
103
104        // Ensure initialization does not actually try to access any of the CallsManager fields.
105        // This also works to return null if it is not overwritten later in the test.
106        doNothing().when(mMockCallsManager).addListener(any(
107                CallsManager.CallsManagerListener.class));
108        doReturn(null).when(mMockCallsManager).getActiveCall();
109        doReturn(null).when(mMockCallsManager).getRingingCall();
110        doReturn(null).when(mMockCallsManager).getHeldCall();
111        doReturn(null).when(mMockCallsManager).getOutgoingCall();
112        doReturn(0).when(mMockCallsManager).getNumHeldCalls();
113        mBluetoothPhoneService = new BluetoothPhoneServiceImpl(mContext, mLock, mMockCallsManager,
114                mock(BluetoothAdapterProxy.class), mMockPhoneAccountRegistrar);
115
116        // Bring in test Bluetooth Headset
117        mBluetoothPhoneService.setBluetoothHeadset(mMockBluetoothHeadset);
118    }
119
120    @Override
121    public void tearDown() throws Exception {
122
123        mBluetoothPhoneService = null;
124        super.tearDown();
125    }
126
127    @SmallTest
128    public void testHeadsetAnswerCall() throws Exception {
129        Call mockCall = createRingingCall();
130
131        boolean callAnswered = mBluetoothPhoneService.mBinder.answerCall();
132
133        verify(mMockCallsManager).answerCall(eq(mockCall), any(int.class));
134        assertEquals(callAnswered, true);
135    }
136
137    @SmallTest
138    public void testHeadsetAnswerCallNull() throws Exception {
139        when(mMockCallsManager.getRingingCall()).thenReturn(null);
140
141        boolean callAnswered = mBluetoothPhoneService.mBinder.answerCall();
142
143        verify(mMockCallsManager,never()).answerCall(any(Call.class), any(int.class));
144        assertEquals(callAnswered, false);
145    }
146
147    @SmallTest
148    public void testHeadsetHangupCall() throws Exception {
149        Call mockCall = createForegroundCall();
150
151        boolean callHungup = mBluetoothPhoneService.mBinder.hangupCall();
152
153        verify(mMockCallsManager).disconnectCall(eq(mockCall));
154        assertEquals(callHungup, true);
155    }
156
157    @SmallTest
158    public void testHeadsetHangupCallNull() throws Exception {
159        when(mMockCallsManager.getForegroundCall()).thenReturn(null);
160
161        boolean callHungup = mBluetoothPhoneService.mBinder.hangupCall();
162
163        verify(mMockCallsManager,never()).disconnectCall(any(Call.class));
164        assertEquals(callHungup, false);
165    }
166
167    @SmallTest
168    public void testHeadsetSendDTMF() throws Exception {
169        Call mockCall = createForegroundCall();
170
171        boolean sentDtmf = mBluetoothPhoneService.mBinder.sendDtmf(TEST_DTMF_TONE);
172
173        verify(mMockCallsManager).playDtmfTone(eq(mockCall), eq((char) TEST_DTMF_TONE));
174        verify(mMockCallsManager).stopDtmfTone(eq(mockCall));
175        assertEquals(sentDtmf, true);
176    }
177
178    @SmallTest
179    public void testHeadsetSendDTMFNull() throws Exception {
180        when(mMockCallsManager.getForegroundCall()).thenReturn(null);
181
182        boolean sentDtmf = mBluetoothPhoneService.mBinder.sendDtmf(TEST_DTMF_TONE);
183
184        verify(mMockCallsManager,never()).playDtmfTone(any(Call.class), anyChar());
185        verify(mMockCallsManager,never()).stopDtmfTone(any(Call.class));
186        assertEquals(sentDtmf, false);
187    }
188
189    @SmallTest
190    public void testGetNetworkOperator() throws Exception {
191        Call mockCall = createForegroundCall();
192        PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
193        when(mMockPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
194                nullable(PhoneAccountHandle.class))).thenReturn(fakePhoneAccount);
195
196        String networkOperator = mBluetoothPhoneService.mBinder.getNetworkOperator();
197
198        assertEquals(networkOperator, "label0");
199    }
200
201    @SmallTest
202    public void testGetNetworkOperatorNoPhoneAccount() throws Exception {
203        when(mMockCallsManager.getForegroundCall()).thenReturn(null);
204
205        String networkOperator = mBluetoothPhoneService.mBinder.getNetworkOperator();
206
207        assertEquals(networkOperator, "label1");
208    }
209
210    @SmallTest
211    public void testGetSubscriberNumber() throws Exception {
212        Call mockCall = createForegroundCall();
213        PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
214        when(mMockPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
215                nullable(PhoneAccountHandle.class))).thenReturn(fakePhoneAccount);
216
217        String subscriberNumber = mBluetoothPhoneService.mBinder.getSubscriberNumber();
218
219        assertEquals(subscriberNumber, TEST_ACCOUNT_ADDRESS + TEST_ACCOUNT_INDEX);
220    }
221
222    @SmallTest
223    public void testGetSubscriberNumberFallbackToTelephony() throws Exception {
224        Call mockCall = createForegroundCall();
225        String fakeNumber = "8675309";
226        when(mMockPhoneAccountRegistrar.getPhoneAccountOfCurrentUser(
227                nullable(PhoneAccountHandle.class))).thenReturn(null);
228        when(mMockPhoneAccountRegistrar.getPhoneAccountUnchecked(
229                nullable(PhoneAccountHandle.class))).thenReturn(null);
230        when(TelephonyManager.from(mContext).getLine1Number()).thenReturn(fakeNumber);
231
232        String subscriberNumber = mBluetoothPhoneService.mBinder.getSubscriberNumber();
233
234        assertEquals(subscriberNumber, fakeNumber);
235    }
236
237    @MediumTest
238    public void testListCurrentCallsOneCall() throws Exception {
239        ArrayList<Call> calls = new ArrayList<>();
240        Call activeCall = createActiveCall();
241        when(activeCall.getState()).thenReturn(CallState.ACTIVE);
242        calls.add(activeCall);
243        when(activeCall.isConference()).thenReturn(false);
244        when(activeCall.getHandle()).thenReturn(Uri.parse("tel:555-000"));
245        when(mMockCallsManager.getCalls()).thenReturn(calls);
246
247        mBluetoothPhoneService.mBinder.listCurrentCalls();
248
249        verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(0), eq(0), eq(false),
250                eq("555000"), eq(PhoneNumberUtils.TOA_Unknown));
251        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
252    }
253
254    @MediumTest
255    public void testConferenceInProgressCDMA() throws Exception {
256        // If two calls are being conferenced and updateHeadsetWithCallState runs while this is
257        // still occuring, it will look like there is an active and held call still while we are
258        // transitioning into a conference.
259        // Call has been put into a CDMA "conference" with one call on hold.
260        ArrayList<Call> calls = new ArrayList<>();
261        Call parentCall = createActiveCall();
262        final Call confCall1 = mock(Call.class);
263        final Call confCall2 = createHeldCall();
264        calls.add(parentCall);
265        calls.add(confCall1);
266        calls.add(confCall2);
267        when(mMockCallsManager.getCalls()).thenReturn(calls);
268        when(confCall1.getState()).thenReturn(CallState.ACTIVE);
269        when(confCall2.getState()).thenReturn(CallState.ACTIVE);
270        when(confCall1.isIncoming()).thenReturn(false);
271        when(confCall2.isIncoming()).thenReturn(true);
272        when(confCall1.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
273                Uri.parse("tel:555-0000")));
274        when(confCall2.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
275                Uri.parse("tel:555-0001")));
276        addCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
277        addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
278        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
279        when(parentCall.getConferenceLevelActiveCall()).thenReturn(confCall1);
280        when(parentCall.isConference()).thenReturn(true);
281        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
282            add(confCall1);
283            add(confCall2);
284        }});
285        //Add links from child calls to parent
286        when(confCall1.getParentCall()).thenReturn(parentCall);
287        when(confCall2.getParentCall()).thenReturn(parentCall);
288
289        mBluetoothPhoneService.mBinder.queryPhoneState();
290        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE),
291                eq(""), eq(128));
292        when(parentCall.wasConferencePreviouslyMerged()).thenReturn(true);
293        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(parentCall);
294        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
295                eq(""), eq(128));
296        when(mMockCallsManager.getHeldCall()).thenReturn(null);
297        // Spurious call to onIsConferencedChanged.
298        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(parentCall);
299        // Make sure the call has only occurred collectively 2 times (not on the third)
300        verify(mMockBluetoothHeadset, times(2)).phoneStateChanged(any(int.class),
301                any(int.class), any(int.class), nullable(String.class), any(int.class));
302    }
303
304    @MediumTest
305    public void testListCurrentCallsCdmaHold() throws Exception {
306        // Call has been put into a CDMA "conference" with one call on hold.
307        ArrayList<Call> calls = new ArrayList<>();
308        Call parentCall = createActiveCall();
309        final Call foregroundCall = mock(Call.class);
310        final Call heldCall = createHeldCall();
311        calls.add(parentCall);
312        calls.add(foregroundCall);
313        calls.add(heldCall);
314        when(mMockCallsManager.getCalls()).thenReturn(calls);
315        when(foregroundCall.getState()).thenReturn(CallState.ACTIVE);
316        when(heldCall.getState()).thenReturn(CallState.ACTIVE);
317        when(foregroundCall.isIncoming()).thenReturn(false);
318        when(heldCall.isIncoming()).thenReturn(true);
319        when(foregroundCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
320                Uri.parse("tel:555-0000")));
321        when(heldCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
322                Uri.parse("tel:555-0001")));
323        addCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
324        addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
325        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
326        when(parentCall.getConferenceLevelActiveCall()).thenReturn(foregroundCall);
327        when(parentCall.isConference()).thenReturn(true);
328        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
329            add(foregroundCall);
330            add(heldCall);
331        }});
332        //Add links from child calls to parent
333        when(foregroundCall.getParentCall()).thenReturn(parentCall);
334        when(heldCall.getParentCall()).thenReturn(parentCall);
335
336        mBluetoothPhoneService.mBinder.listCurrentCalls();
337
338        verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(CALL_STATE_ACTIVE), eq(0),
339                eq(false), eq("5550000"), eq(PhoneNumberUtils.TOA_Unknown));
340        verify(mMockBluetoothHeadset).clccResponse(eq(2), eq(1), eq(CALL_STATE_HELD), eq(0),
341                eq(false), eq("5550001"), eq(PhoneNumberUtils.TOA_Unknown));
342        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
343    }
344
345    @MediumTest
346    public void testListCurrentCallsCdmaConference() throws Exception {
347        // Call is in a true CDMA conference
348        ArrayList<Call> calls = new ArrayList<>();
349        Call parentCall = createActiveCall();
350        final Call confCall1 = mock(Call.class);
351        final Call confCall2 = createHeldCall();
352        calls.add(parentCall);
353        calls.add(confCall1);
354        calls.add(confCall2);
355        when(mMockCallsManager.getCalls()).thenReturn(calls);
356        when(confCall1.getState()).thenReturn(CallState.ACTIVE);
357        when(confCall2.getState()).thenReturn(CallState.ACTIVE);
358        when(confCall1.isIncoming()).thenReturn(false);
359        when(confCall2.isIncoming()).thenReturn(true);
360        when(confCall1.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
361                Uri.parse("tel:555-0000")));
362        when(confCall2.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
363                Uri.parse("tel:555-0001")));
364        removeCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
365        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
366        when(parentCall.wasConferencePreviouslyMerged()).thenReturn(true);
367        when(parentCall.getConferenceLevelActiveCall()).thenReturn(confCall1);
368        when(parentCall.isConference()).thenReturn(true);
369        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
370            add(confCall1);
371            add(confCall2);
372        }});
373        //Add links from child calls to parent
374        when(confCall1.getParentCall()).thenReturn(parentCall);
375        when(confCall2.getParentCall()).thenReturn(parentCall);
376
377        mBluetoothPhoneService.mBinder.listCurrentCalls();
378
379        verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(CALL_STATE_ACTIVE), eq(0),
380                eq(true), eq("5550000"), eq(PhoneNumberUtils.TOA_Unknown));
381        verify(mMockBluetoothHeadset).clccResponse(eq(2), eq(1), eq(CALL_STATE_ACTIVE), eq(0),
382                eq(true), eq("5550001"), eq(PhoneNumberUtils.TOA_Unknown));
383        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
384    }
385
386    @MediumTest
387    public void testWaitingCallClccResponse() throws Exception {
388        ArrayList<Call> calls = new ArrayList<>();
389        when(mMockCallsManager.getCalls()).thenReturn(calls);
390        // This test does not define a value for getForegroundCall(), so this ringing call will
391        // be treated as if it is a waiting call when listCurrentCalls() is invoked.
392        Call waitingCall = createRingingCall();
393        calls.add(waitingCall);
394        when(waitingCall.isIncoming()).thenReturn(true);
395        when(waitingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
396                Uri.parse("tel:555-0000")));
397        when(waitingCall.getState()).thenReturn(CallState.RINGING);
398        when(waitingCall.isConference()).thenReturn(false);
399
400        mBluetoothPhoneService.mBinder.listCurrentCalls();
401        verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_WAITING, 0, false,
402                "5550000", PhoneNumberUtils.TOA_Unknown);
403        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
404        verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
405                anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
406    }
407
408    @MediumTest
409    public void testNewCallClccResponse() throws Exception {
410        ArrayList<Call> calls = new ArrayList<>();
411        when(mMockCallsManager.getCalls()).thenReturn(calls);
412        Call newCall = createForegroundCall();
413        calls.add(newCall);
414        when(newCall.getState()).thenReturn(CallState.NEW);
415        when(newCall.isConference()).thenReturn(false);
416
417        mBluetoothPhoneService.mBinder.listCurrentCalls();
418        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
419        verify(mMockBluetoothHeadset, times(1)).clccResponse(anyInt(),
420                anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
421    }
422
423    @MediumTest
424    public void testRingingCallClccResponse() throws Exception {
425        ArrayList<Call> calls = new ArrayList<>();
426        when(mMockCallsManager.getCalls()).thenReturn(calls);
427        Call ringingCall = createForegroundCall();
428        calls.add(ringingCall);
429        when(ringingCall.getState()).thenReturn(CallState.RINGING);
430        when(ringingCall.isIncoming()).thenReturn(true);
431        when(ringingCall.isConference()).thenReturn(false);
432        when(ringingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
433                Uri.parse("tel:555-0000")));
434
435        mBluetoothPhoneService.mBinder.listCurrentCalls();
436        verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_INCOMING, 0, false,
437                "5550000", PhoneNumberUtils.TOA_Unknown);
438        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
439        verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
440                anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
441    }
442
443    @MediumTest
444    public void testCallClccCache() throws Exception {
445        ArrayList<Call> calls = new ArrayList<>();
446        when(mMockCallsManager.getCalls()).thenReturn(calls);
447        Call ringingCall = createForegroundCall();
448        calls.add(ringingCall);
449        when(ringingCall.getState()).thenReturn(CallState.RINGING);
450        when(ringingCall.isIncoming()).thenReturn(true);
451        when(ringingCall.isConference()).thenReturn(false);
452        when(ringingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
453                Uri.parse("tel:5550000")));
454
455        mBluetoothPhoneService.mBinder.listCurrentCalls();
456        verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_INCOMING, 0, false,
457                "5550000", PhoneNumberUtils.TOA_Unknown);
458
459        // Test Caching of old call indicies in clcc
460        when(ringingCall.getState()).thenReturn(CallState.ACTIVE);
461        Call newHoldingCall = createHeldCall();
462        calls.add(0, newHoldingCall);
463        when(newHoldingCall.getState()).thenReturn(CallState.ON_HOLD);
464        when(newHoldingCall.isIncoming()).thenReturn(true);
465        when(newHoldingCall.isConference()).thenReturn(false);
466        when(newHoldingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
467                Uri.parse("tel:555-0001")));
468
469        mBluetoothPhoneService.mBinder.listCurrentCalls();
470        verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_ACTIVE, 0, false,
471                "5550000", PhoneNumberUtils.TOA_Unknown);
472        verify(mMockBluetoothHeadset).clccResponse(2, 1, CALL_STATE_HELD, 0, false,
473                "5550001", PhoneNumberUtils.TOA_Unknown);
474        verify(mMockBluetoothHeadset, times(2)).clccResponse(0, 0, 0, 0, false, null, 0);
475    }
476
477    @MediumTest
478    public void testAlertingCallClccResponse() throws Exception {
479        ArrayList<Call> calls = new ArrayList<>();
480        when(mMockCallsManager.getCalls()).thenReturn(calls);
481        Call dialingCall = createForegroundCall();
482        calls.add(dialingCall);
483        when(dialingCall.getState()).thenReturn(CallState.DIALING);
484        when(dialingCall.isIncoming()).thenReturn(false);
485        when(dialingCall.isConference()).thenReturn(false);
486        when(dialingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
487                Uri.parse("tel:555-0000")));
488
489        mBluetoothPhoneService.mBinder.listCurrentCalls();
490        verify(mMockBluetoothHeadset).clccResponse(1, 0, CALL_STATE_ALERTING, 0, false,
491                "5550000", PhoneNumberUtils.TOA_Unknown);
492        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
493        verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
494                anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
495    }
496
497    @MediumTest
498    public void testHoldingCallClccResponse() throws Exception {
499        ArrayList<Call> calls = new ArrayList<>();
500        when(mMockCallsManager.getCalls()).thenReturn(calls);
501        Call dialingCall = createForegroundCall();
502        calls.add(dialingCall);
503        when(dialingCall.getState()).thenReturn(CallState.DIALING);
504        when(dialingCall.isIncoming()).thenReturn(false);
505        when(dialingCall.isConference()).thenReturn(false);
506        when(dialingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
507                Uri.parse("tel:555-0000")));
508        Call holdingCall = createHeldCall();
509        calls.add(holdingCall);
510        when(holdingCall.getState()).thenReturn(CallState.ON_HOLD);
511        when(holdingCall.isIncoming()).thenReturn(true);
512        when(holdingCall.isConference()).thenReturn(false);
513        when(holdingCall.getGatewayInfo()).thenReturn(new GatewayInfo(null, null,
514                Uri.parse("tel:555-0001")));
515
516        mBluetoothPhoneService.mBinder.listCurrentCalls();
517        verify(mMockBluetoothHeadset).clccResponse(1, 0, CALL_STATE_ALERTING, 0, false,
518                "5550000", PhoneNumberUtils.TOA_Unknown);
519        verify(mMockBluetoothHeadset).clccResponse(2, 1, CALL_STATE_HELD, 0, false,
520                "5550001", PhoneNumberUtils.TOA_Unknown);
521        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
522        verify(mMockBluetoothHeadset, times(3)).clccResponse(anyInt(),
523                anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
524    }
525
526    @MediumTest
527    public void testListCurrentCallsImsConference() throws Exception {
528        ArrayList<Call> calls = new ArrayList<>();
529        Call parentCall = createActiveCall();
530        calls.add(parentCall);
531        addCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
532        when(parentCall.isConference()).thenReturn(true);
533        when(parentCall.getState()).thenReturn(CallState.ACTIVE);
534        when(parentCall.isIncoming()).thenReturn(true);
535        when(mMockCallsManager.getCalls()).thenReturn(calls);
536
537        mBluetoothPhoneService.mBinder.listCurrentCalls();
538
539        verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(1), eq(CALL_STATE_ACTIVE), eq(0),
540                eq(true), (String) isNull(), eq(-1));
541        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
542    }
543
544    @MediumTest
545    public void testListCurrentCallsHeldImsCepConference() throws Exception {
546        ArrayList<Call> calls = new ArrayList<>();
547        Call parentCall = createHeldCall();
548        Call childCall1 = createActiveCall();
549        Call childCall2 = createActiveCall();
550        calls.add(parentCall);
551        calls.add(childCall1);
552        calls.add(childCall2);
553        addCallCapability(parentCall, Connection.CAPABILITY_MANAGE_CONFERENCE);
554        when(childCall1.getParentCall()).thenReturn(parentCall);
555        when(childCall2.getParentCall()).thenReturn(parentCall);
556
557        when(parentCall.isConference()).thenReturn(true);
558        when(parentCall.getState()).thenReturn(CallState.ON_HOLD);
559        when(childCall1.getState()).thenReturn(CallState.ACTIVE);
560        when(childCall2.getState()).thenReturn(CallState.ACTIVE);
561
562        when(parentCall.isIncoming()).thenReturn(true);
563        when(mMockCallsManager.getCalls()).thenReturn(calls);
564
565        mBluetoothPhoneService.mBinder.listCurrentCalls();
566
567        verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(CALL_STATE_HELD), eq(0),
568                eq(true), (String) isNull(), eq(-1));
569        verify(mMockBluetoothHeadset).clccResponse(eq(2), eq(0), eq(CALL_STATE_HELD), eq(0),
570                eq(true), (String) isNull(), eq(-1));
571        verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
572    }
573
574    @MediumTest
575    public void testQueryPhoneState() throws Exception {
576        Call ringingCall = createRingingCall();
577        when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:5550000"));
578
579        mBluetoothPhoneService.mBinder.queryPhoneState();
580
581        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
582                eq("5550000"), eq(PhoneNumberUtils.TOA_Unknown));
583    }
584
585    @MediumTest
586    public void testCDMAConferenceQueryState() throws Exception {
587        Call parentConfCall = createActiveCall();
588        final Call confCall1 = mock(Call.class);
589        final Call confCall2 = mock(Call.class);
590        when(parentConfCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
591        addCallCapability(parentConfCall, Connection.CAPABILITY_SWAP_CONFERENCE);
592        removeCallCapability(parentConfCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
593        when(parentConfCall.wasConferencePreviouslyMerged()).thenReturn(true);
594        when(parentConfCall.isConference()).thenReturn(true);
595        when(parentConfCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
596            add(confCall1);
597            add(confCall2);
598        }});
599
600        mBluetoothPhoneService.mBinder.queryPhoneState();
601        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
602                eq(""), eq(128));
603    }
604
605    @MediumTest
606    public void testProcessChldTypeReleaseHeldRinging() throws Exception {
607        Call ringingCall = createRingingCall();
608
609        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(CHLD_TYPE_RELEASEHELD);
610
611        verify(mMockCallsManager).rejectCall(eq(ringingCall), eq(false), nullable(String.class));
612        assertEquals(didProcess, true);
613    }
614
615    @MediumTest
616    public void testProcessChldTypeReleaseHeldHold() throws Exception {
617        Call onHoldCall = createHeldCall();
618
619        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(CHLD_TYPE_RELEASEHELD);
620
621        verify(mMockCallsManager).disconnectCall(eq(onHoldCall));
622        assertEquals(didProcess, true);
623    }
624
625    @MediumTest
626    public void testProcessChldReleaseActiveRinging() throws Exception {
627        Call activeCall = createActiveCall();
628        Call ringingCall = createRingingCall();
629
630        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
631                CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD);
632
633        verify(mMockCallsManager).disconnectCall(eq(activeCall));
634        verify(mMockCallsManager).answerCall(eq(ringingCall), any(int.class));
635        assertEquals(didProcess, true);
636    }
637
638    @MediumTest
639    public void testProcessChldReleaseActiveHold() throws Exception {
640        Call activeCall = createActiveCall();
641        Call heldCall = createHeldCall();
642
643        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
644                CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD);
645
646        verify(mMockCallsManager).disconnectCall(eq(activeCall));
647        verify(mMockCallsManager).unholdCall(eq(heldCall));
648        assertEquals(didProcess, true);
649    }
650
651    @MediumTest
652    public void testProcessChldHoldActiveRinging() throws Exception {
653        Call ringingCall = createRingingCall();
654
655        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
656                CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
657
658        verify(mMockCallsManager).answerCall(eq(ringingCall), any(int.class));
659        assertEquals(didProcess, true);
660    }
661
662    @MediumTest
663    public void testProcessChldHoldActiveUnhold() throws Exception {
664        Call heldCall = createHeldCall();
665
666        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
667                CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
668
669        verify(mMockCallsManager).unholdCall(eq(heldCall));
670        assertEquals(didProcess, true);
671    }
672
673    @MediumTest
674    public void testProcessChldHoldActiveHold() throws Exception {
675        Call activeCall = createActiveCall();
676        addCallCapability(activeCall, Connection.CAPABILITY_HOLD);
677
678        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
679                CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
680
681        verify(mMockCallsManager).holdCall(eq(activeCall));
682        assertEquals(didProcess, true);
683    }
684
685    @MediumTest
686    public void testProcessChldAddHeldToConfHolding() throws Exception {
687        Call activeCall = createActiveCall();
688        addCallCapability(activeCall, Connection.CAPABILITY_MERGE_CONFERENCE);
689
690        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(CHLD_TYPE_ADDHELDTOCONF);
691
692        verify(activeCall).mergeConference();
693        assertEquals(didProcess, true);
694    }
695
696    @MediumTest
697    public void testProcessChldAddHeldToConf() throws Exception {
698        Call activeCall = createActiveCall();
699        removeCallCapability(activeCall, Connection.CAPABILITY_MERGE_CONFERENCE);
700        Call conferenceableCall = mock(Call.class);
701        ArrayList<Call> conferenceableCalls = new ArrayList<>();
702        conferenceableCalls.add(conferenceableCall);
703        when(activeCall.getConferenceableCalls()).thenReturn(conferenceableCalls);
704
705        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(CHLD_TYPE_ADDHELDTOCONF);
706
707        verify(mMockCallsManager).conference(activeCall, conferenceableCall);
708        assertEquals(didProcess, true);
709    }
710
711    @MediumTest
712    public void testProcessChldHoldActiveSwapConference() throws Exception {
713        // Create an active CDMA Call with a call on hold and simulate a swapConference().
714        Call parentCall = createActiveCall();
715        final Call foregroundCall = mock(Call.class);
716        final Call heldCall = createHeldCall();
717        addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
718        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
719        when(parentCall.isConference()).thenReturn(true);
720        when(parentCall.wasConferencePreviouslyMerged()).thenReturn(false);
721        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
722            add(foregroundCall);
723            add(heldCall);
724        }});
725
726        boolean didProcess = mBluetoothPhoneService.mBinder.processChld(
727                CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
728
729        verify(parentCall).swapConference();
730        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE), eq(""),
731                eq(128));
732        assertEquals(didProcess, true);
733    }
734
735    // Testing the CallsManager Listener Functionality on Bluetooth
736    @MediumTest
737    public void testOnCallAddedRinging() throws Exception {
738        Call ringingCall = createRingingCall();
739        when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555000"));
740
741        mBluetoothPhoneService.mCallsManagerListener.onCallAdded(ringingCall);
742
743        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
744                eq("555000"), eq(PhoneNumberUtils.TOA_Unknown));
745
746    }
747
748    @MediumTest
749    public void testOnCallAddedCdmaActiveHold() throws Exception {
750        // Call has been put into a CDMA "conference" with one call on hold.
751        Call parentCall = createActiveCall();
752        final Call foregroundCall = mock(Call.class);
753        final Call heldCall = createHeldCall();
754        addCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
755        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
756        when(parentCall.isConference()).thenReturn(true);
757        when(parentCall.getChildCalls()).thenReturn(new LinkedList<Call>() {{
758            add(foregroundCall);
759            add(heldCall);
760        }});
761
762        mBluetoothPhoneService.mCallsManagerListener.onCallAdded(parentCall);
763
764        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE),
765                eq(""), eq(128));
766
767    }
768
769    @MediumTest
770    public void testOnCallRemoved() throws Exception {
771        Call activeCall = createActiveCall();
772        mBluetoothPhoneService.mCallsManagerListener.onCallAdded(activeCall);
773        doReturn(null).when(mMockCallsManager).getActiveCall();
774        mBluetoothPhoneService.mCallsManagerListener.onCallRemoved(activeCall);
775
776        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_IDLE),
777                eq(""), eq(128));
778    }
779
780    @MediumTest
781    public void testOnCallStateChangedConnectingCall() throws Exception {
782        Call activeCall = mock(Call.class);
783        Call connectingCall = mock(Call.class);
784        when(connectingCall.getState()).thenReturn(CallState.CONNECTING);
785        ArrayList<Call> calls = new ArrayList<>();
786        calls.add(connectingCall);
787        calls.add(activeCall);
788        when(mMockCallsManager.getCalls()).thenReturn(calls);
789
790        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(activeCall,
791                CallState.ACTIVE, CallState.ON_HOLD);
792
793        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
794                anyString(), anyInt());
795    }
796
797    @MediumTest
798    public void testOnCallStateChangedDialing() throws Exception {
799        Call activeCall = createActiveCall();
800
801        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(activeCall,
802                CallState.CONNECTING, CallState.DIALING);
803
804        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
805                anyString(), anyInt());
806    }
807
808    @MediumTest
809    public void testOnCallStateChangedAlerting() throws Exception {
810        Call outgoingCall = createOutgoingCall();
811
812        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(outgoingCall,
813                CallState.NEW, CallState.DIALING);
814
815        verify(mMockBluetoothHeadset).phoneStateChanged(0, 0, CALL_STATE_DIALING, "", 128);
816        verify(mMockBluetoothHeadset).phoneStateChanged(0, 0, CALL_STATE_ALERTING, "", 128);
817    }
818
819    @MediumTest
820    public void testOnCallStateChanged() throws Exception {
821        Call ringingCall = createRingingCall();
822        when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
823        mBluetoothPhoneService.mCallsManagerListener.onCallAdded(ringingCall);
824
825        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
826                eq("555-0000"), eq(PhoneNumberUtils.TOA_Unknown));
827
828        //Switch to active
829        doReturn(null).when(mMockCallsManager).getRingingCall();
830        when(mMockCallsManager.getActiveCall()).thenReturn(ringingCall);
831
832        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(ringingCall,
833                CallState.RINGING, CallState.ACTIVE);
834
835        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
836                eq(""), eq(128));
837    }
838
839    @MediumTest
840    public void testOnCallStateChangedGSMSwap() throws Exception {
841        Call heldCall = createHeldCall();
842        when(heldCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
843        doReturn(2).when(mMockCallsManager).getNumHeldCalls();
844        mBluetoothPhoneService.mCallsManagerListener.onCallStateChanged(heldCall,
845                CallState.ACTIVE, CallState.ON_HOLD);
846
847        verify(mMockBluetoothHeadset, never()).phoneStateChanged(eq(0), eq(2), eq(CALL_STATE_HELD),
848                eq("5550000"), eq(PhoneNumberUtils.TOA_Unknown));
849    }
850
851    @MediumTest
852    public void testOnIsConferencedChanged() throws Exception {
853        // Start with two calls that are being merged into a CDMA conference call. The
854        // onIsConferencedChanged method will be called multiple times during the call. Make sure
855        // that the bluetooth phone state is updated properly.
856        Call parentCall = createActiveCall();
857        Call activeCall = mock(Call.class);
858        Call heldCall = createHeldCall();
859        when(activeCall.getParentCall()).thenReturn(parentCall);
860        when(heldCall.getParentCall()).thenReturn(parentCall);
861        ArrayList<Call> calls = new ArrayList<>();
862        calls.add(activeCall);
863        when(parentCall.getChildCalls()).thenReturn(calls);
864        when(parentCall.isConference()).thenReturn(true);
865        removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
866        addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
867        when(parentCall.wasConferencePreviouslyMerged()).thenReturn(false);
868
869        // Be sure that onIsConferencedChanged rejects spurious changes during set up of
870        // CDMA "conference"
871        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(activeCall);
872        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
873                anyString(), anyInt());
874        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(heldCall);
875        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
876                anyString(), anyInt());
877        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(parentCall);
878        verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
879                anyString(), anyInt());
880
881        calls.add(heldCall);
882        mBluetoothPhoneService.mCallsManagerListener.onIsConferencedChanged(parentCall);
883        verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE),
884                eq(""), eq(128));
885    }
886
887    @MediumTest
888    public void testBluetoothAdapterReceiver() throws Exception {
889        Call ringingCall = createRingingCall();
890        when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:5550000"));
891
892        Intent intent = new Intent();
893        intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);
894        mBluetoothPhoneService.mBluetoothAdapterReceiver.onReceive(mContext, intent);
895
896        verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
897                eq("5550000"), eq(PhoneNumberUtils.TOA_Unknown));
898    }
899
900    private void addCallCapability(Call call, int capability) {
901        when(call.can(capability)).thenReturn(true);
902    }
903
904    private void removeCallCapability(Call call, int capability) {
905        when(call.can(capability)).thenReturn(false);
906    }
907
908    private Call createActiveCall() {
909        Call call = mock(Call.class);
910        when(mMockCallsManager.getActiveCall()).thenReturn(call);
911        return call;
912    }
913
914    private Call createRingingCall() {
915        Call call = mock(Call.class);
916        when(mMockCallsManager.getRingingCall()).thenReturn(call);
917        return call;
918    }
919
920    private Call createHeldCall() {
921        Call call = mock(Call.class);
922        when(mMockCallsManager.getHeldCall()).thenReturn(call);
923        return call;
924    }
925
926    private Call createOutgoingCall() {
927        Call call = mock(Call.class);
928        when(mMockCallsManager.getOutgoingCall()).thenReturn(call);
929        return call;
930    }
931
932    private Call createForegroundCall() {
933        Call call = mock(Call.class);
934        when(mMockCallsManager.getForegroundCall()).thenReturn(call);
935        return call;
936    }
937
938    private static ComponentName makeQuickConnectionServiceComponentName() {
939        return new ComponentName("com.android.server.telecom.tests",
940                "com.android.server.telecom.tests.MockConnectionService");
941    }
942
943    private static PhoneAccountHandle makeQuickAccountHandle(String id) {
944        return new PhoneAccountHandle(makeQuickConnectionServiceComponentName(), id,
945                Binder.getCallingUserHandle());
946    }
947
948    private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) {
949        return new PhoneAccount.Builder(makeQuickAccountHandle(id), "label" + idx);
950    }
951
952    private PhoneAccount makeQuickAccount(String id, int idx) {
953        return makeQuickAccountBuilder(id, idx)
954                .setAddress(Uri.parse(TEST_ACCOUNT_ADDRESS + idx))
955                .setSubscriptionAddress(Uri.parse("tel:555-000" + idx))
956                .setCapabilities(idx)
957                .setIcon(Icon.createWithResource(
958                        "com.android.server.telecom.tests", R.drawable.stat_sys_phone_call))
959                .setShortDescription("desc" + idx)
960                .setIsEnabled(true)
961                .build();
962    }
963}
964