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