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