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