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 "gd/rust/topshim/hfp/hfp_shim.h"
18
19 #include "btif/include/btif_hf.h"
20 #include "gd/os/log.h"
21 #include "gd/rust/topshim/common/utils.h"
22 #include "include/hardware/bt_hf.h"
23 #include "src/profiles/hfp.rs.h"
24 #include "types/raw_address.h"
25
26 namespace rusty = ::bluetooth::topshim::rust;
27
28 namespace bluetooth {
29 namespace topshim {
30 namespace rust {
31 namespace internal {
32 static HfpIntf* g_hfpif;
33
connection_state_cb(bluetooth::headset::bthf_connection_state_t state,RawAddress * addr)34 static void connection_state_cb(bluetooth::headset::bthf_connection_state_t state, RawAddress* addr) {
35 RustRawAddress raddr = rusty::CopyToRustAddress(*addr);
36 rusty::hfp_connection_state_callback(state, raddr);
37 }
38
audio_state_cb(bluetooth::headset::bthf_audio_state_t state,RawAddress * addr)39 static void audio_state_cb(bluetooth::headset::bthf_audio_state_t state, RawAddress* addr) {
40 RustRawAddress raddr = rusty::CopyToRustAddress(*addr);
41 rusty::hfp_audio_state_callback(state, raddr);
42 }
43
44 } // namespace internal
45
46 class DBusHeadsetCallbacks : public headset::Callbacks {
47 public:
GetInstance(headset::Interface * headset)48 static Callbacks* GetInstance(headset::Interface* headset) {
49 static Callbacks* instance = new DBusHeadsetCallbacks(headset);
50 return instance;
51 }
52
DBusHeadsetCallbacks(headset::Interface * headset)53 DBusHeadsetCallbacks(headset::Interface* headset) : headset_(headset) {
54 call_status = 0;
55 };
56
57 // headset::Callbacks
ConnectionStateCallback(headset::bthf_connection_state_t state,RawAddress * bd_addr)58 void ConnectionStateCallback(headset::bthf_connection_state_t state, RawAddress* bd_addr) override {
59 LOG_INFO("ConnectionStateCallback from %s", bd_addr->ToString().c_str());
60 topshim::rust::internal::connection_state_cb(state, bd_addr);
61 }
62
AudioStateCallback(headset::bthf_audio_state_t state,RawAddress * bd_addr)63 void AudioStateCallback(headset::bthf_audio_state_t state, RawAddress* bd_addr) override {
64 LOG_INFO("AudioStateCallback %u from %s", state, bd_addr->ToString().c_str());
65 topshim::rust::internal::audio_state_cb(state, bd_addr);
66
67 switch (state) {
68 case headset::bthf_audio_state_t::BTHF_AUDIO_STATE_CONNECTED:
69 SetCallStatus(1, bd_addr);
70 // This triggers a +VGS command to set the speaker volume for HFP
71 // devices.
72 // TODO(b/215089433): Add a set volume API and have client to handle the
73 // set volume when start.
74 headset_->VolumeControl(headset::bthf_volume_type_t::BTHF_VOLUME_TYPE_SPK, 5, bd_addr);
75 return;
76 case headset::bthf_audio_state_t::BTHF_AUDIO_STATE_DISCONNECTED:
77 SetCallStatus(0, bd_addr);
78 return;
79 default:
80 return;
81 }
82 }
83
VoiceRecognitionCallback(headset::bthf_vr_state_t state,RawAddress * bd_addr)84 void VoiceRecognitionCallback(
85 [[maybe_unused]] headset::bthf_vr_state_t state, [[maybe_unused]] RawAddress* bd_addr) override {}
86
AnswerCallCallback(RawAddress * bd_addr)87 void AnswerCallCallback([[maybe_unused]] RawAddress* bd_addr) override {}
88
HangupCallCallback(RawAddress * bd_addr)89 void HangupCallCallback([[maybe_unused]] RawAddress* bd_addr) override {}
90
VolumeControlCallback(headset::bthf_volume_type_t type,int volume,RawAddress * bd_addr)91 void VolumeControlCallback(
92 [[maybe_unused]] headset::bthf_volume_type_t type,
93 [[maybe_unused]] int volume,
94 [[maybe_unused]] RawAddress* bd_addr) override {}
95
DialCallCallback(char * number,RawAddress * bd_addr)96 void DialCallCallback([[maybe_unused]] char* number, [[maybe_unused]] RawAddress* bd_addr) override {}
97
DtmfCmdCallback(char tone,RawAddress * bd_addr)98 void DtmfCmdCallback([[maybe_unused]] char tone, [[maybe_unused]] RawAddress* bd_addr) override {}
99
NoiseReductionCallback(headset::bthf_nrec_t nrec,RawAddress * bd_addr)100 void NoiseReductionCallback(
101 [[maybe_unused]] headset::bthf_nrec_t nrec, [[maybe_unused]] RawAddress* bd_addr) override {}
102
WbsCallback(headset::bthf_wbs_config_t wbs,RawAddress * bd_addr)103 void WbsCallback([[maybe_unused]] headset::bthf_wbs_config_t wbs, [[maybe_unused]] RawAddress* bd_addr) override {}
104
AtChldCallback(headset::bthf_chld_type_t chld,RawAddress * bd_addr)105 void AtChldCallback([[maybe_unused]] headset::bthf_chld_type_t chld, [[maybe_unused]] RawAddress* bd_addr) override {}
106
AtCnumCallback(RawAddress * bd_addr)107 void AtCnumCallback([[maybe_unused]] RawAddress* bd_addr) override {}
108
AtCindCallback(RawAddress * bd_addr)109 void AtCindCallback(RawAddress* bd_addr) override {
110 // This is required to setup the SLC, the format of the response should be
111 // +CIND: <call>,<callsetup>,<service>,<signal>,<roam>,<battery>,<callheld>
112 LOG_WARN("Respond +CIND: 0,0,1,5,0,5,0 to AT+CIND? from %s", bd_addr->ToString().c_str());
113
114 // headset::Interface::CindResponse's parameters are similar but different
115 // from the actual CIND response. It will construct the final response for
116 // you based on the arguments you provide.
117 // CindResponse(network_service_availability, active_call_num,
118 // held_call_num, callsetup_state, signal_strength,
119 // roam_state, battery_level, bd_addr);
120 headset_->CindResponse(1, 0, 0, headset::BTHF_CALL_STATE_IDLE, 5, 0, 5, bd_addr);
121 }
122
AtCopsCallback(RawAddress * bd_addr)123 void AtCopsCallback(RawAddress* bd_addr) override {
124 LOG_WARN("Respond +COPS: 0 to AT+COPS? from %s", bd_addr->ToString().c_str());
125 headset_->CopsResponse("", bd_addr);
126 }
127
AtClccCallback(RawAddress * bd_addr)128 void AtClccCallback(RawAddress* bd_addr) override {
129 // Reply +CLCC:<idx>,<dir>,<status>,<mode>,<mprty>[,<number>,<type>] if
130 // there is an active audio connection. Simply rely OK otherwise.
131 // This is required for some headsets to start to send actual data to AG.
132 if (call_status)
133 headset_->ClccResponse(
134 /*index=*/1,
135 /*dir=*/headset::BTHF_CALL_DIRECTION_OUTGOING,
136 /*state=*/headset::BTHF_CALL_STATE_ACTIVE,
137 /*mode=*/headset::BTHF_CALL_TYPE_VOICE,
138 /*multi_party=*/headset::BTHF_CALL_MPTY_TYPE_SINGLE,
139 /*number=*/"",
140 /*type=*/headset::BTHF_CALL_ADDRTYPE_UNKNOWN,
141 bd_addr);
142
143 headset_->AtResponse(headset::BTHF_AT_RESPONSE_OK, 0, bd_addr);
144 }
145
UnknownAtCallback(char * at_string,RawAddress * bd_addr)146 void UnknownAtCallback(char* at_string, RawAddress* bd_addr) override {
147 LOG_WARN("Reply Error to UnknownAtCallback:%s", at_string);
148 headset_->AtResponse(headset::BTHF_AT_RESPONSE_ERROR, 0, bd_addr);
149 }
150
KeyPressedCallback(RawAddress * bd_addr)151 void KeyPressedCallback([[maybe_unused]] RawAddress* bd_addr) override {}
152
AtBindCallback(char * at_string,RawAddress * bd_addr)153 void AtBindCallback(char* at_string, RawAddress* bd_addr) override {
154 LOG_WARN(
155 "AT+BIND %s from addr %s: Bluetooth HF Indicators is not supported.", at_string, bd_addr->ToString().c_str());
156 }
157
AtBievCallback(headset::bthf_hf_ind_type_t ind_id,int ind_value,RawAddress * bd_addr)158 void AtBievCallback(headset::bthf_hf_ind_type_t ind_id, int ind_value, RawAddress* bd_addr) override {
159 LOG_WARN(
160 "AT+BIEV=%d,%d from addr %s: Bluetooth HF Indicators is not supported.",
161 ind_id,
162 ind_value,
163 bd_addr->ToString().c_str());
164 }
165
AtBiaCallback(bool service,bool roam,bool signal,bool battery,RawAddress * bd_addr)166 void AtBiaCallback(bool service, bool roam, bool signal, bool battery, RawAddress* bd_addr) override {
167 LOG_WARN("AT+BIA=,,%d,%d,%d,%d,from addr %s", service, signal, roam, battery, bd_addr->ToString().c_str());
168 }
169
170 private:
171 headset::Interface* headset_;
172 int call_status;
173
SetCallStatus(int call,RawAddress * bd_addr)174 void SetCallStatus(int call, RawAddress* bd_addr) {
175 if (call == call_status) return;
176
177 if (call) {
178 // This triggers a +CIEV command to set the call status for HFP
179 // devices. It is required along with the SCO establishment for some
180 // devices to provide sound.
181 headset_->PhoneStateChange(
182 /*num_active=*/1,
183 /*num_held=*/0,
184 /*call_setup_state=*/headset::bthf_call_state_t::BTHF_CALL_STATE_IDLE,
185 /*number=*/"",
186 /*type=*/(headset::bthf_call_addrtype_t)0,
187 /*name=*/"",
188 /*bd_addr=*/bd_addr);
189 } else {
190 headset_->PhoneStateChange(
191 /*num_active=*/0,
192 /*num_held=*/0,
193 /*call_setup_state=*/headset::bthf_call_state_t::BTHF_CALL_STATE_IDLE,
194 /*number=*/"",
195 /*type=*/(headset::bthf_call_addrtype_t)0,
196 /*name=*/"",
197 /*bd_addr=*/bd_addr);
198 }
199
200 call_status = call;
201 }
202 };
203
init()204 int HfpIntf::init() {
205 return intf_->Init(DBusHeadsetCallbacks::GetInstance(intf_), 1, false);
206 }
207
connect(RustRawAddress bt_addr)208 int HfpIntf::connect(RustRawAddress bt_addr) {
209 RawAddress addr = rusty::CopyFromRustAddress(bt_addr);
210 return intf_->Connect(&addr);
211 }
212
connect_audio(RustRawAddress bt_addr)213 int HfpIntf::connect_audio(RustRawAddress bt_addr) {
214 RawAddress addr = rusty::CopyFromRustAddress(bt_addr);
215 return intf_->ConnectAudio(&addr);
216 }
217
disconnect(RustRawAddress bt_addr)218 int HfpIntf::disconnect(RustRawAddress bt_addr) {
219 RawAddress addr = rusty::CopyFromRustAddress(bt_addr);
220 return intf_->Disconnect(&addr);
221 }
222
disconnect_audio(RustRawAddress bt_addr)223 int HfpIntf::disconnect_audio(RustRawAddress bt_addr) {
224 RawAddress addr = rusty::CopyFromRustAddress(bt_addr);
225 return intf_->DisconnectAudio(&addr);
226 }
227
cleanup()228 void HfpIntf::cleanup() {}
229
GetHfpProfile(const unsigned char * btif)230 std::unique_ptr<HfpIntf> GetHfpProfile(const unsigned char* btif) {
231 if (internal::g_hfpif) std::abort();
232
233 const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif);
234
235 auto hfpif = std::make_unique<HfpIntf>(const_cast<headset::Interface*>(
236 reinterpret_cast<const headset::Interface*>(btif_->get_profile_interface("handsfree"))));
237 internal::g_hfpif = hfpif.get();
238
239 return hfpif;
240 }
241
242 } // namespace rust
243 } // namespace topshim
244 } // namespace bluetooth
245