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.settings.bluetooth;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.ArgumentMatchers.eq;
23 import static org.mockito.Mockito.mock;
24 import static org.mockito.Mockito.spy;
25 import static org.mockito.Mockito.verify;
26 import static org.mockito.Mockito.when;
27 
28 import android.bluetooth.BluetoothClass;
29 import android.bluetooth.BluetoothDevice;
30 import android.bluetooth.BluetoothProfile;
31 import android.content.Context;
32 import android.platform.test.flag.junit.CheckFlagsRule;
33 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
34 import android.platform.test.flag.junit.SetFlagsRule;
35 import android.sysprop.BluetoothProperties;
36 
37 import androidx.preference.Preference;
38 import androidx.preference.PreferenceCategory;
39 import androidx.preference.SwitchPreferenceCompat;
40 
41 import com.android.settings.flags.Flags;
42 import com.android.settings.testutils.FakeFeatureFactory;
43 import com.android.settings.testutils.shadow.ShadowBluetoothDevice;
44 import com.android.settingslib.R;
45 import com.android.settingslib.bluetooth.A2dpProfile;
46 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
47 import com.android.settingslib.bluetooth.LeAudioProfile;
48 import com.android.settingslib.bluetooth.LocalBluetoothManager;
49 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
50 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
51 import com.android.settingslib.bluetooth.MapProfile;
52 import com.android.settingslib.bluetooth.PbapServerProfile;
53 
54 import com.google.common.collect.ImmutableList;
55 import com.google.common.collect.ImmutableSet;
56 import com.google.common.collect.Lists;
57 
58 import org.junit.Rule;
59 import org.junit.Test;
60 import org.junit.runner.RunWith;
61 import org.mockito.Mock;
62 import org.robolectric.RobolectricTestRunner;
63 import org.robolectric.annotation.Config;
64 
65 import java.util.ArrayList;
66 import java.util.HashMap;
67 import java.util.HashSet;
68 import java.util.List;
69 import java.util.Map;
70 import java.util.Set;
71 
72 @RunWith(RobolectricTestRunner.class)
73 @Config(shadows = ShadowBluetoothDevice.class)
74 public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsControllerTestBase {
75     @Rule
76     public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
77     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
78 
79     private static final String LE_DEVICE_MODEL = "le_audio_headset";
80     private static final String NON_LE_DEVICE_MODEL = "non_le_audio_headset";
81     private BluetoothDetailsProfilesController mController;
82     private List<LocalBluetoothProfile> mConnectableProfiles;
83     private PreferenceCategory mProfiles;
84     private BluetoothFeatureProvider mFeatureProvider;
85 
86     @Mock
87     private LocalBluetoothManager mLocalManager;
88     @Mock
89     private LocalBluetoothProfileManager mProfileManager;
90     @Mock
91     private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager;
92 
93     @Override
setUp()94     public void setUp() {
95         super.setUp();
96 
97         FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
98         mFeatureProvider = fakeFeatureFactory.getBluetoothFeatureProvider();
99 
100         mProfiles = spy(new PreferenceCategory(mContext));
101         when(mProfiles.getPreferenceManager()).thenReturn(mPreferenceManager);
102 
103         mConnectableProfiles = new ArrayList<>();
104         when(mLocalManager.getProfileManager()).thenReturn(mProfileManager);
105         when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedBluetoothDeviceManager);
106         when(mCachedBluetoothDeviceManager.getCachedDevicesCopy())
107                 .thenReturn(ImmutableList.of(mCachedDevice));
108         when(mCachedDevice.getConnectableProfiles()).thenAnswer(invocation ->
109             new ArrayList<>(mConnectableProfiles)
110         );
111 
112         setupDevice(mDeviceConfig);
113         mController = new BluetoothDetailsProfilesController(mContext, mFragment, mLocalManager,
114                 mCachedDevice, mLifecycle);
115         mProfiles.setKey(mController.getPreferenceKey());
116         mController.mProfilesContainer = mProfiles;
117         mScreen.addPreference(mProfiles);
118         BluetoothProperties.le_audio_allow_list(Lists.newArrayList(LE_DEVICE_MODEL));
119     }
120 
121     static class FakeBluetoothProfile implements LocalBluetoothProfile {
122 
123         private Set<BluetoothDevice> mConnectedDevices = new HashSet<>();
124         private Map<BluetoothDevice, Boolean> mPreferred = new HashMap<>();
125         private Context mContext;
126         private int mNameResourceId;
127 
FakeBluetoothProfile(Context context, int nameResourceId)128         private FakeBluetoothProfile(Context context, int nameResourceId) {
129             mContext = context;
130             mNameResourceId = nameResourceId;
131         }
132 
133         @Override
toString()134         public String toString() {
135             return mContext.getString(mNameResourceId);
136         }
137 
138         @Override
accessProfileEnabled()139         public boolean accessProfileEnabled() {
140             return true;
141         }
142 
143         @Override
isAutoConnectable()144         public boolean isAutoConnectable() {
145             return true;
146         }
147 
148         @Override
getConnectionStatus(BluetoothDevice device)149         public int getConnectionStatus(BluetoothDevice device) {
150             if (mConnectedDevices.contains(device)) {
151                 return BluetoothProfile.STATE_CONNECTED;
152             } else {
153                 return BluetoothProfile.STATE_DISCONNECTED;
154             }
155         }
156 
157         @Override
isEnabled(BluetoothDevice device)158         public boolean isEnabled(BluetoothDevice device) {
159             return mPreferred.getOrDefault(device, false);
160         }
161 
162         @Override
getConnectionPolicy(BluetoothDevice device)163         public int getConnectionPolicy(BluetoothDevice device) {
164             return isEnabled(device)
165                     ? BluetoothProfile.CONNECTION_POLICY_ALLOWED
166                     : BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
167         }
168 
169         @Override
setEnabled(BluetoothDevice device, boolean enabled)170         public boolean setEnabled(BluetoothDevice device, boolean enabled) {
171             mPreferred.put(device, enabled);
172             return true;
173         }
174 
175         @Override
isProfileReady()176         public boolean isProfileReady() {
177             return true;
178         }
179 
180         @Override
getProfileId()181         public int getProfileId() {
182             return 0;
183         }
184 
185         @Override
getOrdinal()186         public int getOrdinal() {
187             return 0;
188         }
189 
190         @Override
getNameResource(BluetoothDevice device)191         public int getNameResource(BluetoothDevice device) {
192             return mNameResourceId;
193         }
194 
195         @Override
getSummaryResourceForDevice(BluetoothDevice device)196         public int getSummaryResourceForDevice(BluetoothDevice device) {
197             return Utils.getConnectionStateSummary(getConnectionStatus(device));
198         }
199 
200         @Override
getDrawableResource(BluetoothClass btClass)201         public int getDrawableResource(BluetoothClass btClass) {
202             return 0;
203         }
204     }
205 
206     /**
207      * Creates and adds a mock LocalBluetoothProfile to the list of connectable profiles for the
208      * device.
209      * @param profileNameResId  the resource id for the name used by this profile
210      * @param deviceIsPreferred  whether this profile should start out as enabled for the device
211      */
addFakeProfile(int profileNameResId, boolean deviceIsPreferred)212     private LocalBluetoothProfile addFakeProfile(int profileNameResId,
213             boolean deviceIsPreferred) {
214         LocalBluetoothProfile profile = new FakeBluetoothProfile(mContext, profileNameResId);
215         profile.setEnabled(mDevice, deviceIsPreferred);
216         mConnectableProfiles.add(profile);
217         when(mProfileManager.getProfileByName(eq(profile.toString()))).thenReturn(profile);
218         return profile;
219     }
220 
221     /**
222      * Returns the list of SwitchPreferenceCompat objects added to the screen - there should be one
223      * per Bluetooth profile.
224      */
getProfileSwitches(boolean expectOnlyMConnectable)225     private List<SwitchPreferenceCompat> getProfileSwitches(boolean expectOnlyMConnectable) {
226         if (expectOnlyMConnectable) {
227             assertThat(mConnectableProfiles).isNotEmpty();
228             assertThat(mProfiles.getPreferenceCount() - 1).isEqualTo(mConnectableProfiles.size());
229         }
230         List<SwitchPreferenceCompat> result = new ArrayList<>();
231         for (int i = 0; i < mProfiles.getPreferenceCount(); i++) {
232             final Preference preference = mProfiles.getPreference(i);
233             if (preference instanceof SwitchPreferenceCompat) {
234                 result.add((SwitchPreferenceCompat) preference);
235             }
236         }
237         return result;
238     }
239 
verifyProfileSwitchTitles(List<SwitchPreferenceCompat> switches)240     private void verifyProfileSwitchTitles(List<SwitchPreferenceCompat> switches) {
241         for (int i = 0; i < switches.size(); i++) {
242             String expectedTitle =
243                 mContext.getString(mConnectableProfiles.get(i).getNameResource(mDevice));
244             assertThat(switches.get(i).getTitle()).isEqualTo(expectedTitle);
245         }
246     }
247 
248     @Test
oneProfile()249     public void oneProfile() {
250         addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
251         showScreen(mController);
252         verifyProfileSwitchTitles(getProfileSwitches(true));
253     }
254 
255     @Test
multipleProfiles()256     public void multipleProfiles() {
257         addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
258         addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_headset, false);
259         showScreen(mController);
260         List<SwitchPreferenceCompat> switches = getProfileSwitches(true);
261         verifyProfileSwitchTitles(switches);
262         assertThat(switches.get(0).isChecked()).isTrue();
263         assertThat(switches.get(1).isChecked()).isFalse();
264 
265         // Both switches should be enabled.
266         assertThat(switches.get(0).isEnabled()).isTrue();
267         assertThat(switches.get(1).isEnabled()).isTrue();
268 
269         // Make device busy.
270         when(mCachedDevice.isBusy()).thenReturn(true);
271         mController.onDeviceAttributesChanged();
272 
273         // There should have been no new switches added.
274         assertThat(mProfiles.getPreferenceCount()).isEqualTo(3);
275 
276         // Make sure both switches got disabled.
277         assertThat(switches.get(0).isEnabled()).isFalse();
278         assertThat(switches.get(1).isEnabled()).isFalse();
279     }
280 
281     @Test
disableThenReenableOneProfile()282     public void disableThenReenableOneProfile() {
283         addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
284         addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_headset, true);
285         showScreen(mController);
286         List<SwitchPreferenceCompat> switches = getProfileSwitches(true);
287         SwitchPreferenceCompat pref = switches.get(0);
288 
289         // Clicking the pref should cause the profile to become not-preferred.
290         assertThat(pref.isChecked()).isTrue();
291         pref.performClick();
292         assertThat(pref.isChecked()).isFalse();
293         assertThat(mConnectableProfiles.get(0).isEnabled(mDevice)).isFalse();
294 
295         // Make sure no new preferences were added.
296         assertThat(mProfiles.getPreferenceCount()).isEqualTo(3);
297 
298         // Clicking the pref again should make the profile once again preferred.
299         pref.performClick();
300         assertThat(pref.isChecked()).isTrue();
301         assertThat(mConnectableProfiles.get(0).isEnabled(mDevice)).isTrue();
302 
303         // Make sure we still haven't gotten any new preferences added.
304         assertThat(mProfiles.getPreferenceCount()).isEqualTo(3);
305     }
306 
307     @Test
disconnectedDeviceOneProfile()308     public void disconnectedDeviceOneProfile() {
309         setupDevice(makeDefaultDeviceConfig().setConnected(false).setConnectionSummary(null));
310         addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
311         showScreen(mController);
312         verifyProfileSwitchTitles(getProfileSwitches(true));
313     }
314 
315     @Test
pbapProfileStartsEnabled()316     public void pbapProfileStartsEnabled() {
317         setupDevice(makeDefaultDeviceConfig());
318         mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
319         PbapServerProfile psp = mock(PbapServerProfile.class);
320         when(psp.getNameResource(mDevice))
321                 .thenReturn(com.android.settingslib.R.string.bluetooth_profile_pbap);
322         when(psp.getSummaryResourceForDevice(mDevice))
323                 .thenReturn(R.string.bluetooth_profile_pbap_summary);
324         when(psp.toString()).thenReturn(PbapServerProfile.NAME);
325         when(psp.isProfileReady()).thenReturn(true);
326         when(mProfileManager.getPbapProfile()).thenReturn(psp);
327 
328         showScreen(mController);
329         List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
330         assertThat(switches.size()).isEqualTo(1);
331         SwitchPreferenceCompat pref = switches.get(0);
332         assertThat(pref.getTitle()).isEqualTo(
333                 mContext.getString(com.android.settingslib.R.string.bluetooth_profile_pbap));
334         assertThat(pref.isChecked()).isTrue();
335 
336         pref.performClick();
337         assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
338         assertThat(mDevice.getPhonebookAccessPermission())
339                 .isEqualTo(BluetoothDevice.ACCESS_REJECTED);
340     }
341 
342     @Test
pbapProfileStartsDisabled()343     public void pbapProfileStartsDisabled() {
344         setupDevice(makeDefaultDeviceConfig());
345         mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
346         PbapServerProfile psp = mock(PbapServerProfile.class);
347         when(psp.getNameResource(mDevice))
348                 .thenReturn(com.android.settingslib.R.string.bluetooth_profile_pbap);
349         when(psp.getSummaryResourceForDevice(mDevice))
350                 .thenReturn(R.string.bluetooth_profile_pbap_summary);
351         when(psp.toString()).thenReturn(PbapServerProfile.NAME);
352         when(psp.isProfileReady()).thenReturn(true);
353         when(mProfileManager.getPbapProfile()).thenReturn(psp);
354 
355         showScreen(mController);
356         List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
357         assertThat(switches.size()).isEqualTo(1);
358         SwitchPreferenceCompat pref = switches.get(0);
359         assertThat(pref.getTitle()).isEqualTo(
360                 mContext.getString(com.android.settingslib.R.string.bluetooth_profile_pbap));
361         assertThat(pref.isChecked()).isFalse();
362 
363         pref.performClick();
364         assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
365         assertThat(mDevice.getPhonebookAccessPermission())
366                 .isEqualTo(BluetoothDevice.ACCESS_ALLOWED);
367     }
368 
369     @Test
mapProfile()370     public void mapProfile() {
371         setupDevice(makeDefaultDeviceConfig());
372         MapProfile mapProfile = mock(MapProfile.class);
373         when(mapProfile.getNameResource(mDevice))
374                 .thenReturn(com.android.settingslib.R.string.bluetooth_profile_map);
375         when(mapProfile.isProfileReady()).thenReturn(true);
376         when(mProfileManager.getMapProfile()).thenReturn(mapProfile);
377         when(mProfileManager.getProfileByName(eq(mapProfile.toString()))).thenReturn(mapProfile);
378         mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
379         showScreen(mController);
380         List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
381         assertThat(switches.size()).isEqualTo(1);
382         SwitchPreferenceCompat pref = switches.get(0);
383         assertThat(pref.getTitle()).isEqualTo(
384                 mContext.getString(com.android.settingslib.R.string.bluetooth_profile_map));
385         assertThat(pref.isChecked()).isFalse();
386 
387         pref.performClick();
388         assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
389         assertThat(mDevice.getMessageAccessPermission()).isEqualTo(BluetoothDevice.ACCESS_ALLOWED);
390     }
391 
addMockA2dpProfile(boolean preferred, boolean supportsHighQualityAudio, boolean highQualityAudioEnabled)392     private A2dpProfile addMockA2dpProfile(boolean preferred, boolean supportsHighQualityAudio,
393             boolean highQualityAudioEnabled) {
394         A2dpProfile profile = mock(A2dpProfile.class);
395         when(mProfileManager.getProfileByName(eq(profile.toString()))).thenReturn(profile);
396         when(profile.getNameResource(mDevice))
397                 .thenReturn(com.android.settingslib.R.string.bluetooth_profile_a2dp);
398         when(profile.getHighQualityAudioOptionLabel(mDevice)).thenReturn(
399                 mContext.getString(com.android.settingslib.R
400                         .string.bluetooth_profile_a2dp_high_quality_unknown_codec));
401         when(profile.supportsHighQualityAudio(mDevice)).thenReturn(supportsHighQualityAudio);
402         when(profile.isHighQualityAudioEnabled(mDevice)).thenReturn(highQualityAudioEnabled);
403         when(profile.isEnabled(mDevice)).thenReturn(preferred);
404         when(profile.isProfileReady()).thenReturn(true);
405         mConnectableProfiles.add(profile);
406         return profile;
407     }
408 
getHighQualityAudioPref()409     private SwitchPreferenceCompat getHighQualityAudioPref() {
410         return (SwitchPreferenceCompat) mScreen.findPreference(
411                 BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
412     }
413 
414     @Test
highQualityAudio_prefIsPresentWhenSupported()415     public void highQualityAudio_prefIsPresentWhenSupported() {
416         setupDevice(makeDefaultDeviceConfig());
417         addMockA2dpProfile(true, true, true);
418         showScreen(mController);
419         SwitchPreferenceCompat pref = getHighQualityAudioPref();
420         assertThat(pref.getKey()).isEqualTo(
421                 BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
422 
423         // Make sure the preference works when clicked on.
424         pref.performClick();
425         A2dpProfile profile = (A2dpProfile) mConnectableProfiles.get(0);
426         verify(profile).setHighQualityAudioEnabled(mDevice, false);
427         pref.performClick();
428         verify(profile).setHighQualityAudioEnabled(mDevice, true);
429     }
430 
431     @Test
highQualityAudio_prefIsAbsentWhenNotSupported()432     public void highQualityAudio_prefIsAbsentWhenNotSupported() {
433         setupDevice(makeDefaultDeviceConfig());
434         addMockA2dpProfile(true, false, false);
435         showScreen(mController);
436         assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
437         SwitchPreferenceCompat pref = (SwitchPreferenceCompat) mProfiles.getPreference(0);
438         assertThat(pref.getKey())
439             .isNotEqualTo(BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
440         assertThat(pref.getTitle()).isEqualTo(
441                 mContext.getString(com.android.settingslib.R.string.bluetooth_profile_a2dp));
442     }
443 
444     @Test
highQualityAudio_busyDeviceDisablesSwitch()445     public void highQualityAudio_busyDeviceDisablesSwitch() {
446         setupDevice(makeDefaultDeviceConfig());
447         addMockA2dpProfile(true, true, true);
448         when(mCachedDevice.isBusy()).thenReturn(true);
449         showScreen(mController);
450         SwitchPreferenceCompat pref = getHighQualityAudioPref();
451         assertThat(pref.isEnabled()).isFalse();
452     }
453 
454     @Test
highQualityAudio_mediaAudioDisabledAndReEnabled()455     public void highQualityAudio_mediaAudioDisabledAndReEnabled() {
456         setupDevice(makeDefaultDeviceConfig());
457         A2dpProfile audioProfile = addMockA2dpProfile(true, true, true);
458         showScreen(mController);
459         assertThat(mProfiles.getPreferenceCount()).isEqualTo(3);
460 
461         // Disabling media audio should cause the high quality audio switch to disappear, but not
462         // the regular audio one.
463         SwitchPreferenceCompat audioPref =
464                 (SwitchPreferenceCompat) mScreen.findPreference(audioProfile.toString());
465         audioPref.performClick();
466         verify(audioProfile).setEnabled(mDevice, false);
467         when(audioProfile.isEnabled(mDevice)).thenReturn(false);
468         mController.onDeviceAttributesChanged();
469         assertThat(audioPref.isVisible()).isTrue();
470         SwitchPreferenceCompat highQualityAudioPref = getHighQualityAudioPref();
471         assertThat(highQualityAudioPref.isVisible()).isFalse();
472 
473         // And re-enabling media audio should make high quality switch to reappear.
474         audioPref.performClick();
475         verify(audioProfile).setEnabled(mDevice, true);
476         when(audioProfile.isEnabled(mDevice)).thenReturn(true);
477         mController.onDeviceAttributesChanged();
478         highQualityAudioPref = getHighQualityAudioPref();
479         assertThat(highQualityAudioPref.isVisible()).isTrue();
480     }
481 
482     @Test
highQualityAudio_mediaAudioStartsDisabled()483     public void highQualityAudio_mediaAudioStartsDisabled() {
484         setupDevice(makeDefaultDeviceConfig());
485         A2dpProfile audioProfile = addMockA2dpProfile(false, true, true);
486         showScreen(mController);
487         SwitchPreferenceCompat audioPref = mScreen.findPreference(audioProfile.toString());
488         SwitchPreferenceCompat highQualityAudioPref = getHighQualityAudioPref();
489         assertThat(audioPref).isNotNull();
490         assertThat(audioPref.isChecked()).isFalse();
491         assertThat(highQualityAudioPref).isNotNull();
492         assertThat(highQualityAudioPref.isVisible()).isFalse();
493     }
494 
495     @Test
onResume_addServiceListener()496     public void onResume_addServiceListener() {
497         mController.onResume();
498 
499         verify(mProfileManager).addServiceListener(mController);
500     }
501 
502     @Test
onPause_removeServiceListener()503     public void onPause_removeServiceListener() {
504         mController.onPause();
505 
506         verify(mProfileManager).removeServiceListener(mController);
507     }
508 
509     @Test
isDeviceInAllowList_returnTrue()510     public void isDeviceInAllowList_returnTrue() {
511         assertThat(mController.isModelNameInAllowList(LE_DEVICE_MODEL)).isTrue();
512     }
513 
514     @Test
isDeviceInAllowList_returnFalse()515     public void isDeviceInAllowList_returnFalse() {
516         assertThat(mController.isModelNameInAllowList(null)).isFalse();
517         assertThat(mController.isModelNameInAllowList(NON_LE_DEVICE_MODEL)).isFalse();
518     }
519 
520     @Test
prefKeyInBlockingList_hideToggle()521     public void prefKeyInBlockingList_hideToggle() {
522         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_BLUETOOTH_PROFILE_TOGGLE_VISIBILITY_CHECKER);
523         setupDevice(makeDefaultDeviceConfig());
524 
525         LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
526         when(leAudioProfile.getNameResource(mDevice))
527                 .thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
528         when(leAudioProfile.isProfileReady()).thenReturn(true);
529         when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
530         when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
531         when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
532                 .thenReturn(ImmutableSet.of("LE_AUDIO"));
533         mConnectableProfiles.add(leAudioProfile);
534 
535         showScreen(mController);
536 
537         List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
538         assertThat(switches.get(0).isVisible()).isFalse();
539     }
540 
541     @Test
prefKeyNotInBlockingList_showToggle()542     public void prefKeyNotInBlockingList_showToggle() {
543         mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_BLUETOOTH_PROFILE_TOGGLE_VISIBILITY_CHECKER);
544         setupDevice(makeDefaultDeviceConfig());
545 
546         LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
547         when(leAudioProfile.getNameResource(mDevice))
548                 .thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
549         when(leAudioProfile.isProfileReady()).thenReturn(true);
550         when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
551         when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
552         when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
553                 .thenReturn(ImmutableSet.of("A2DP"));
554         mConnectableProfiles.add(leAudioProfile);
555 
556         showScreen(mController);
557 
558         List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
559         assertThat(switches.get(0).isVisible()).isTrue();
560     }
561 
562     @Test
classicAudioDeviceWithLeAudio_showLeAudioToggle()563     public void classicAudioDeviceWithLeAudio_showLeAudioToggle() {
564         mSetFlagsRule.enableFlags(Flags.FLAG_HIDE_LE_AUDIO_TOGGLE_FOR_LE_AUDIO_ONLY_DEVICE);
565         setupDevice(makeDefaultDeviceConfig());
566 
567         LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
568         when(leAudioProfile.getNameResource(mDevice))
569                 .thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
570         when(leAudioProfile.isProfileReady()).thenReturn(true);
571         when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
572         when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
573         mConnectableProfiles.add(leAudioProfile);
574         when(mCachedDevice.getProfiles())
575                 .thenAnswer(
576                         invocation ->
577                                 ImmutableList.of(
578                                         leAudioProfile, addMockA2dpProfile(false, false, false)));
579 
580         showScreen(mController);
581 
582         List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
583         assertThat(switches.get(0).isVisible()).isTrue();
584     }
585 
586     @Test
leAudioOnlyDevice_hideLeAudioToggle()587     public void leAudioOnlyDevice_hideLeAudioToggle() {
588         mSetFlagsRule.enableFlags(Flags.FLAG_HIDE_LE_AUDIO_TOGGLE_FOR_LE_AUDIO_ONLY_DEVICE);
589         setupDevice(makeDefaultDeviceConfig());
590 
591         LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
592         when(leAudioProfile.getNameResource(mDevice))
593                 .thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
594         when(leAudioProfile.isProfileReady()).thenReturn(true);
595         when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
596         when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
597         mConnectableProfiles.add(leAudioProfile);
598         when(mCachedDevice.getProfiles())
599                 .thenAnswer(invocation -> ImmutableList.of(leAudioProfile));
600 
601         showScreen(mController);
602 
603         List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
604         assertThat(switches.get(0).isVisible()).isFalse();
605     }
606 }
607