1 /*
2 * Copyright (C) 2022 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 #include <set>
18
19 #include "aidl/android/hardware/bluetooth/audio/ChannelMode.h"
20 #include "aidl/android/hardware/bluetooth/audio/CodecId.h"
21 #include "aidl_android_hardware_bluetooth_audio_setting_enums.h"
22 #define LOG_TAG "BTAudioCodecsProviderAidl"
23
24 #include "BluetoothLeAudioCodecsProvider.h"
25
26 namespace aidl {
27 namespace android {
28 namespace hardware {
29 namespace bluetooth {
30 namespace audio {
31
32 static const char* kLeAudioCodecCapabilitiesFile =
33 "/vendor/etc/le_audio_codec_capabilities.xml";
34
35 static const AudioLocation kStereoAudio = static_cast<AudioLocation>(
36 static_cast<uint8_t>(AudioLocation::FRONT_LEFT) |
37 static_cast<uint8_t>(AudioLocation::FRONT_RIGHT));
38 static const AudioLocation kMonoAudio = AudioLocation::UNKNOWN;
39
40 static std::vector<LeAudioCodecCapabilitiesSetting> leAudioCodecCapabilities;
41
42 static bool isInvalidFileContent = false;
43
44 std::optional<setting::LeAudioOffloadSetting>
ParseFromLeAudioOffloadSettingFile()45 BluetoothLeAudioCodecsProvider::ParseFromLeAudioOffloadSettingFile() {
46 auto le_audio_offload_setting =
47 setting::readLeAudioOffloadSetting(kLeAudioCodecCapabilitiesFile);
48 if (!le_audio_offload_setting.has_value()) {
49 LOG(ERROR) << __func__ << ": Failed to read "
50 << kLeAudioCodecCapabilitiesFile;
51 }
52 return le_audio_offload_setting;
53 }
54
55 std::unordered_map<SessionType, std::vector<CodecInfo>>
GetLeAudioCodecInfo(const std::optional<setting::LeAudioOffloadSetting> & le_audio_offload_setting)56 BluetoothLeAudioCodecsProvider::GetLeAudioCodecInfo(
57 const std::optional<setting::LeAudioOffloadSetting>&
58 le_audio_offload_setting) {
59 // Load from previous storage if present
60 if (!session_codecs_map_.empty()) return session_codecs_map_;
61
62 isInvalidFileContent = true;
63 if (!le_audio_offload_setting.has_value()) return {};
64
65 // Load scenario, configuration, codec configuration and strategy
66 LoadConfigurationToMap(le_audio_offload_setting);
67 if (supported_scenarios_.empty() || configuration_map_.empty() ||
68 codec_configuration_map_.empty() || strategy_configuration_map_.empty())
69 return {};
70
71 // Map each configuration into a CodecInfo
72 std::unordered_map<std::string, CodecInfo> config_codec_info_map_;
73
74 for (auto& p : configuration_map_) {
75 // Initialize new CodecInfo for the config
76 auto config_name = p.first;
77
78 // Getting informations from codecConfig and strategyConfig
79 const auto codec_config_name = p.second.getCodecConfiguration();
80 const auto strategy_config_name = p.second.getStrategyConfiguration();
81 const auto codec_configuration_map_iter =
82 codec_configuration_map_.find(codec_config_name);
83 if (codec_configuration_map_iter == codec_configuration_map_.end())
84 continue;
85 const auto strategy_configuration_map_iter =
86 strategy_configuration_map_.find(strategy_config_name);
87 if (strategy_configuration_map_iter == strategy_configuration_map_.end())
88 continue;
89
90 if (config_codec_info_map_.count(config_name) == 0)
91 config_codec_info_map_[config_name] = CodecInfo();
92
93 const auto& codec_config = codec_configuration_map_iter->second;
94 const auto codec = codec_config.getCodec();
95 const auto& strategy_config = strategy_configuration_map_iter->second;
96 const auto strategy_config_channel_count =
97 strategy_config.getChannelCount();
98
99 // Initiate information
100 auto& codec_info = config_codec_info_map_[config_name];
101 switch (codec) {
102 case setting::CodecType::LC3:
103 codec_info.name = "LC3";
104 codec_info.id = CodecId::Core::LC3;
105 break;
106 default:
107 codec_info.name = "UNDEFINE";
108 codec_info.id = CodecId::Vendor();
109 break;
110 }
111 codec_info.transport =
112 CodecInfo::Transport::make<CodecInfo::Transport::Tag::leAudio>();
113
114 // Mapping codec configuration information
115 auto& transport =
116 codec_info.transport.get<CodecInfo::Transport::Tag::leAudio>();
117 transport.samplingFrequencyHz.push_back(
118 codec_config.getSamplingFrequency());
119 // Mapping octetsPerCodecFrame to bitdepth for easier comparison.
120 transport.bitdepth.push_back(codec_config.getOctetsPerCodecFrame());
121 transport.frameDurationUs.push_back(codec_config.getFrameDurationUs());
122 switch (strategy_config.getAudioLocation()) {
123 case setting::AudioLocation::MONO:
124 if (strategy_config_channel_count == 1)
125 transport.channelMode.push_back(ChannelMode::MONO);
126 else
127 transport.channelMode.push_back(ChannelMode::DUALMONO);
128 break;
129 case setting::AudioLocation::STEREO:
130 transport.channelMode.push_back(ChannelMode::STEREO);
131 break;
132 default:
133 transport.channelMode.push_back(ChannelMode::UNKNOWN);
134 break;
135 }
136 }
137
138 // Goes through every scenario, deduplicate configuration, skip the invalid
139 // config references (e.g. the "invalid" entries in the xml file).
140 std::set<std::string> encoding_config, decoding_config, broadcast_config;
141 for (auto& s : supported_scenarios_) {
142 if (s.hasEncode() && config_codec_info_map_.count(s.getEncode())) {
143 encoding_config.insert(s.getEncode());
144 }
145 if (s.hasDecode() && config_codec_info_map_.count(s.getDecode())) {
146 decoding_config.insert(s.getDecode());
147 }
148 if (s.hasBroadcast() && config_codec_info_map_.count(s.getBroadcast())) {
149 broadcast_config.insert(s.getBroadcast());
150 }
151 }
152
153 // Split by session types and add results
154 const auto encoding_path =
155 SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
156 const auto decoding_path =
157 SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
158 const auto broadcast_path =
159 SessionType::LE_AUDIO_BROADCAST_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
160 session_codecs_map_ =
161 std::unordered_map<SessionType, std::vector<CodecInfo>>();
162 session_codecs_map_[encoding_path] = std::vector<CodecInfo>();
163 session_codecs_map_[decoding_path] = std::vector<CodecInfo>();
164 session_codecs_map_[broadcast_path] = std::vector<CodecInfo>();
165 session_codecs_map_[encoding_path].reserve(encoding_config.size());
166 session_codecs_map_[decoding_path].reserve(decoding_config.size());
167 session_codecs_map_[broadcast_path].reserve(broadcast_config.size());
168 for (auto& c : encoding_config)
169 session_codecs_map_[encoding_path].push_back(config_codec_info_map_[c]);
170 for (auto& c : decoding_config)
171 session_codecs_map_[decoding_path].push_back(config_codec_info_map_[c]);
172 for (auto& c : broadcast_config)
173 session_codecs_map_[broadcast_path].push_back(config_codec_info_map_[c]);
174
175 isInvalidFileContent = session_codecs_map_.empty();
176
177 return session_codecs_map_;
178 }
179
180 std::vector<LeAudioCodecCapabilitiesSetting>
GetLeAudioCodecCapabilities(const std::optional<setting::LeAudioOffloadSetting> & le_audio_offload_setting)181 BluetoothLeAudioCodecsProvider::GetLeAudioCodecCapabilities(
182 const std::optional<setting::LeAudioOffloadSetting>&
183 le_audio_offload_setting) {
184 if (!leAudioCodecCapabilities.empty()) {
185 return leAudioCodecCapabilities;
186 }
187
188 isInvalidFileContent = true;
189
190 if (!le_audio_offload_setting.has_value()) {
191 LOG(ERROR)
192 << __func__
193 << ": input le_audio_offload_setting content need to be non empty";
194 return {};
195 }
196
197 LoadConfigurationToMap(le_audio_offload_setting);
198 if (supported_scenarios_.empty() || configuration_map_.empty() ||
199 codec_configuration_map_.empty() || strategy_configuration_map_.empty())
200 return {};
201
202 leAudioCodecCapabilities =
203 ComposeLeAudioCodecCapabilities(supported_scenarios_);
204 isInvalidFileContent = leAudioCodecCapabilities.empty();
205
206 return leAudioCodecCapabilities;
207 }
208
ClearLeAudioCodecCapabilities()209 void BluetoothLeAudioCodecsProvider::ClearLeAudioCodecCapabilities() {
210 leAudioCodecCapabilities.clear();
211 configuration_map_.clear();
212 codec_configuration_map_.clear();
213 strategy_configuration_map_.clear();
214 session_codecs_map_.clear();
215 supported_scenarios_.clear();
216 }
217
GetScenarios(const std::optional<setting::LeAudioOffloadSetting> & le_audio_offload_setting)218 std::vector<setting::Scenario> BluetoothLeAudioCodecsProvider::GetScenarios(
219 const std::optional<setting::LeAudioOffloadSetting>&
220 le_audio_offload_setting) {
221 std::vector<setting::Scenario> supported_scenarios;
222 if (le_audio_offload_setting->hasScenarioList()) {
223 for (const auto& scenario_list :
224 le_audio_offload_setting->getScenarioList()) {
225 if (!scenario_list.hasScenario()) {
226 continue;
227 }
228 for (const auto& scenario : scenario_list.getScenario()) {
229 if (scenario.hasEncode() && scenario.hasDecode()) {
230 supported_scenarios.push_back(scenario);
231 }
232 }
233 }
234 }
235 return supported_scenarios;
236 }
237
UpdateConfigurationsToMap(const std::optional<setting::LeAudioOffloadSetting> & le_audio_offload_setting)238 void BluetoothLeAudioCodecsProvider::UpdateConfigurationsToMap(
239 const std::optional<setting::LeAudioOffloadSetting>&
240 le_audio_offload_setting) {
241 if (le_audio_offload_setting->hasConfigurationList()) {
242 for (const auto& configuration_list :
243 le_audio_offload_setting->getConfigurationList()) {
244 if (!configuration_list.hasConfiguration()) {
245 continue;
246 }
247 for (const auto& configuration : configuration_list.getConfiguration()) {
248 if (configuration.hasName() && configuration.hasCodecConfiguration() &&
249 configuration.hasStrategyConfiguration()) {
250 configuration_map_.insert(
251 make_pair(configuration.getName(), configuration));
252 }
253 }
254 }
255 }
256 }
257
UpdateCodecConfigurationsToMap(const std::optional<setting::LeAudioOffloadSetting> & le_audio_offload_setting)258 void BluetoothLeAudioCodecsProvider::UpdateCodecConfigurationsToMap(
259 const std::optional<setting::LeAudioOffloadSetting>&
260 le_audio_offload_setting) {
261 if (le_audio_offload_setting->hasCodecConfigurationList()) {
262 for (const auto& codec_configuration_list :
263 le_audio_offload_setting->getCodecConfigurationList()) {
264 if (!codec_configuration_list.hasCodecConfiguration()) {
265 continue;
266 }
267 for (const auto& codec_configuration :
268 codec_configuration_list.getCodecConfiguration()) {
269 if (IsValidCodecConfiguration(codec_configuration)) {
270 codec_configuration_map_.insert(
271 make_pair(codec_configuration.getName(), codec_configuration));
272 }
273 }
274 }
275 }
276 }
277
UpdateStrategyConfigurationsToMap(const std::optional<setting::LeAudioOffloadSetting> & le_audio_offload_setting)278 void BluetoothLeAudioCodecsProvider::UpdateStrategyConfigurationsToMap(
279 const std::optional<setting::LeAudioOffloadSetting>&
280 le_audio_offload_setting) {
281 if (le_audio_offload_setting->hasStrategyConfigurationList()) {
282 for (const auto& strategy_configuration_list :
283 le_audio_offload_setting->getStrategyConfigurationList()) {
284 if (!strategy_configuration_list.hasStrategyConfiguration()) {
285 continue;
286 }
287 for (const auto& strategy_configuration :
288 strategy_configuration_list.getStrategyConfiguration()) {
289 if (IsValidStrategyConfiguration(strategy_configuration)) {
290 strategy_configuration_map_.insert(make_pair(
291 strategy_configuration.getName(), strategy_configuration));
292 }
293 }
294 }
295 }
296 }
297
LoadConfigurationToMap(const std::optional<setting::LeAudioOffloadSetting> & le_audio_offload_setting)298 void BluetoothLeAudioCodecsProvider::LoadConfigurationToMap(
299 const std::optional<setting::LeAudioOffloadSetting>&
300 le_audio_offload_setting) {
301 ClearLeAudioCodecCapabilities();
302
303 supported_scenarios_ = GetScenarios(le_audio_offload_setting);
304 if (supported_scenarios_.empty()) {
305 LOG(ERROR) << __func__ << ": No scenarios in "
306 << kLeAudioCodecCapabilitiesFile;
307 return;
308 }
309
310 UpdateConfigurationsToMap(le_audio_offload_setting);
311 if (configuration_map_.empty()) {
312 LOG(ERROR) << __func__ << ": No configurations in "
313 << kLeAudioCodecCapabilitiesFile;
314 return;
315 }
316
317 UpdateCodecConfigurationsToMap(le_audio_offload_setting);
318 if (codec_configuration_map_.empty()) {
319 LOG(ERROR) << __func__ << ": No codec configurations in "
320 << kLeAudioCodecCapabilitiesFile;
321 return;
322 }
323
324 UpdateStrategyConfigurationsToMap(le_audio_offload_setting);
325 if (strategy_configuration_map_.empty()) {
326 LOG(ERROR) << __func__ << ": No strategy configurations in "
327 << kLeAudioCodecCapabilitiesFile;
328 return;
329 }
330 }
331
332 std::vector<LeAudioCodecCapabilitiesSetting>
ComposeLeAudioCodecCapabilities(const std::vector<setting::Scenario> & supported_scenarios)333 BluetoothLeAudioCodecsProvider::ComposeLeAudioCodecCapabilities(
334 const std::vector<setting::Scenario>& supported_scenarios) {
335 std::vector<LeAudioCodecCapabilitiesSetting> le_audio_codec_capabilities;
336 for (const auto& scenario : supported_scenarios) {
337 UnicastCapability unicast_encode_capability =
338 GetUnicastCapability(scenario.getEncode());
339 UnicastCapability unicast_decode_capability =
340 GetUnicastCapability(scenario.getDecode());
341 BroadcastCapability broadcast_capability = {.codecType =
342 CodecType::UNKNOWN};
343
344 if (scenario.hasBroadcast()) {
345 broadcast_capability = GetBroadcastCapability(scenario.getBroadcast());
346 }
347
348 // At least one capability should be valid
349 if (unicast_encode_capability.codecType == CodecType::UNKNOWN &&
350 unicast_decode_capability.codecType == CodecType::UNKNOWN &&
351 broadcast_capability.codecType == CodecType::UNKNOWN) {
352 LOG(ERROR) << __func__ << ": None of the capability is valid.";
353 continue;
354 }
355
356 le_audio_codec_capabilities.push_back(
357 {.unicastEncodeCapability = unicast_encode_capability,
358 .unicastDecodeCapability = unicast_decode_capability,
359 .broadcastCapability = broadcast_capability});
360 }
361 return le_audio_codec_capabilities;
362 }
363
GetUnicastCapability(const std::string & coding_direction)364 UnicastCapability BluetoothLeAudioCodecsProvider::GetUnicastCapability(
365 const std::string& coding_direction) {
366 if (coding_direction == "invalid") {
367 return {.codecType = CodecType::UNKNOWN};
368 }
369
370 auto configuration_iter = configuration_map_.find(coding_direction);
371 if (configuration_iter == configuration_map_.end()) {
372 return {.codecType = CodecType::UNKNOWN};
373 }
374
375 auto codec_configuration_iter = codec_configuration_map_.find(
376 configuration_iter->second.getCodecConfiguration());
377 if (codec_configuration_iter == codec_configuration_map_.end()) {
378 return {.codecType = CodecType::UNKNOWN};
379 }
380
381 auto strategy_configuration_iter = strategy_configuration_map_.find(
382 configuration_iter->second.getStrategyConfiguration());
383 if (strategy_configuration_iter == strategy_configuration_map_.end()) {
384 return {.codecType = CodecType::UNKNOWN};
385 }
386
387 CodecType codec_type =
388 GetCodecType(codec_configuration_iter->second.getCodec());
389 if (codec_type == CodecType::LC3) {
390 return ComposeUnicastCapability(
391 codec_type,
392 GetAudioLocation(
393 strategy_configuration_iter->second.getAudioLocation()),
394 strategy_configuration_iter->second.getConnectedDevice(),
395 strategy_configuration_iter->second.getChannelCount(),
396 ComposeLc3Capability(codec_configuration_iter->second));
397 } else if (codec_type == CodecType::APTX_ADAPTIVE_LE ||
398 codec_type == CodecType::APTX_ADAPTIVE_LEX) {
399 return ComposeUnicastCapability(
400 codec_type,
401 GetAudioLocation(
402 strategy_configuration_iter->second.getAudioLocation()),
403 strategy_configuration_iter->second.getConnectedDevice(),
404 strategy_configuration_iter->second.getChannelCount(),
405 ComposeAptxAdaptiveLeCapability(codec_configuration_iter->second));
406 }
407 return {.codecType = CodecType::UNKNOWN};
408 }
409
GetBroadcastCapability(const std::string & coding_direction)410 BroadcastCapability BluetoothLeAudioCodecsProvider::GetBroadcastCapability(
411 const std::string& coding_direction) {
412 if (coding_direction == "invalid") {
413 return {.codecType = CodecType::UNKNOWN};
414 }
415
416 auto configuration_iter = configuration_map_.find(coding_direction);
417 if (configuration_iter == configuration_map_.end()) {
418 return {.codecType = CodecType::UNKNOWN};
419 }
420
421 auto codec_configuration_iter = codec_configuration_map_.find(
422 configuration_iter->second.getCodecConfiguration());
423 if (codec_configuration_iter == codec_configuration_map_.end()) {
424 return {.codecType = CodecType::UNKNOWN};
425 }
426
427 auto strategy_configuration_iter = strategy_configuration_map_.find(
428 configuration_iter->second.getStrategyConfiguration());
429 if (strategy_configuration_iter == strategy_configuration_map_.end()) {
430 return {.codecType = CodecType::UNKNOWN};
431 }
432
433 CodecType codec_type =
434 GetCodecType(codec_configuration_iter->second.getCodec());
435 std::vector<std::optional<Lc3Capabilities>> bcastLc3Cap(
436 1, std::optional(ComposeLc3Capability(codec_configuration_iter->second)));
437
438 if (codec_type == CodecType::LC3) {
439 return ComposeBroadcastCapability(
440 codec_type,
441 GetAudioLocation(
442 strategy_configuration_iter->second.getAudioLocation()),
443 strategy_configuration_iter->second.getChannelCount(), bcastLc3Cap);
444 }
445 return {.codecType = CodecType::UNKNOWN};
446 }
447
448 template <class T>
ComposeBroadcastCapability(const CodecType & codec_type,const AudioLocation & audio_location,const uint8_t & channel_count,const std::vector<T> & capability)449 BroadcastCapability BluetoothLeAudioCodecsProvider::ComposeBroadcastCapability(
450 const CodecType& codec_type, const AudioLocation& audio_location,
451 const uint8_t& channel_count, const std::vector<T>& capability) {
452 return {.codecType = codec_type,
453 .supportedChannel = audio_location,
454 .channelCountPerStream = channel_count,
455 .leAudioCodecCapabilities = std::optional(capability)};
456 }
457
458 template <class T>
ComposeUnicastCapability(const CodecType & codec_type,const AudioLocation & audio_location,const uint8_t & device_cnt,const uint8_t & channel_count,const T & capability)459 UnicastCapability BluetoothLeAudioCodecsProvider::ComposeUnicastCapability(
460 const CodecType& codec_type, const AudioLocation& audio_location,
461 const uint8_t& device_cnt, const uint8_t& channel_count,
462 const T& capability) {
463 return {
464 .codecType = codec_type,
465 .supportedChannel = audio_location,
466 .deviceCount = device_cnt,
467 .channelCountPerDevice = channel_count,
468 .leAudioCodecCapabilities =
469 UnicastCapability::LeAudioCodecCapabilities(capability),
470 };
471 }
472
ComposeLc3Capability(const setting::CodecConfiguration & codec_configuration)473 Lc3Capabilities BluetoothLeAudioCodecsProvider::ComposeLc3Capability(
474 const setting::CodecConfiguration& codec_configuration) {
475 return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()},
476 .frameDurationUs = {codec_configuration.getFrameDurationUs()},
477 .octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}};
478 }
479
480 AptxAdaptiveLeCapabilities
ComposeAptxAdaptiveLeCapability(const setting::CodecConfiguration & codec_configuration)481 BluetoothLeAudioCodecsProvider::ComposeAptxAdaptiveLeCapability(
482 const setting::CodecConfiguration& codec_configuration) {
483 return {.samplingFrequencyHz = {codec_configuration.getSamplingFrequency()},
484 .frameDurationUs = {codec_configuration.getFrameDurationUs()},
485 .octetsPerFrame = {codec_configuration.getOctetsPerCodecFrame()}};
486 }
487
GetAudioLocation(const setting::AudioLocation & audio_location)488 AudioLocation BluetoothLeAudioCodecsProvider::GetAudioLocation(
489 const setting::AudioLocation& audio_location) {
490 switch (audio_location) {
491 case setting::AudioLocation::MONO:
492 return kMonoAudio;
493 case setting::AudioLocation::STEREO:
494 return kStereoAudio;
495 default:
496 return AudioLocation::UNKNOWN;
497 }
498 }
499
GetCodecType(const setting::CodecType & codec_type)500 CodecType BluetoothLeAudioCodecsProvider::GetCodecType(
501 const setting::CodecType& codec_type) {
502 switch (codec_type) {
503 case setting::CodecType::LC3:
504 return CodecType::LC3;
505 case setting::CodecType::APTX_ADAPTIVE_LE:
506 return CodecType::APTX_ADAPTIVE_LE;
507 case setting::CodecType::APTX_ADAPTIVE_LEX:
508 return CodecType::APTX_ADAPTIVE_LEX;
509 default:
510 return CodecType::UNKNOWN;
511 }
512 }
513
IsValidCodecConfiguration(const setting::CodecConfiguration & codec_configuration)514 bool BluetoothLeAudioCodecsProvider::IsValidCodecConfiguration(
515 const setting::CodecConfiguration& codec_configuration) {
516 return codec_configuration.hasName() && codec_configuration.hasCodec() &&
517 codec_configuration.hasSamplingFrequency() &&
518 codec_configuration.hasFrameDurationUs() &&
519 codec_configuration.hasOctetsPerCodecFrame();
520 }
521
IsValidStrategyConfiguration(const setting::StrategyConfiguration & strategy_configuration)522 bool BluetoothLeAudioCodecsProvider::IsValidStrategyConfiguration(
523 const setting::StrategyConfiguration& strategy_configuration) {
524 if (!strategy_configuration.hasName() ||
525 !strategy_configuration.hasAudioLocation() ||
526 !strategy_configuration.hasConnectedDevice() ||
527 !strategy_configuration.hasChannelCount()) {
528 return false;
529 }
530 if (strategy_configuration.getAudioLocation() ==
531 setting::AudioLocation::STEREO) {
532 if ((strategy_configuration.getConnectedDevice() == 2 &&
533 strategy_configuration.getChannelCount() == 1) ||
534 (strategy_configuration.getConnectedDevice() == 1 &&
535 strategy_configuration.getChannelCount() == 2)) {
536 // Stereo
537 // 1. two connected device, one for L one for R
538 // 2. one connected device for both L and R
539 return true;
540 } else if (strategy_configuration.getConnectedDevice() == 0 &&
541 strategy_configuration.getChannelCount() == 2) {
542 // Broadcast
543 return true;
544 }
545 } else if (strategy_configuration.getAudioLocation() ==
546 setting::AudioLocation::MONO) {
547 if (strategy_configuration.getConnectedDevice() == 1 &&
548 strategy_configuration.getChannelCount() == 1) {
549 // Mono
550 return true;
551 }
552 }
553 return false;
554 }
555
556 } // namespace audio
557 } // namespace bluetooth
558 } // namespace hardware
559 } // namespace android
560 } // namespace aidl
561