| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.settingslib.bluetooth; |
| |
| import android.bluetooth.BluetoothA2dp; |
| import android.bluetooth.BluetoothCodecConfig; |
| import android.bluetooth.BluetoothCodecStatus; |
| import android.bluetooth.BluetoothDevice; |
| import android.bluetooth.BluetoothProfile; |
| import android.content.Context; |
| import android.content.res.Resources; |
| |
| import com.android.settingslib.R; |
| import com.android.settingslib.TestConfig; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.mockito.invocation.InvocationOnMock; |
| import org.mockito.stubbing.Answer; |
| import org.robolectric.RobolectricTestRunner; |
| import org.robolectric.annotation.Config; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.mockito.Matchers.any; |
| import static org.mockito.Matchers.eq; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.when; |
| |
| @RunWith(RobolectricTestRunner.class) |
| @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) |
| public class A2dpProfileTest { |
| |
| @Mock Context mContext; |
| @Mock LocalBluetoothAdapter mAdapter; |
| @Mock CachedBluetoothDeviceManager mDeviceManager; |
| @Mock LocalBluetoothProfileManager mProfileManager; |
| @Mock BluetoothDevice mDevice; |
| @Mock BluetoothA2dp mBluetoothA2dp; |
| @Mock BluetoothA2dpWrapper mBluetoothA2dpWrapper; |
| BluetoothProfile.ServiceListener mServiceListener; |
| |
| A2dpProfile mProfile; |
| |
| @Before |
| public void setUp() { |
| MockitoAnnotations.initMocks(this); |
| |
| // Capture the A2dpServiceListener our A2dpProfile will pass during its constructor, so that |
| // we can call its onServiceConnected method and get it to use our mock BluetoothA2dp |
| // object. |
| doAnswer((invocation) -> { |
| mServiceListener = (BluetoothProfile.ServiceListener) invocation.getArguments()[1]; |
| return null; |
| }).when(mAdapter).getProfileProxy(any(Context.class), any(), eq(BluetoothProfile.A2DP)); |
| |
| mProfile = new A2dpProfile(mContext, mAdapter, mDeviceManager, mProfileManager); |
| mProfile.setWrapperFactory((service) -> { return mBluetoothA2dpWrapper; }); |
| mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp); |
| } |
| |
| @Test |
| public void supportsHighQualityAudio() { |
| when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED); |
| assertThat(mProfile.supportsHighQualityAudio(mDevice)).isTrue(); |
| |
| when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED); |
| assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse(); |
| |
| when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_SUPPORT_UNKNOWN); |
| assertThat(mProfile.supportsHighQualityAudio(mDevice)).isFalse(); |
| } |
| |
| @Test |
| public void isHighQualityAudioEnabled() { |
| when(mBluetoothA2dpWrapper.getOptionalCodecsEnabled(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED); |
| assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue(); |
| |
| when(mBluetoothA2dpWrapper.getOptionalCodecsEnabled(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED); |
| assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse(); |
| |
| // If we don't have a stored pref for whether optional codecs should be enabled or not, |
| // then isHighQualityAudioEnabled() should return true or false based on whether optional |
| // codecs are supported. If the device is connected then we should ask it directly, but if |
| // the device isn't connected then rely on the stored pref about such support. |
| when(mBluetoothA2dpWrapper.getOptionalCodecsEnabled(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN); |
| when(mBluetoothA2dp.getConnectionState(any())).thenReturn( |
| BluetoothProfile.STATE_DISCONNECTED); |
| |
| when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED); |
| assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse(); |
| |
| when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED); |
| assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue(); |
| |
| when(mBluetoothA2dp.getConnectionState(any())).thenReturn( |
| BluetoothProfile.STATE_CONNECTED); |
| BluetoothCodecStatus status = mock(BluetoothCodecStatus.class); |
| when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status); |
| BluetoothCodecConfig config = mock(BluetoothCodecConfig.class); |
| when(status.getCodecConfig()).thenReturn(config); |
| when(config.isMandatoryCodec()).thenReturn(false); |
| assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isTrue(); |
| when(config.isMandatoryCodec()).thenReturn(true); |
| assertThat(mProfile.isHighQualityAudioEnabled(mDevice)).isFalse(); |
| } |
| |
| // Strings to use in fake resource lookups. |
| private static String KNOWN_CODEC_LABEL = "Use high quality audio: %1$s"; |
| private static String UNKNOWN_CODEC_LABEL = "Use high quality audio"; |
| private static String[] CODEC_NAMES = |
| new String[] { "Default", "SBC", "AAC", "aptX", "aptX HD", "LDAC" }; |
| |
| /** |
| * Helper for setting up several tests of getHighQualityAudioOptionLabel |
| */ |
| private void setupLabelTest() { |
| // SettingsLib doesn't have string resource lookup working for robotests, so fake our own |
| // string loading. |
| when(mContext.getString(eq(R.string.bluetooth_profile_a2dp_high_quality), |
| any(String.class))).thenAnswer((invocation) -> { |
| return String.format(KNOWN_CODEC_LABEL, invocation.getArguments()[1]); |
| }); |
| when(mContext.getString(eq(R.string.bluetooth_profile_a2dp_high_quality_unknown_codec))) |
| .thenReturn(UNKNOWN_CODEC_LABEL); |
| |
| final Resources res = mock(Resources.class); |
| when(mContext.getResources()).thenReturn(res); |
| when(res.getStringArray(eq(R.array.bluetooth_a2dp_codec_titles))) |
| .thenReturn(CODEC_NAMES); |
| |
| // Most tests want to simulate optional codecs being supported by the device, so do that |
| // by default here. |
| when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED); |
| } |
| |
| @Test |
| public void getLableCodecsNotSupported() { |
| setupLabelTest(); |
| when(mBluetoothA2dpWrapper.supportsOptionalCodecs(any())).thenReturn( |
| BluetoothA2dp.OPTIONAL_CODECS_NOT_SUPPORTED); |
| assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL); |
| } |
| |
| @Test |
| public void getLabelDeviceDisconnected() { |
| setupLabelTest(); |
| when(mBluetoothA2dp.getConnectionState(any())).thenReturn( |
| BluetoothProfile.STATE_DISCONNECTED); |
| assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL); |
| } |
| |
| @Test |
| public void getLabelDeviceConnectedButNotHighQualityCodec() { |
| setupLabelTest(); |
| when(mBluetoothA2dp.getConnectionState(any())).thenReturn( |
| BluetoothProfile.STATE_CONNECTED); |
| BluetoothCodecStatus status = mock(BluetoothCodecStatus.class); |
| BluetoothCodecConfig config = mock(BluetoothCodecConfig.class); |
| BluetoothCodecConfig[] configs = {config}; |
| when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status); |
| when(status.getCodecsSelectableCapabilities()).thenReturn(configs); |
| |
| when(config.isMandatoryCodec()).thenReturn(true); |
| assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo(UNKNOWN_CODEC_LABEL); |
| } |
| |
| @Test |
| public void getLabelDeviceConnectedWithHighQualityCodec() { |
| setupLabelTest(); |
| when(mBluetoothA2dp.getConnectionState(any())).thenReturn( |
| BluetoothProfile.STATE_CONNECTED); |
| BluetoothCodecStatus status = mock(BluetoothCodecStatus.class); |
| BluetoothCodecConfig config = mock(BluetoothCodecConfig.class); |
| BluetoothCodecConfig[] configs = {config}; |
| when(mBluetoothA2dpWrapper.getCodecStatus(mDevice)).thenReturn(status); |
| when(status.getCodecsSelectableCapabilities()).thenReturn(configs); |
| |
| when(config.isMandatoryCodec()).thenReturn(false); |
| when(config.getCodecType()).thenReturn(4); |
| when(config.getCodecName()).thenReturn("LDAC"); |
| assertThat(mProfile.getHighQualityAudioOptionLabel(mDevice)).isEqualTo( |
| String.format(KNOWN_CODEC_LABEL, config.getCodecName())); |
| } |
| } |