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