Files
packages_apps_Settings/src/com/android/settings/development/bluetooth/AbstractBluetoothDialogPreferenceController.java
Omer Osman b61e129f35 Add support for selection of Opus in Developer Options
Stub implementation for LC3 is added due to the interfaces being
present. The BT stack does not currently implement LC3 over A2DP.

Bug: 226441860
Test: BluetoothCodecDialogPreferenceControllerTest

Change-Id: I40546c97370872b37c0258d10e67a3871f9d9af5
2022-07-29 05:23:55 +00:00

287 lines
11 KiB
Java

/*
* Copyright (C) 2019 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.settings.development.bluetooth;
import static android.bluetooth.BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.util.Log;
import androidx.preference.Preference;
import com.android.settings.development.BluetoothA2dpConfigStore;
import com.android.settingslib.core.lifecycle.Lifecycle;
import java.util.List;
/**
* Abstract class for Bluetooth A2DP config dialog controller in developer option.
*/
public abstract class AbstractBluetoothDialogPreferenceController extends
AbstractBluetoothPreferenceController implements BaseBluetoothDialogPreference.Callback {
private static final String TAG = "AbstractBtDlgCtr";
private static final int SOURCE_CODEC_TYPE_OPUS = 6; // TODO(b/240635097): remove in U
protected static final int[] CODEC_TYPES = {SOURCE_CODEC_TYPE_OPUS,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC};
protected static final int[] SAMPLE_RATES = {BluetoothCodecConfig.SAMPLE_RATE_192000,
BluetoothCodecConfig.SAMPLE_RATE_176400,
BluetoothCodecConfig.SAMPLE_RATE_96000,
BluetoothCodecConfig.SAMPLE_RATE_88200,
BluetoothCodecConfig.SAMPLE_RATE_48000,
BluetoothCodecConfig.SAMPLE_RATE_44100};
protected static final int[] BITS_PER_SAMPLES = {BluetoothCodecConfig.BITS_PER_SAMPLE_32,
BluetoothCodecConfig.BITS_PER_SAMPLE_24,
BluetoothCodecConfig.BITS_PER_SAMPLE_16};
protected static final int[] CHANNEL_MODES = {BluetoothCodecConfig.CHANNEL_MODE_STEREO,
BluetoothCodecConfig.CHANNEL_MODE_MONO};
protected final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore;
public AbstractBluetoothDialogPreferenceController(Context context, Lifecycle lifecycle,
BluetoothA2dpConfigStore store) {
super(context, lifecycle, store);
mBluetoothA2dpConfigStore = store;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
}
@Override
public CharSequence getSummary() {
return ((BaseBluetoothDialogPreference) mPreference).generateSummary(
getCurrentConfigIndex());
}
@Override
public void onIndexUpdated(int index) {
final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp;
if (bluetoothA2dp == null) {
return;
}
writeConfigurationValues(index);
final BluetoothCodecConfig codecConfig = mBluetoothA2dpConfigStore.createCodecConfig();
BluetoothDevice activeDevice = getA2dpActiveDevice();
if (activeDevice != null) {
bluetoothA2dp.setCodecConfigPreference(activeDevice, codecConfig);
}
mPreference.setSummary(((BaseBluetoothDialogPreference) mPreference).generateSummary(
index));
}
@Override
public int getCurrentConfigIndex() {
final BluetoothCodecConfig codecConfig = getCurrentCodecConfig();
if (codecConfig == null) {
Log.d(TAG, "Unable to get current config index. Current codec Config is null.");
return getDefaultIndex();
}
return getCurrentIndexByConfig(codecConfig);
}
@Override
public void onBluetoothServiceConnected(BluetoothA2dp bluetoothA2dp) {
super.onBluetoothServiceConnected(bluetoothA2dp);
initConfigStore();
}
private void initConfigStore() {
final BluetoothCodecConfig config = getCurrentCodecConfig();
if (config == null) {
return;
}
mBluetoothA2dpConfigStore.setCodecType(config.getCodecType());
mBluetoothA2dpConfigStore.setSampleRate(config.getSampleRate());
mBluetoothA2dpConfigStore.setBitsPerSample(config.getBitsPerSample());
mBluetoothA2dpConfigStore.setChannelMode(config.getChannelMode());
mBluetoothA2dpConfigStore.setCodecPriority(CODEC_PRIORITY_HIGHEST);
mBluetoothA2dpConfigStore.setCodecSpecific1Value(config.getCodecSpecific1());
}
/**
* Updates the new value to the {@link BluetoothA2dpConfigStore}.
*
* @param newValue the new setting value
*/
protected abstract void writeConfigurationValues(int newValue);
/**
* To get the current A2DP index value.
*
* @param config for the current {@link BluetoothCodecConfig}.
* @return the current index.
*/
protected abstract int getCurrentIndexByConfig(BluetoothCodecConfig config);
/**
* @return the default index.
*/
protected int getDefaultIndex() {
return ((BaseBluetoothDialogPreference) mPreference).getDefaultIndex();
}
/**
* To get the current A2DP codec config.
*
* @return {@link BluetoothCodecConfig}.
*/
protected BluetoothCodecConfig getCurrentCodecConfig() {
final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp;
if (bluetoothA2dp == null) {
return null;
}
BluetoothDevice activeDevice = getA2dpActiveDevice();
if (activeDevice == null) {
Log.d(TAG, "Unable to get current codec config. No active device.");
return null;
}
final BluetoothCodecStatus codecStatus =
bluetoothA2dp.getCodecStatus(activeDevice);
if (codecStatus == null) {
Log.d(TAG, "Unable to get current codec config. Codec status is null");
return null;
}
return codecStatus.getCodecConfig();
}
/**
* To get the selectable A2DP configs.
*
* @return Array of {@link BluetoothCodecConfig}.
*/
protected List<BluetoothCodecConfig> getSelectableConfigs(BluetoothDevice device) {
final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp;
if (bluetoothA2dp == null) {
return null;
}
BluetoothDevice bluetoothDevice =
(device != null) ? device : getA2dpActiveDevice();
if (bluetoothDevice == null) {
return null;
}
final BluetoothCodecStatus codecStatus = bluetoothA2dp.getCodecStatus(bluetoothDevice);
if (codecStatus != null) {
return codecStatus.getCodecsSelectableCapabilities();
}
return null;
}
/**
* To get the selectable A2DP config by codec type.
*
* @return {@link BluetoothCodecConfig}.
*/
protected BluetoothCodecConfig getSelectableByCodecType(int codecTypeValue) {
BluetoothDevice activeDevice = getA2dpActiveDevice();
if (activeDevice == null) {
Log.d(TAG, "Unable to get selectable config. No active device.");
return null;
}
final List<BluetoothCodecConfig> configs = getSelectableConfigs(activeDevice);
for (BluetoothCodecConfig config : configs) {
if (config.getCodecType() == codecTypeValue) {
return config;
}
}
Log.d(TAG, "Unable to find matching codec config, type is " + codecTypeValue);
return null;
}
/**
* Method to notify controller when the HD audio(optional codec) state is changed.
*
* @param enabled Is {@code true} when the setting is enabled.
*/
public void onHDAudioEnabled(boolean enabled) {}
static int getHighestCodec(BluetoothA2dp bluetoothA2dp, BluetoothDevice activeDevice,
List<BluetoothCodecConfig> configs) {
if (configs == null) {
Log.d(TAG, "Unable to get highest codec. Configs are empty");
return BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID;
}
// If HD audio is not enabled, SBC is the only one available codec.
if (bluetoothA2dp.isOptionalCodecsEnabled(activeDevice)
!= BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
return BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC;
}
for (int i = 0; i < CODEC_TYPES.length; i++) {
for (BluetoothCodecConfig config : configs) {
if (config.getCodecType() == CODEC_TYPES[i]) {
return CODEC_TYPES[i];
}
}
}
return BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID;
}
static int getHighestSampleRate(BluetoothCodecConfig config) {
if (config == null) {
Log.d(TAG, "Unable to get highest sample rate. Config is empty");
return BluetoothCodecConfig.SAMPLE_RATE_NONE;
}
final int capability = config.getSampleRate();
for (int i = 0; i < SAMPLE_RATES.length; i++) {
if ((capability & SAMPLE_RATES[i]) != 0) {
return SAMPLE_RATES[i];
}
}
return BluetoothCodecConfig.SAMPLE_RATE_NONE;
}
static int getHighestBitsPerSample(BluetoothCodecConfig config) {
if (config == null) {
Log.d(TAG, "Unable to get highest bits per sample. Config is empty");
return BluetoothCodecConfig.BITS_PER_SAMPLE_NONE;
}
final int capability = config.getBitsPerSample();
for (int i = 0; i < BITS_PER_SAMPLES.length; i++) {
if ((capability & BITS_PER_SAMPLES[i]) != 0) {
return BITS_PER_SAMPLES[i];
}
}
return BluetoothCodecConfig.BITS_PER_SAMPLE_NONE;
}
static int getHighestChannelMode(BluetoothCodecConfig config) {
if (config == null) {
Log.d(TAG, "Unable to get highest channel mode. Config is empty");
return BluetoothCodecConfig.CHANNEL_MODE_NONE;
}
final int capability = config.getChannelMode();
for (int i = 0; i < CHANNEL_MODES.length; i++) {
if ((capability & CHANNEL_MODES[i]) != 0) {
return CHANNEL_MODES[i];
}
}
return BluetoothCodecConfig.CHANNEL_MODE_NONE;
}
}