1 /* 2 * Copyright (C) 2019 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.development.bluetooth; 18 19 import android.bluetooth.BluetoothA2dp; 20 import android.bluetooth.BluetoothCodecConfig; 21 import android.bluetooth.BluetoothCodecStatus; 22 import android.bluetooth.BluetoothDevice; 23 import android.content.Context; 24 import android.util.Log; 25 26 import androidx.preference.Preference; 27 28 import com.android.settings.development.BluetoothA2dpConfigStore; 29 import com.android.settingslib.core.lifecycle.Lifecycle; 30 31 /** 32 * Abstract class for Bluetooth A2DP config dialog controller in developer option. 33 */ 34 public abstract class AbstractBluetoothDialogPreferenceController extends 35 AbstractBluetoothPreferenceController implements BaseBluetoothDialogPreference.Callback { 36 37 private static final String TAG = "AbstractBtDlgCtr"; 38 39 protected static final int[] CODEC_TYPES = {BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, 40 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, 41 BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, 42 BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, 43 BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC}; 44 protected static final int[] SAMPLE_RATES = {BluetoothCodecConfig.SAMPLE_RATE_192000, 45 BluetoothCodecConfig.SAMPLE_RATE_176400, 46 BluetoothCodecConfig.SAMPLE_RATE_96000, 47 BluetoothCodecConfig.SAMPLE_RATE_88200, 48 BluetoothCodecConfig.SAMPLE_RATE_48000, 49 BluetoothCodecConfig.SAMPLE_RATE_44100}; 50 protected static final int[] BITS_PER_SAMPLES = {BluetoothCodecConfig.BITS_PER_SAMPLE_32, 51 BluetoothCodecConfig.BITS_PER_SAMPLE_24, 52 BluetoothCodecConfig.BITS_PER_SAMPLE_16}; 53 protected static final int[] CHANNEL_MODES = {BluetoothCodecConfig.CHANNEL_MODE_STEREO, 54 BluetoothCodecConfig.CHANNEL_MODE_MONO}; 55 56 protected final BluetoothA2dpConfigStore mBluetoothA2dpConfigStore; 57 AbstractBluetoothDialogPreferenceController(Context context, Lifecycle lifecycle, BluetoothA2dpConfigStore store)58 public AbstractBluetoothDialogPreferenceController(Context context, Lifecycle lifecycle, 59 BluetoothA2dpConfigStore store) { 60 super(context, lifecycle, store); 61 mBluetoothA2dpConfigStore = store; 62 } 63 64 @Override updateState(Preference preference)65 public void updateState(Preference preference) { 66 super.updateState(preference); 67 } 68 69 @Override getSummary()70 public CharSequence getSummary() { 71 return ((BaseBluetoothDialogPreference) mPreference).generateSummary( 72 getCurrentConfigIndex()); 73 } 74 75 @Override onIndexUpdated(int index)76 public void onIndexUpdated(int index) { 77 final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp; 78 if (bluetoothA2dp == null) { 79 return; 80 } 81 writeConfigurationValues(index); 82 final BluetoothCodecConfig codecConfig = mBluetoothA2dpConfigStore.createCodecConfig(); 83 BluetoothDevice activeDevice = mBluetoothA2dp.getActiveDevice(); 84 if (activeDevice != null) { 85 bluetoothA2dp.setCodecConfigPreference(activeDevice, codecConfig); 86 } 87 mPreference.setSummary(((BaseBluetoothDialogPreference) mPreference).generateSummary( 88 index)); 89 } 90 91 @Override getCurrentConfigIndex()92 public int getCurrentConfigIndex() { 93 final BluetoothCodecConfig codecConfig = getCurrentCodecConfig(); 94 if (codecConfig == null) { 95 Log.d(TAG, "Unable to get current config index. Current codec Config is null."); 96 return getDefaultIndex(); 97 } 98 return getCurrentIndexByConfig(codecConfig); 99 } 100 101 @Override onBluetoothServiceConnected(BluetoothA2dp bluetoothA2dp)102 public void onBluetoothServiceConnected(BluetoothA2dp bluetoothA2dp) { 103 super.onBluetoothServiceConnected(bluetoothA2dp); 104 initConfigStore(); 105 } 106 initConfigStore()107 private void initConfigStore() { 108 final BluetoothCodecConfig config = getCurrentCodecConfig(); 109 if (config == null) { 110 return; 111 } 112 mBluetoothA2dpConfigStore.setCodecType(config.getCodecType()); 113 mBluetoothA2dpConfigStore.setSampleRate(config.getSampleRate()); 114 mBluetoothA2dpConfigStore.setBitsPerSample(config.getBitsPerSample()); 115 mBluetoothA2dpConfigStore.setChannelMode(config.getChannelMode()); 116 mBluetoothA2dpConfigStore.setCodecPriority(config.getCodecPriority()); 117 mBluetoothA2dpConfigStore.setCodecSpecific1Value(config.getCodecSpecific1()); 118 } 119 120 /** 121 * Updates the new value to the {@link BluetoothA2dpConfigStore}. 122 * 123 * @param newValue the new setting value 124 */ writeConfigurationValues(int newValue)125 protected abstract void writeConfigurationValues(int newValue); 126 127 /** 128 * To get the current A2DP index value. 129 * 130 * @param config for the current {@link BluetoothCodecConfig}. 131 * @return the current index. 132 */ getCurrentIndexByConfig(BluetoothCodecConfig config)133 protected abstract int getCurrentIndexByConfig(BluetoothCodecConfig config); 134 135 /** 136 * @return the default index. 137 */ getDefaultIndex()138 protected int getDefaultIndex() { 139 return ((BaseBluetoothDialogPreference) mPreference).getDefaultIndex(); 140 } 141 142 /** 143 * To get the current A2DP codec config. 144 * 145 * @return {@link BluetoothCodecConfig}. 146 */ getCurrentCodecConfig()147 protected BluetoothCodecConfig getCurrentCodecConfig() { 148 final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp; 149 if (bluetoothA2dp == null) { 150 return null; 151 } 152 BluetoothDevice activeDevice = bluetoothA2dp.getActiveDevice(); 153 if (activeDevice == null) { 154 Log.d(TAG, "Unable to get current codec config. No active device."); 155 return null; 156 } 157 final BluetoothCodecStatus codecStatus = 158 bluetoothA2dp.getCodecStatus(activeDevice); 159 if (codecStatus == null) { 160 Log.d(TAG, "Unable to get current codec config. Codec status is null"); 161 return null; 162 } 163 return codecStatus.getCodecConfig(); 164 } 165 166 /** 167 * To get the selectable A2DP configs. 168 * 169 * @return Array of {@link BluetoothCodecConfig}. 170 */ getSelectableConfigs(BluetoothDevice device)171 protected BluetoothCodecConfig[] getSelectableConfigs(BluetoothDevice device) { 172 final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp; 173 if (bluetoothA2dp == null) { 174 return null; 175 } 176 BluetoothDevice bluetoothDevice = 177 (device != null) ? device : bluetoothA2dp.getActiveDevice(); 178 if (bluetoothDevice == null) { 179 return null; 180 } 181 final BluetoothCodecStatus codecStatus = bluetoothA2dp.getCodecStatus(bluetoothDevice); 182 if (codecStatus != null) { 183 return codecStatus.getCodecsSelectableCapabilities(); 184 } 185 return null; 186 } 187 188 /** 189 * To get the selectable A2DP config by codec type. 190 * 191 * @return {@link BluetoothCodecConfig}. 192 */ getSelectableByCodecType(int codecTypeValue)193 protected BluetoothCodecConfig getSelectableByCodecType(int codecTypeValue) { 194 BluetoothDevice activeDevice = mBluetoothA2dp.getActiveDevice(); 195 if (activeDevice == null) { 196 Log.d(TAG, "Unable to get selectable config. No active device."); 197 return null; 198 } 199 final BluetoothCodecConfig[] configs = getSelectableConfigs(activeDevice); 200 if (configs == null) { 201 Log.d(TAG, "Unable to get selectable config. Selectable configs is empty."); 202 return null; 203 } 204 for (BluetoothCodecConfig config : configs) { 205 if (config.getCodecType() == codecTypeValue) { 206 return config; 207 } 208 } 209 Log.d(TAG, "Unable to find matching codec config, type is " + codecTypeValue); 210 return null; 211 } 212 213 /** 214 * Method to notify controller when the HD audio(optional codec) state is changed. 215 * 216 * @param enabled Is {@code true} when the setting is enabled. 217 */ onHDAudioEnabled(boolean enabled)218 public void onHDAudioEnabled(boolean enabled) {} 219 getHighestCodec(BluetoothCodecConfig[] configs)220 static int getHighestCodec(BluetoothCodecConfig[] configs) { 221 if (configs == null) { 222 Log.d(TAG, "Unable to get highest codec. Configs are empty"); 223 return BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID; 224 } 225 for (int i = 0; i < CODEC_TYPES.length; i++) { 226 for (int j = 0; j < configs.length; j++) { 227 if ((configs[j].getCodecType() == CODEC_TYPES[i])) { 228 return CODEC_TYPES[i]; 229 } 230 } 231 } 232 return BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID; 233 } 234 getHighestSampleRate(BluetoothCodecConfig config)235 static int getHighestSampleRate(BluetoothCodecConfig config) { 236 if (config == null) { 237 Log.d(TAG, "Unable to get highest sample rate. Config is empty"); 238 return BluetoothCodecConfig.SAMPLE_RATE_NONE; 239 } 240 final int capability = config.getSampleRate(); 241 for (int i = 0; i < SAMPLE_RATES.length; i++) { 242 if ((capability & SAMPLE_RATES[i]) != 0) { 243 return SAMPLE_RATES[i]; 244 } 245 } 246 return BluetoothCodecConfig.SAMPLE_RATE_NONE; 247 } 248 getHighestBitsPerSample(BluetoothCodecConfig config)249 static int getHighestBitsPerSample(BluetoothCodecConfig config) { 250 if (config == null) { 251 Log.d(TAG, "Unable to get highest bits per sample. Config is empty"); 252 return BluetoothCodecConfig.BITS_PER_SAMPLE_NONE; 253 } 254 final int capability = config.getBitsPerSample(); 255 for (int i = 0; i < BITS_PER_SAMPLES.length; i++) { 256 if ((capability & BITS_PER_SAMPLES[i]) != 0) { 257 return BITS_PER_SAMPLES[i]; 258 } 259 } 260 return BluetoothCodecConfig.BITS_PER_SAMPLE_NONE; 261 } 262 getHighestChannelMode(BluetoothCodecConfig config)263 static int getHighestChannelMode(BluetoothCodecConfig config) { 264 if (config == null) { 265 Log.d(TAG, "Unable to get highest channel mode. Config is empty"); 266 return BluetoothCodecConfig.CHANNEL_MODE_NONE; 267 } 268 final int capability = config.getChannelMode(); 269 for (int i = 0; i < CHANNEL_MODES.length; i++) { 270 if ((capability & CHANNEL_MODES[i]) != 0) { 271 return CHANNEL_MODES[i]; 272 } 273 } 274 return BluetoothCodecConfig.CHANNEL_MODE_NONE; 275 } 276 } 277