Loading packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +83 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.settingslib.bluetooth; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; Loading @@ -26,16 +28,22 @@ import android.content.Context; import android.os.ParcelUuid; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.R; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public final class A2dpProfile implements LocalBluetoothProfile { public class A2dpProfile implements LocalBluetoothProfile { private static final String TAG = "A2dpProfile"; private static boolean V = false; private Context mContext; private BluetoothA2dp mService; BluetoothA2dpWrapper.Factory mWrapperFactory; private BluetoothA2dpWrapper mServiceWrapper; private boolean mIsProfileReady; private final LocalBluetoothAdapter mLocalAdapter; Loading @@ -59,6 +67,7 @@ public final class A2dpProfile implements LocalBluetoothProfile { public void onServiceConnected(int profile, BluetoothProfile proxy) { if (V) Log.d(TAG,"Bluetooth service connected"); mService = (BluetoothA2dp) proxy; mServiceWrapper = mWrapperFactory.getInstance(mService); // We just bound to the service, so refresh the UI for any connected A2DP devices. List<BluetoothDevice> deviceList = mService.getConnectedDevices(); while (!deviceList.isEmpty()) { Loading Loading @@ -88,11 +97,18 @@ public final class A2dpProfile implements LocalBluetoothProfile { A2dpProfile(Context context, LocalBluetoothAdapter adapter, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager) { mContext = context; mLocalAdapter = adapter; mDeviceManager = deviceManager; mProfileManager = profileManager; mLocalAdapter.getProfileProxy(context, new A2dpServiceListener(), BluetoothProfile.A2DP); mWrapperFactory = new BluetoothA2dpWrapperImpl.Factory(); } @VisibleForTesting void setWrapperFactory(BluetoothA2dpWrapper.Factory factory) { mWrapperFactory = factory; } public boolean isConnectable() { Loading Loading @@ -173,6 +189,72 @@ public final class A2dpProfile implements LocalBluetoothProfile { return false; } public boolean supportsHighQualityAudio(BluetoothDevice device) { int support = mServiceWrapper.supportsOptionalCodecs(device); return support == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED; } public boolean isHighQualityAudioEnabled(BluetoothDevice device) { int enabled = mServiceWrapper.getOptionalCodecsEnabled(device); if (enabled != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN) { return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED; } else if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED && supportsHighQualityAudio(device)) { // Since we don't have a stored preference and the device isn't connected, just return // true since the default behavior when the device gets connected in the future would be // to have optional codecs enabled. return true; } BluetoothCodecConfig codecConfig = null; if (mServiceWrapper.getCodecStatus() != null) { codecConfig = mServiceWrapper.getCodecStatus().getCodecConfig(); } if (codecConfig != null) { return !codecConfig.isMandatoryCodec(); } else { return false; } } public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) { int prefValue = enabled ? BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED : BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED; mServiceWrapper.setOptionalCodecsEnabled(device, prefValue); if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) { return; } if (enabled) { mService.enableOptionalCodecs(); } else { mService.disableOptionalCodecs(); } } public String getHighQualityAudioOptionLabel(BluetoothDevice device) { int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec; if (!supportsHighQualityAudio(device) || getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) { return mContext.getString(unknownCodecId); } // We want to get the highest priority codec, since that's the one that will be used with // this device, and see if it is high-quality (ie non-mandatory). BluetoothCodecConfig[] selectable = null; if (mServiceWrapper.getCodecStatus() != null) { selectable = mServiceWrapper.getCodecStatus().getCodecsSelectableCapabilities(); // To get the highest priority, we sort in reverse. Arrays.sort(selectable, (a, b) -> { return b.getCodecPriority() - a.getCodecPriority(); }); } if (selectable == null || selectable.length < 1 || selectable[0].isMandatoryCodec()) { return mContext.getString(unknownCodecId); } return mContext.getString(R.string.bluetooth_profile_a2dp_high_quality, selectable[0].getCodecName()); } public String toString() { return NAME; } Loading packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java 0 → 100644 +58 −0 Original line number Diff line number Diff line /* * 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.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; /** * This interface replicates some methods of android.bluetooth.BluetoothA2dp that are new and not * yet available in our current version of Robolectric. It provides a thin wrapper to call the real * methods in production and a mock in tests. */ public interface BluetoothA2dpWrapper { static interface Factory { BluetoothA2dpWrapper getInstance(BluetoothA2dp service); } /** * @return the real {@code BluetoothA2dp} object */ BluetoothA2dp getService(); /** * Wraps {@code BluetoothA2dp.getCodecStatus} */ public BluetoothCodecStatus getCodecStatus(); /** * Wraps {@code BluetoothA2dp.supportsOptionalCodecs} */ int supportsOptionalCodecs(BluetoothDevice device); /** * Wraps {@code BluetoothA2dp.getOptionalCodecsEnabled} */ int getOptionalCodecsEnabled(BluetoothDevice device); /** * Wraps {@code BluetoothA2dp.setOptionalCodecsEnabled} */ void setOptionalCodecsEnabled(BluetoothDevice device, int value); } packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java 0 → 100644 +62 −0 Original line number Diff line number Diff line /* * 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.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; public class BluetoothA2dpWrapperImpl implements BluetoothA2dpWrapper { public static class Factory implements BluetoothA2dpWrapper.Factory { @Override public BluetoothA2dpWrapper getInstance(BluetoothA2dp service) { return new BluetoothA2dpWrapperImpl(service); } } private BluetoothA2dp mService; public BluetoothA2dpWrapperImpl(BluetoothA2dp service) { mService = service; } @Override public BluetoothA2dp getService() { return mService; } @Override public BluetoothCodecStatus getCodecStatus() { return mService.getCodecStatus(); } @Override public int supportsOptionalCodecs(BluetoothDevice device) { return mService.supportsOptionalCodecs(device); } @Override public int getOptionalCodecsEnabled(BluetoothDevice device) { return mService.getOptionalCodecsEnabled(device); } @Override public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { mService.setOptionalCodecsEnabled(device, value); } } packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +1 −1 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ import java.util.Map; * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile * objects for the available Bluetooth profiles. */ public final class LocalBluetoothProfileManager { public class LocalBluetoothProfileManager { private static final String TAG = "LocalBluetoothProfileManager"; private static final boolean DEBUG = Utils.D; /** Singleton instance. */ Loading packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java 0 → 100644 +25 −0 Original line number Diff line number Diff line /* * 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 android.bluetooth; /** * A placeholder class to prevent ClassNotFound exceptions caused by lack of visibility. */ public class BluetoothCodecConfig { public boolean isMandatoryCodec() { return true; } public String getCodecName() { return null;} } Loading
packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java +83 −1 Original line number Diff line number Diff line Loading @@ -19,6 +19,8 @@ package com.android.settingslib.bluetooth; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothCodecConfig; import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; Loading @@ -26,16 +28,22 @@ import android.content.Context; import android.os.ParcelUuid; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.settingslib.R; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public final class A2dpProfile implements LocalBluetoothProfile { public class A2dpProfile implements LocalBluetoothProfile { private static final String TAG = "A2dpProfile"; private static boolean V = false; private Context mContext; private BluetoothA2dp mService; BluetoothA2dpWrapper.Factory mWrapperFactory; private BluetoothA2dpWrapper mServiceWrapper; private boolean mIsProfileReady; private final LocalBluetoothAdapter mLocalAdapter; Loading @@ -59,6 +67,7 @@ public final class A2dpProfile implements LocalBluetoothProfile { public void onServiceConnected(int profile, BluetoothProfile proxy) { if (V) Log.d(TAG,"Bluetooth service connected"); mService = (BluetoothA2dp) proxy; mServiceWrapper = mWrapperFactory.getInstance(mService); // We just bound to the service, so refresh the UI for any connected A2DP devices. List<BluetoothDevice> deviceList = mService.getConnectedDevices(); while (!deviceList.isEmpty()) { Loading Loading @@ -88,11 +97,18 @@ public final class A2dpProfile implements LocalBluetoothProfile { A2dpProfile(Context context, LocalBluetoothAdapter adapter, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager) { mContext = context; mLocalAdapter = adapter; mDeviceManager = deviceManager; mProfileManager = profileManager; mLocalAdapter.getProfileProxy(context, new A2dpServiceListener(), BluetoothProfile.A2DP); mWrapperFactory = new BluetoothA2dpWrapperImpl.Factory(); } @VisibleForTesting void setWrapperFactory(BluetoothA2dpWrapper.Factory factory) { mWrapperFactory = factory; } public boolean isConnectable() { Loading Loading @@ -173,6 +189,72 @@ public final class A2dpProfile implements LocalBluetoothProfile { return false; } public boolean supportsHighQualityAudio(BluetoothDevice device) { int support = mServiceWrapper.supportsOptionalCodecs(device); return support == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED; } public boolean isHighQualityAudioEnabled(BluetoothDevice device) { int enabled = mServiceWrapper.getOptionalCodecsEnabled(device); if (enabled != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN) { return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED; } else if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED && supportsHighQualityAudio(device)) { // Since we don't have a stored preference and the device isn't connected, just return // true since the default behavior when the device gets connected in the future would be // to have optional codecs enabled. return true; } BluetoothCodecConfig codecConfig = null; if (mServiceWrapper.getCodecStatus() != null) { codecConfig = mServiceWrapper.getCodecStatus().getCodecConfig(); } if (codecConfig != null) { return !codecConfig.isMandatoryCodec(); } else { return false; } } public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) { int prefValue = enabled ? BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED : BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED; mServiceWrapper.setOptionalCodecsEnabled(device, prefValue); if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) { return; } if (enabled) { mService.enableOptionalCodecs(); } else { mService.disableOptionalCodecs(); } } public String getHighQualityAudioOptionLabel(BluetoothDevice device) { int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec; if (!supportsHighQualityAudio(device) || getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) { return mContext.getString(unknownCodecId); } // We want to get the highest priority codec, since that's the one that will be used with // this device, and see if it is high-quality (ie non-mandatory). BluetoothCodecConfig[] selectable = null; if (mServiceWrapper.getCodecStatus() != null) { selectable = mServiceWrapper.getCodecStatus().getCodecsSelectableCapabilities(); // To get the highest priority, we sort in reverse. Arrays.sort(selectable, (a, b) -> { return b.getCodecPriority() - a.getCodecPriority(); }); } if (selectable == null || selectable.length < 1 || selectable[0].isMandatoryCodec()) { return mContext.getString(unknownCodecId); } return mContext.getString(R.string.bluetooth_profile_a2dp_high_quality, selectable[0].getCodecName()); } public String toString() { return NAME; } Loading
packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapper.java 0 → 100644 +58 −0 Original line number Diff line number Diff line /* * 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.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; /** * This interface replicates some methods of android.bluetooth.BluetoothA2dp that are new and not * yet available in our current version of Robolectric. It provides a thin wrapper to call the real * methods in production and a mock in tests. */ public interface BluetoothA2dpWrapper { static interface Factory { BluetoothA2dpWrapper getInstance(BluetoothA2dp service); } /** * @return the real {@code BluetoothA2dp} object */ BluetoothA2dp getService(); /** * Wraps {@code BluetoothA2dp.getCodecStatus} */ public BluetoothCodecStatus getCodecStatus(); /** * Wraps {@code BluetoothA2dp.supportsOptionalCodecs} */ int supportsOptionalCodecs(BluetoothDevice device); /** * Wraps {@code BluetoothA2dp.getOptionalCodecsEnabled} */ int getOptionalCodecsEnabled(BluetoothDevice device); /** * Wraps {@code BluetoothA2dp.setOptionalCodecsEnabled} */ void setOptionalCodecsEnabled(BluetoothDevice device, int value); }
packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothA2dpWrapperImpl.java 0 → 100644 +62 −0 Original line number Diff line number Diff line /* * 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.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; public class BluetoothA2dpWrapperImpl implements BluetoothA2dpWrapper { public static class Factory implements BluetoothA2dpWrapper.Factory { @Override public BluetoothA2dpWrapper getInstance(BluetoothA2dp service) { return new BluetoothA2dpWrapperImpl(service); } } private BluetoothA2dp mService; public BluetoothA2dpWrapperImpl(BluetoothA2dp service) { mService = service; } @Override public BluetoothA2dp getService() { return mService; } @Override public BluetoothCodecStatus getCodecStatus() { return mService.getCodecStatus(); } @Override public int supportsOptionalCodecs(BluetoothDevice device) { return mService.supportsOptionalCodecs(device); } @Override public int getOptionalCodecsEnabled(BluetoothDevice device) { return mService.getOptionalCodecsEnabled(device); } @Override public void setOptionalCodecsEnabled(BluetoothDevice device, int value) { mService.setOptionalCodecsEnabled(device, value); } }
packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +1 −1 Original line number Diff line number Diff line Loading @@ -42,7 +42,7 @@ import java.util.Map; * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile * objects for the available Bluetooth profiles. */ public final class LocalBluetoothProfileManager { public class LocalBluetoothProfileManager { private static final String TAG = "LocalBluetoothProfileManager"; private static final boolean DEBUG = Utils.D; /** Singleton instance. */ Loading
packages/SettingsLib/tests/robotests/src/android/bluetooth/BluetoothCodecConfig.java 0 → 100644 +25 −0 Original line number Diff line number Diff line /* * 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 android.bluetooth; /** * A placeholder class to prevent ClassNotFound exceptions caused by lack of visibility. */ public class BluetoothCodecConfig { public boolean isMandatoryCodec() { return true; } public String getCodecName() { return null;} }