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 #define LOG_TAG "BTAudioProviderSession_2_1"
18 
19 #include "BluetoothAudioSession_2_1.h"
20 
21 #include <android-base/logging.h>
22 #include <android-base/stringprintf.h>
23 
24 namespace android {
25 namespace bluetooth {
26 namespace audio {
27 using SessionType_2_1 =
28     ::android::hardware::bluetooth::audio::V2_1::SessionType;
29 using SessionType_2_0 =
30     ::android::hardware::bluetooth::audio::V2_0::SessionType;
31 
32 ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
33     BluetoothAudioSession_2_1::invalidSoftwareAudioConfiguration = {};
34 ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
35     BluetoothAudioSession_2_1::invalidOffloadAudioConfiguration = {};
36 
37 namespace {
is_2_0_session_type(const::android::hardware::bluetooth::audio::V2_1::SessionType & session_type)38 bool is_2_0_session_type(
39     const ::android::hardware::bluetooth::audio::V2_1::SessionType&
40         session_type) {
41   if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
42       session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
43       session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) {
44     return true;
45   } else {
46     return false;
47   }
48 }
49 }  // namespace
50 
BluetoothAudioSession_2_1(const::android::hardware::bluetooth::audio::V2_1::SessionType & session_type)51 BluetoothAudioSession_2_1::BluetoothAudioSession_2_1(
52     const ::android::hardware::bluetooth::audio::V2_1::SessionType&
53         session_type)
54     : audio_session(BluetoothAudioSessionInstance::GetSessionInstance(
55           static_cast<SessionType_2_0>(session_type))) {
56   if (is_2_0_session_type(session_type)) {
57     session_type_2_1_ = (SessionType_2_1::UNKNOWN);
58   } else {
59     session_type_2_1_ = (session_type);
60   }
61 }
62 
63 std::shared_ptr<BluetoothAudioSession>
GetAudioSession()64 BluetoothAudioSession_2_1::GetAudioSession() {
65   return audio_session;
66 }
67 
68 // The control function is for the bluetooth_audio module to get the current
69 // AudioConfiguration
70 const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
GetAudioConfig()71 BluetoothAudioSession_2_1::GetAudioConfig() {
72   std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
73   if (audio_session->IsSessionReady()) {
74     // If session is unknown it means it should be 2.0 type
75     if (session_type_2_1_ != SessionType_2_1::UNKNOWN)
76       return audio_config_2_1_;
77 
78     ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration toConf;
79     const AudioConfiguration fromConf = GetAudioSession()->GetAudioConfig();
80     // pcmConfig only differs between 2.0 and 2.1 in AudioConfiguration
81     if (fromConf.getDiscriminator() ==
82         AudioConfiguration::hidl_discriminator::codecConfig) {
83       toConf.codecConfig() = fromConf.codecConfig();
84     } else {
85       toConf.pcmConfig() = {
86           .sampleRate = static_cast<
87               ::android::hardware::bluetooth::audio::V2_1::SampleRate>(
88               fromConf.pcmConfig().sampleRate),
89           .channelMode = fromConf.pcmConfig().channelMode,
90           .bitsPerSample = fromConf.pcmConfig().bitsPerSample,
91           .dataIntervalUs = 0};
92     }
93     return toConf;
94   } else if (session_type_2_1_ ==
95              SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
96     return kInvalidOffloadAudioConfiguration;
97   } else {
98     return kInvalidSoftwareAudioConfiguration;
99   }
100 }
101 
UpdateAudioConfig(const::android::hardware::bluetooth::audio::V2_1::AudioConfiguration & audio_config)102 bool BluetoothAudioSession_2_1::UpdateAudioConfig(
103     const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
104         audio_config) {
105   bool is_software_session =
106       (session_type_2_1_ == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
107        session_type_2_1_ ==
108            SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
109        session_type_2_1_ ==
110            SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
111        session_type_2_1_ ==
112            SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
113   bool is_offload_session =
114       (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH);
115   auto audio_config_discriminator = audio_config.getDiscriminator();
116   bool is_software_audio_config =
117       (is_software_session &&
118        audio_config_discriminator ==
119            ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
120                hidl_discriminator::pcmConfig);
121   bool is_offload_audio_config =
122       (is_offload_session &&
123        audio_config_discriminator ==
124            ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
125                hidl_discriminator::codecConfig);
126   if (!is_software_audio_config && !is_offload_audio_config) {
127     return false;
128   }
129   audio_config_2_1_ = audio_config;
130   return true;
131 }
132 
133 // The report function is used to report that the Bluetooth stack has started
134 // this session without any failure, and will invoke session_changed_cb_ to
135 // notify those registered bluetooth_audio outputs
OnSessionStarted(const sp<IBluetoothAudioPort> stack_iface,const DataMQ::Descriptor * dataMQ,const::android::hardware::bluetooth::audio::V2_1::AudioConfiguration & audio_config)136 void BluetoothAudioSession_2_1::OnSessionStarted(
137     const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
138     const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
139         audio_config) {
140   if (session_type_2_1_ == SessionType_2_1::UNKNOWN) {
141     ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration config;
142     if (audio_config.getDiscriminator() ==
143         ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
144             hidl_discriminator::codecConfig) {
145       config.codecConfig(audio_config.codecConfig());
146     } else {
147       auto& tmpPcm = audio_config.pcmConfig();
148       config.pcmConfig(
149           ::android::hardware::bluetooth::audio::V2_0::PcmParameters{
150               .sampleRate = static_cast<SampleRate>(tmpPcm.sampleRate),
151               .channelMode = tmpPcm.channelMode,
152               .bitsPerSample = tmpPcm.bitsPerSample
153               /*dataIntervalUs is not passed to 2.0 */
154           });
155     }
156 
157     audio_session->OnSessionStarted(stack_iface, dataMQ, config);
158   } else {
159     std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
160     if (stack_iface == nullptr) {
161       LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
162                  << ", IBluetoothAudioPort Invalid";
163     } else if (!UpdateAudioConfig(audio_config)) {
164       LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
165                  << ", AudioConfiguration=" << toString(audio_config)
166                  << " Invalid";
167     } else if (!audio_session->UpdateDataPath(dataMQ)) {
168       LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
169                  << " DataMQ Invalid";
170       audio_config_2_1_ =
171           (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH
172                ? kInvalidOffloadAudioConfiguration
173                : kInvalidSoftwareAudioConfiguration);
174     } else {
175       audio_session->stack_iface_ = stack_iface;
176       LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_)
177                 << ", AudioConfiguration=" << toString(audio_config);
178       audio_session->ReportSessionStatus();
179     };
180   }
181 }
182 
183 std::unique_ptr<BluetoothAudioSessionInstance_2_1>
184     BluetoothAudioSessionInstance_2_1::instance_ptr =
185         std::unique_ptr<BluetoothAudioSessionInstance_2_1>(
186             new BluetoothAudioSessionInstance_2_1());
187 
188 // API to fetch the session of A2DP / Hearing Aid
189 std::shared_ptr<BluetoothAudioSession_2_1>
GetSessionInstance(const SessionType_2_1 & session_type)190 BluetoothAudioSessionInstance_2_1::GetSessionInstance(
191     const SessionType_2_1& session_type) {
192   std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
193   if (!instance_ptr->sessions_map_.empty()) {
194     auto entry = instance_ptr->sessions_map_.find(session_type);
195     if (entry != instance_ptr->sessions_map_.end()) {
196       return entry->second;
197     }
198   }
199   std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
200       std::make_shared<BluetoothAudioSession_2_1>(session_type);
201   instance_ptr->sessions_map_[session_type] = session_ptr;
202   return session_ptr;
203 }
204 
205 }  // namespace audio
206 }  // namespace bluetooth
207 }  // namespace android
208