1 /*
2 * Copyright (C) 2021 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 "rust/topshim/hfp/hfp_shim.h"
18
19 #include <bluetooth/log.h>
20
21 #include "btif/include/btif_hf.h"
22 #include "common/strings.h"
23 #include "device/include/interop.h"
24 #include "include/hardware/bt_hf.h"
25 #include "os/log.h"
26 #include "src/profiles/hfp.rs.h"
27 #include "types/raw_address.h"
28
29 namespace rusty = ::bluetooth::topshim::rust;
30
31 namespace bluetooth {
32 namespace topshim {
33 namespace rust {
34 namespace internal {
35 static HfpIntf* g_hfpif;
36
connection_state_cb(bluetooth::headset::bthf_connection_state_t state,RawAddress * addr)37 static void connection_state_cb(bluetooth::headset::bthf_connection_state_t state, RawAddress* addr) {
38 rusty::hfp_connection_state_callback(state, *addr);
39 }
40
audio_state_cb(bluetooth::headset::bthf_audio_state_t state,RawAddress * addr)41 static void audio_state_cb(bluetooth::headset::bthf_audio_state_t state, RawAddress* addr) {
42 rusty::hfp_audio_state_callback(state, *addr);
43 }
44
volume_update_cb(uint8_t volume,RawAddress * addr)45 static void volume_update_cb(uint8_t volume, RawAddress* addr) {
46 rusty::hfp_volume_update_callback(volume, *addr);
47 }
48
mic_volume_update_cb(uint8_t volume,RawAddress * addr)49 static void mic_volume_update_cb(uint8_t volume, RawAddress* addr) {
50 rusty::hfp_mic_volume_update_callback(volume, *addr);
51 }
52
vendor_specific_at_command_cb(char * at_string,RawAddress * addr)53 static void vendor_specific_at_command_cb(char* at_string, RawAddress* addr) {
54 rusty::hfp_vendor_specific_at_command_callback(::rust::String{at_string}, *addr);
55 }
56
battery_level_update_cb(uint8_t battery_level,RawAddress * addr)57 static void battery_level_update_cb(uint8_t battery_level, RawAddress* addr) {
58 rusty::hfp_battery_level_update_callback(battery_level, *addr);
59 }
60
indicator_query_cb(RawAddress * addr)61 static void indicator_query_cb(RawAddress* addr) {
62 rusty::hfp_indicator_query_callback(*addr);
63 }
64
current_calls_query_cb(RawAddress * addr)65 static void current_calls_query_cb(RawAddress* addr) {
66 rusty::hfp_current_calls_query_callback(*addr);
67 }
68
answer_call_cb(RawAddress * addr)69 static void answer_call_cb(RawAddress* addr) {
70 rusty::hfp_answer_call_callback(*addr);
71 }
72
hangup_call_cb(RawAddress * addr)73 static void hangup_call_cb(RawAddress* addr) {
74 rusty::hfp_hangup_call_callback(*addr);
75 }
76
dial_call_cb(char * number,RawAddress * addr)77 static void dial_call_cb(char* number, RawAddress* addr) {
78 rusty::hfp_dial_call_callback(::rust::String{number}, *addr);
79 }
80
call_hold_cb(bluetooth::headset::bthf_chld_type_t chld,RawAddress * addr)81 static void call_hold_cb(bluetooth::headset::bthf_chld_type_t chld, RawAddress* addr) {
82 rusty::CallHoldCommand chld_rs;
83 switch (chld) {
84 case bluetooth::headset::BTHF_CHLD_TYPE_RELEASEHELD:
85 chld_rs = rusty::CallHoldCommand::ReleaseHeld;
86 break;
87 case bluetooth::headset::BTHF_CHLD_TYPE_RELEASEACTIVE_ACCEPTHELD:
88 chld_rs = rusty::CallHoldCommand::ReleaseActiveAcceptHeld;
89 break;
90 case bluetooth::headset::BTHF_CHLD_TYPE_HOLDACTIVE_ACCEPTHELD:
91 chld_rs = rusty::CallHoldCommand::HoldActiveAcceptHeld;
92 break;
93 case bluetooth::headset::BTHF_CHLD_TYPE_ADDHELDTOCONF:
94 chld_rs = rusty::CallHoldCommand::AddHeldToConf;
95 break;
96 default:
97 log::fatal("Unhandled enum value from C++");
98 }
99 rusty::hfp_call_hold_callback(chld_rs, *addr);
100 }
101
from_rust_call_state(rusty::CallState state)102 static headset::bthf_call_state_t from_rust_call_state(rusty::CallState state) {
103 switch (state) {
104 case rusty::CallState::Idle:
105 return headset::BTHF_CALL_STATE_IDLE;
106 case rusty::CallState::Incoming:
107 return headset::BTHF_CALL_STATE_INCOMING;
108 case rusty::CallState::Dialing:
109 return headset::BTHF_CALL_STATE_DIALING;
110 case rusty::CallState::Alerting:
111 return headset::BTHF_CALL_STATE_ALERTING;
112 case rusty::CallState::Active:
113 return headset::BTHF_CALL_STATE_ACTIVE;
114 case rusty::CallState::Held:
115 return headset::BTHF_CALL_STATE_HELD;
116 default:
117 log::fatal("Unhandled enum value from Rust");
118 }
119 }
120
debug_dump_cb(bool active,uint16_t codec_id,int total_num_decoded_frames,double packet_loss_ratio,uint64_t begin_ts,uint64_t end_ts,const char * pkt_status_in_hex,const char * pkt_status_in_binary)121 static void debug_dump_cb(
122 bool active,
123 uint16_t codec_id,
124 int total_num_decoded_frames,
125 double packet_loss_ratio,
126 uint64_t begin_ts,
127 uint64_t end_ts,
128 const char* pkt_status_in_hex,
129 const char* pkt_status_in_binary) {
130 rusty::hfp_debug_dump_callback(
131 active,
132 codec_id,
133 total_num_decoded_frames,
134 packet_loss_ratio,
135 begin_ts,
136 end_ts,
137 ::rust::String{pkt_status_in_hex},
138 ::rust::String{pkt_status_in_binary});
139 }
140 } // namespace internal
141
142 class DBusHeadsetCallbacks : public headset::Callbacks {
143 public:
GetInstance(headset::Interface * headset)144 static Callbacks* GetInstance(headset::Interface* headset) {
145 static Callbacks* instance = new DBusHeadsetCallbacks(headset);
146 return instance;
147 }
148
DBusHeadsetCallbacks(headset::Interface * headset)149 DBusHeadsetCallbacks(headset::Interface* headset) : headset_(headset){};
150
151 // headset::Callbacks
ConnectionStateCallback(headset::bthf_connection_state_t state,RawAddress * bd_addr)152 void ConnectionStateCallback(headset::bthf_connection_state_t state, RawAddress* bd_addr) override {
153 log::info("ConnectionStateCallback from {}", *bd_addr);
154 topshim::rust::internal::connection_state_cb(state, bd_addr);
155 }
156
AudioStateCallback(headset::bthf_audio_state_t state,RawAddress * bd_addr)157 void AudioStateCallback(headset::bthf_audio_state_t state, RawAddress* bd_addr) override {
158 log::info("AudioStateCallback {} from {}", state, *bd_addr);
159 topshim::rust::internal::audio_state_cb(state, bd_addr);
160 }
161
VoiceRecognitionCallback(headset::bthf_vr_state_t state,RawAddress * bd_addr)162 void VoiceRecognitionCallback(
163 [[maybe_unused]] headset::bthf_vr_state_t state, [[maybe_unused]] RawAddress* bd_addr) override {}
164
AnswerCallCallback(RawAddress * bd_addr)165 void AnswerCallCallback(RawAddress* bd_addr) override {
166 topshim::rust::internal::answer_call_cb(bd_addr);
167 }
168
HangupCallCallback(RawAddress * bd_addr)169 void HangupCallCallback(RawAddress* bd_addr) override {
170 topshim::rust::internal::hangup_call_cb(bd_addr);
171 }
172
VolumeControlCallback(headset::bthf_volume_type_t type,int volume,RawAddress * bd_addr)173 void VolumeControlCallback(headset::bthf_volume_type_t type, int volume, RawAddress* bd_addr) override {
174 if (volume < 0) return;
175 if (volume > 15) volume = 15;
176 if (type == headset::bthf_volume_type_t::BTHF_VOLUME_TYPE_SPK) {
177 log::info("VolumeControlCallback (Spk) {} from {}", volume, *bd_addr);
178 topshim::rust::internal::volume_update_cb(volume, bd_addr);
179 } else if (type == headset::bthf_volume_type_t::BTHF_VOLUME_TYPE_MIC) {
180 log::info("VolumeControlCallback (Mic) {} from {}", volume, *bd_addr);
181 topshim::rust::internal::mic_volume_update_cb(volume, bd_addr);
182 }
183 }
184
DialCallCallback(char * number,RawAddress * bd_addr)185 void DialCallCallback(char* number, RawAddress* bd_addr) override {
186 topshim::rust::internal::dial_call_cb(number, bd_addr);
187 }
188
DtmfCmdCallback(char tone,RawAddress * bd_addr)189 void DtmfCmdCallback([[maybe_unused]] char tone, [[maybe_unused]] RawAddress* bd_addr) override {}
190
NoiseReductionCallback(headset::bthf_nrec_t nrec,RawAddress * bd_addr)191 void NoiseReductionCallback(
192 [[maybe_unused]] headset::bthf_nrec_t nrec, [[maybe_unused]] RawAddress* bd_addr) override {}
193
WbsCallback(headset::bthf_wbs_config_t wbs,RawAddress * addr)194 void WbsCallback(headset::bthf_wbs_config_t wbs, RawAddress* addr) override {
195 log::info("WbsCallback {} from {}", wbs, *addr);
196 rusty::hfp_wbs_caps_update_callback(wbs == headset::BTHF_WBS_YES, *addr);
197 }
198
SwbCallback(headset::bthf_swb_codec_t codec,headset::bthf_swb_config_t swb,RawAddress * addr)199 void SwbCallback(
200 headset::bthf_swb_codec_t codec, headset::bthf_swb_config_t swb, RawAddress* addr) override {
201 log::info("SwbCallback codec:{}, swb:{} from {}", codec, swb, *addr);
202 rusty::hfp_swb_caps_update_callback(
203 (codec == headset::BTHF_SWB_CODEC_LC3 && swb == headset::BTHF_SWB_YES), *addr);
204 }
205
AtChldCallback(headset::bthf_chld_type_t chld,RawAddress * bd_addr)206 void AtChldCallback(headset::bthf_chld_type_t chld, RawAddress* bd_addr) override {
207 topshim::rust::internal::call_hold_cb(chld, bd_addr);
208 }
209
AtCnumCallback(RawAddress * bd_addr)210 void AtCnumCallback(RawAddress* bd_addr) override {
211 // Send an OK response to HF to indicate that we have no subscriber info.
212 // This is mandatory support for passing HFP/AG/NUM/BV-01-I.
213 headset_->AtResponse(headset::BTHF_AT_RESPONSE_OK, 0, bd_addr);
214 }
215
AtCindCallback(RawAddress * bd_addr)216 void AtCindCallback(RawAddress* bd_addr) override {
217 topshim::rust::internal::indicator_query_cb(bd_addr);
218 }
219
AtCopsCallback(RawAddress * bd_addr)220 void AtCopsCallback(RawAddress* bd_addr) override {
221 log::warn("Respond +COPS: 0 to AT+COPS? from {}", *bd_addr);
222 headset_->CopsResponse("", bd_addr);
223 }
224
AtClccCallback(RawAddress * bd_addr)225 void AtClccCallback(RawAddress* bd_addr) override {
226 topshim::rust::internal::current_calls_query_cb(bd_addr);
227 }
228
UnknownAtCallback(char * at_string,RawAddress * bd_addr)229 void UnknownAtCallback(char* at_string, RawAddress* bd_addr) override {
230 const std::string at_command = common::ToString(at_string);
231 // We are able to support +XAPL, +IPHONEACCEV, and +XEVENT commands,
232 // everything else will get an error reply.
233 const bool is_xapl = at_command.find("+XAPL") != std::string::npos;
234 const bool is_iphoneaccev = at_command.find("+IPHONEACCEV") != std::string::npos;
235 const bool is_xevent = at_command.find("+XEVENT") != std::string::npos;
236 if (!is_xapl && !is_iphoneaccev && !is_xevent) {
237 log::warn("Reply Error to UnknownAtCallback:{}", at_string);
238 headset_->AtResponse(headset::BTHF_AT_RESPONSE_ERROR, 0, bd_addr);
239 return;
240 }
241
242 if (is_xapl) {
243 // Respond that we support battery level reporting only (2).
244 headset_->FormattedAtResponse("+XAPL=iPhone,2", bd_addr);
245 }
246
247 // Ack all supported commands and bubble commands up for further processing
248 // if desired.
249 topshim::rust::internal::vendor_specific_at_command_cb(at_string, bd_addr);
250 headset_->AtResponse(headset::BTHF_AT_RESPONSE_OK, 0, bd_addr);
251 }
252
KeyPressedCallback(RawAddress * bd_addr)253 void KeyPressedCallback([[maybe_unused]] RawAddress* bd_addr) override {}
254
AtBindCallback(char * at_string,RawAddress * bd_addr)255 void AtBindCallback(char* at_string, RawAddress* bd_addr) override {
256 log::warn(
257 "AT+BIND {} from addr {}: Bluetooth HF Indicators is not supported.", at_string, *bd_addr);
258 }
259
AtBievCallback(headset::bthf_hf_ind_type_t ind_id,int ind_value,RawAddress * bd_addr)260 void AtBievCallback(headset::bthf_hf_ind_type_t ind_id, int ind_value, RawAddress* bd_addr) override {
261 switch (ind_id) {
262 case headset::bthf_hf_ind_type_t::BTHF_HF_IND_ENHANCED_DRIVER_SAFETY:
263 // We don't do anything with this but we do know what it is, send OK.
264 headset_->AtResponse(headset::BTHF_AT_RESPONSE_OK, 0, bd_addr);
265 break;
266 case headset::bthf_hf_ind_type_t::BTHF_HF_IND_BATTERY_LEVEL_STATUS:
267 topshim::rust::internal::battery_level_update_cb(ind_value, bd_addr);
268 headset_->AtResponse(headset::BTHF_AT_RESPONSE_OK, 0, bd_addr);
269 break;
270 default:
271 log::warn("AT+BIEV indicator {} with value {} from addr {}", ind_id, ind_value, *bd_addr);
272 return;
273 }
274 }
275
AtBiaCallback(bool service,bool roam,bool signal,bool battery,RawAddress * bd_addr)276 void AtBiaCallback(bool service, bool roam, bool signal, bool battery, RawAddress* bd_addr) override {
277 log::warn("AT+BIA=,,{},{},{},{},from addr {}", service, signal, roam, battery, *bd_addr);
278 }
279
DebugDumpCallback(bool active,uint16_t codec_id,int total_num_decoded_frames,double packet_loss_ratio,uint64_t begin_ts,uint64_t end_ts,const char * pkt_status_in_hex,const char * pkt_status_in_binary)280 void DebugDumpCallback(
281 bool active,
282 uint16_t codec_id,
283 int total_num_decoded_frames,
284 double packet_loss_ratio,
285 uint64_t begin_ts,
286 uint64_t end_ts,
287 const char* pkt_status_in_hex,
288 const char* pkt_status_in_binary) override {
289 log::warn(
290 "DebugDumpCallback {} {} {} {:f} {} {} {} {}",
291 active,
292 codec_id,
293 total_num_decoded_frames,
294 packet_loss_ratio,
295 (unsigned long long)begin_ts,
296 (unsigned long long)end_ts,
297 pkt_status_in_hex,
298 pkt_status_in_binary);
299 topshim::rust::internal::debug_dump_cb(
300 active,
301 codec_id,
302 total_num_decoded_frames,
303 packet_loss_ratio,
304 begin_ts,
305 end_ts,
306 pkt_status_in_hex,
307 pkt_status_in_binary);
308 }
309
310 private:
311 headset::Interface* headset_;
312 };
313
init()314 int HfpIntf::init() {
315 return intf_->Init(DBusHeadsetCallbacks::GetInstance(intf_), 1, false);
316 }
317
connect(RawAddress addr)318 uint32_t HfpIntf::connect(RawAddress addr) {
319 return intf_->Connect(&addr);
320 }
321
connect_audio(RawAddress addr,bool sco_offload,int disabled_codecs)322 int HfpIntf::connect_audio(RawAddress addr, bool sco_offload, int disabled_codecs) {
323 intf_->SetScoOffloadEnabled(sco_offload);
324 return intf_->ConnectAudio(&addr, disabled_codecs);
325 }
326
set_active_device(RawAddress addr)327 int HfpIntf::set_active_device(RawAddress addr) {
328 return intf_->SetActiveDevice(&addr);
329 }
330
set_volume(int8_t volume,RawAddress addr)331 int HfpIntf::set_volume(int8_t volume, RawAddress addr) {
332 return intf_->VolumeControl(headset::bthf_volume_type_t::BTHF_VOLUME_TYPE_SPK, volume, &addr);
333 }
334
set_mic_volume(int8_t volume,RawAddress addr)335 uint32_t HfpIntf::set_mic_volume(int8_t volume, RawAddress addr) {
336 return intf_->VolumeControl(headset::bthf_volume_type_t::BTHF_VOLUME_TYPE_MIC, volume, &addr);
337 }
338
disconnect(RawAddress addr)339 uint32_t HfpIntf::disconnect(RawAddress addr) {
340 return intf_->Disconnect(&addr);
341 }
342
disconnect_audio(RawAddress addr)343 int HfpIntf::disconnect_audio(RawAddress addr) {
344 return intf_->DisconnectAudio(&addr);
345 }
346
device_status_notification(TelephonyDeviceStatus status,RawAddress addr)347 uint32_t HfpIntf::device_status_notification(TelephonyDeviceStatus status, RawAddress addr) {
348 return intf_->DeviceStatusNotification(
349 status.network_available ? headset::BTHF_NETWORK_STATE_AVAILABLE
350 : headset::BTHF_NETWORK_STATE_NOT_AVAILABLE,
351 status.roaming ? headset::BTHF_SERVICE_TYPE_ROAMING : headset::BTHF_SERVICE_TYPE_HOME,
352 status.signal_strength,
353 status.battery_level,
354 &addr);
355 }
356
indicator_query_response(TelephonyDeviceStatus device_status,PhoneState phone_state,RawAddress addr)357 uint32_t HfpIntf::indicator_query_response(
358 TelephonyDeviceStatus device_status, PhoneState phone_state, RawAddress addr) {
359 return intf_->CindResponse(
360 device_status.network_available ? 1 : 0,
361 phone_state.num_active,
362 phone_state.num_held,
363 topshim::rust::internal::from_rust_call_state(phone_state.state),
364 device_status.signal_strength,
365 device_status.roaming ? 1 : 0,
366 device_status.battery_level,
367 &addr);
368 }
369
current_calls_query_response(const::rust::Vec<CallInfo> & call_list,RawAddress addr)370 uint32_t HfpIntf::current_calls_query_response(
371 const ::rust::Vec<CallInfo>& call_list, RawAddress addr) {
372 for (const auto& c : call_list) {
373 std::string number{c.number};
374 intf_->ClccResponse(
375 c.index,
376 c.dir_incoming ? headset::BTHF_CALL_DIRECTION_INCOMING
377 : headset::BTHF_CALL_DIRECTION_OUTGOING,
378 topshim::rust::internal::from_rust_call_state(c.state),
379 /*mode=*/headset::BTHF_CALL_TYPE_VOICE,
380 /*multi_party=*/headset::BTHF_CALL_MPTY_TYPE_SINGLE,
381 number.c_str(),
382 /*type=*/headset::BTHF_CALL_ADDRTYPE_UNKNOWN,
383 &addr);
384 }
385
386 // NULL termination (Completes response)
387 return intf_->ClccResponse(
388 /*index=*/0,
389 /*dir=*/(headset::bthf_call_direction_t)0,
390 /*state=*/(headset::bthf_call_state_t)0,
391 /*mode=*/(headset::bthf_call_mode_t)0,
392 /*multi_party=*/(headset::bthf_call_mpty_type_t)0,
393 /*number=*/"",
394 /*type=*/(headset::bthf_call_addrtype_t)0,
395 &addr);
396 }
397
phone_state_change(PhoneState phone_state,const::rust::String & number_rs,RawAddress addr)398 uint32_t HfpIntf::phone_state_change(
399 PhoneState phone_state, const ::rust::String& number_rs, RawAddress addr) {
400 std::string number{number_rs};
401 return intf_->PhoneStateChange(
402 phone_state.num_active,
403 phone_state.num_held,
404 topshim::rust::internal::from_rust_call_state(phone_state.state),
405 number.c_str(),
406 /*type=*/(headset::bthf_call_addrtype_t)0,
407 /*name=*/"",
408 &addr);
409 }
410
simple_at_response(bool ok,RawAddress addr)411 uint32_t HfpIntf::simple_at_response(bool ok, RawAddress addr) {
412 return intf_->AtResponse(
413 (ok ? headset::BTHF_AT_RESPONSE_OK : headset::BTHF_AT_RESPONSE_ERROR), 0, &addr);
414 }
415
debug_dump()416 void HfpIntf::debug_dump() {
417 intf_->DebugDump();
418 }
419
cleanup()420 void HfpIntf::cleanup() {}
421
GetHfpProfile(const unsigned char * btif)422 std::unique_ptr<HfpIntf> GetHfpProfile(const unsigned char* btif) {
423 if (internal::g_hfpif) std::abort();
424
425 const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif);
426
427 auto hfpif = std::make_unique<HfpIntf>(const_cast<headset::Interface*>(
428 reinterpret_cast<const headset::Interface*>(btif_->get_profile_interface("handsfree"))));
429 internal::g_hfpif = hfpif.get();
430
431 return hfpif;
432 }
433
interop_insert_call_when_sco_start(RawAddress addr)434 bool interop_insert_call_when_sco_start(RawAddress addr) {
435 return interop_match_addr(interop_feature_t::INTEROP_INSERT_CALL_WHEN_SCO_START, &addr);
436 }
437
438 } // namespace rust
439 } // namespace topshim
440 } // namespace bluetooth
441