1 /* 2 * Copyright 2018 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.bluetooth.a2dp; 18 19 import android.bluetooth.BluetoothCodecConfig; 20 import android.bluetooth.BluetoothCodecConfig.CodecPriority; 21 import android.bluetooth.BluetoothCodecStatus; 22 import android.bluetooth.BluetoothDevice; 23 import android.content.Context; 24 import android.content.res.Resources; 25 import android.content.res.Resources.NotFoundException; 26 import android.media.AudioManager; 27 import android.util.Log; 28 29 import com.android.bluetooth.R; 30 31 import java.util.Arrays; 32 import java.util.Objects; 33 /* 34 * A2DP Codec Configuration setup. 35 */ 36 class A2dpCodecConfig { 37 private static final boolean DBG = true; 38 private static final String TAG = "A2dpCodecConfig"; 39 40 private Context mContext; 41 private A2dpNativeInterface mA2dpNativeInterface; 42 43 private BluetoothCodecConfig[] mCodecConfigPriorities; 44 private @CodecPriority int mA2dpSourceCodecPrioritySbc = 45 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 46 private @CodecPriority int mA2dpSourceCodecPriorityAac = 47 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 48 private @CodecPriority int mA2dpSourceCodecPriorityAptx = 49 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 50 private @CodecPriority int mA2dpSourceCodecPriorityAptxHd = 51 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 52 private @CodecPriority int mA2dpSourceCodecPriorityLdac = 53 BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 54 55 private BluetoothCodecConfig[] mCodecConfigOffloading = new BluetoothCodecConfig[0]; 56 A2dpCodecConfig(Context context, A2dpNativeInterface a2dpNativeInterface)57 A2dpCodecConfig(Context context, A2dpNativeInterface a2dpNativeInterface) { 58 mContext = context; 59 mA2dpNativeInterface = a2dpNativeInterface; 60 mCodecConfigPriorities = assignCodecConfigPriorities(); 61 62 AudioManager audioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE); 63 if (audioManager == null) { 64 Log.w(TAG, "Can't obtain the codec offloading prefernece from null AudioManager"); 65 return; 66 } 67 mCodecConfigOffloading = audioManager.getHwOffloadEncodingFormatsSupportedForA2DP() 68 .toArray(mCodecConfigOffloading); 69 } 70 codecConfigPriorities()71 BluetoothCodecConfig[] codecConfigPriorities() { 72 return mCodecConfigPriorities; 73 } 74 codecConfigOffloading()75 BluetoothCodecConfig[] codecConfigOffloading() { 76 return mCodecConfigOffloading; 77 } 78 setCodecConfigPreference(BluetoothDevice device, BluetoothCodecStatus codecStatus, BluetoothCodecConfig newCodecConfig)79 void setCodecConfigPreference(BluetoothDevice device, 80 BluetoothCodecStatus codecStatus, 81 BluetoothCodecConfig newCodecConfig) { 82 Objects.requireNonNull(codecStatus); 83 84 // Check whether the codecConfig is selectable for this Bluetooth device. 85 BluetoothCodecConfig[] selectableCodecs = codecStatus.getCodecsSelectableCapabilities(); 86 if (!Arrays.asList(selectableCodecs).stream().anyMatch(codec -> 87 codec.isMandatoryCodec())) { 88 // Do not set codec preference to native if the selectableCodecs not contain mandatory 89 // codec. The reason could be remote codec negotiation is not completed yet. 90 Log.w(TAG, "setCodecConfigPreference: must have mandatory codec before changing."); 91 return; 92 } 93 if (!codecStatus.isCodecConfigSelectable(newCodecConfig)) { 94 Log.w(TAG, "setCodecConfigPreference: invalid codec " 95 + Objects.toString(newCodecConfig)); 96 return; 97 } 98 99 // Check whether the codecConfig would change current codec config. 100 int prioritizedCodecType = getPrioitizedCodecType(newCodecConfig, selectableCodecs); 101 BluetoothCodecConfig currentCodecConfig = codecStatus.getCodecConfig(); 102 if (prioritizedCodecType == currentCodecConfig.getCodecType() 103 && (prioritizedCodecType != newCodecConfig.getCodecType() 104 || (currentCodecConfig.similarCodecFeedingParameters(newCodecConfig) 105 && currentCodecConfig.sameCodecSpecificParameters(newCodecConfig)))) { 106 // Same codec with same parameters, no need to send this request to native. 107 Log.w(TAG, "setCodecConfigPreference: codec not changed."); 108 return; 109 } 110 111 BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1]; 112 codecConfigArray[0] = newCodecConfig; 113 mA2dpNativeInterface.setCodecConfigPreference(device, codecConfigArray); 114 } 115 enableOptionalCodecs(BluetoothDevice device, BluetoothCodecConfig currentCodecConfig)116 void enableOptionalCodecs(BluetoothDevice device, BluetoothCodecConfig currentCodecConfig) { 117 if (currentCodecConfig != null && !currentCodecConfig.isMandatoryCodec()) { 118 Log.i(TAG, "enableOptionalCodecs: already using optional codec " 119 + currentCodecConfig.getCodecName()); 120 return; 121 } 122 123 BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities(); 124 if (codecConfigArray == null) { 125 return; 126 } 127 128 // Set the mandatory codec's priority to default, and remove the rest 129 for (int i = 0; i < codecConfigArray.length; i++) { 130 BluetoothCodecConfig codecConfig = codecConfigArray[i]; 131 if (!codecConfig.isMandatoryCodec()) { 132 codecConfigArray[i] = null; 133 } 134 } 135 136 mA2dpNativeInterface.setCodecConfigPreference(device, codecConfigArray); 137 } 138 disableOptionalCodecs(BluetoothDevice device, BluetoothCodecConfig currentCodecConfig)139 void disableOptionalCodecs(BluetoothDevice device, BluetoothCodecConfig currentCodecConfig) { 140 if (currentCodecConfig != null && currentCodecConfig.isMandatoryCodec()) { 141 Log.i(TAG, "disableOptionalCodecs: already using mandatory codec."); 142 return; 143 } 144 145 BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities(); 146 if (codecConfigArray == null) { 147 return; 148 } 149 // Set the mandatory codec's priority to highest, and remove the rest 150 for (int i = 0; i < codecConfigArray.length; i++) { 151 BluetoothCodecConfig codecConfig = codecConfigArray[i]; 152 if (codecConfig.isMandatoryCodec()) { 153 codecConfig.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST); 154 } else { 155 codecConfigArray[i] = null; 156 } 157 } 158 mA2dpNativeInterface.setCodecConfigPreference(device, codecConfigArray); 159 } 160 161 // Get the codec type of the highest priority of selectableCodecs and codecConfig. getPrioitizedCodecType(BluetoothCodecConfig codecConfig, BluetoothCodecConfig[] selectableCodecs)162 private int getPrioitizedCodecType(BluetoothCodecConfig codecConfig, 163 BluetoothCodecConfig[] selectableCodecs) { 164 BluetoothCodecConfig prioritizedCodecConfig = codecConfig; 165 for (BluetoothCodecConfig config : selectableCodecs) { 166 if (prioritizedCodecConfig == null) { 167 prioritizedCodecConfig = config; 168 } 169 if (config.getCodecPriority() > prioritizedCodecConfig.getCodecPriority()) { 170 prioritizedCodecConfig = config; 171 } 172 } 173 return prioritizedCodecConfig.getCodecType(); 174 } 175 176 // Assign the A2DP Source codec config priorities assignCodecConfigPriorities()177 private BluetoothCodecConfig[] assignCodecConfigPriorities() { 178 Resources resources = mContext.getResources(); 179 if (resources == null) { 180 return null; 181 } 182 183 int value; 184 try { 185 value = resources.getInteger(R.integer.a2dp_source_codec_priority_sbc); 186 } catch (NotFoundException e) { 187 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 188 } 189 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) && (value 190 < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 191 mA2dpSourceCodecPrioritySbc = value; 192 } 193 194 try { 195 value = resources.getInteger(R.integer.a2dp_source_codec_priority_aac); 196 } catch (NotFoundException e) { 197 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 198 } 199 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) && (value 200 < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 201 mA2dpSourceCodecPriorityAac = value; 202 } 203 204 try { 205 value = resources.getInteger(R.integer.a2dp_source_codec_priority_aptx); 206 } catch (NotFoundException e) { 207 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 208 } 209 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) && (value 210 < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 211 mA2dpSourceCodecPriorityAptx = value; 212 } 213 214 try { 215 value = resources.getInteger(R.integer.a2dp_source_codec_priority_aptx_hd); 216 } catch (NotFoundException e) { 217 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 218 } 219 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) && (value 220 < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 221 mA2dpSourceCodecPriorityAptxHd = value; 222 } 223 224 try { 225 value = resources.getInteger(R.integer.a2dp_source_codec_priority_ldac); 226 } catch (NotFoundException e) { 227 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 228 } 229 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) && (value 230 < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 231 mA2dpSourceCodecPriorityLdac = value; 232 } 233 234 BluetoothCodecConfig codecConfig; 235 BluetoothCodecConfig[] codecConfigArray = 236 new BluetoothCodecConfig[BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX]; 237 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, 238 mA2dpSourceCodecPrioritySbc, BluetoothCodecConfig.SAMPLE_RATE_NONE, 239 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig 240 .CHANNEL_MODE_NONE, 0 /* codecSpecific1 */, 241 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 0 /* codecSpecific4 */); 242 codecConfigArray[0] = codecConfig; 243 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, 244 mA2dpSourceCodecPriorityAac, BluetoothCodecConfig.SAMPLE_RATE_NONE, 245 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig 246 .CHANNEL_MODE_NONE, 0 /* codecSpecific1 */, 247 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 0 /* codecSpecific4 */); 248 codecConfigArray[1] = codecConfig; 249 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, 250 mA2dpSourceCodecPriorityAptx, BluetoothCodecConfig.SAMPLE_RATE_NONE, 251 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig 252 .CHANNEL_MODE_NONE, 0 /* codecSpecific1 */, 253 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 0 /* codecSpecific4 */); 254 codecConfigArray[2] = codecConfig; 255 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, 256 mA2dpSourceCodecPriorityAptxHd, BluetoothCodecConfig.SAMPLE_RATE_NONE, 257 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig 258 .CHANNEL_MODE_NONE, 0 /* codecSpecific1 */, 259 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 0 /* codecSpecific4 */); 260 codecConfigArray[3] = codecConfig; 261 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, 262 mA2dpSourceCodecPriorityLdac, BluetoothCodecConfig.SAMPLE_RATE_NONE, 263 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig 264 .CHANNEL_MODE_NONE, 0 /* codecSpecific1 */, 265 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 0 /* codecSpecific4 */); 266 codecConfigArray[4] = codecConfig; 267 268 return codecConfigArray; 269 } 270 } 271 272