1 /*
2  * Copyright (C) 2020 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 
17 package com.android.bluetooth.telephony;
18 
19 import org.junit.After;
20 import org.junit.Assert;
21 import org.junit.Before;
22 import org.junit.Rule;
23 import org.junit.Test;
24 import org.junit.runner.RunWith;
25 import org.mockito.ArgumentCaptor;
26 import org.mockito.Mock;
27 import org.mockito.MockitoAnnotations;
28 
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.Mockito.*;
31 
32 import android.bluetooth.BluetoothAdapter;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.Intent;
36 import android.content.IntentFilter;
37 import android.net.Uri;
38 import android.os.Binder;
39 import android.os.Bundle;
40 import android.os.IBinder;
41 import android.telecom.BluetoothCallQualityReport;
42 import android.telecom.Call;
43 import android.telecom.Connection;
44 import android.telecom.GatewayInfo;
45 import android.telecom.PhoneAccount;
46 import android.telecom.PhoneAccountHandle;
47 import android.telecom.TelecomManager;
48 import android.telephony.PhoneNumberUtils;
49 import android.telephony.TelephonyManager;
50 import android.util.Log;
51 
52 import androidx.test.core.app.ApplicationProvider;
53 import androidx.test.rule.ServiceTestRule;
54 import androidx.test.filters.MediumTest;
55 import androidx.test.runner.AndroidJUnit4;
56 
57 import com.android.bluetooth.hfp.BluetoothHeadsetProxy;
58 
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.List;
62 import java.util.UUID;
63 import java.util.concurrent.TimeUnit;
64 
65 /**
66  * Tests for {@link BluetoothInCallService}
67  */
68 @MediumTest
69 @RunWith(AndroidJUnit4.class)
70 public class BluetoothInCallServiceTest {
71 
72     private static final int TEST_DTMF_TONE = 0;
73     private static final String TEST_ACCOUNT_ADDRESS = "//foo.com/";
74     private static final int TEST_ACCOUNT_INDEX = 0;
75 
76     private static final int CALL_STATE_ACTIVE = 0;
77     private static final int CALL_STATE_HELD = 1;
78     private static final int CALL_STATE_DIALING = 2;
79     private static final int CALL_STATE_ALERTING = 3;
80     private static final int CALL_STATE_INCOMING = 4;
81     private static final int CALL_STATE_WAITING = 5;
82     private static final int CALL_STATE_IDLE = 6;
83     private static final int CALL_STATE_DISCONNECTED = 7;
84     // Terminate all held or set UDUB("busy") to a waiting call
85     private static final int CHLD_TYPE_RELEASEHELD = 0;
86     // Terminate all active calls and accepts a waiting/held call
87     private static final int CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD = 1;
88     // Hold all active calls and accepts a waiting/held call
89     private static final int CHLD_TYPE_HOLDACTIVE_ACCEPTHELD = 2;
90     // Add all held calls to a conference
91     private static final int CHLD_TYPE_ADDHELDTOCONF = 3;
92 
93     private TestableBluetoothInCallService mBluetoothInCallService;
94     @Rule public final ServiceTestRule mServiceRule
95             = ServiceTestRule.withTimeout(1, TimeUnit.SECONDS);
96 
97     @Mock private BluetoothHeadsetProxy mMockBluetoothHeadset;
98     @Mock private BluetoothInCallService.CallInfo mMockCallInfo;
99     @Mock private TelephonyManager mMockTelephonyManager;
100 
101     public class TestableBluetoothInCallService extends BluetoothInCallService {
102         @Override
onBind(Intent intent)103         public IBinder onBind(Intent intent) {
104             IBinder binder = super.onBind(intent);
105             IntentFilter intentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
106             registerReceiver(mBluetoothAdapterReceiver, intentFilter);
107             mTelephonyManager = getSystemService(TelephonyManager.class);
108             mTelecomManager = getSystemService(TelecomManager.class);
109             return binder;
110         }
111         @Override
enforceModifyPermission()112         protected void enforceModifyPermission() {}
113     }
114 
115     @Before
setUp()116     public void setUp() throws Exception {
117         MockitoAnnotations.initMocks(this);
118 
119         // Create the service Intent.
120         Intent serviceIntent =
121                 new Intent(ApplicationProvider.getApplicationContext(),
122                         TestableBluetoothInCallService.class);
123         // Bind the service
124         mServiceRule.bindService(serviceIntent);
125 
126         // Ensure initialization does not actually try to access any of the CallsManager fields.
127         // This also works to return null if it is not overwritten later in the test.
128         doReturn(null).when(mMockCallInfo).getActiveCall();
129         doReturn(null).when(mMockCallInfo)
130                 .getRingingOrSimulatedRingingCall();
131         doReturn(null).when(mMockCallInfo).getHeldCall();
132         doReturn(null).when(mMockCallInfo).getOutgoingCall();
133         doReturn(0).when(mMockCallInfo).getNumHeldCalls();
134         doReturn(false).when(mMockCallInfo).hasOnlyDisconnectedCalls();
135         doReturn(true).when(mMockCallInfo).isNullCall(null);
136         doReturn(false).when(mMockCallInfo).isNullCall(notNull());
137 
138         mBluetoothInCallService = new TestableBluetoothInCallService();
139         mBluetoothInCallService.setBluetoothHeadset(mMockBluetoothHeadset);
140         mBluetoothInCallService.mCallInfo = mMockCallInfo;
141     }
142 
143     @After
tearDown()144     public void tearDown() throws Exception {
145         mServiceRule.unbindService();
146         mBluetoothInCallService = null;
147     }
148 
149     @Test
testHeadsetAnswerCall()150     public void testHeadsetAnswerCall() throws Exception {
151         BluetoothCall mockCall = createRingingCall();
152 
153         boolean callAnswered = mBluetoothInCallService.answerCall();
154         verify(mockCall).answer(any(int.class));
155 
156         Assert.assertTrue(callAnswered);
157     }
158 
159     @Test
testHeadsetAnswerCallNull()160     public void testHeadsetAnswerCallNull() throws Exception {
161         when(mMockCallInfo.getRingingOrSimulatedRingingCall()).thenReturn(null);
162 
163         boolean callAnswered = mBluetoothInCallService.answerCall();
164         Assert.assertFalse(callAnswered);
165     }
166 
167     @Test
testHeadsetHangupCall()168     public void testHeadsetHangupCall() throws Exception {
169         BluetoothCall mockCall = createForegroundCall();
170 
171         boolean callHungup = mBluetoothInCallService.hangupCall();
172 
173         verify(mockCall).disconnect();
174         Assert.assertTrue(callHungup);
175     }
176 
177     @Test
testHeadsetHangupCallNull()178     public void testHeadsetHangupCallNull() throws Exception {
179         when(mMockCallInfo.getForegroundCall()).thenReturn(null);
180 
181         boolean callHungup = mBluetoothInCallService.hangupCall();
182         Assert.assertFalse(callHungup);
183     }
184 
185     @Test
testHeadsetSendDTMF()186     public void testHeadsetSendDTMF() throws Exception {
187         BluetoothCall mockCall = createForegroundCall();
188 
189         boolean sentDtmf = mBluetoothInCallService.sendDtmf(TEST_DTMF_TONE);
190 
191         verify(mockCall).playDtmfTone(eq((char) TEST_DTMF_TONE));
192         verify(mockCall).stopDtmfTone();
193         Assert.assertTrue(sentDtmf);
194     }
195 
196     @Test
testHeadsetSendDTMFNull()197     public void testHeadsetSendDTMFNull() throws Exception {
198         when(mMockCallInfo.getForegroundCall()).thenReturn(null);
199 
200         boolean sentDtmf = mBluetoothInCallService.sendDtmf(TEST_DTMF_TONE);
201         Assert.assertFalse(sentDtmf);
202     }
203 
204     @Test
testGetNetworkOperator()205     public void testGetNetworkOperator() throws Exception {
206         PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
207         when(mMockCallInfo.getBestPhoneAccount()).thenReturn(fakePhoneAccount);
208 
209         String networkOperator = mBluetoothInCallService.getNetworkOperator();
210         Assert.assertEquals(networkOperator, "label0");
211     }
212 
213     @Test
testGetNetworkOperatorNoPhoneAccount()214     public void testGetNetworkOperatorNoPhoneAccount() throws Exception {
215         when(mMockCallInfo.getForegroundCall()).thenReturn(null);
216         when(mMockTelephonyManager.getNetworkOperatorName()).thenReturn("label1");
217         mBluetoothInCallService.mTelephonyManager = mMockTelephonyManager;
218 
219         String networkOperator = mBluetoothInCallService.getNetworkOperator();
220         Assert.assertEquals(networkOperator, "label1");
221     }
222 
223     @Test
testGetSubscriberNumber()224     public void testGetSubscriberNumber() throws Exception {
225         PhoneAccount fakePhoneAccount = makeQuickAccount("id0", TEST_ACCOUNT_INDEX);
226         when(mMockCallInfo.getBestPhoneAccount()).thenReturn(fakePhoneAccount);
227 
228         String subscriberNumber = mBluetoothInCallService.getSubscriberNumber();
229         Assert.assertEquals(subscriberNumber, TEST_ACCOUNT_ADDRESS + TEST_ACCOUNT_INDEX);
230     }
231 
232     @Test
testGetSubscriberNumberFallbackToTelephony()233     public void testGetSubscriberNumberFallbackToTelephony() throws Exception {
234         String fakeNumber = "8675309";
235         when(mMockCallInfo.getBestPhoneAccount()).thenReturn(null);
236         when(mMockTelephonyManager.getLine1Number())
237                 .thenReturn(fakeNumber);
238         mBluetoothInCallService.mTelephonyManager = mMockTelephonyManager;
239 
240         String subscriberNumber = mBluetoothInCallService.getSubscriberNumber();
241         Assert.assertEquals(subscriberNumber, fakeNumber);
242     }
243 
244     @Test
testListCurrentCallsOneCall()245     public void testListCurrentCallsOneCall() throws Exception {
246         ArrayList<BluetoothCall> calls = new ArrayList<>();
247         BluetoothCall activeCall = createActiveCall();
248         when(activeCall.getState()).thenReturn(Call.STATE_ACTIVE);
249         calls.add(activeCall);
250         mBluetoothInCallService.onCallAdded(activeCall);
251         when(activeCall.isConference()).thenReturn(false);
252         when(activeCall.getHandle()).thenReturn(Uri.parse("tel:555-000"));
253         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
254 
255         clearInvocations(mMockBluetoothHeadset);
256         mBluetoothInCallService.listCurrentCalls();
257 
258         verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(0), eq(0), eq(false),
259                 eq("555000"), eq(PhoneNumberUtils.TOA_Unknown));
260         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
261     }
262 
263     /**
264      * Verifies bluetooth call quality reports are properly parceled and set as a call event to
265      * Telecom.
266      */
267     @Test
testBluetoothCallQualityReport()268     public void testBluetoothCallQualityReport() {
269         BluetoothCall activeCall = createForegroundCall();
270         when(activeCall.isCallNull()).thenReturn(false);
271         mBluetoothInCallService.onCallAdded(activeCall);
272 
273         mBluetoothInCallService.sendBluetoothCallQualityReport(
274                 10, // long timestamp
275                 20, // int rssi
276                 30, // int snr
277                 40, // int retransmissionCount
278                 50, // int packetsNotReceiveCount
279                 60 // int negativeAcknowledgementCount
280         );
281 
282         ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
283         verify(activeCall).sendCallEvent(
284                 eq(BluetoothCallQualityReport.EVENT_BLUETOOTH_CALL_QUALITY_REPORT),
285                 bundleCaptor.capture());
286         Bundle bundle = bundleCaptor.getValue();
287         BluetoothCallQualityReport report = (BluetoothCallQualityReport) bundle.get(
288                 BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT);
289         Assert.assertEquals(10, report.getSentTimestampMillis());
290         Assert.assertEquals(20, report.getRssiDbm());
291         Assert.assertEquals(30, report.getSnrDb());
292         Assert.assertEquals(40, report.getRetransmittedPacketsCount());
293         Assert.assertEquals(50, report.getPacketsNotReceivedCount());
294         Assert.assertEquals(60, report.getNegativeAcknowledgementCount());
295     }
296 
297     @Test
testListCurrentCallsSilentRinging()298     public void testListCurrentCallsSilentRinging() throws Exception {
299         ArrayList<BluetoothCall> calls = new ArrayList<>();
300         BluetoothCall silentRingingCall = createActiveCall();
301         when(silentRingingCall.getState()).thenReturn(Call.STATE_RINGING);
302         when(silentRingingCall.isSilentRingingRequested()).thenReturn(true);
303         calls.add(silentRingingCall);
304         mBluetoothInCallService.onCallAdded(silentRingingCall);
305 
306         when(silentRingingCall.isConference()).thenReturn(false);
307         when(silentRingingCall.getHandle()).thenReturn(Uri.parse("tel:555-000"));
308         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
309         when(mMockCallInfo.getRingingOrSimulatedRingingCall()).thenReturn(silentRingingCall);
310 
311         clearInvocations(mMockBluetoothHeadset);
312         mBluetoothInCallService.listCurrentCalls();
313 
314         verify(mMockBluetoothHeadset, never()).clccResponse(eq(1), eq(0), eq(0), eq(0), eq(false),
315                 eq("555000"), eq(PhoneNumberUtils.TOA_Unknown));
316         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
317     }
318 
319     @Test
testConferenceInProgressCDMA()320     public void testConferenceInProgressCDMA() throws Exception {
321         // If two calls are being conferenced and updateHeadsetWithCallState runs while this is
322         // still occurring, it will look like there is an active and held BluetoothCall still while
323         // we are transitioning into a conference.
324         // BluetoothCall has been put into a CDMA "conference" with one BluetoothCall on hold.
325         ArrayList<BluetoothCall>   calls = new ArrayList<>();
326         BluetoothCall parentCall = createActiveCall();
327         final BluetoothCall confCall1 = getMockCall();
328         final BluetoothCall confCall2 = createHeldCall();
329         calls.add(parentCall);
330         calls.add(confCall1);
331         calls.add(confCall2);
332         mBluetoothInCallService.onCallAdded(parentCall);
333         mBluetoothInCallService.onCallAdded(confCall1);
334         mBluetoothInCallService.onCallAdded(confCall2);
335 
336         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
337         when(confCall1.getState()).thenReturn(Call.STATE_ACTIVE);
338         when(confCall2.getState()).thenReturn(Call.STATE_ACTIVE);
339         when(confCall1.isIncoming()).thenReturn(false);
340         when(confCall2.isIncoming()).thenReturn(true);
341         when(confCall1.getGatewayInfo()).thenReturn(
342                 new GatewayInfo(null, null, Uri.parse("tel:555-0000")));
343         when(confCall2.getGatewayInfo()).thenReturn(
344                 new GatewayInfo(null, null, Uri.parse("tel:555-0001")));
345         addCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
346         addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
347         removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
348         String confCall1Id = confCall1.getTelecomCallId();
349         when(parentCall.getGenericConferenceActiveChildCallId())
350                 .thenReturn(confCall1Id);
351         when(parentCall.isConference()).thenReturn(true);
352         List<String> childrenIds = Arrays.asList(confCall1.getTelecomCallId(),
353                 confCall2.getTelecomCallId());
354         when(parentCall.getChildrenIds()).thenReturn(childrenIds);
355         //Add links from child calls to parent
356         String parentId = parentCall.getTelecomCallId();
357         when(confCall1.getParentId()).thenReturn(parentId);
358         when(confCall2.getParentId()).thenReturn(parentId);
359 
360         clearInvocations(mMockBluetoothHeadset);
361         mBluetoothInCallService.queryPhoneState();
362         verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE),
363                 eq(""), eq(128), nullable(String.class));
364 
365         when(parentCall.wasConferencePreviouslyMerged()).thenReturn(true);
366         List<BluetoothCall> children =
367                 mBluetoothInCallService.getBluetoothCallsByIds(parentCall.getChildrenIds());
368         mBluetoothInCallService.getCallback(parentCall)
369                 .onChildrenChanged(parentCall, children);
370         verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
371                 eq(""), eq(128), nullable(String.class));
372 
373         when(mMockCallInfo.getHeldCall()).thenReturn(null);
374         // Spurious BluetoothCall to onIsConferencedChanged.
375         mBluetoothInCallService.getCallback(parentCall)
376                 .onChildrenChanged(parentCall, children);
377         // Make sure the BluetoothCall has only occurred collectively 2 times (not on the third)
378         verify(mMockBluetoothHeadset, times(2)).phoneStateChanged(any(int.class),
379                 any(int.class), any(int.class), nullable(String.class), any(int.class),
380                 nullable(String.class));
381     }
382 
383     @Test
testListCurrentCallsCdmaHold()384     public void testListCurrentCallsCdmaHold() throws Exception {
385         // BluetoothCall has been put into a CDMA "conference" with one BluetoothCall on hold.
386         List<BluetoothCall> calls = new ArrayList<BluetoothCall>();
387         BluetoothCall parentCall = createActiveCall();
388         when(parentCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
389         final BluetoothCall foregroundCall = getMockCall();
390         when(foregroundCall.getHandle()).thenReturn(Uri.parse("tel:555-0001"));
391         final BluetoothCall heldCall = createHeldCall();
392         when(heldCall.getHandle()).thenReturn(Uri.parse("tel:555-0002"));
393         calls.add(parentCall);
394         calls.add(foregroundCall);
395         calls.add(heldCall);
396         mBluetoothInCallService.onCallAdded(parentCall);
397         mBluetoothInCallService.onCallAdded(foregroundCall);
398         mBluetoothInCallService.onCallAdded(heldCall);
399 
400         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
401         when(foregroundCall.getState()).thenReturn(Call.STATE_ACTIVE);
402         when(heldCall.getState()).thenReturn(Call.STATE_ACTIVE);
403         when(foregroundCall.isIncoming()).thenReturn(false);
404         when(heldCall.isIncoming()).thenReturn(true);
405         when(foregroundCall.getGatewayInfo()).thenReturn(
406                 new GatewayInfo(null, null, Uri.parse("tel:555-0001")));
407         when(heldCall.getGatewayInfo()).thenReturn(
408                 new GatewayInfo(null, null, Uri.parse("tel:555-0002")));
409         addCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
410         addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
411         removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
412 
413         String foregroundCallId = foregroundCall.getTelecomCallId();
414         when(parentCall.getGenericConferenceActiveChildCallId()).thenReturn(foregroundCallId);
415         when(parentCall.isConference()).thenReturn(true);
416         List<String> childrenIds = Arrays.asList(foregroundCall.getTelecomCallId(),
417                 heldCall.getTelecomCallId());
418         when(parentCall.getChildrenIds()).thenReturn(childrenIds);
419         //Add links from child calls to parent
420         String parentId = parentCall.getTelecomCallId();
421         when(foregroundCall.getParentId()).thenReturn(parentId);
422         when(heldCall.getParentId()).thenReturn(parentId);
423         when(parentCall.hasProperty(Call.Details.PROPERTY_GENERIC_CONFERENCE)).thenReturn(true);
424 
425         clearInvocations(mMockBluetoothHeadset);
426         mBluetoothInCallService.listCurrentCalls();
427 
428         verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(CALL_STATE_ACTIVE), eq(0),
429                 eq(false), eq("5550001"), eq(PhoneNumberUtils.TOA_Unknown));
430         verify(mMockBluetoothHeadset).clccResponse(eq(2), eq(1), eq(CALL_STATE_HELD), eq(0),
431                 eq(false), eq("5550002"), eq(PhoneNumberUtils.TOA_Unknown));
432         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
433     }
434 
435     @Test
testListCurrentCallsCdmaConference()436     public void testListCurrentCallsCdmaConference() throws Exception {
437         // BluetoothCall is in a true CDMA conference
438         ArrayList<BluetoothCall> calls = new ArrayList<>();
439         BluetoothCall parentCall = createActiveCall();
440         final BluetoothCall confCall1 = getMockCall();
441         final BluetoothCall confCall2 = createHeldCall();
442         when(parentCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
443         when(confCall1.getHandle()).thenReturn(Uri.parse("tel:555-0001"));
444         when(confCall2.getHandle()).thenReturn(Uri.parse("tel:555-0002"));
445         calls.add(parentCall);
446         calls.add(confCall1);
447         calls.add(confCall2);
448         mBluetoothInCallService.onCallAdded(parentCall);
449         mBluetoothInCallService.onCallAdded(confCall1);
450         mBluetoothInCallService.onCallAdded(confCall2);
451 
452         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
453         when(confCall1.getState()).thenReturn(Call.STATE_ACTIVE);
454         when(confCall2.getState()).thenReturn(Call.STATE_ACTIVE);
455         when(confCall1.isIncoming()).thenReturn(false);
456         when(confCall2.isIncoming()).thenReturn(true);
457         when(confCall1.getGatewayInfo()).thenReturn(
458                 new GatewayInfo(null, null, Uri.parse("tel:555-0000")));
459         when(confCall2.getGatewayInfo()).thenReturn(
460                 new GatewayInfo(null, null, Uri.parse("tel:555-0001")));
461         removeCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
462         removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
463         when(parentCall.wasConferencePreviouslyMerged()).thenReturn(true);
464         //when(parentCall.getConferenceLevelActiveCall()).thenReturn(confCall1);
465         when(parentCall.isConference()).thenReturn(true);
466         List<String> childrenIds = Arrays.asList(confCall1.getTelecomCallId(),
467             confCall2.getTelecomCallId());
468         when(parentCall.getChildrenIds()).thenReturn(childrenIds);
469         //Add links from child calls to parent
470         String parentId = parentCall.getTelecomCallId();
471         when(confCall1.getParentId()).thenReturn(parentId);
472         when(confCall2.getParentId()).thenReturn(parentId);
473         when(parentCall.hasProperty(Call.Details.PROPERTY_GENERIC_CONFERENCE)).thenReturn(true);
474 
475         clearInvocations(mMockBluetoothHeadset);
476         mBluetoothInCallService.listCurrentCalls();
477 
478         verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(CALL_STATE_ACTIVE), eq(0),
479                 eq(true), eq("5550000"), eq(PhoneNumberUtils.TOA_Unknown));
480         verify(mMockBluetoothHeadset).clccResponse(eq(2), eq(1), eq(CALL_STATE_ACTIVE), eq(0),
481                 eq(true), eq("5550001"), eq(PhoneNumberUtils.TOA_Unknown));
482         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
483     }
484 
485     @Test
testWaitingCallClccResponse()486     public void testWaitingCallClccResponse() throws Exception {
487         ArrayList<BluetoothCall> calls = new ArrayList<>();
488         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
489         // This test does not define a value for getForegroundCall(), so this ringing
490         // BluetoothCall will be treated as if it is a waiting BluetoothCall
491         // when listCurrentCalls() is invoked.
492         BluetoothCall waitingCall = createRingingCall();
493         calls.add(waitingCall);
494         mBluetoothInCallService.onCallAdded(waitingCall);
495 
496         when(waitingCall.isIncoming()).thenReturn(true);
497         when(waitingCall.getGatewayInfo()).thenReturn(
498                 new GatewayInfo(null, null, Uri.parse("tel:555-0000")));
499         when(waitingCall.getState()).thenReturn(Call.STATE_RINGING);
500         when(waitingCall.isConference()).thenReturn(false);
501         when(waitingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
502 
503         clearInvocations(mMockBluetoothHeadset);
504         mBluetoothInCallService.listCurrentCalls();
505         verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_WAITING, 0, false,
506                 "5550000", PhoneNumberUtils.TOA_Unknown);
507         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
508         verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
509                 anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
510     }
511 
512     @Test
testNewCallClccResponse()513     public void testNewCallClccResponse() throws Exception {
514         ArrayList<BluetoothCall> calls = new ArrayList<>();
515         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
516         BluetoothCall newCall = createForegroundCall();
517         calls.add(newCall);
518         mBluetoothInCallService.onCallAdded(newCall);
519 
520         when(newCall.getState()).thenReturn(Call.STATE_NEW);
521         when(newCall.isConference()).thenReturn(false);
522 
523         clearInvocations(mMockBluetoothHeadset);
524         mBluetoothInCallService.listCurrentCalls();
525         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
526         verify(mMockBluetoothHeadset, times(1)).clccResponse(anyInt(),
527                 anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
528     }
529 
530     @Test
testRingingCallClccResponse()531     public void testRingingCallClccResponse() throws Exception {
532         ArrayList<BluetoothCall> calls = new ArrayList<>();
533         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
534         BluetoothCall ringingCall = createForegroundCall();
535         calls.add(ringingCall);
536         mBluetoothInCallService.onCallAdded(ringingCall);
537 
538         when(ringingCall.getState()).thenReturn(Call.STATE_RINGING);
539         when(ringingCall.isIncoming()).thenReturn(true);
540         when(ringingCall.isConference()).thenReturn(false);
541         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
542         when(ringingCall.getGatewayInfo()).thenReturn(
543                 new GatewayInfo(null, null, Uri.parse("tel:555-0000")));
544 
545         clearInvocations(mMockBluetoothHeadset);
546         mBluetoothInCallService.listCurrentCalls();
547         verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_INCOMING, 0, false,
548                 "5550000", PhoneNumberUtils.TOA_Unknown);
549         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
550         verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
551                 anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
552     }
553 
554     @Test
testCallClccCache()555     public void testCallClccCache() throws Exception {
556         ArrayList<BluetoothCall> calls = new ArrayList<>();
557         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
558         BluetoothCall ringingCall = createForegroundCall();
559         calls.add(ringingCall);
560         mBluetoothInCallService.onCallAdded(ringingCall);
561 
562         when(ringingCall.getState()).thenReturn(Call.STATE_RINGING);
563         when(ringingCall.isIncoming()).thenReturn(true);
564         when(ringingCall.isConference()).thenReturn(false);
565         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:5550000"));
566         when(ringingCall.getGatewayInfo()).thenReturn(
567                 new GatewayInfo(null, null, Uri.parse("tel:5550000")));
568 
569         clearInvocations(mMockBluetoothHeadset);
570         mBluetoothInCallService.listCurrentCalls();
571         verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_INCOMING, 0, false,
572                 "5550000", PhoneNumberUtils.TOA_Unknown);
573 
574         // Test Caching of old BluetoothCall indices in clcc
575         when(ringingCall.getState()).thenReturn(Call.STATE_ACTIVE);
576         BluetoothCall newHoldingCall = createHeldCall();
577         calls.add(0, newHoldingCall);
578         mBluetoothInCallService.onCallAdded(newHoldingCall);
579 
580         when(newHoldingCall.getState()).thenReturn(Call.STATE_HOLDING);
581         when(newHoldingCall.isIncoming()).thenReturn(true);
582         when(newHoldingCall.isConference()).thenReturn(false);
583         when(newHoldingCall.getHandle()).thenReturn(Uri.parse("tel:555-0001"));
584         when(newHoldingCall.getGatewayInfo()).thenReturn(
585                 new GatewayInfo(null, null, Uri.parse("tel:555-0001")));
586 
587         mBluetoothInCallService.listCurrentCalls();
588         verify(mMockBluetoothHeadset).clccResponse(1, 1, CALL_STATE_ACTIVE, 0, false,
589                 "5550000", PhoneNumberUtils.TOA_Unknown);
590         verify(mMockBluetoothHeadset).clccResponse(2, 1, CALL_STATE_HELD, 0, false,
591                 "5550001", PhoneNumberUtils.TOA_Unknown);
592         verify(mMockBluetoothHeadset, times(2)).clccResponse(0, 0, 0, 0, false, null, 0);
593     }
594 
595     @Test
testAlertingCallClccResponse()596     public void testAlertingCallClccResponse() throws Exception {
597         ArrayList<BluetoothCall> calls = new ArrayList<>();
598         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
599         BluetoothCall dialingCall = createForegroundCall();
600         calls.add(dialingCall);
601         mBluetoothInCallService.onCallAdded(dialingCall);
602 
603         when(dialingCall.getState()).thenReturn(Call.STATE_DIALING);
604         when(dialingCall.isIncoming()).thenReturn(false);
605         when(dialingCall.isConference()).thenReturn(false);
606         when(dialingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
607         when(dialingCall.getGatewayInfo()).thenReturn(
608                 new GatewayInfo(null, null, Uri.parse("tel:555-0000")));
609 
610         clearInvocations(mMockBluetoothHeadset);
611         mBluetoothInCallService.listCurrentCalls();
612         verify(mMockBluetoothHeadset).clccResponse(1, 0, CALL_STATE_ALERTING, 0, false,
613                 "5550000", PhoneNumberUtils.TOA_Unknown);
614         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
615         verify(mMockBluetoothHeadset, times(2)).clccResponse(anyInt(),
616                 anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
617     }
618 
619     @Test
testHoldingCallClccResponse()620     public void testHoldingCallClccResponse() throws Exception {
621         ArrayList<BluetoothCall> calls = new ArrayList<>();
622         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
623         BluetoothCall dialingCall = createForegroundCall();
624         calls.add(dialingCall);
625         mBluetoothInCallService.onCallAdded(dialingCall);
626 
627         when(dialingCall.getState()).thenReturn(Call.STATE_DIALING);
628         when(dialingCall.isIncoming()).thenReturn(false);
629         when(dialingCall.isConference()).thenReturn(false);
630         when(dialingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
631         when(dialingCall.getGatewayInfo()).thenReturn(
632                 new GatewayInfo(null, null, Uri.parse("tel:555-0000")));
633         BluetoothCall holdingCall = createHeldCall();
634         calls.add(holdingCall);
635         mBluetoothInCallService.onCallAdded(holdingCall);
636 
637         when(holdingCall.getState()).thenReturn(Call.STATE_HOLDING);
638         when(holdingCall.isIncoming()).thenReturn(true);
639         when(holdingCall.isConference()).thenReturn(false);
640         when(holdingCall.getHandle()).thenReturn(Uri.parse("tel:555-0001"));
641         when(holdingCall.getGatewayInfo()).thenReturn(
642                 new GatewayInfo(null, null, Uri.parse("tel:555-0001")));
643 
644         clearInvocations(mMockBluetoothHeadset);
645         mBluetoothInCallService.listCurrentCalls();
646         verify(mMockBluetoothHeadset).clccResponse(1, 0, CALL_STATE_ALERTING, 0, false,
647                 "5550000", PhoneNumberUtils.TOA_Unknown);
648         verify(mMockBluetoothHeadset).clccResponse(2, 1, CALL_STATE_HELD, 0, false,
649                 "5550001", PhoneNumberUtils.TOA_Unknown);
650         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
651         verify(mMockBluetoothHeadset, times(3)).clccResponse(anyInt(),
652                 anyInt(), anyInt(), anyInt(), anyBoolean(), nullable(String.class), anyInt());
653     }
654 
655     @Test
testListCurrentCallsImsConference()656     public void testListCurrentCallsImsConference() throws Exception {
657         ArrayList<BluetoothCall> calls = new ArrayList<>();
658         BluetoothCall parentCall = createActiveCall();
659         calls.add(parentCall);
660         mBluetoothInCallService.onCallAdded(parentCall);
661 
662         addCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
663         when(parentCall.isConference()).thenReturn(true);
664         when(parentCall.getState()).thenReturn(Call.STATE_ACTIVE);
665         when(parentCall.isIncoming()).thenReturn(true);
666         when(parentCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
667         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
668 
669         clearInvocations(mMockBluetoothHeadset);
670         mBluetoothInCallService.listCurrentCalls();
671 
672         verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(1), eq(CALL_STATE_ACTIVE), eq(0),
673                 eq(true), eq("5550000"), eq(129));
674         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
675     }
676 
677     @Test
testListCurrentCallsHeldImsCepConference()678     public void testListCurrentCallsHeldImsCepConference() throws Exception {
679         ArrayList<BluetoothCall> calls = new ArrayList<>();
680         BluetoothCall parentCall = createHeldCall();
681         BluetoothCall childCall1 = createActiveCall();
682         BluetoothCall childCall2 = createActiveCall();
683         when(parentCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
684         when(childCall1.getHandle()).thenReturn(Uri.parse("tel:555-0001"));
685         when(childCall2.getHandle()).thenReturn(Uri.parse("tel:555-0002"));
686 
687         calls.add(parentCall);
688         calls.add(childCall1);
689         calls.add(childCall2);
690         mBluetoothInCallService.onCallAdded(parentCall);
691         mBluetoothInCallService.onCallAdded(childCall1);
692         mBluetoothInCallService.onCallAdded(childCall2);
693 
694         addCallCapability(parentCall, Connection.CAPABILITY_MANAGE_CONFERENCE);
695         String parentId = parentCall.getTelecomCallId();
696         when(childCall1.getParentId()).thenReturn(parentId);
697         when(childCall2.getParentId()).thenReturn(parentId);
698 
699         when(parentCall.isConference()).thenReturn(true);
700         when(parentCall.getState()).thenReturn(Call.STATE_HOLDING);
701         when(childCall1.getState()).thenReturn(Call.STATE_ACTIVE);
702         when(childCall2.getState()).thenReturn(Call.STATE_ACTIVE);
703         when(parentCall.hasProperty(Call.Details.PROPERTY_GENERIC_CONFERENCE)).thenReturn(true);
704 
705         when(parentCall.isIncoming()).thenReturn(true);
706         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
707 
708         clearInvocations(mMockBluetoothHeadset);
709         mBluetoothInCallService.listCurrentCalls();
710 
711         verify(mMockBluetoothHeadset).clccResponse(eq(1), eq(0), eq(CALL_STATE_HELD), eq(0),
712                 eq(true), eq("5550001"), eq(PhoneNumberUtils.TOA_Unknown));
713         verify(mMockBluetoothHeadset).clccResponse(eq(2), eq(0), eq(CALL_STATE_HELD), eq(0),
714                 eq(true), eq("5550002"), eq(PhoneNumberUtils.TOA_Unknown));
715         verify(mMockBluetoothHeadset).clccResponse(0, 0, 0, 0, false, null, 0);
716     }
717 
718     @Test
testQueryPhoneState()719     public void testQueryPhoneState() throws Exception {
720         BluetoothCall ringingCall = createRingingCall();
721         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:5550000"));
722 
723         clearInvocations(mMockBluetoothHeadset);
724         mBluetoothInCallService.queryPhoneState();
725 
726         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
727                 eq("5550000"), eq(PhoneNumberUtils.TOA_Unknown), nullable(String.class));
728     }
729 
730     @Test
testCDMAConferenceQueryState()731     public void testCDMAConferenceQueryState() throws Exception {
732         BluetoothCall parentConfCall = createActiveCall();
733         final BluetoothCall confCall1 = getMockCall();
734         final BluetoothCall confCall2 = getMockCall();
735         mBluetoothInCallService.onCallAdded(confCall1);
736         mBluetoothInCallService.onCallAdded(confCall2);
737         when(parentConfCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
738         addCallCapability(parentConfCall, Connection.CAPABILITY_SWAP_CONFERENCE);
739         removeCallCapability(parentConfCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
740         when(parentConfCall.wasConferencePreviouslyMerged()).thenReturn(true);
741         when(parentConfCall.isConference()).thenReturn(true);
742         List<String> childrenIds = Arrays.asList(confCall1.getTelecomCallId(),
743                 confCall2.getTelecomCallId());
744         when(parentConfCall.getChildrenIds()).thenReturn(childrenIds);
745 
746         clearInvocations(mMockBluetoothHeadset);
747         mBluetoothInCallService.queryPhoneState();
748         verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
749                 eq(""), eq(128), nullable(String.class));
750     }
751 
752     @Test
testProcessChldTypeReleaseHeldRinging()753     public void testProcessChldTypeReleaseHeldRinging() throws Exception {
754         BluetoothCall ringingCall = createRingingCall();
755         Log.i("BluetoothInCallService", "asdf start " + Integer.toString(ringingCall.hashCode()));
756 
757         boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_RELEASEHELD);
758 
759         verify(ringingCall).reject(eq(false), nullable(String.class));
760         Assert.assertTrue(didProcess);
761     }
762 
763     @Test
testProcessChldTypeReleaseHeldHold()764     public void testProcessChldTypeReleaseHeldHold() throws Exception {
765         BluetoothCall onHoldCall = createHeldCall();
766         boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_RELEASEHELD);
767 
768         verify(onHoldCall).disconnect();
769         Assert.assertTrue(didProcess);
770     }
771 
772     @Test
testProcessChldReleaseActiveRinging()773     public void testProcessChldReleaseActiveRinging() throws Exception {
774         BluetoothCall activeCall = createActiveCall();
775         BluetoothCall ringingCall = createRingingCall();
776 
777         boolean didProcess = mBluetoothInCallService.processChld(
778                 CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD);
779 
780         verify(activeCall).disconnect();
781         verify(ringingCall).answer(any(int.class));
782         Assert.assertTrue(didProcess);
783     }
784 
785     @Test
testProcessChldReleaseActiveHold()786     public void testProcessChldReleaseActiveHold() throws Exception {
787         BluetoothCall activeCall = createActiveCall();
788         BluetoothCall heldCall = createHeldCall();
789 
790         boolean didProcess = mBluetoothInCallService.processChld(
791                 CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD);
792 
793         verify(activeCall).disconnect();
794         // BluetoothCall unhold will occur as part of CallsManager auto-unholding
795         // the background BluetoothCall on its own.
796         Assert.assertTrue(didProcess);
797     }
798 
799     @Test
testProcessChldHoldActiveRinging()800     public void testProcessChldHoldActiveRinging() throws Exception {
801         BluetoothCall ringingCall = createRingingCall();
802 
803         boolean didProcess = mBluetoothInCallService.processChld(
804                 CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
805 
806         verify(ringingCall).answer(any(int.class));
807         Assert.assertTrue(didProcess);
808     }
809 
810     @Test
testProcessChldHoldActiveUnhold()811     public void testProcessChldHoldActiveUnhold() throws Exception {
812         BluetoothCall heldCall = createHeldCall();
813 
814         boolean didProcess = mBluetoothInCallService.processChld(
815                 CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
816 
817         verify(heldCall).unhold();
818         Assert.assertTrue(didProcess);
819     }
820 
821     @Test
testProcessChldHoldActiveHold()822     public void testProcessChldHoldActiveHold() throws Exception {
823         BluetoothCall activeCall = createActiveCall();
824         addCallCapability(activeCall, Connection.CAPABILITY_HOLD);
825 
826         boolean didProcess = mBluetoothInCallService.processChld(
827                 CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
828 
829         verify(activeCall).hold();
830         Assert.assertTrue(didProcess);
831     }
832 
833     @Test
testProcessChldAddHeldToConfHolding()834     public void testProcessChldAddHeldToConfHolding() throws Exception {
835         BluetoothCall activeCall = createActiveCall();
836         addCallCapability(activeCall, Connection.CAPABILITY_MERGE_CONFERENCE);
837 
838         boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_ADDHELDTOCONF);
839 
840         verify(activeCall).mergeConference();
841         Assert.assertTrue(didProcess);
842     }
843 
844     @Test
testProcessChldAddHeldToConf()845     public void testProcessChldAddHeldToConf() throws Exception {
846         BluetoothCall activeCall = createActiveCall();
847         removeCallCapability(activeCall, Connection.CAPABILITY_MERGE_CONFERENCE);
848         BluetoothCall conferenceableCall = getMockCall();
849         ArrayList<String> conferenceableCalls = new ArrayList<>();
850         conferenceableCalls.add(conferenceableCall.getTelecomCallId());
851         mBluetoothInCallService.onCallAdded(conferenceableCall);
852 
853         when(activeCall.getConferenceableCalls()).thenReturn(conferenceableCalls);
854 
855         boolean didProcess = mBluetoothInCallService.processChld(CHLD_TYPE_ADDHELDTOCONF);
856 
857         verify(activeCall).conference(conferenceableCall);
858         Assert.assertTrue(didProcess);
859     }
860 
861     @Test
testProcessChldHoldActiveSwapConference()862     public void testProcessChldHoldActiveSwapConference() throws Exception {
863         // Create an active CDMA BluetoothCall with a BluetoothCall on hold
864         // and simulate a swapConference().
865         BluetoothCall parentCall = createActiveCall();
866         final BluetoothCall foregroundCall = getMockCall();
867         when(foregroundCall.getHandle()).thenReturn(Uri.parse("tel:555-0001"));
868         final BluetoothCall heldCall = createHeldCall();
869         when(heldCall.getHandle()).thenReturn(Uri.parse("tel:555-0002"));
870         addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
871         removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
872         when(parentCall.isConference()).thenReturn(true);
873         when(parentCall.wasConferencePreviouslyMerged()).thenReturn(false);
874         when(heldCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
875         List<String> childrenIds = Arrays.asList(foregroundCall.getTelecomCallId(),
876                 heldCall.getTelecomCallId());
877         when(parentCall.getChildrenIds()).thenReturn(childrenIds);
878 
879         clearInvocations(mMockBluetoothHeadset);
880         boolean didProcess = mBluetoothInCallService.processChld(
881                 CHLD_TYPE_HOLDACTIVE_ACCEPTHELD);
882 
883         verify(parentCall).swapConference();
884         verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE), eq(""),
885                 eq(128), nullable(String.class));
886         Assert.assertTrue(didProcess);
887     }
888 
889     // Testing the CallsManager Listener Functionality on Bluetooth
890     @Test
testOnCallAddedRinging()891     public void testOnCallAddedRinging() throws Exception {
892         BluetoothCall ringingCall = createRingingCall();
893         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555000"));
894 
895         mBluetoothInCallService.onCallAdded(ringingCall);
896 
897         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
898                 eq("555000"), eq(PhoneNumberUtils.TOA_Unknown), nullable(String.class));
899     }
900 
901     @Test
testSilentRingingCallState()902     public void testSilentRingingCallState() throws Exception {
903         BluetoothCall ringingCall = createRingingCall();
904         when(ringingCall.isSilentRingingRequested()).thenReturn(true);
905         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555000"));
906 
907         mBluetoothInCallService.onCallAdded(ringingCall);
908 
909         verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
910                 anyString(), anyInt(), nullable(String.class));
911     }
912 
913     @Test
testOnCallAddedCdmaActiveHold()914     public void testOnCallAddedCdmaActiveHold() throws Exception {
915         // BluetoothCall has been put into a CDMA "conference" with one BluetoothCall on hold.
916         BluetoothCall parentCall = createActiveCall();
917         final BluetoothCall foregroundCall = getMockCall();
918         final BluetoothCall heldCall = createHeldCall();
919         when(foregroundCall.getHandle()).thenReturn(Uri.parse("tel:555-0001"));
920         when(heldCall.getHandle()).thenReturn(Uri.parse("tel:555-0002"));
921         addCallCapability(parentCall, Connection.CAPABILITY_MERGE_CONFERENCE);
922         removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
923         when(parentCall.isConference()).thenReturn(true);
924         List<String> childrenIds = Arrays.asList(foregroundCall.getTelecomCallId(),
925                 heldCall.getTelecomCallId());
926         when(parentCall.getChildrenIds()).thenReturn(childrenIds);
927 
928         mBluetoothInCallService.onCallAdded(parentCall);
929 
930         verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE),
931                 eq(""), eq(128), nullable(String.class));
932     }
933 
934     @Test
testOnCallRemoved()935     public void testOnCallRemoved() throws Exception {
936         BluetoothCall activeCall = createActiveCall();
937         mBluetoothInCallService.onCallAdded(activeCall);
938         doReturn(null).when(mMockCallInfo).getActiveCall();
939         when(activeCall.getHandle()).thenReturn(Uri.parse("tel:555-0001"));
940         mBluetoothInCallService.onCallRemoved(activeCall);
941 
942         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_IDLE),
943                 eq(""), eq(128), nullable(String.class));
944     }
945 
946     @Test
testOnCallStateChangedConnectingCall()947     public void testOnCallStateChangedConnectingCall() throws Exception {
948         BluetoothCall activeCall = getMockCall();
949         BluetoothCall connectingCall = getMockCall();
950         when(connectingCall.getState()).thenReturn(Call.STATE_CONNECTING);
951         ArrayList<BluetoothCall> calls = new ArrayList<>();
952         calls.add(connectingCall);
953         calls.add(activeCall);
954         mBluetoothInCallService.onCallAdded(connectingCall);
955         mBluetoothInCallService.onCallAdded(activeCall);
956         when(mMockCallInfo.getBluetoothCalls()).thenReturn(calls);
957 
958         mBluetoothInCallService.getCallback(activeCall)
959                 .onStateChanged(activeCall, Call.STATE_HOLDING);
960 
961         verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
962                 anyString(), anyInt(), nullable(String.class));
963     }
964 
965     @Test
testOnCallAddedAudioProcessing()966     public void testOnCallAddedAudioProcessing() throws Exception {
967         BluetoothCall call = getMockCall();
968         when(call.getState()).thenReturn(Call.STATE_AUDIO_PROCESSING);
969         mBluetoothInCallService.onCallAdded(call);
970 
971         verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
972                 anyString(), anyInt(), nullable(String.class));
973     }
974 
975     @Test
testOnCallStateChangedRingingToAudioProcessing()976     public void testOnCallStateChangedRingingToAudioProcessing() throws Exception {
977         BluetoothCall ringingCall = createRingingCall();
978         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555000"));
979 
980         mBluetoothInCallService.onCallAdded(ringingCall);
981 
982         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
983                 eq("555000"), eq(PhoneNumberUtils.TOA_Unknown), nullable(String.class));
984 
985         when(ringingCall.getState()).thenReturn(Call.STATE_AUDIO_PROCESSING);
986         when(mMockCallInfo.getRingingOrSimulatedRingingCall()).thenReturn(null);
987 
988         mBluetoothInCallService.getCallback(ringingCall)
989                 .onStateChanged(ringingCall, Call.STATE_AUDIO_PROCESSING);
990 
991         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_IDLE),
992                 eq(""), eq(128), nullable(String.class));
993     }
994 
995     @Test
testOnCallStateChangedAudioProcessingToSimulatedRinging()996     public void testOnCallStateChangedAudioProcessingToSimulatedRinging() throws Exception {
997         BluetoothCall ringingCall = createRingingCall();
998         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
999         mBluetoothInCallService.onCallAdded(ringingCall);
1000         mBluetoothInCallService.getCallback(ringingCall)
1001                 .onStateChanged(ringingCall, Call.STATE_SIMULATED_RINGING);
1002 
1003         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
1004                 eq("555-0000"), eq(PhoneNumberUtils.TOA_Unknown), nullable(String.class));
1005     }
1006 
1007     @Test
testOnCallStateChangedAudioProcessingToActive()1008     public void testOnCallStateChangedAudioProcessingToActive() throws Exception {
1009         BluetoothCall activeCall = createActiveCall();
1010         when(activeCall.getState()).thenReturn(Call.STATE_ACTIVE);
1011         mBluetoothInCallService.onCallAdded(activeCall);
1012         mBluetoothInCallService.getCallback(activeCall)
1013                 .onStateChanged(activeCall, Call.STATE_ACTIVE);
1014 
1015         verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
1016                 eq(""), eq(128), nullable(String.class));
1017     }
1018 
1019     @Test
testOnCallStateChangedDialing()1020     public void testOnCallStateChangedDialing() throws Exception {
1021         BluetoothCall activeCall = createActiveCall();
1022 
1023         // make "mLastState" STATE_CONNECTING
1024         BluetoothInCallService.CallStateCallback callback =
1025                 mBluetoothInCallService.new CallStateCallback(Call.STATE_CONNECTING);
1026         mBluetoothInCallService.mCallbacks.put(
1027                 activeCall.getTelecomCallId(), callback);
1028 
1029         mBluetoothInCallService.mCallbacks.get(activeCall.getTelecomCallId())
1030                 .onStateChanged(activeCall, Call.STATE_DIALING);
1031 
1032         verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
1033                 anyString(), anyInt(), nullable(String.class));
1034     }
1035 
1036     @Test
testOnCallStateChangedAlerting()1037     public void testOnCallStateChangedAlerting() throws Exception {
1038         BluetoothCall outgoingCall = createOutgoingCall();
1039         mBluetoothInCallService.onCallAdded(outgoingCall);
1040         mBluetoothInCallService.getCallback(outgoingCall)
1041                 .onStateChanged(outgoingCall, Call.STATE_DIALING);
1042 
1043         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_DIALING),
1044                 eq(""), eq(128), nullable(String.class));
1045         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_ALERTING),
1046                 eq(""), eq(128), nullable(String.class));
1047     }
1048 
1049     @Test
testOnCallStateChangedDisconnected()1050     public void testOnCallStateChangedDisconnected() throws Exception {
1051         BluetoothCall disconnectedCall = createDisconnectedCall();
1052         doReturn(true).when(mMockCallInfo).hasOnlyDisconnectedCalls();
1053         mBluetoothInCallService.onCallAdded(disconnectedCall);
1054         mBluetoothInCallService.getCallback(disconnectedCall)
1055                 .onStateChanged(disconnectedCall, Call.STATE_DISCONNECTED);
1056         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_DISCONNECTED),
1057                 eq(""), eq(128), nullable(String.class));
1058     }
1059 
1060     @Test
testOnCallStateChanged()1061     public void testOnCallStateChanged() throws Exception {
1062         BluetoothCall ringingCall = createRingingCall();
1063         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
1064         mBluetoothInCallService.onCallAdded(ringingCall);
1065 
1066         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
1067                 eq("555-0000"), eq(PhoneNumberUtils.TOA_Unknown), nullable(String.class));
1068 
1069         //Switch to active
1070         doReturn(null).when(mMockCallInfo).getRingingOrSimulatedRingingCall();
1071         when(mMockCallInfo.getActiveCall()).thenReturn(ringingCall);
1072 
1073         mBluetoothInCallService.getCallback(ringingCall)
1074                 .onStateChanged(ringingCall, Call.STATE_ACTIVE);
1075 
1076         verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(0), eq(CALL_STATE_IDLE),
1077                 eq(""), eq(128), nullable(String.class));
1078     }
1079 
1080     @Test
testOnCallStateChangedGSMSwap()1081     public void testOnCallStateChangedGSMSwap() throws Exception {
1082         BluetoothCall heldCall = createHeldCall();
1083         when(heldCall.getHandle()).thenReturn(Uri.parse("tel:555-0000"));
1084         mBluetoothInCallService.onCallAdded(heldCall);
1085         doReturn(2).when(mMockCallInfo).getNumHeldCalls();
1086 
1087         clearInvocations(mMockBluetoothHeadset);
1088         mBluetoothInCallService.getCallback(heldCall)
1089                 .onStateChanged(heldCall, Call.STATE_HOLDING);
1090 
1091         verify(mMockBluetoothHeadset, never()).phoneStateChanged(eq(0), eq(2), eq(CALL_STATE_HELD),
1092                 eq("5550000"), eq(PhoneNumberUtils.TOA_Unknown), nullable(String.class));
1093     }
1094 
1095     @Test
testOnParentOnChildrenChanged()1096     public void testOnParentOnChildrenChanged() throws Exception {
1097         // Start with two calls that are being merged into a CDMA conference call. The
1098         // onIsConferencedChanged method will be called multiple times during the call. Make sure
1099         // that the bluetooth phone state is updated properly.
1100         BluetoothCall parentCall = createActiveCall();
1101         BluetoothCall activeCall = getMockCall();
1102         BluetoothCall heldCall = createHeldCall();
1103         mBluetoothInCallService.onCallAdded(parentCall);
1104         mBluetoothInCallService.onCallAdded(activeCall);
1105         mBluetoothInCallService.onCallAdded(heldCall);
1106         String parentId = parentCall.getTelecomCallId();
1107         when(activeCall.getParentId()).thenReturn(parentId);
1108         when(heldCall.getParentId()).thenReturn(parentId);
1109 
1110         ArrayList<String> calls = new ArrayList<>();
1111         calls.add(activeCall.getTelecomCallId());
1112 
1113         when(parentCall.getChildrenIds()).thenReturn(calls);
1114         when(parentCall.isConference()).thenReturn(true);
1115 
1116         removeCallCapability(parentCall, Connection.CAPABILITY_CONFERENCE_HAS_NO_CHILDREN);
1117         addCallCapability(parentCall, Connection.CAPABILITY_SWAP_CONFERENCE);
1118         when(parentCall.wasConferencePreviouslyMerged()).thenReturn(false);
1119 
1120         clearInvocations(mMockBluetoothHeadset);
1121         // Be sure that onIsConferencedChanged rejects spurious changes during set up of
1122         // CDMA "conference"
1123         mBluetoothInCallService.getCallback(activeCall).onParentChanged(activeCall);
1124         verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
1125                 anyString(), anyInt(), nullable(String.class));
1126 
1127         mBluetoothInCallService.getCallback(heldCall).onParentChanged(heldCall);
1128         verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
1129                 anyString(), anyInt(), nullable(String.class));
1130 
1131         mBluetoothInCallService.getCallback(parentCall)
1132                 .onChildrenChanged(
1133                         parentCall,
1134                         mBluetoothInCallService.getBluetoothCallsByIds(calls));
1135         verify(mMockBluetoothHeadset, never()).phoneStateChanged(anyInt(), anyInt(), anyInt(),
1136                 anyString(), anyInt(), nullable(String.class));
1137 
1138         calls.add(heldCall.getTelecomCallId());
1139         mBluetoothInCallService.onCallAdded(heldCall);
1140         mBluetoothInCallService.getCallback(parentCall)
1141                 .onChildrenChanged(
1142                         parentCall,
1143                         mBluetoothInCallService.getBluetoothCallsByIds(calls));
1144         verify(mMockBluetoothHeadset).phoneStateChanged(eq(1), eq(1), eq(CALL_STATE_IDLE),
1145                 eq(""), eq(128), nullable(String.class));
1146     }
1147 
1148     @Test
testBluetoothAdapterReceiver()1149     public void testBluetoothAdapterReceiver() throws Exception {
1150         BluetoothCall ringingCall = createRingingCall();
1151         when(ringingCall.getHandle()).thenReturn(Uri.parse("tel:5550000"));
1152 
1153         Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
1154         intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);
1155         clearInvocations(mMockBluetoothHeadset);
1156         mBluetoothInCallService.mBluetoothAdapterReceiver
1157                 = mBluetoothInCallService.new BluetoothAdapterReceiver();
1158         mBluetoothInCallService.mBluetoothAdapterReceiver
1159                 .onReceive(mBluetoothInCallService, intent);
1160 
1161         verify(mMockBluetoothHeadset).phoneStateChanged(eq(0), eq(0), eq(CALL_STATE_INCOMING),
1162                 eq("5550000"), eq(PhoneNumberUtils.TOA_Unknown), nullable(String.class));
1163     }
1164 
addCallCapability(BluetoothCall call, int capability)1165     private void addCallCapability(BluetoothCall call, int capability) {
1166         when(call.can(capability)).thenReturn(true);
1167     }
1168 
removeCallCapability(BluetoothCall call, int capability)1169     private void removeCallCapability(BluetoothCall call, int capability) {
1170         when(call.can(capability)).thenReturn(false);
1171     }
1172 
createActiveCall()1173     private BluetoothCall createActiveCall() {
1174         BluetoothCall call = getMockCall();
1175         when(mMockCallInfo.getActiveCall()).thenReturn(call);
1176         return call;
1177     }
1178 
createRingingCall()1179     private BluetoothCall createRingingCall() {
1180         BluetoothCall call = getMockCall();
1181         Log.i("BluetoothInCallService", "asdf creaete " + Integer.toString(call.hashCode()));
1182         when(mMockCallInfo.getRingingOrSimulatedRingingCall()).thenReturn(call);
1183         return call;
1184     }
1185 
createHeldCall()1186     private BluetoothCall createHeldCall() {
1187         BluetoothCall call = getMockCall();
1188         when(mMockCallInfo.getHeldCall()).thenReturn(call);
1189         return call;
1190     }
1191 
createOutgoingCall()1192     private BluetoothCall createOutgoingCall() {
1193         BluetoothCall call = getMockCall();
1194         when(mMockCallInfo.getOutgoingCall()).thenReturn(call);
1195         return call;
1196     }
1197 
createDisconnectedCall()1198     private BluetoothCall createDisconnectedCall() {
1199         BluetoothCall call = getMockCall();
1200         when(mMockCallInfo.getCallByState(Call.STATE_DISCONNECTED)).thenReturn(call);
1201         return call;
1202     }
1203 
createForegroundCall()1204     private BluetoothCall createForegroundCall() {
1205         BluetoothCall call = getMockCall();
1206         when(mMockCallInfo.getForegroundCall()).thenReturn(call);
1207         return call;
1208     }
1209 
makeQuickConnectionServiceComponentName()1210     private static ComponentName makeQuickConnectionServiceComponentName() {
1211         return new ComponentName("com.android.server.telecom.tests",
1212                 "com.android.server.telecom.tests.MockConnectionService");
1213     }
1214 
makeQuickAccountHandle(String id)1215     private static PhoneAccountHandle makeQuickAccountHandle(String id) {
1216         return new PhoneAccountHandle(makeQuickConnectionServiceComponentName(), id,
1217                 Binder.getCallingUserHandle());
1218     }
1219 
makeQuickAccountBuilder(String id, int idx)1220     private PhoneAccount.Builder makeQuickAccountBuilder(String id, int idx) {
1221         return new PhoneAccount.Builder(makeQuickAccountHandle(id), "label" + idx);
1222     }
1223 
makeQuickAccount(String id, int idx)1224     private PhoneAccount makeQuickAccount(String id, int idx) {
1225         return makeQuickAccountBuilder(id, idx)
1226                 .setAddress(Uri.parse(TEST_ACCOUNT_ADDRESS + idx))
1227                 .setSubscriptionAddress(Uri.parse("tel:555-000" + idx))
1228                 .setCapabilities(idx)
1229                 .setShortDescription("desc" + idx)
1230                 .setIsEnabled(true)
1231                 .build();
1232     }
1233 
getMockCall()1234     private BluetoothCall getMockCall() {
1235         BluetoothCall call = mock(com.android.bluetooth.telephony.BluetoothCall.class);
1236         String uuid = UUID.randomUUID().toString();
1237         when(call.getTelecomCallId()).thenReturn(uuid);
1238         return call;
1239     }
1240 }
1241