• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.btservice;
18 
19 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
20 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
21 import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
22 import static android.bluetooth.BluetoothProfile.STATE_CONNECTED;
23 import static android.bluetooth.BluetoothProfile.STATE_CONNECTING;
24 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
25 import static android.bluetooth.BluetoothProfile.STATE_DISCONNECTING;
26 
27 import static com.android.bluetooth.TestUtils.MockitoRule;
28 import static com.android.bluetooth.TestUtils.getTestDevice;
29 
30 import static org.mockito.Mockito.any;
31 import static org.mockito.Mockito.anyBoolean;
32 import static org.mockito.Mockito.anyInt;
33 import static org.mockito.Mockito.clearInvocations;
34 import static org.mockito.Mockito.doReturn;
35 import static org.mockito.Mockito.eq;
36 import static org.mockito.Mockito.inOrder;
37 import static org.mockito.Mockito.never;
38 import static org.mockito.Mockito.times;
39 import static org.mockito.Mockito.verify;
40 import static org.mockito.Mockito.when;
41 
42 import android.bluetooth.BluetoothAdapter;
43 import android.bluetooth.BluetoothCsipSetCoordinator;
44 import android.bluetooth.BluetoothDevice;
45 import android.bluetooth.BluetoothProfile;
46 import android.bluetooth.BluetoothUuid;
47 import android.os.ParcelUuid;
48 import android.platform.test.annotations.DisableFlags;
49 import android.platform.test.annotations.EnableFlags;
50 import android.platform.test.flag.junit.SetFlagsRule;
51 
52 import androidx.room.Room;
53 import androidx.test.filters.MediumTest;
54 import androidx.test.platform.app.InstrumentationRegistry;
55 import androidx.test.runner.AndroidJUnit4;
56 
57 import com.android.bluetooth.TestLooper;
58 import com.android.bluetooth.TestUtils;
59 import com.android.bluetooth.Utils;
60 import com.android.bluetooth.a2dp.A2dpService;
61 import com.android.bluetooth.btservice.storage.DatabaseManager;
62 import com.android.bluetooth.btservice.storage.MetadataDatabase;
63 import com.android.bluetooth.csip.CsipSetCoordinatorService;
64 import com.android.bluetooth.flags.Flags;
65 import com.android.bluetooth.hap.HapClientService;
66 import com.android.bluetooth.hearingaid.HearingAidService;
67 import com.android.bluetooth.hfp.HeadsetService;
68 import com.android.bluetooth.le_audio.LeAudioService;
69 import com.android.bluetooth.util.SystemProperties;
70 
71 import org.junit.After;
72 import org.junit.Before;
73 import org.junit.Rule;
74 import org.junit.Test;
75 import org.junit.runner.RunWith;
76 import org.mockito.InOrder;
77 import org.mockito.Mock;
78 
79 import java.util.ArrayList;
80 import java.util.Collections;
81 import java.util.List;
82 
83 /** Test cases for {@link PhonePolicy}. */
84 @MediumTest
85 @RunWith(AndroidJUnit4.class)
86 public class PhonePolicyTest {
87     @Rule public final MockitoRule mMockitoRule = new MockitoRule();
88     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
89 
90     @Mock private AdapterService mAdapterService;
91     @Mock private ServiceFactory mServiceFactory;
92     @Mock private HeadsetService mHeadsetService;
93     @Mock private A2dpService mA2dpService;
94     @Mock private LeAudioService mLeAudioService;
95     @Mock private DatabaseManager mDatabaseManager;
96     @Mock private CsipSetCoordinatorService mCsipSetCoordinatorService;
97     @Mock private HearingAidService mHearingAidService;
98     @Mock private HapClientService mHapClientService;
99     @Mock private SystemProperties.MockableSystemProperties mProperties;
100 
101     private static final int MAX_CONNECTED_AUDIO_DEVICES = 5;
102 
103     private final BluetoothDevice mDevice = getTestDevice(0);
104     private final BluetoothDevice mDevice2 = getTestDevice(1);
105     private PhonePolicy mPhonePolicy;
106     private boolean mOriginalDualModeState;
107     private TestLooper mLooper;
108 
109     private List<BluetoothDevice> mLeAudioAllowedConnectionPolicyList;
110 
111     @Before
setUp()112     public void setUp() throws Exception {
113         mLeAudioAllowedConnectionPolicyList = new ArrayList<>();
114         mLooper = new TestLooper();
115 
116         // Stub A2DP and HFP
117         doReturn(true).when(mHeadsetService).connect(any());
118         doReturn(true).when(mA2dpService).connect(any());
119 
120         doReturn(BluetoothAdapter.STATE_ON).when(mAdapterService).getState();
121         doReturn(MAX_CONNECTED_AUDIO_DEVICES).when(mAdapterService).getMaxConnectedAudioDevices();
122         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
123         // Setup the mocked factory to return mocked services
124         doReturn(mHeadsetService).when(mServiceFactory).getHeadsetService();
125         doReturn(mA2dpService).when(mServiceFactory).getA2dpService();
126         doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService();
127         doReturn(mCsipSetCoordinatorService).when(mServiceFactory).getCsipSetCoordinatorService();
128         doReturn(mHearingAidService).when(mServiceFactory).getHearingAidService();
129         doReturn(mHapClientService).when(mServiceFactory).getHapClientService();
130 
131         // Most common default
132         doReturn(CONNECTION_POLICY_UNKNOWN).when(mHeadsetService).getConnectionPolicy(any());
133         doReturn(CONNECTION_POLICY_UNKNOWN).when(mA2dpService).getConnectionPolicy(any());
134         doReturn(CONNECTION_POLICY_UNKNOWN).when(mLeAudioService).getConnectionPolicy(any());
135         doReturn(STATE_DISCONNECTED).when(mA2dpService).getConnectionState(any());
136         doReturn(STATE_DISCONNECTED).when(mHeadsetService).getConnectionState(any());
137         doReturn(Collections.emptyList()).when(mA2dpService).getConnectedDevices();
138         doReturn(Collections.emptyList()).when(mHeadsetService).getConnectedDevices();
139 
140         SystemProperties.mProperties = mProperties;
141 
142         mPhonePolicy = new PhonePolicy(mAdapterService, mLooper.getLooper(), mServiceFactory);
143         mOriginalDualModeState = Utils.isDualModeAudioEnabled();
144     }
145 
146     @After
tearDown()147     public void tearDown() throws Exception {
148         SystemProperties.mProperties = null;
149         Utils.setDualModeAudioStateForTesting(mOriginalDualModeState);
150     }
151 
getLeAudioConnectionPolicy(BluetoothDevice dev)152     int getLeAudioConnectionPolicy(BluetoothDevice dev) {
153         if (!mLeAudioAllowedConnectionPolicyList.contains(dev)) {
154             return CONNECTION_POLICY_UNKNOWN;
155         }
156         return CONNECTION_POLICY_ALLOWED;
157     }
158 
setLeAudioAllowedConnectionPolicy(BluetoothDevice dev)159     boolean setLeAudioAllowedConnectionPolicy(BluetoothDevice dev) {
160         mLeAudioAllowedConnectionPolicyList.add(dev);
161         return true;
162     }
163 
164     /**
165      * Test that when new UUIDs are refreshed for a device then we set the priorities for various
166      * profiles accurately. The following profiles should have ON priorities: A2DP, HFP, HID and PAN
167      */
168     @Test
testProcessInitProfilePriorities()169     public void testProcessInitProfilePriorities() {
170         mPhonePolicy.mAutoConnectProfilesSupported = false;
171 
172         ParcelUuid[] uuids = {BluetoothUuid.HFP, BluetoothUuid.A2DP_SINK};
173         mPhonePolicy.onUuidsDiscovered(mDevice, uuids);
174 
175         verify(mDatabaseManager)
176                 .setProfileConnectionPolicy(
177                         mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_ALLOWED);
178         verify(mDatabaseManager)
179                 .setProfileConnectionPolicy(
180                         mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_ALLOWED);
181     }
182 
processInitProfilePriorities_LeAudioOnlyHelper( int csipGroupId, int groupSize, boolean dualMode, boolean ashaDevice)183     private void processInitProfilePriorities_LeAudioOnlyHelper(
184             int csipGroupId, int groupSize, boolean dualMode, boolean ashaDevice) {
185         Utils.setDualModeAudioStateForTesting(false);
186         mPhonePolicy.mLeAudioEnabledByDefault = true;
187         mPhonePolicy.mAutoConnectProfilesSupported = true;
188         doReturn(false)
189                 .when(mProperties)
190                 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean());
191 
192         int testedDeviceType = BluetoothDevice.DEVICE_TYPE_LE;
193         if (dualMode) {
194             /* If CSIP mode, use DUAL type only for single device */
195             testedDeviceType = BluetoothDevice.DEVICE_TYPE_DUAL;
196         }
197 
198         List<BluetoothDevice> allConnectedDevices = new ArrayList<>();
199         for (int i = 0; i < groupSize; i++) {
200             BluetoothDevice device = getTestDevice(i);
201             allConnectedDevices.add(device);
202         }
203 
204         doReturn(csipGroupId).when(mCsipSetCoordinatorService).getGroupId(any(), any());
205         doReturn(groupSize).when(mCsipSetCoordinatorService).getDesiredGroupSize(csipGroupId);
206 
207         /* Build list of test UUIDs */
208         int numOfServices = 1;
209         if (groupSize > 1) {
210             numOfServices++;
211         }
212         if (ashaDevice) {
213             numOfServices++;
214         }
215         ParcelUuid[] uuids = new ParcelUuid[numOfServices];
216         int iter = 0;
217         uuids[iter++] = BluetoothUuid.LE_AUDIO;
218         if (groupSize > 1) {
219             uuids[iter++] = BluetoothUuid.COORDINATED_SET;
220         }
221         if (ashaDevice) {
222             uuids[iter++] = BluetoothUuid.HEARING_AID;
223         }
224 
225         doReturn(uuids).when(mAdapterService).getRemoteUuids(any());
226         doReturn(true)
227                 .when(mAdapterService)
228                 .isProfileSupported(any(), eq(BluetoothProfile.LE_AUDIO));
229         doReturn(ashaDevice)
230                 .when(mAdapterService)
231                 .isProfileSupported(any(), eq(BluetoothProfile.HEARING_AID));
232 
233         List<BluetoothDevice> connectedDevices = new ArrayList<>();
234         doReturn(connectedDevices)
235                 .when(mCsipSetCoordinatorService)
236                 .getGroupDevicesOrdered(csipGroupId);
237 
238         for (BluetoothDevice dev : allConnectedDevices) {
239             when(mLeAudioService.setConnectionPolicy(dev, CONNECTION_POLICY_ALLOWED))
240                     .thenAnswer(
241                             invocation -> {
242                                 return setLeAudioAllowedConnectionPolicy(dev);
243                             });
244             when(mLeAudioService.getConnectionPolicy(dev))
245                     .thenAnswer(
246                             invocation -> {
247                                 return getLeAudioConnectionPolicy(dev);
248                             });
249 
250             /* First device is always LE only second depends on dualMode */
251             if (groupSize == 1 || connectedDevices.size() >= 1) {
252                 doReturn(testedDeviceType).when(mAdapterService).getRemoteType(dev);
253             } else {
254                 doReturn(BluetoothDevice.DEVICE_TYPE_LE).when(mAdapterService).getRemoteType(dev);
255             }
256 
257             mPhonePolicy.onUuidsDiscovered(dev, uuids);
258             if (groupSize > 1) {
259                 connectedDevices.add(dev);
260                 // Simulate CSIP connection
261                 mPhonePolicy.profileConnectionStateChanged(
262                         BluetoothProfile.CSIP_SET_COORDINATOR,
263                         dev,
264                         STATE_DISCONNECTED,
265                         STATE_CONNECTED);
266                 mLooper.dispatchAll();
267             }
268         }
269     }
270 
271     @Test
272     @EnableFlags(Flags.FLAG_LEAUDIO_ALLOW_LEAUDIO_ONLY_DEVICES)
testConnectLeAudioOnlyDevices_BandedHeadphones()273     public void testConnectLeAudioOnlyDevices_BandedHeadphones() {
274         // Single device, no CSIP
275         processInitProfilePriorities_LeAudioOnlyHelper(
276                 BluetoothCsipSetCoordinator.GROUP_ID_INVALID, 1, false, false);
277         verify(mLeAudioService)
278                 .setConnectionPolicy(any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED));
279     }
280 
281     @Test
282     @EnableFlags(Flags.FLAG_LEAUDIO_ALLOW_LEAUDIO_ONLY_DEVICES)
testConnectLeAudioOnlyDevices_CsipSet()283     public void testConnectLeAudioOnlyDevices_CsipSet() {
284         // CSIP Le Audio only devices
285         processInitProfilePriorities_LeAudioOnlyHelper(1, 2, false, false);
286         verify(mLeAudioService, times(2))
287                 .setConnectionPolicy(any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED));
288     }
289 
290     @Test
291     @EnableFlags(Flags.FLAG_LEAUDIO_ALLOW_LEAUDIO_ONLY_DEVICES)
testConnectLeAudioOnlyDevices_DualModeCsipSet()292     public void testConnectLeAudioOnlyDevices_DualModeCsipSet() {
293         // CSIP Dual mode devices
294         processInitProfilePriorities_LeAudioOnlyHelper(1, 2, true, false);
295         verify(mLeAudioService, never())
296                 .setConnectionPolicy(any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED));
297     }
298 
299     @Test
300     @EnableFlags(Flags.FLAG_LEAUDIO_ALLOW_LEAUDIO_ONLY_DEVICES)
testConnectLeAudioOnlyDevices_AshaAndCsipSet()301     public void testConnectLeAudioOnlyDevices_AshaAndCsipSet() {
302         // CSIP Dual mode devices
303         processInitProfilePriorities_LeAudioOnlyHelper(1, 2, false, true);
304         verify(mLeAudioService, never())
305                 .setConnectionPolicy(any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED));
306     }
307 
308     @Test
testProcessInitProfilePriorities_WithAutoConnect()309     public void testProcessInitProfilePriorities_WithAutoConnect() {
310         mPhonePolicy.mAutoConnectProfilesSupported = true;
311 
312         // Inject an event for UUIDs updated for a remote device with only HFP enabled
313         ParcelUuid[] uuids = new ParcelUuid[2];
314         uuids[0] = BluetoothUuid.HFP;
315         uuids[1] = BluetoothUuid.A2DP_SINK;
316         mPhonePolicy.onUuidsDiscovered(mDevice, uuids);
317 
318         // Check auto connect
319         verify(mA2dpService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
320         verify(mHeadsetService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
321     }
322 
323     @Test
testProcessInitProfilePriorities_LeAudioDisabledByDefault()324     public void testProcessInitProfilePriorities_LeAudioDisabledByDefault() {
325         when(mAdapterService.isLeAudioAllowed(mDevice)).thenReturn(true);
326 
327         // Auto connect to LE audio, HFP, A2DP
328         processInitProfilePriorities_LeAudioHelper(true, true, false, false);
329         verify(mLeAudioService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
330         verify(mA2dpService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
331         verify(mHeadsetService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
332 
333         // Does not auto connect and allow HFP and A2DP to be connected
334         processInitProfilePriorities_LeAudioHelper(true, false, false, false);
335         verify(mDatabaseManager)
336                 .setProfileConnectionPolicy(
337                         mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_ALLOWED);
338         verify(mDatabaseManager)
339                 .setProfileConnectionPolicy(
340                         mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_ALLOWED);
341         verify(mDatabaseManager)
342                 .setProfileConnectionPolicy(
343                         mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_ALLOWED);
344 
345         // Auto connect to HFP and A2DP but disallow LE Audio
346         processInitProfilePriorities_LeAudioHelper(false, true, false, false);
347         verify(mDatabaseManager)
348                 .setProfileConnectionPolicy(
349                         mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_FORBIDDEN);
350         verify(mA2dpService, times(2)).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
351         verify(mHeadsetService, times(2)).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
352 
353         // Does not auto connect and disallow LE Audio to be connected
354         processInitProfilePriorities_LeAudioHelper(false, false, false, false);
355         verify(mDatabaseManager, times(2))
356                 .setProfileConnectionPolicy(
357                         mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_FORBIDDEN);
358         verify(mDatabaseManager, times(2))
359                 .setProfileConnectionPolicy(
360                         mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_ALLOWED);
361         verify(mDatabaseManager, times(2))
362                 .setProfileConnectionPolicy(
363                         mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_ALLOWED);
364     }
365 
366     @Test
testProcessInitProfilePriorities_LeAudioEnabledByDefault()367     public void testProcessInitProfilePriorities_LeAudioEnabledByDefault() {
368         when(mAdapterService.isLeAudioAllowed(mDevice)).thenReturn(true);
369 
370         // Auto connect to LE audio, HFP, A2DP
371         processInitProfilePriorities_LeAudioHelper(true, true, true, false);
372         verify(mLeAudioService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
373         verify(mA2dpService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
374         verify(mHeadsetService).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
375 
376         // Does not auto connect and allow HFP and A2DP to be connected
377         processInitProfilePriorities_LeAudioHelper(true, false, true, false);
378         verify(mDatabaseManager)
379                 .setProfileConnectionPolicy(
380                         mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_ALLOWED);
381         verify(mDatabaseManager)
382                 .setProfileConnectionPolicy(
383                         mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_ALLOWED);
384         verify(mDatabaseManager)
385                 .setProfileConnectionPolicy(
386                         mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_ALLOWED);
387 
388         // Auto connect to LE audio but disallow HFP and A2DP
389         processInitProfilePriorities_LeAudioHelper(false, true, true, false);
390         verify(mLeAudioService, times(2)).setConnectionPolicy(mDevice, CONNECTION_POLICY_ALLOWED);
391         verify(mDatabaseManager)
392                 .setProfileConnectionPolicy(
393                         mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_FORBIDDEN);
394         verify(mDatabaseManager)
395                 .setProfileConnectionPolicy(
396                         mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_FORBIDDEN);
397 
398         // Does not auto connect and disallow HFP and A2DP to be connected
399         processInitProfilePriorities_LeAudioHelper(false, false, true, false);
400         verify(mDatabaseManager, times(2))
401                 .setProfileConnectionPolicy(
402                         mDevice, BluetoothProfile.LE_AUDIO, CONNECTION_POLICY_ALLOWED);
403         verify(mDatabaseManager, times(2))
404                 .setProfileConnectionPolicy(
405                         mDevice, BluetoothProfile.HEADSET, CONNECTION_POLICY_FORBIDDEN);
406         verify(mDatabaseManager, times(2))
407                 .setProfileConnectionPolicy(
408                         mDevice, BluetoothProfile.A2DP, CONNECTION_POLICY_FORBIDDEN);
409     }
410 
processInitProfilePriorities_LeAudioHelper( boolean dualModeEnabled, boolean autoConnect, boolean leAudioEnabledByDefault, boolean bypassLeAudioAllowlist)411     private void processInitProfilePriorities_LeAudioHelper(
412             boolean dualModeEnabled,
413             boolean autoConnect,
414             boolean leAudioEnabledByDefault,
415             boolean bypassLeAudioAllowlist) {
416         Utils.setDualModeAudioStateForTesting(dualModeEnabled);
417         mPhonePolicy.mLeAudioEnabledByDefault = leAudioEnabledByDefault;
418         mPhonePolicy.mAutoConnectProfilesSupported = autoConnect;
419         doReturn(bypassLeAudioAllowlist)
420                 .when(mProperties)
421                 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean());
422 
423         // Inject an event for UUIDs updated for a remote device with only HFP enabled
424         ParcelUuid[] uuids = new ParcelUuid[3];
425         uuids[0] = BluetoothUuid.HFP;
426         uuids[1] = BluetoothUuid.A2DP_SINK;
427         uuids[2] = BluetoothUuid.LE_AUDIO;
428         mPhonePolicy.onUuidsDiscovered(mDevice, uuids);
429     }
430 
431     /* In this test we want to check following scenario
432      * 1. First Le Audio set member bonds/connect and user switch LeAudio toggle
433      * 2. Second device connects later, and LeAudio shall be enabled automatically
434      */
435     @Test
testLateConnectOfLeAudioEnabled_DualModeBud()436     public void testLateConnectOfLeAudioEnabled_DualModeBud() {
437         Utils.setDualModeAudioStateForTesting(false);
438         mPhonePolicy.mLeAudioEnabledByDefault = true;
439         mPhonePolicy.mAutoConnectProfilesSupported = true;
440 
441         /* Just for the moment, set to true to setup first device */
442         doReturn(true)
443                 .when(mProperties)
444                 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean());
445 
446         int csipGroupId = 1;
447         int groupSize = 2;
448 
449         List<BluetoothDevice> connectedDevices = new ArrayList<>();
450         when(mCsipSetCoordinatorService.getDesiredGroupSize(csipGroupId)).thenReturn(groupSize);
451         when(mCsipSetCoordinatorService.getGroupId(any(), any())).thenReturn(csipGroupId);
452         when(mLeAudioService.getGroupId(any())).thenReturn(csipGroupId);
453         when(mCsipSetCoordinatorService.getGroupDevicesOrdered(csipGroupId))
454                 .thenReturn(connectedDevices);
455 
456         // Connect first set member
457         connectedDevices.add(mDevice);
458 
459         /* Build list of test UUIDs */
460         ParcelUuid[] uuids = new ParcelUuid[4];
461         uuids[0] = BluetoothUuid.LE_AUDIO;
462         uuids[1] = BluetoothUuid.COORDINATED_SET;
463         uuids[2] = BluetoothUuid.A2DP_SINK;
464         uuids[3] = BluetoothUuid.HFP;
465 
466         // Prepare common handlers
467         doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any());
468         doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any());
469 
470         when(mLeAudioService.setConnectionPolicy(
471                         any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED)))
472                 .thenAnswer(
473                         invocation -> {
474                             return setLeAudioAllowedConnectionPolicy(invocation.getArgument(0));
475                         });
476         when(mLeAudioService.getConnectionPolicy(any(BluetoothDevice.class)))
477                 .thenAnswer(
478                         invocation -> {
479                             return getLeAudioConnectionPolicy(invocation.getArgument(0));
480                         });
481         when(mLeAudioService.getGroupDevices(csipGroupId)).thenReturn(connectedDevices);
482 
483         when(mAdapterService.getRemoteUuids(any(BluetoothDevice.class))).thenReturn(uuids);
484         when(mAdapterService.isProfileSupported(
485                         any(BluetoothDevice.class), eq(BluetoothProfile.HEARING_AID)))
486                 .thenReturn(false);
487         when(mAdapterService.isProfileSupported(
488                         any(BluetoothDevice.class), eq(BluetoothProfile.LE_AUDIO)))
489                 .thenReturn(true);
490 
491         /* Always DualMode for test purpose */
492         when(mAdapterService.getRemoteType(any(BluetoothDevice.class)))
493                 .thenReturn(BluetoothDevice.DEVICE_TYPE_DUAL);
494 
495         // Inject first devices
496         mPhonePolicy.onUuidsDiscovered(mDevice, uuids);
497         mPhonePolicy.profileConnectionStateChanged(
498                 BluetoothProfile.CSIP_SET_COORDINATOR,
499                 mDevice,
500                 STATE_DISCONNECTED,
501                 STATE_CONNECTED);
502         mLooper.dispatchAll();
503 
504         // Verify connection policy is set properly
505         verify(mLeAudioService).setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_ALLOWED));
506 
507         mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, mDevice);
508         mLooper.dispatchAll();
509 
510         verify(mA2dpService).setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_FORBIDDEN));
511         verify(mHeadsetService).setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_FORBIDDEN));
512 
513         /* Remove bypass and check that second set member will be added*/
514         doReturn(false)
515                 .when(mProperties)
516                 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean());
517 
518         // Now connect second device and make sure
519         // Connect first set member
520         connectedDevices.add(mDevice2);
521 
522         // Inject second set member connection
523         mPhonePolicy.onUuidsDiscovered(mDevice2, uuids);
524         mPhonePolicy.profileConnectionStateChanged(
525                 BluetoothProfile.CSIP_SET_COORDINATOR,
526                 mDevice2,
527                 STATE_DISCONNECTED,
528                 STATE_CONNECTED);
529         mLooper.dispatchAll();
530 
531         // Verify connection policy is set properly
532         verify(mLeAudioService).setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_ALLOWED));
533 
534         mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, mDevice2);
535         mLooper.dispatchAll();
536 
537         verify(mA2dpService).setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_FORBIDDEN));
538         verify(mHeadsetService).setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_FORBIDDEN));
539     }
540 
541     @Test
testLateConnectOfLeAudioEnabled_AshaAndLeAudioBud()542     public void testLateConnectOfLeAudioEnabled_AshaAndLeAudioBud() {
543         Utils.setDualModeAudioStateForTesting(false);
544         mPhonePolicy.mLeAudioEnabledByDefault = true;
545         mPhonePolicy.mAutoConnectProfilesSupported = true;
546 
547         /* Just for the moment, set to true to setup first device */
548         doReturn(true)
549                 .when(mProperties)
550                 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean());
551 
552         int csipGroupId = 1;
553         int groupSize = 2;
554 
555         List<BluetoothDevice> connectedDevices = new ArrayList<>();
556         when(mCsipSetCoordinatorService.getDesiredGroupSize(csipGroupId)).thenReturn(groupSize);
557         when(mCsipSetCoordinatorService.getGroupId(any(), any())).thenReturn(csipGroupId);
558         when(mLeAudioService.getGroupId(any())).thenReturn(csipGroupId);
559         when(mCsipSetCoordinatorService.getGroupDevicesOrdered(csipGroupId))
560                 .thenReturn(connectedDevices);
561 
562         // Connect first set member
563         connectedDevices.add(mDevice);
564 
565         /* Build list of test UUIDs */
566         ParcelUuid[] uuids = new ParcelUuid[4];
567         uuids[0] = BluetoothUuid.LE_AUDIO;
568         uuids[1] = BluetoothUuid.COORDINATED_SET;
569         uuids[2] = BluetoothUuid.HEARING_AID;
570         uuids[3] = BluetoothUuid.HAS;
571 
572         // Prepare common handlers
573         doReturn(CONNECTION_POLICY_ALLOWED).when(mHearingAidService).getConnectionPolicy(any());
574 
575         when(mLeAudioService.setConnectionPolicy(
576                         any(BluetoothDevice.class), eq(CONNECTION_POLICY_ALLOWED)))
577                 .thenAnswer(
578                         invocation -> {
579                             return setLeAudioAllowedConnectionPolicy(invocation.getArgument(0));
580                         });
581         when(mLeAudioService.getConnectionPolicy(any(BluetoothDevice.class)))
582                 .thenAnswer(
583                         invocation -> {
584                             return getLeAudioConnectionPolicy(invocation.getArgument(0));
585                         });
586         when(mLeAudioService.getGroupDevices(csipGroupId)).thenReturn(connectedDevices);
587 
588         when(mAdapterService.getRemoteUuids(any(BluetoothDevice.class))).thenReturn(uuids);
589         when(mAdapterService.isProfileSupported(
590                         any(BluetoothDevice.class), eq(BluetoothProfile.HEARING_AID)))
591                 .thenReturn(false);
592         when(mAdapterService.isProfileSupported(
593                         any(BluetoothDevice.class), eq(BluetoothProfile.LE_AUDIO)))
594                 .thenReturn(true);
595 
596         /* Always DualMode for test purpose */
597         when(mAdapterService.getRemoteType(any(BluetoothDevice.class)))
598                 .thenReturn(BluetoothDevice.DEVICE_TYPE_LE);
599 
600         // Inject first devices
601         mPhonePolicy.onUuidsDiscovered(mDevice, uuids);
602         mPhonePolicy.profileConnectionStateChanged(
603                 BluetoothProfile.CSIP_SET_COORDINATOR,
604                 mDevice,
605                 STATE_DISCONNECTED,
606                 STATE_CONNECTED);
607         mLooper.dispatchAll();
608 
609         // Verify connection policy is set properly
610         verify(mLeAudioService).setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_ALLOWED));
611 
612         mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, mDevice);
613         mLooper.dispatchAll();
614 
615         verify(mHearingAidService)
616                 .setConnectionPolicy(eq(mDevice), eq(CONNECTION_POLICY_FORBIDDEN));
617 
618         /* Remove bypass and check that second set member will be added*/
619         doReturn(false)
620                 .when(mProperties)
621                 .getBoolean(eq(PhonePolicy.BYPASS_LE_AUDIO_ALLOWLIST_PROPERTY), anyBoolean());
622 
623         // Now connect second device and make sure
624         // Connect first set member
625         connectedDevices.add(mDevice2);
626 
627         // Inject second set member connection
628         mPhonePolicy.onUuidsDiscovered(mDevice2, uuids);
629         mPhonePolicy.profileConnectionStateChanged(
630                 BluetoothProfile.CSIP_SET_COORDINATOR,
631                 mDevice2,
632                 STATE_DISCONNECTED,
633                 STATE_CONNECTED);
634         mLooper.dispatchAll();
635 
636         // Verify connection policy is set properly
637         verify(mLeAudioService).setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_ALLOWED));
638 
639         mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.LE_AUDIO, mDevice2);
640         mLooper.dispatchAll();
641 
642         verify(mHearingAidService)
643                 .setConnectionPolicy(eq(mDevice2), eq(CONNECTION_POLICY_FORBIDDEN));
644     }
645 
646     /**
647      * Test that when the adapter is turned ON then we call autoconnect on devices that have HFP and
648      * A2DP enabled. NOTE that the assumption is that we have already done the pairing previously
649      * and hence the priorities for the device is already set to AUTO_CONNECT over HFP and A2DP (as
650      * part of post pairing process).
651      */
652     @Test
testAdapterOnAutoConnect()653     public void testAdapterOnAutoConnect() {
654         // Return desired values from the mocked object(s)
655         when(mAdapterService.isQuietModeEnabled()).thenReturn(false);
656 
657         // Return a list of connection order
658         when(mDatabaseManager.getMostRecentlyConnectedA2dpDevice()).thenReturn(mDevice);
659         when(mAdapterService.getBondState(mDevice)).thenReturn(BluetoothDevice.BOND_BONDED);
660 
661         doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any());
662         doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any());
663 
664         // Inject an event that the adapter is turned on.
665         mPhonePolicy.onBluetoothStateChange(BluetoothAdapter.STATE_OFF, BluetoothAdapter.STATE_ON);
666 
667         // Check that we got a request to connect over HFP and A2DP
668         verify(mA2dpService).connect(eq(mDevice));
669         verify(mHeadsetService).connect(eq(mDevice));
670     }
671 
672     /** Test that when an active device is disconnected, we will not auto connect it */
673     @Test
testDisconnectNoAutoConnect()674     public void testDisconnectNoAutoConnect() {
675         // Return desired values from the mocked object(s)
676         when(mAdapterService.isQuietModeEnabled()).thenReturn(false);
677 
678         // Return a list of connection order
679         List<BluetoothDevice> connectionOrder = new ArrayList<>();
680         connectionOrder.add(mDevice);
681         connectionOrder.add(mDevice2);
682         connectionOrder.add(getTestDevice(2));
683         connectionOrder.add(getTestDevice(3));
684 
685         doReturn(mDevice).when(mDatabaseManager).getMostRecentlyConnectedA2dpDevice();
686 
687         // Make all devices auto connect
688         doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any());
689         doReturn(CONNECTION_POLICY_FORBIDDEN)
690                 .when(mA2dpService)
691                 .getConnectionPolicy(eq(connectionOrder.get(3)));
692 
693         // Make one of the device active
694         mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.A2DP, connectionOrder.get(0));
695         mLooper.dispatchAll();
696 
697         // Only calls setConnection on device connectionOrder.get(0) with STATE_CONNECTED
698         verify(mDatabaseManager).setConnection(mDevice, BluetoothProfile.A2DP);
699         verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(1)), anyInt());
700         verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(2)), anyInt());
701         verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(3)), anyInt());
702 
703         // Make another device active
704         when(mHeadsetService.getConnectionState(connectionOrder.get(1)))
705                 .thenReturn(STATE_CONNECTED);
706         mPhonePolicy.profileActiveDeviceChanged(BluetoothProfile.A2DP, connectionOrder.get(1));
707         mLooper.dispatchAll();
708 
709         // Only calls setConnection on device connectionOrder.get(1) with STATE_CONNECTED
710         verify(mDatabaseManager).setConnection(connectionOrder.get(0), BluetoothProfile.A2DP);
711         verify(mDatabaseManager).setConnection(connectionOrder.get(1), BluetoothProfile.A2DP);
712         verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(2)), anyInt());
713         verify(mDatabaseManager, never()).setConnection(eq(connectionOrder.get(3)), anyInt());
714 
715         // Disconnect a2dp for the device from previous STATE_CONNECTED
716         when(mHeadsetService.getConnectionState(connectionOrder.get(1)))
717                 .thenReturn(STATE_DISCONNECTED);
718         mPhonePolicy.profileConnectionStateChanged(
719                 BluetoothProfile.A2DP, connectionOrder.get(1), STATE_CONNECTED, STATE_DISCONNECTED);
720         mLooper.dispatchAll();
721 
722         // Verify that we do not call setConnection, nor setDisconnection on disconnect
723         // from previous STATE_CONNECTED
724         verify(mDatabaseManager)
725                 .setConnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP));
726         verify(mDatabaseManager, never())
727                 .setDisconnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP));
728 
729         // Disconnect a2dp for the device from previous STATE_DISCONNECTING
730         mPhonePolicy.profileConnectionStateChanged(
731                 BluetoothProfile.A2DP,
732                 connectionOrder.get(1),
733                 STATE_DISCONNECTING,
734                 STATE_DISCONNECTED);
735         mLooper.dispatchAll();
736 
737         // Verify that we do not call setConnection, but instead setDisconnection on disconnect
738         verify(mDatabaseManager)
739                 .setConnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP));
740         verify(mDatabaseManager)
741                 .setDisconnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP));
742 
743         // Make the current active device fail to connect
744         when(mA2dpService.getConnectionState(connectionOrder.get(1)))
745                 .thenReturn(STATE_DISCONNECTED);
746         updateProfileConnectionStateHelper(
747                 connectionOrder.get(1),
748                 BluetoothProfile.HEADSET,
749                 STATE_CONNECTING,
750                 STATE_DISCONNECTED);
751         mLooper.dispatchAll();
752 
753         // Verify we don't call deleteConnection as that only happens when we disconnect a2dp
754         verify(mDatabaseManager)
755                 .setDisconnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.A2DP));
756 
757         // Verify we didn't have any unexpected calls to setConnection or deleteConnection
758         verify(mDatabaseManager, times(2)).setConnection(any(BluetoothDevice.class), anyInt());
759         verify(mDatabaseManager)
760                 .setDisconnection(eq(connectionOrder.get(1)), eq(BluetoothProfile.HEADSET));
761     }
762 
763     /**
764      * Test that we will try to re-connect to a profile on a device if other profile(s) are
765      * connected. This is to add robustness to the connection mechanism
766      */
767     @Test
testReconnectOnPartialConnect()768     public void testReconnectOnPartialConnect() {
769         BluetoothDevice[] bondedDevices = {mDevice};
770         doReturn(bondedDevices).when(mAdapterService).getBondedDevices();
771 
772         doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any());
773         doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any());
774 
775         // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP
776         // To enable that we need to make sure that HeadsetService returns the device as list of
777         // connected devices
778         ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>();
779         hsConnectedDevices.add(mDevice);
780         when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices);
781         // Also the A2DP should say that its not connected for same device
782         when(mA2dpService.getConnectionState(mDevice)).thenReturn(STATE_DISCONNECTED);
783 
784         // ACL is connected, lets simulate this.
785         when(mAdapterService.getConnectionState(mDevice))
786                 .thenReturn(BluetoothDevice.CONNECTION_STATE_ENCRYPTED_BREDR);
787 
788         // We send a connection successful for one profile since the re-connect *only* works if we
789         // have already connected successfully over one of the profiles
790         updateProfileConnectionStateHelper(
791                 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED);
792 
793         // Check that we get a call to A2DP connect
794         verify(mA2dpService).connect(eq(mDevice));
795     }
796 
797     /**
798      * Test that connectOtherProfile will not trigger any actions when ACL is disconnected. This is
799      * to add robustness to the connection mechanism
800      */
801     @Test
testConnectOtherProfileWhileDeviceIsDisconnected()802     public void testConnectOtherProfileWhileDeviceIsDisconnected() {
803         BluetoothDevice[] bondedDevices = {mDevice};
804         doReturn(bondedDevices).when(mAdapterService).getBondedDevices();
805 
806         doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any());
807         doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any());
808 
809         // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP
810         // To enable that we need to make sure that HeadsetService returns the device as list of
811         // connected devices
812         doReturn(List.of(mDevice)).when(mHeadsetService).getConnectedDevices();
813 
814         // ACL is disconnected just after HEADSET profile got connected and connectOtherProfile
815         // was scheduled. Lets simulate this.
816         when(mAdapterService.getConnectionState(mDevice))
817                 .thenReturn(BluetoothDevice.CONNECTION_STATE_DISCONNECTED);
818 
819         // We send a connection successful for one profile since the re-connect *only* works if we
820         // have already connected successfully over one of the profiles
821         updateProfileConnectionStateHelper(
822                 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED);
823 
824         // Check that there will be no A2DP connect
825         verify(mA2dpService, never()).connect(eq(mDevice));
826     }
827 
828     /**
829      * Test that we will try to re-connect to a profile on a device next time if a previous attempt
830      * failed partially. This will make sure the connection mechanism still works at next try while
831      * the previous attempt is some profiles connected on a device but some not.
832      */
833     @Test
testReconnectOnPartialConnect_PreviousPartialFail()834     public void testReconnectOnPartialConnect_PreviousPartialFail() {
835         InOrder mInOrder = inOrder(mA2dpService);
836         // ACL is connected, lets simulate this.
837         doReturn(STATE_CONNECTED).when(mAdapterService).getConnectionState(mDevice);
838         doReturn(mDevice).when(mDatabaseManager).getMostRecentlyConnectedA2dpDevice();
839         doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any());
840         doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any());
841 
842         // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP
843         // To enable that we need to make sure that HeadsetService returns the device among a list
844         // of connected devices
845         doReturn(List.of(mDevice)).when(mHeadsetService).getConnectedDevices();
846 
847         // We send a connection success event for one profile since the re-connect *only* works if
848         // we have already connected successfully over one of the profiles
849         updateProfileConnectionStateHelper(
850                 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED);
851 
852         // Check that we get a call to A2DP reconnect
853         mInOrder.verify(mA2dpService).connect(mDevice);
854 
855         // We send a connection failure event for the attempted profile, and keep the connected
856         // profile connected.
857         updateProfileConnectionStateHelper(
858                 mDevice, BluetoothProfile.A2DP, STATE_CONNECTING, STATE_DISCONNECTED);
859 
860         // Verify no one changes the priority of the failed profile
861         mInOrder.verify(mA2dpService, never()).setConnectionPolicy(eq(mDevice), anyInt());
862 
863         // Send a connection success event for one profile again without disconnecting all profiles
864         updateProfileConnectionStateHelper(
865                 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED);
866 
867         // Check that we won't get a call to A2DP reconnect again before all profiles disconnected
868         mInOrder.verify(mA2dpService, never()).connect(mDevice);
869 
870         // Send a disconnection event for all connected profiles
871         doReturn(Collections.emptyList()).when(mHeadsetService).getConnectedDevices();
872         updateProfileConnectionStateHelper(
873                 mDevice, BluetoothProfile.HEADSET, STATE_CONNECTED, STATE_DISCONNECTED);
874 
875         // Send a connection success event for one profile again to trigger re-connect
876         doReturn(List.of(mDevice)).when(mHeadsetService).getConnectedDevices();
877         updateProfileConnectionStateHelper(
878                 mDevice, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED);
879 
880         // Check that we get a call to A2DP connect again
881         mInOrder.verify(mA2dpService).connect(mDevice);
882     }
883 
884     /**
885      * Test that when the adapter is turned ON then call auto connect on devices that only has HFP
886      * enabled. NOTE that the assumption is that we have already done the pairing previously and
887      * hence the priorities for the device is already set to AUTO_CONNECT over HFP (as part of post
888      * pairing process).
889      */
890     @Test
891     @DisableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE)
testAutoConnectHfpOnly()892     public void testAutoConnectHfpOnly() {
893 
894         // Return desired values from the mocked object(s)
895         doReturn(false).when(mAdapterService).isQuietModeEnabled();
896 
897         MetadataDatabase mDatabase =
898                 Room.inMemoryDatabaseBuilder(
899                                 InstrumentationRegistry.getInstrumentation().getTargetContext(),
900                                 MetadataDatabase.class)
901                         .build();
902         DatabaseManager db = new DatabaseManager(mAdapterService);
903         doReturn(db).when(mAdapterService).getDatabase();
904         PhonePolicy phonePolicy =
905                 new PhonePolicy(mAdapterService, mLooper.getLooper(), mServiceFactory);
906 
907         db.start(mDatabase);
908         TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper());
909 
910         // Return a device that is HFP only
911 
912         db.setConnection(mDevice, BluetoothProfile.HEADSET);
913         doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(eq(mDevice));
914 
915         // wait for all MSG_UPDATE_DATABASE
916         TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper());
917 
918         phonePolicy.autoConnect();
919 
920         // Check that we got a request to connect over HFP for each device
921         verify(mHeadsetService).connect(eq(mDevice));
922     }
923 
924     @Test
925     @EnableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE)
autoConnect_whenMultiHfp_startConnection()926     public void autoConnect_whenMultiHfp_startConnection() {
927         // Return desired values from the mocked object(s)
928         doReturn(false).when(mAdapterService).isQuietModeEnabled();
929 
930         MetadataDatabase mDatabase =
931                 Room.inMemoryDatabaseBuilder(
932                                 InstrumentationRegistry.getInstrumentation().getTargetContext(),
933                                 MetadataDatabase.class)
934                         .build();
935         DatabaseManager db = new DatabaseManager(mAdapterService);
936         doReturn(db).when(mAdapterService).getDatabase();
937         PhonePolicy phonePolicy =
938                 new PhonePolicy(mAdapterService, mLooper.getLooper(), mServiceFactory);
939 
940         db.start(mDatabase);
941         TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper());
942 
943         List<BluetoothDevice> devices =
944                 List.of(getTestDevice(1), getTestDevice(2), getTestDevice(3));
945 
946         for (BluetoothDevice device : devices) {
947             db.setConnection(device, BluetoothProfile.HEADSET);
948             doReturn(CONNECTION_POLICY_ALLOWED)
949                     .when(mHeadsetService)
950                     .getConnectionPolicy(eq(device));
951         }
952         // wait for all MSG_UPDATE_DATABASE
953         TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper());
954 
955         phonePolicy.autoConnect();
956 
957         // Check that we got a request to connect over HFP for each device
958         for (BluetoothDevice device : devices) {
959             verify(mHeadsetService).connect(eq(device));
960         }
961     }
962 
963     @Test
964     @EnableFlags(Flags.FLAG_AUTO_CONNECT_ON_MULTIPLE_HFP_WHEN_NO_A2DP_DEVICE)
autoConnect_whenMultiHfpAndDisconnection_startConnection()965     public void autoConnect_whenMultiHfpAndDisconnection_startConnection() {
966         // Return desired values from the mocked object(s)
967         doReturn(false).when(mAdapterService).isQuietModeEnabled();
968 
969         MetadataDatabase mDatabase =
970                 Room.inMemoryDatabaseBuilder(
971                                 InstrumentationRegistry.getInstrumentation().getTargetContext(),
972                                 MetadataDatabase.class)
973                         .build();
974         DatabaseManager db = new DatabaseManager(mAdapterService);
975         doReturn(db).when(mAdapterService).getDatabase();
976         PhonePolicy phonePolicy =
977                 new PhonePolicy(mAdapterService, mLooper.getLooper(), mServiceFactory);
978 
979         db.start(mDatabase);
980         TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper());
981 
982         BluetoothDevice deviceToDisconnect = getTestDevice(0);
983         db.setConnection(deviceToDisconnect, BluetoothProfile.HEADSET);
984         doReturn(CONNECTION_POLICY_ALLOWED)
985                 .when(mHeadsetService)
986                 .getConnectionPolicy(eq(deviceToDisconnect));
987 
988         List<BluetoothDevice> devices =
989                 List.of(getTestDevice(1), getTestDevice(2), getTestDevice(3));
990 
991         for (BluetoothDevice device : devices) {
992             db.setConnection(device, BluetoothProfile.HEADSET);
993             doReturn(CONNECTION_POLICY_ALLOWED)
994                     .when(mHeadsetService)
995                     .getConnectionPolicy(eq(device));
996         }
997 
998         db.setDisconnection(deviceToDisconnect, BluetoothProfile.HEADSET);
999 
1000         // wait for all MSG_UPDATE_DATABASE
1001         TestUtils.waitForLooperToFinishScheduledTask(db.getHandlerLooper());
1002 
1003         phonePolicy.autoConnect();
1004 
1005         // Check that we got a request to connect over HFP for each device
1006         for (BluetoothDevice device : devices) {
1007             verify(mHeadsetService).connect(eq(device));
1008         }
1009         // Except for the device that was manually disconnected
1010         verify(mHeadsetService, never()).connect(eq(deviceToDisconnect));
1011     }
1012 
1013     /**
1014      * Test that a second device will auto-connect if there is already one connected device.
1015      *
1016      * <p>Even though we currently only set one device to be auto connect. The consumer of the auto
1017      * connect property works independently so that we will connect to all devices that are in auto
1018      * connect mode.
1019      */
1020     @Test
testAutoConnectMultipleDevices()1021     public void testAutoConnectMultipleDevices() {
1022         final int kMaxTestDevices = 3;
1023         BluetoothDevice[] testDevices = new BluetoothDevice[kMaxTestDevices];
1024         ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>();
1025         ArrayList<BluetoothDevice> a2dpConnectedDevices = new ArrayList<>();
1026 
1027         for (int i = 0; i < kMaxTestDevices; i++) {
1028             BluetoothDevice testDevice = getTestDevice(i);
1029             testDevices[i] = testDevice;
1030 
1031             // ACL is connected, lets simulate this.
1032             when(mAdapterService.getConnectionState(testDevice)).thenReturn(STATE_CONNECTED);
1033 
1034             // Return PRIORITY_AUTO_CONNECT over HFP and A2DP. This would imply that the profiles
1035             // are auto-connectable.
1036             when(mHeadsetService.getConnectionPolicy(testDevice))
1037                     .thenReturn(CONNECTION_POLICY_ALLOWED);
1038             when(mA2dpService.getConnectionPolicy(testDevice))
1039                     .thenReturn(CONNECTION_POLICY_ALLOWED);
1040             // We want to trigger (in CONNECT_OTHER_PROFILES_TIMEOUT) a call to connect A2DP
1041             // To enable that we need to make sure that HeadsetService returns the device as list
1042             // of connected devices.
1043             hsConnectedDevices.add(testDevice);
1044             // Connect A2DP for all devices except the last one
1045             if (i < (kMaxTestDevices - 2)) {
1046                 a2dpConnectedDevices.add(testDevice);
1047             }
1048         }
1049         BluetoothDevice a2dpNotConnectedDevice1 = hsConnectedDevices.get(kMaxTestDevices - 1);
1050         BluetoothDevice a2dpNotConnectedDevice2 = hsConnectedDevices.get(kMaxTestDevices - 2);
1051 
1052         when(mAdapterService.getBondedDevices()).thenReturn(testDevices);
1053         when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices);
1054         when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices);
1055         // Two of the A2DP devices are not connected
1056         when(mA2dpService.getConnectionState(a2dpNotConnectedDevice1))
1057                 .thenReturn(STATE_DISCONNECTED);
1058         when(mA2dpService.getConnectionState(a2dpNotConnectedDevice2))
1059                 .thenReturn(STATE_DISCONNECTED);
1060 
1061         // We send a connection successful for one profile since the re-connect *only* works if we
1062         // have already connected successfully over one of the profiles
1063         updateProfileConnectionStateHelper(
1064                 a2dpNotConnectedDevice1,
1065                 BluetoothProfile.HEADSET,
1066                 STATE_DISCONNECTED,
1067                 STATE_CONNECTED);
1068 
1069         // We send a connection successful for one profile since the re-connect *only* works if we
1070         // have already connected successfully over one of the profiles
1071         updateProfileConnectionStateHelper(
1072                 a2dpNotConnectedDevice2,
1073                 BluetoothProfile.HEADSET,
1074                 STATE_DISCONNECTED,
1075                 STATE_CONNECTED);
1076 
1077         // Check that we get a call to A2DP connect
1078         verify(mA2dpService).connect(eq(a2dpNotConnectedDevice1));
1079         verify(mA2dpService).connect(eq(a2dpNotConnectedDevice2));
1080     }
1081 
1082     /**
1083      * Test that the connection policy of all devices are set as appropriate if there is one
1084      * connected device. - The HFP and A2DP connect priority for connected devices is set to
1085      * BluetoothProfile.PRIORITY_AUTO_CONNECT - The HFP and A2DP connect priority for bonded devices
1086      * is set to CONNECTION_POLICY_ALLOWED
1087      */
1088     @Test
testSetConnectionPolicyMultipleDevices()1089     public void testSetConnectionPolicyMultipleDevices() {
1090         // testDevices[0] - connected for both HFP and A2DP
1091         // testDevices[1] - connected only for HFP - will auto-connect for A2DP
1092         // testDevices[2] - connected only for A2DP - will auto-connect for HFP
1093         // testDevices[3] - not connected
1094         final int kMaxTestDevices = 4;
1095         BluetoothDevice[] testDevices = new BluetoothDevice[kMaxTestDevices];
1096         ArrayList<BluetoothDevice> hsConnectedDevices = new ArrayList<>();
1097         ArrayList<BluetoothDevice> a2dpConnectedDevices = new ArrayList<>();
1098 
1099         for (int i = 0; i < kMaxTestDevices; i++) {
1100             BluetoothDevice testDevice = getTestDevice(i);
1101             testDevices[i] = testDevice;
1102 
1103             // ACL is connected, lets simulate this.
1104             when(mAdapterService.getConnectionState(testDevices[i])).thenReturn(STATE_CONNECTED);
1105 
1106             // Connect HFP and A2DP for each device as appropriate.
1107             // Return PRIORITY_AUTO_CONNECT only for testDevices[0]
1108             if (i == 0) {
1109                 hsConnectedDevices.add(testDevice);
1110                 a2dpConnectedDevices.add(testDevice);
1111                 when(mHeadsetService.getConnectionPolicy(testDevice))
1112                         .thenReturn(CONNECTION_POLICY_ALLOWED);
1113                 when(mA2dpService.getConnectionPolicy(testDevice))
1114                         .thenReturn(CONNECTION_POLICY_ALLOWED);
1115             }
1116             if (i == 1) {
1117                 hsConnectedDevices.add(testDevice);
1118                 when(mHeadsetService.getConnectionPolicy(testDevice))
1119                         .thenReturn(CONNECTION_POLICY_ALLOWED);
1120                 when(mA2dpService.getConnectionPolicy(testDevice))
1121                         .thenReturn(CONNECTION_POLICY_ALLOWED);
1122             }
1123             if (i == 2) {
1124                 a2dpConnectedDevices.add(testDevice);
1125                 when(mHeadsetService.getConnectionPolicy(testDevice))
1126                         .thenReturn(CONNECTION_POLICY_ALLOWED);
1127                 when(mA2dpService.getConnectionPolicy(testDevice))
1128                         .thenReturn(CONNECTION_POLICY_ALLOWED);
1129             }
1130             if (i == 3) {
1131                 // Device not connected
1132                 when(mHeadsetService.getConnectionPolicy(testDevice))
1133                         .thenReturn(CONNECTION_POLICY_ALLOWED);
1134                 when(mA2dpService.getConnectionPolicy(testDevice))
1135                         .thenReturn(CONNECTION_POLICY_ALLOWED);
1136             }
1137         }
1138         when(mAdapterService.getBondedDevices()).thenReturn(testDevices);
1139         when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices);
1140         when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices);
1141         // Some of the devices are not connected
1142         // testDevices[0] - connected for both HFP and A2DP
1143         when(mHeadsetService.getConnectionState(testDevices[0])).thenReturn(STATE_CONNECTED);
1144         when(mA2dpService.getConnectionState(testDevices[0])).thenReturn(STATE_CONNECTED);
1145         // testDevices[1] - connected only for HFP - will auto-connect for A2DP
1146         when(mHeadsetService.getConnectionState(testDevices[1])).thenReturn(STATE_CONNECTED);
1147         when(mA2dpService.getConnectionState(testDevices[1])).thenReturn(STATE_DISCONNECTED);
1148         // testDevices[2] - connected only for A2DP - will auto-connect for HFP
1149         when(mHeadsetService.getConnectionState(testDevices[2])).thenReturn(STATE_DISCONNECTED);
1150         when(mA2dpService.getConnectionState(testDevices[2])).thenReturn(STATE_CONNECTED);
1151         // testDevices[3] - not connected
1152         when(mHeadsetService.getConnectionState(testDevices[3])).thenReturn(STATE_DISCONNECTED);
1153         when(mA2dpService.getConnectionState(testDevices[3])).thenReturn(STATE_DISCONNECTED);
1154 
1155         // Generate connection state changed for HFP for testDevices[1] and trigger
1156         // auto-connect for A2DP.
1157         updateProfileConnectionStateHelper(
1158                 testDevices[1], BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED);
1159 
1160         // Check that we get a call to A2DP connect
1161         verify(mA2dpService).connect(eq(testDevices[1]));
1162 
1163         // testDevices[1] auto-connect completed for A2DP
1164         a2dpConnectedDevices.add(testDevices[1]);
1165         when(mA2dpService.getConnectedDevices()).thenReturn(a2dpConnectedDevices);
1166         when(mA2dpService.getConnectionState(testDevices[1])).thenReturn(STATE_CONNECTED);
1167 
1168         // Check the connect priorities for all devices
1169         // - testDevices[0] - connected for HFP and A2DP: setConnectionPolicy() should not be called
1170         // - testDevices[1] - connection state changed for HFP should no longer trigger auto
1171         //                    connect priority change since it is now triggered by A2DP active
1172         //                    device change intent
1173         // - testDevices[2] - connected for A2DP: setConnectionPolicy() should not be called
1174         // - testDevices[3] - not connected for HFP nor A2DP: setConnectionPolicy() should not be
1175         //                    called
1176         verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[0]), anyInt());
1177         verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[0]), anyInt());
1178         verify(mHeadsetService, never())
1179                 .setConnectionPolicy(
1180                         eq(testDevices[1]), eq(BluetoothProfile.PRIORITY_AUTO_CONNECT));
1181         verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[1]), anyInt());
1182         verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[2]), anyInt());
1183         verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[2]), anyInt());
1184         verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[3]), anyInt());
1185         verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[3]), anyInt());
1186         clearInvocations(mHeadsetService, mA2dpService);
1187 
1188         // Generate connection state changed for A2DP for testDevices[2] and trigger
1189         // auto-connect for HFP.
1190         updateProfileConnectionStateHelper(
1191                 testDevices[2], BluetoothProfile.A2DP, STATE_DISCONNECTED, STATE_CONNECTED);
1192 
1193         // Check that we get a call to HFP connect
1194         verify(mHeadsetService).connect(eq(testDevices[2]));
1195 
1196         // testDevices[2] auto-connect completed for HFP
1197         hsConnectedDevices.add(testDevices[2]);
1198         when(mHeadsetService.getConnectedDevices()).thenReturn(hsConnectedDevices);
1199         when(mHeadsetService.getConnectionState(testDevices[2])).thenReturn(STATE_CONNECTED);
1200 
1201         // Check the connect priorities for all devices
1202         // - testDevices[0] - connected for HFP and A2DP: setConnectionPolicy() should not be called
1203         // - testDevices[1] - connected for HFP and A2DP: setConnectionPolicy() should not be called
1204         // - testDevices[2] - connection state changed for A2DP should no longer trigger auto
1205         //                    connect priority change since it is now triggered by A2DP
1206         //                    active device change intent
1207         // - testDevices[3] - not connected for HFP nor A2DP: setConnectionPolicy() should not be
1208         //                    called
1209         verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[0]), anyInt());
1210         verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[0]), anyInt());
1211         verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[1]), anyInt());
1212         verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[1]), anyInt());
1213         verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[2]), anyInt());
1214         verify(mA2dpService, never())
1215                 .setConnectionPolicy(
1216                         eq(testDevices[2]), eq(BluetoothProfile.PRIORITY_AUTO_CONNECT));
1217         verify(mHeadsetService, never()).setConnectionPolicy(eq(testDevices[3]), anyInt());
1218         verify(mA2dpService, never()).setConnectionPolicy(eq(testDevices[3]), anyInt());
1219         clearInvocations(mHeadsetService, mA2dpService);
1220     }
1221 
1222     /** Test that we will not try to reconnect on a profile if all the connections failed */
1223     @Test
testNoReconnectOnNoConnect()1224     public void testNoReconnectOnNoConnect() {
1225         BluetoothDevice[] bondedDevices = {mDevice};
1226         doReturn(bondedDevices).when(mAdapterService).getBondedDevices();
1227 
1228         doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any());
1229         doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any());
1230 
1231         mPhonePolicy.handleAclConnected(mDevice);
1232         mLooper.dispatchAll();
1233 
1234         // Check that we don't get any calls to reconnect
1235         verify(mA2dpService, never()).connect(any());
1236         verify(mHeadsetService, never()).connect(any());
1237     }
1238 
1239     /**
1240      * Test that we will not try to reconnect on a profile if all the connections failed with
1241      * multiple devices
1242      */
1243     @Test
testNoReconnectOnNoConnect_MultiDevice()1244     public void testNoReconnectOnNoConnect_MultiDevice() {
1245         BluetoothDevice[] bondedDevices = {mDevice, mDevice2};
1246         doReturn(bondedDevices).when(mAdapterService).getBondedDevices();
1247 
1248         doReturn(CONNECTION_POLICY_ALLOWED).when(mHeadsetService).getConnectionPolicy(any());
1249         doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any());
1250 
1251         doReturn(List.of(mDevice2)).when(mHeadsetService).getConnectedDevices();
1252         doReturn(List.of(mDevice2)).when(mA2dpService).getConnectedDevices();
1253 
1254         doReturn(STATE_CONNECTED).when(mA2dpService).getConnectionState(eq(mDevice2));
1255         doReturn(STATE_CONNECTED).when(mHeadsetService).getConnectionState(eq(mDevice2));
1256 
1257         doReturn(STATE_CONNECTED).when(mAdapterService).getConnectionState(any());
1258 
1259         // We send a connection successful for one profile since the re-connect *only* works if we
1260         // have already connected successfully over one of the profiles
1261         mPhonePolicy.handleAclConnected(mDevice);
1262         mLooper.dispatchAll();
1263 
1264         // Check that we don't get any calls to reconnect
1265         verify(mA2dpService, never()).connect(eq(mDevice));
1266         verify(mHeadsetService, never()).connect(eq(mDevice));
1267     }
1268 
1269     /**
1270      * Test that we will try to connect to other profiles of a device if it is partially connected
1271      */
1272     @Test
testReconnectOnPartialConnect_MultiDevice()1273     public void testReconnectOnPartialConnect_MultiDevice() {
1274         BluetoothDevice[] bondedDevices = {mDevice, mDevice2};
1275         doReturn(bondedDevices).when(mAdapterService).getBondedDevices();
1276 
1277         doReturn(List.of(mDevice2)).when(mHeadsetService).getConnectedDevices();
1278 
1279         doReturn(STATE_CONNECTED).when(mAdapterService).getConnectionState(eq(mDevice2));
1280         doReturn(CONNECTION_POLICY_ALLOWED).when(mA2dpService).getConnectionPolicy(any());
1281 
1282         // We send a connection successful for one profile since the re-connect *only* works if we
1283         // have already connected successfully over one of the profiles
1284         updateProfileConnectionStateHelper(
1285                 mDevice2, BluetoothProfile.HEADSET, STATE_DISCONNECTED, STATE_CONNECTED);
1286 
1287         // Check that we do get A2DP call to reconnect, because HEADSET just got connected
1288         verify(mA2dpService).connect(eq(mDevice2));
1289     }
1290 
1291     @Test
discoversUuids_whenNullUuids_policyIsNotChanged()1292     public void discoversUuids_whenNullUuids_policyIsNotChanged() {
1293         mPhonePolicy.onUuidsDiscovered(mDevice, null);
1294 
1295         verify(mHeadsetService, never()).setConnectionPolicy(any(), anyInt());
1296         verify(mA2dpService, never()).setConnectionPolicy(any(), anyInt());
1297     }
1298 
updateProfileConnectionStateHelper( BluetoothDevice device, int profileId, int prevState, int nextState)1299     private void updateProfileConnectionStateHelper(
1300             BluetoothDevice device, int profileId, int prevState, int nextState) {
1301         switch (profileId) {
1302             case BluetoothProfile.A2DP ->
1303                     doReturn(nextState).when(mA2dpService).getConnectionState(device);
1304             case BluetoothProfile.HEADSET ->
1305                     doReturn(nextState).when(mHeadsetService).getConnectionState(device);
1306         }
1307         mPhonePolicy.profileConnectionStateChanged(profileId, device, prevState, nextState);
1308         mLooper.dispatchAll();
1309         mLooper.moveTimeForward(PhonePolicy.CONNECT_OTHER_PROFILES_TIMEOUT.toMillis());
1310         mLooper.dispatchAll();
1311     }
1312 }
1313