1 /* Spa hsphfpd backend
2  *
3  * Based on previous work for pulseaudio by: Pali Rohár <pali.rohar@gmail.com>
4  * Copyright © 2020 Collabora Ltd.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23  * DEALINGS IN THE SOFTWARE.
24  */
25 
26 #include <errno.h>
27 #include <unistd.h>
28 #include <sys/socket.h>
29 
30 #include <dbus/dbus.h>
31 
32 #include <spa/support/log.h>
33 #include <spa/support/loop.h>
34 #include <spa/support/dbus.h>
35 #include <spa/support/plugin.h>
36 #include <spa/utils/string.h>
37 #include <spa/utils/type.h>
38 #include <spa/param/audio/raw.h>
39 
40 #include "defs.h"
41 
42 static struct spa_log_topic log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.hsphfpd");
43 #undef SPA_LOG_TOPIC_DEFAULT
44 #define SPA_LOG_TOPIC_DEFAULT &log_topic
45 
46 struct impl {
47 	struct spa_bt_backend this;
48 
49 	struct spa_bt_monitor *monitor;
50 
51 	struct spa_log *log;
52 	struct spa_loop *main_loop;
53 	struct spa_dbus *dbus;
54 	DBusConnection *conn;
55 
56 	const struct spa_bt_quirks *quirks;
57 
58 	struct spa_list endpoint_list;
59 	bool endpoints_listed;
60 
61 	char *hsphfpd_service_id;
62 
63 	bool acquire_in_progress;
64 
65 	unsigned int filters_added:1;
66 	unsigned int msbc_supported:1;
67 };
68 
69 enum hsphfpd_volume_control {
70 	HSPHFPD_VOLUME_CONTROL_NONE = 1,
71 	HSPHFPD_VOLUME_CONTROL_LOCAL,
72 	HSPHFPD_VOLUME_CONTROL_REMOTE,
73 };
74 
75 enum hsphfpd_profile {
76 	HSPHFPD_PROFILE_HEADSET = 1,
77 	HSPHFPD_PROFILE_HANDSFREE,
78 };
79 
80 enum hsphfpd_role {
81 	HSPHFPD_ROLE_CLIENT = 1,
82 	HSPHFPD_ROLE_GATEWAY,
83 };
84 
85 struct hsphfpd_transport_data {
86 	char *transport_path;
87 	bool rx_soft_volume;
88 	bool tx_soft_volume;
89 	int rx_volume_gain;
90 	int tx_volume_gain;
91 	int max_rx_volume_gain;
92 	int max_tx_volume_gain;
93 	enum hsphfpd_volume_control rx_volume_control;
94 	enum hsphfpd_volume_control tx_volume_control;
95 };
96 
97 struct hsphfpd_endpoint {
98 	struct spa_list link;
99 	char *path;
100 	bool valid;
101 	bool connected;
102 	char *remote_address;
103 	char *local_address;
104 	enum hsphfpd_profile profile;
105 	enum hsphfpd_role role;
106 	int air_codecs;
107 };
108 
109 #define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager"
110 
111 #define HSPHFPD_APPLICATION_MANAGER_INTERFACE HSPHFPD_SERVICE ".ApplicationManager"
112 #define HSPHFPD_ENDPOINT_INTERFACE            HSPHFPD_SERVICE ".Endpoint"
113 #define HSPHFPD_AUDIO_AGENT_INTERFACE         HSPHFPD_SERVICE ".AudioAgent"
114 #define HSPHFPD_AUDIO_TRANSPORT_INTERFACE     HSPHFPD_SERVICE ".AudioTransport"
115 
116 #define APPLICATION_OBJECT_MANAGER_PATH    "/Profile/hsphfpd/manager"
117 #define HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ "/Profile/hsphfpd/pcm_s16le_8khz_agent"
118 #define HSPHFP_AUDIO_CLIENT_MSBC           "/Profile/hsphfpd/msbc_agent"
119 
120 #define HSPHFP_AIR_CODEC_CVSD           "CVSD"
121 #define HSPHFP_AIR_CODEC_MSBC           "mSBC"
122 #define HSPHFP_AGENT_CODEC_PCM          "PCM_s16le_8kHz"
123 #define HSPHFP_AGENT_CODEC_MSBC         "mSBC"
124 
125 #define APPLICATION_OBJECT_MANAGER_INTROSPECT_XML                              \
126     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                                  \
127     "<node>\n"                                                                 \
128     " <interface name=\"" DBUS_INTERFACE_OBJECTMANAGER "\">\n"                 \
129     "  <method name=\"GetManagedObjects\">\n"                                  \
130     "   <arg name=\"objects\" direction=\"out\" type=\"a{oa{sa{sv}}}\"/>\n"    \
131     "  </method>\n"                                                            \
132     "  <signal name=\"InterfacesAdded\">\n"                                    \
133     "   <arg name=\"object\" type=\"o\"/>\n"                                   \
134     "   <arg name=\"interfaces\" type=\"a{sa{sv}}\"/>\n"                       \
135     "  </signal>\n"                                                            \
136     "  <signal name=\"InterfacesRemoved\">\n"                                  \
137     "   <arg name=\"object\" type=\"o\"/>\n"                                   \
138     "   <arg name=\"interfaces\" type=\"as\"/>\n"                              \
139     "  </signal>\n"                                                            \
140     " </interface>\n"                                                          \
141     " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"                \
142     "  <method name=\"Introspect\">\n"                                         \
143     "   <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"                   \
144     "  </method>\n"                                                            \
145     " </interface>\n"                                                          \
146     "</node>\n"
147 
148 #define AUDIO_AGENT_ENDPOINT_INTROSPECT_XML                           \
149 	DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
150 	"<node>\n"                                                          \
151 	" <interface name=\"" HSPHFPD_AUDIO_AGENT_INTERFACE "\">\n"         \
152 	"  <method name=\"NewConnection\">\n"                               \
153 	"   <arg name=\"audio_transport\" direction=\"in\" type=\"o\"/>\n"  \
154 	"   <arg name=\"fd\" direction=\"in\" type=\"h\"/>\n"               \
155 	"   <arg name=\"properties\" direction=\"in\" type=\"a{sv}\"/>\n"   \
156 	"  </method>\n"                                                     \
157 	"  <property name=\"AgentCodec\" type=\"s\" access=\"read\"/>\n"    \
158 	" </interface>\n"                                                   \
159 	" <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"         \
160 	"  <method name=\"Introspect\">\n"                                  \
161 	"   <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"            \
162 	"  </method>\n"                                                     \
163 	" </interface>\n"                                                   \
164 	"</node>\n"
165 
166 #define HSPHFPD_ERROR_INVALID_ARGUMENTS   HSPHFPD_SERVICE ".Error.InvalidArguments"
167 #define HSPHFPD_ERROR_ALREADY_EXISTS      HSPHFPD_SERVICE ".Error.AlreadyExists"
168 #define HSPHFPD_ERROR_DOES_NOT_EXISTS     HSPHFPD_SERVICE ".Error.DoesNotExist"
169 #define HSPHFPD_ERROR_NOT_CONNECTED       HSPHFPD_SERVICE ".Error.NotConnected"
170 #define HSPHFPD_ERROR_ALREADY_CONNECTED   HSPHFPD_SERVICE ".Error.AlreadyConnected"
171 #define HSPHFPD_ERROR_IN_PROGRESS         HSPHFPD_SERVICE ".Error.InProgress"
172 #define HSPHFPD_ERROR_IN_USE              HSPHFPD_SERVICE ".Error.InUse"
173 #define HSPHFPD_ERROR_NOT_SUPPORTED       HSPHFPD_SERVICE ".Error.NotSupported"
174 #define HSPHFPD_ERROR_NOT_AVAILABLE       HSPHFPD_SERVICE ".Error.NotAvailable"
175 #define HSPHFPD_ERROR_FAILED              HSPHFPD_SERVICE ".Error.Failed"
176 #define HSPHFPD_ERROR_REJECTED            HSPHFPD_SERVICE ".Error.Rejected"
177 #define HSPHFPD_ERROR_CANCELED            HSPHFPD_SERVICE ".Error.Canceled"
178 
endpoint_find(struct impl * backend,const char * path)179 static struct hsphfpd_endpoint *endpoint_find(struct impl *backend, const char *path)
180 {
181 	struct hsphfpd_endpoint *d;
182 	spa_list_for_each(d, &backend->endpoint_list, link)
183 		if (spa_streq(d->path, path))
184 			return d;
185 	return NULL;
186 }
187 
endpoint_free(struct hsphfpd_endpoint * endpoint)188 static void endpoint_free(struct hsphfpd_endpoint *endpoint)
189 {
190 	spa_list_remove(&endpoint->link);
191 	free(endpoint->path);
192 	if (endpoint->local_address)
193 		free(endpoint->local_address);
194 	if (endpoint->remote_address)
195 		free(endpoint->remote_address);
196 }
197 
hsphfpd_cmp_transport_path(struct spa_bt_transport * t,const void * data)198 static bool hsphfpd_cmp_transport_path(struct spa_bt_transport *t, const void *data)
199 {
200 	struct hsphfpd_transport_data *td = t->user_data;
201 	if (spa_streq(td->transport_path, data))
202 		return true;
203 
204 	return false;
205 }
206 
check_signature(DBusMessage * m,const char sig[])207 static inline bool check_signature(DBusMessage *m, const char sig[])
208 {
209 	return spa_streq(dbus_message_get_signature(m), sig);
210 }
211 
set_dbus_property(struct impl * backend,const char * service,const char * path,const char * interface,const char * property,int type,void * value)212 static int set_dbus_property(struct impl *backend,
213                              const char *service,
214                              const char *path,
215                              const char *interface,
216                              const char *property,
217                              int type,
218                              void *value)
219 {
220 	DBusMessage *m, *r;
221 	DBusMessageIter iter;
222 	DBusError err;
223 
224 	m = dbus_message_new_method_call(HSPHFPD_SERVICE, path, DBUS_INTERFACE_PROPERTIES, "Set");
225 	if (m == NULL)
226 		return -ENOMEM;
227 	dbus_message_append_args(m, DBUS_TYPE_STRING, &interface, DBUS_TYPE_STRING, &property, DBUS_TYPE_INVALID);
228 	dbus_message_iter_init_append(m, &iter);
229 	dbus_message_iter_append_basic(&iter, type, value);
230 
231 	dbus_error_init(&err);
232 
233 	r = dbus_connection_send_with_reply_and_block(backend->conn, m, -1, &err);
234 	dbus_message_unref(m);
235 	m = NULL;
236 
237 	if (r == NULL) {
238 		spa_log_error(backend->log, "Transport Set() failed for transport %s (%s)", path, err.message);
239 		dbus_error_free(&err);
240 		return -EIO;
241 	}
242 
243 	if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
244 		spa_log_error(backend->log, "Set() returned error: %s", dbus_message_get_error_name(r));
245 		return -EIO;
246 	}
247 
248 	dbus_message_unref(r);
249 	return 0;
250 }
251 
set_rx_volume_gain_property(const struct spa_bt_transport * transport,uint16_t gain)252 static inline void set_rx_volume_gain_property(const struct spa_bt_transport *transport, uint16_t gain)
253 {
254 	struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this);
255 	struct hsphfpd_transport_data *transport_data = transport->user_data;
256 
257 	if (transport->fd < 0 || transport_data->rx_volume_control <= HSPHFPD_VOLUME_CONTROL_NONE)
258 		return;
259 	if (set_dbus_property(backend, HSPHFPD_SERVICE, transport_data->transport_path,
260 	                      HSPHFPD_AUDIO_TRANSPORT_INTERFACE, "RxVolumeGain",
261 	                      DBUS_TYPE_UINT16, &gain))
262 		spa_log_error(backend->log, "Changing rx volume gain to %u for transport %s failed",
263 		              (unsigned)gain, transport_data->transport_path);
264 }
265 
set_tx_volume_gain_property(const struct spa_bt_transport * transport,uint16_t gain)266 static inline void set_tx_volume_gain_property(const struct spa_bt_transport *transport, uint16_t gain)
267 {
268 	struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this);
269 	struct hsphfpd_transport_data *transport_data = transport->user_data;
270 
271 	if (transport->fd < 0 || transport_data->tx_volume_control <= HSPHFPD_VOLUME_CONTROL_NONE)
272 		return;
273 	if (set_dbus_property(backend, HSPHFPD_SERVICE, transport_data->transport_path,
274 	                      HSPHFPD_AUDIO_TRANSPORT_INTERFACE, "TxVolumeGain",
275 	                      DBUS_TYPE_UINT16, &gain))
276 		spa_log_error(backend->log, "Changing tx volume gain to %u for transport %s failed",
277 		              (unsigned)gain, transport_data->transport_path);
278 }
279 
parse_transport_properties_values(struct impl * backend,const char * transport_path,DBusMessageIter * i,const char ** endpoint_path,const char ** air_codec,enum hsphfpd_volume_control * rx_volume_control,enum hsphfpd_volume_control * tx_volume_control,uint16_t * rx_volume_gain,uint16_t * tx_volume_gain,uint16_t * mtu)280 static void parse_transport_properties_values(struct impl *backend,
281                                               const char *transport_path,
282                                               DBusMessageIter *i,
283                                               const char **endpoint_path,
284                                               const char **air_codec,
285                                               enum hsphfpd_volume_control *rx_volume_control,
286                                               enum hsphfpd_volume_control *tx_volume_control,
287                                               uint16_t *rx_volume_gain,
288                                               uint16_t *tx_volume_gain,
289                                               uint16_t *mtu)
290 {
291 	DBusMessageIter element_i;
292 
293 	spa_assert(i);
294 
295 	dbus_message_iter_recurse(i, &element_i);
296 
297 	while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
298 		DBusMessageIter dict_i, variant_i;
299 		const char *key;
300 
301 		dbus_message_iter_recurse(&element_i, &dict_i);
302 
303 		if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_STRING) {
304 			spa_log_error(backend->log, "Received invalid property for transport %s", transport_path);
305 			return;
306 		}
307 
308 		dbus_message_iter_get_basic(&dict_i, &key);
309 
310 		if (!dbus_message_iter_next(&dict_i)) {
311 			spa_log_error(backend->log, "Received invalid property for transport %s", transport_path);
312 			return;
313 		}
314 
315 		if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_VARIANT) {
316 			spa_log_error(backend->log, "Received invalid property for transport %s", transport_path);
317 			return;
318 		}
319 
320 		dbus_message_iter_recurse(&dict_i, &variant_i);
321 
322 		switch (dbus_message_iter_get_arg_type(&variant_i)) {
323 			case DBUS_TYPE_STRING:
324 				if (spa_streq(key, "RxVolumeControl") || spa_streq(key, "TxVolumeControl")) {
325 					const char *value;
326 					enum hsphfpd_volume_control volume_control;
327 
328 					dbus_message_iter_get_basic(&variant_i, &value);
329 					if (spa_streq(value, "none"))
330 						volume_control = HSPHFPD_VOLUME_CONTROL_NONE;
331 					else if (spa_streq(value, "local"))
332 						volume_control = HSPHFPD_VOLUME_CONTROL_LOCAL;
333 					else if (spa_streq(value, "remote"))
334 						volume_control = HSPHFPD_VOLUME_CONTROL_REMOTE;
335 					else
336 						volume_control = 0;
337 
338 					if (!volume_control)
339 						spa_log_warn(backend->log, "Transport %s received invalid '%s' property value '%s', ignoring", transport_path, key, value);
340 					else if (spa_streq(key, "RxVolumeControl"))
341 						*rx_volume_control = volume_control;
342 					else if (spa_streq(key, "TxVolumeControl"))
343 						*tx_volume_control = volume_control;
344 				} else if (spa_streq(key, "AirCodec"))
345 					dbus_message_iter_get_basic(&variant_i, air_codec);
346 				break;
347 
348 			case DBUS_TYPE_UINT16:
349 				if (spa_streq(key, "MTU"))
350 					dbus_message_iter_get_basic(&variant_i, mtu);
351 				else if (spa_streq(key, "RxVolumeGain"))
352 					dbus_message_iter_get_basic(&variant_i, rx_volume_gain);
353 				else if (spa_streq(key, "TxVolumeGain"))
354 					dbus_message_iter_get_basic(&variant_i, tx_volume_gain);
355 				break;
356 
357 			case DBUS_TYPE_OBJECT_PATH:
358 				if (spa_streq(key, "Endpoint"))
359 					dbus_message_iter_get_basic(&variant_i, endpoint_path);
360 				break;
361 		}
362 
363 		dbus_message_iter_next(&element_i);
364 	}
365 }
366 
hsphfpd_parse_transport_properties(struct impl * backend,struct spa_bt_transport * transport,DBusMessageIter * i)367 static void hsphfpd_parse_transport_properties(struct impl *backend, struct spa_bt_transport *transport, DBusMessageIter *i)
368 {
369 	struct hsphfpd_transport_data *transport_data = transport->user_data;
370 	const char *endpoint_path = NULL;
371 	const char *air_codec = NULL;
372 	enum hsphfpd_volume_control rx_volume_control = 0;
373 	enum hsphfpd_volume_control tx_volume_control = 0;
374 	uint16_t rx_volume_gain = -1;
375 	uint16_t tx_volume_gain = -1;
376 	uint16_t mtu = 0;
377 	bool rx_volume_gain_changed = false;
378 	bool tx_volume_gain_changed = false;
379 	bool rx_volume_control_changed = false;
380 	bool tx_volume_control_changed = false;
381 	bool rx_soft_volume_changed = false;
382 	bool tx_soft_volume_changed = false;
383 
384 	parse_transport_properties_values(backend, transport_data->transport_path, i, &endpoint_path,
385 	                                  &air_codec, &rx_volume_control, &tx_volume_control,
386 																		&rx_volume_gain, &tx_volume_gain, &mtu);
387 
388 	if (endpoint_path)
389 			spa_log_warn(backend->log, "Transport %s received a duplicate '%s' property, ignoring",
390 			             transport_data->transport_path, "Endpoint");
391 
392 	if (air_codec)
393 			spa_log_warn(backend->log, "Transport %s received a duplicate '%s' property, ignoring",
394 			             transport_data->transport_path, "AirCodec");
395 
396 	if (mtu)
397 			spa_log_warn(backend->log, "Transport %s received a duplicate '%s' property, ignoring",
398 			             transport_data->transport_path, "MTU");
399 
400 	if (rx_volume_control) {
401 		if (!!transport_data->rx_soft_volume != !!(rx_volume_control != HSPHFPD_VOLUME_CONTROL_REMOTE)) {
402 			spa_log_info(backend->log, "Transport %s changed rx soft volume from %d to %d",
403 			             transport_data->transport_path, transport_data->rx_soft_volume,
404 			             (rx_volume_control != HSPHFPD_VOLUME_CONTROL_REMOTE));
405 			transport_data->rx_soft_volume = (rx_volume_control != HSPHFPD_VOLUME_CONTROL_REMOTE);
406 			rx_soft_volume_changed = true;
407 		}
408 		if (transport_data->rx_volume_control != rx_volume_control) {
409 			transport_data->rx_volume_control = rx_volume_control;
410 			rx_volume_control_changed = true;
411 		}
412 	}
413 
414 	if (tx_volume_control) {
415 		if (!!transport_data->tx_soft_volume != !!(tx_volume_control != HSPHFPD_VOLUME_CONTROL_REMOTE)) {
416 			spa_log_info(backend->log, "Transport %s changed tx soft volume from %d to %d",
417 			             transport_data->transport_path, transport_data->rx_soft_volume,
418 			             (tx_volume_control != HSPHFPD_VOLUME_CONTROL_REMOTE));
419 			transport_data->tx_soft_volume = (tx_volume_control != HSPHFPD_VOLUME_CONTROL_REMOTE);
420 			tx_soft_volume_changed = true;
421 		}
422 		if (transport_data->tx_volume_control != tx_volume_control) {
423 			transport_data->tx_volume_control = tx_volume_control;
424 			tx_volume_control_changed = true;
425 		}
426 	}
427 
428 	if (rx_volume_gain != (uint16_t)-1) {
429 		if (transport_data->rx_volume_gain != rx_volume_gain) {
430 			spa_log_info(backend->log, "Transport %s changed rx volume gain from %u to %u",
431 			             transport_data->transport_path, (unsigned)transport_data->rx_volume_gain, (unsigned)rx_volume_gain);
432 			transport_data->rx_volume_gain = rx_volume_gain;
433 			rx_volume_gain_changed = true;
434 		}
435 	}
436 
437 	if (tx_volume_gain != (uint16_t)-1) {
438 		if (transport_data->tx_volume_gain != tx_volume_gain) {
439 			spa_log_info(backend->log, "Transport %s changed tx volume gain from %u to %u",
440 			             transport_data->transport_path, (unsigned)transport_data->tx_volume_gain, (unsigned)tx_volume_gain);
441 			transport_data->tx_volume_gain = tx_volume_gain;
442 			tx_volume_gain_changed = true;
443 		}
444 	}
445 
446 #if 0
447 	if (rx_volume_gain_changed || rx_soft_volume_changed)
448 		pa_hook_fire(pa_bluetooth_discovery_hook(transport_data->hsphfpd->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_RX_VOLUME_GAIN_CHANGED), transport);
449 
450 	if (tx_volume_gain_changed || tx_soft_volume_changed)
451 		pa_hook_fire(pa_bluetooth_discovery_hook(transport_data->hsphfpd->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_TX_VOLUME_GAIN_CHANGED), transport);
452 #else
453 	spa_log_debug(backend->log, "RX volume gain changed: %d, soft volume changed: %d", rx_volume_gain_changed, rx_soft_volume_changed);
454 	spa_log_debug(backend->log, "TX volume gain changed: %d, soft volume changed: %d", tx_volume_gain_changed, tx_soft_volume_changed);
455 #endif
456 
457 	if (rx_volume_control_changed)
458 		set_rx_volume_gain_property(transport, transport_data->rx_volume_gain);
459 
460 	if (tx_volume_control_changed)
461 		set_tx_volume_gain_property(transport, transport_data->tx_volume_gain);
462 }
463 
audio_agent_get_property(DBusConnection * conn,DBusMessage * m,const char * path,void * userdata)464 static DBusHandlerResult audio_agent_get_property(DBusConnection *conn, DBusMessage *m, const char *path, void *userdata)
465 {
466 	const char *interface;
467 	const char *property;
468 	const char *agent_codec;
469 	DBusMessage *r = NULL;
470 
471 	if (!check_signature(m, "ss")) {
472 		r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid signature in method call");
473 		goto fail;
474 	}
475 
476 	if (dbus_message_get_args(m, NULL,
477 	                          DBUS_TYPE_STRING, &interface,
478 														DBUS_TYPE_STRING, &property,
479 														DBUS_TYPE_INVALID) == FALSE) {
480 		r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid arguments in method call");
481 		goto fail;
482 	}
483 
484 	if (!spa_streq(interface, HSPHFPD_AUDIO_AGENT_INTERFACE))
485 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
486 
487 	if (!spa_streq(property, "AgentCodec")) {
488 		r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid property in method call");
489 		goto fail;
490 	}
491 
492 	if (spa_streq(path, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ))
493 		agent_codec = HSPHFP_AGENT_CODEC_PCM;
494 	else if (spa_streq(path, HSPHFP_AUDIO_CLIENT_MSBC))
495 		agent_codec = HSPHFP_AGENT_CODEC_MSBC;
496 	else {
497 		r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid path in method call");
498 		goto fail;
499 	}
500 
501 	if ((r = dbus_message_new_method_return(m)) == NULL)
502 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
503 	if (!dbus_message_append_args(r, DBUS_TYPE_STRING, &agent_codec, DBUS_TYPE_INVALID))
504 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
505 
506 fail:
507 	if (!dbus_connection_send(conn, r, NULL))
508 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
509 
510 	dbus_message_unref(r);
511 	return DBUS_HANDLER_RESULT_HANDLED;
512 }
513 
audio_agent_getall_properties(DBusConnection * conn,DBusMessage * m,const char * path,void * userdata)514 static DBusHandlerResult audio_agent_getall_properties(DBusConnection *conn, DBusMessage *m, const char *path, void *userdata)
515 {
516 	const char *interface;
517 	DBusMessageIter iter, array, dict, data;
518 	const char *agent_codec_key = "AgentCodec";
519 	const char *agent_codec;
520 	DBusMessage *r = NULL;
521 
522 	if (!check_signature(m, "s")) {
523 		r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid signature in method call");
524 		goto fail;
525 	}
526 
527 	if (dbus_message_get_args(m, NULL,
528 	                          DBUS_TYPE_STRING, &interface,
529 	                          DBUS_TYPE_INVALID) == FALSE) {
530 		r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid arguments in method call");
531 		goto fail;
532 	}
533 
534 	if (spa_streq(path, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ))
535 		agent_codec = HSPHFP_AGENT_CODEC_PCM;
536 	else if (spa_streq(path, HSPHFP_AUDIO_CLIENT_MSBC))
537 		agent_codec = HSPHFP_AGENT_CODEC_MSBC;
538 	else {
539 		r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid path in method call");
540 		goto fail;
541 	}
542 
543 	if (!spa_streq(interface, HSPHFPD_AUDIO_AGENT_INTERFACE))
544 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
545 
546 	if ((r = dbus_message_new_method_return(m)) == NULL)
547 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
548 	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sv}", &array);
549 	dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &dict);
550 	dbus_message_iter_append_basic(&dict, DBUS_TYPE_STRING, &agent_codec_key);
551 	dbus_message_iter_open_container(&dict, DBUS_TYPE_VARIANT, "s", &data);
552 	dbus_message_iter_append_basic(&data, DBUS_TYPE_BOOLEAN, &agent_codec);
553 	dbus_message_iter_close_container(&dict, &data);
554 	dbus_message_iter_close_container(&array, &dict);
555 	dbus_message_iter_close_container(&iter, &array);
556 
557 fail:
558 	if (!dbus_connection_send(conn, r, NULL))
559 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
560 
561 	dbus_message_unref(r);
562 	return DBUS_HANDLER_RESULT_HANDLED;
563 }
564 
hsphfpd_new_audio_connection(DBusConnection * conn,DBusMessage * m,const char * path,void * userdata)565 static DBusHandlerResult hsphfpd_new_audio_connection(DBusConnection *conn, DBusMessage *m, const char *path, void *userdata)
566 {
567 	struct impl *backend = userdata;
568 	DBusMessageIter arg_i;
569 	const char *transport_path;
570 	int fd;
571 	const char *sender;
572 	const char *endpoint_path = NULL;
573 	const char *air_codec = NULL;
574 	enum hsphfpd_volume_control rx_volume_control = 0;
575 	enum hsphfpd_volume_control tx_volume_control = 0;
576 	uint16_t rx_volume_gain = -1;
577 	uint16_t tx_volume_gain = -1;
578 	uint16_t mtu = 0;
579 	unsigned int codec;
580 	struct hsphfpd_endpoint *endpoint;
581 	struct spa_bt_transport *transport;
582 	struct hsphfpd_transport_data *transport_data;
583 	DBusMessage *r = NULL;
584 
585 	if (!check_signature(m, "oha{sv}")) {
586 		r = dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, "Invalid signature in method call");
587 		goto fail;
588 	}
589 
590 	if (!dbus_message_iter_init(m, &arg_i))
591 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
592 
593 	dbus_message_iter_get_basic(&arg_i, &transport_path);
594 	dbus_message_iter_next(&arg_i);
595 	dbus_message_iter_get_basic(&arg_i, &fd);
596 
597 	spa_log_debug(backend->log, "NewConnection %s, fd %d", transport_path, fd);
598 
599 	sender = dbus_message_get_sender(m);
600 	if (!spa_streq(sender, backend->hsphfpd_service_id)) {
601 		close(fd);
602 		spa_log_error(backend->log, "Sender '%s' is not authorized", sender);
603 		r = dbus_message_new_error_printf(m, HSPHFPD_ERROR_REJECTED, "Sender '%s' is not authorized", sender);
604 		goto fail;
605 	}
606 
607 	if (spa_streq(path, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ))
608 		codec = HFP_AUDIO_CODEC_CVSD;
609 	else if (spa_streq(path, HSPHFP_AUDIO_CLIENT_MSBC))
610 		codec = HFP_AUDIO_CODEC_MSBC;
611 	else {
612 		r = dbus_message_new_error(m, HSPHFPD_ERROR_REJECTED, "Invalid path");
613 		goto fail;
614 	}
615 
616 	dbus_message_iter_next(&arg_i);
617 	parse_transport_properties_values(backend, transport_path, &arg_i,
618 	                                  &endpoint_path, &air_codec,
619 	                                  &rx_volume_control, &tx_volume_control,
620 																		&rx_volume_gain, &tx_volume_gain,
621 																		&mtu);
622 
623 	if (!endpoint_path) {
624 		close(fd);
625 		spa_log_error(backend->log, "Endpoint property was not specified");
626 		r = dbus_message_new_error(m, HSPHFPD_ERROR_REJECTED, "Endpoint property was not specified");
627 		goto fail;
628 	}
629 
630 	if (!air_codec) {
631 		close(fd);
632 		spa_log_error(backend->log, "AirCodec property was not specified");
633 		r = dbus_message_new_error(m, HSPHFPD_ERROR_REJECTED, "AirCodec property was not specified");
634 		goto fail;
635 	}
636 
637 	if (!rx_volume_control) {
638 		close(fd);
639 		spa_log_error(backend->log, "RxVolumeControl property was not specified");
640 		r = dbus_message_new_error(m, HSPHFPD_ERROR_REJECTED, "RxVolumeControl property was not specified");
641 		goto fail;
642 	}
643 
644 	if (!tx_volume_control) {
645 		close(fd);
646 		spa_log_error(backend->log, "TxVolumeControl property was not specified");
647 		r = dbus_message_new_error(m, HSPHFPD_ERROR_REJECTED, "TxVolumeControl property was not specified");
648 		goto fail;
649 	}
650 
651 	if (rx_volume_control != HSPHFPD_VOLUME_CONTROL_NONE) {
652 		if (rx_volume_gain == (uint16_t)-1) {
653 			close(fd);
654 			spa_log_error(backend->log, "RxVolumeGain property was not specified, but VolumeControl is not none");
655 			r = dbus_message_new_error(m, HSPHFPD_ERROR_REJECTED, "RxVolumeGain property was not specified, but VolumeControl is not none");
656 			goto fail;
657 		}
658 	} else {
659 		rx_volume_gain = 15; /* No volume control, so set maximal value */
660 	}
661 
662 	if (tx_volume_control != HSPHFPD_VOLUME_CONTROL_NONE) {
663 		if (tx_volume_gain == (uint16_t)-1) {
664 			close(fd);
665 			spa_log_error(backend->log, "TxVolumeGain property was not specified, but VolumeControl is not none");
666 			r = dbus_message_new_error(m, HSPHFPD_ERROR_REJECTED, "TxVolumeGain property was not specified, but VolumeControl is not none");
667 			goto fail;
668 		}
669 	} else {
670 		tx_volume_gain = 15; /* No volume control, so set maximal value */
671 	}
672 
673 	if (!mtu) {
674 		close(fd);
675 		spa_log_error(backend->log, "MTU property was not specified");
676 		r = dbus_message_new_error(m, HSPHFPD_ERROR_REJECTED, "MTU property was not specified");
677 		goto fail;
678 	}
679 
680 	endpoint = endpoint_find(backend, endpoint_path);
681 	if (!endpoint) {
682 		close(fd);
683 		spa_log_error(backend->log, "Endpoint %s does not exist", endpoint_path);
684 		r = dbus_message_new_error_printf(m, HSPHFPD_ERROR_REJECTED, "Endpoint %s does not exist", endpoint_path);
685 		goto fail;
686 	}
687 
688 	if (!endpoint->valid) {
689 		close(fd);
690 		spa_log_error(backend->log, "Endpoint %s is not valid", endpoint_path);
691 		r = dbus_message_new_error_printf(m, HSPHFPD_ERROR_REJECTED, "Endpoint %s is not valid", endpoint_path);
692 		goto fail;
693 	}
694 
695 	transport = spa_bt_transport_find(backend->monitor, endpoint_path);
696 	if (!transport) {
697 		close(fd);
698 		spa_log_error(backend->log, "Endpoint %s is not connected", endpoint_path);
699 		r = dbus_message_new_error_printf(m, HSPHFPD_ERROR_REJECTED, "Endpoint %s is not connected", endpoint_path);
700 		goto fail;
701 	}
702 
703 	if (transport->codec != codec)
704 		spa_log_warn(backend->log, "Expecting codec to be %d, got %d", transport->codec, codec);
705 
706 	if (transport->fd >= 0) {
707 		close(fd);
708 		spa_log_error(backend->log, "Endpoint %s has already active transport", endpoint_path);
709 		r = dbus_message_new_error_printf(m, HSPHFPD_ERROR_REJECTED, "Endpoint %s has already active transport", endpoint_path);
710 		goto fail;
711 	}
712 
713 	transport_data = transport->user_data;
714 	transport_data->transport_path = strdup(transport_path);
715 	transport_data->rx_soft_volume = (rx_volume_control != HSPHFPD_VOLUME_CONTROL_REMOTE);
716 	transport_data->tx_soft_volume = (tx_volume_control != HSPHFPD_VOLUME_CONTROL_REMOTE);
717 	transport_data->rx_volume_gain = rx_volume_gain;
718 	transport_data->tx_volume_gain = tx_volume_gain;
719 	transport_data->rx_volume_control = rx_volume_control;
720 	transport_data->tx_volume_control = tx_volume_control;
721 
722 #if 0
723 	pa_hook_fire(pa_bluetooth_discovery_hook(hsphfpd->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_RX_VOLUME_GAIN_CHANGED), transport);
724 	pa_hook_fire(pa_bluetooth_discovery_hook(hsphfpd->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_TX_VOLUME_GAIN_CHANGED), transport);
725 #endif
726 
727 	transport->read_mtu = mtu;
728 	transport->write_mtu = mtu;
729 
730 	transport->fd = fd;
731 
732 	if ((r = dbus_message_new_method_return(m)) == NULL)
733 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
734 
735 fail:
736 	if (r) {
737 		DBusHandlerResult res = DBUS_HANDLER_RESULT_HANDLED;
738 		if (!dbus_connection_send(backend->conn, r, NULL))
739 			res = DBUS_HANDLER_RESULT_NEED_MEMORY;
740 		dbus_message_unref(r);
741 		return res;
742 	}
743 
744 	return DBUS_HANDLER_RESULT_HANDLED;
745 }
746 
audio_agent_endpoint_handler(DBusConnection * c,DBusMessage * m,void * userdata)747 static DBusHandlerResult audio_agent_endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata)
748 {
749 	struct impl *backend = userdata;
750 	const char *path, *interface, *member;
751 	DBusMessage *r;
752 	DBusHandlerResult res;
753 
754 	path = dbus_message_get_path(m);
755 	interface = dbus_message_get_interface(m);
756 	member = dbus_message_get_member(m);
757 
758 	spa_log_debug(backend->log, "dbus: path=%s, interface=%s, member=%s", path, interface, member);
759 
760 	if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
761 		const char *xml = AUDIO_AGENT_ENDPOINT_INTROSPECT_XML;
762 
763 		if ((r = dbus_message_new_method_return(m)) == NULL)
764 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
765 		if (!dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID))
766 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
767 		if (!dbus_connection_send(backend->conn, r, NULL))
768 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
769 
770 		dbus_message_unref(r);
771 		res = DBUS_HANDLER_RESULT_HANDLED;
772 	} else if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "Get"))
773 		res = audio_agent_get_property(c, m, path, userdata);
774 	else if (dbus_message_is_method_call(m, DBUS_INTERFACE_PROPERTIES, "GetAll"))
775 		res = audio_agent_getall_properties(c, m, path, userdata);
776 	else if (dbus_message_is_method_call(m, HSPHFPD_AUDIO_AGENT_INTERFACE, "NewConnection"))
777 		res = hsphfpd_new_audio_connection(c, m, path, userdata);
778 	else
779 		res = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
780 
781 	return res;
782 }
783 
append_audio_agent_object(DBusMessageIter * iter,const char * endpoint,const char * agent_codec)784 static void append_audio_agent_object(DBusMessageIter *iter, const char *endpoint, const char *agent_codec)
785 {
786 	const char *interface_name = HSPHFPD_AUDIO_AGENT_INTERFACE;
787 	DBusMessageIter object, array, entry, dict, codec, data;
788 	char *str = "AgentCodec";
789 
790 	dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &object);
791 	dbus_message_iter_append_basic(&object, DBUS_TYPE_OBJECT_PATH, &endpoint);
792 
793 	dbus_message_iter_open_container(&object, DBUS_TYPE_ARRAY, "{sa{sv}}", &array);
794 
795 	dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
796 	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &interface_name);
797 
798 	dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY, "{sv}", &dict);
799 
800 	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &codec);
801 	dbus_message_iter_append_basic(&codec, DBUS_TYPE_STRING, &str);
802 	dbus_message_iter_open_container(&codec, DBUS_TYPE_VARIANT, "s", &data);
803 	dbus_message_iter_append_basic(&data, DBUS_TYPE_STRING, &agent_codec);
804 	dbus_message_iter_close_container(&codec, &data);
805 	dbus_message_iter_close_container(&dict, &codec);
806 
807 	dbus_message_iter_close_container(&entry, &dict);
808 	dbus_message_iter_close_container(&array, &entry);
809 	dbus_message_iter_close_container(&object, &array);
810 	dbus_message_iter_close_container(iter, &object);
811 }
812 
application_object_manager_handler(DBusConnection * c,DBusMessage * m,void * userdata)813 static DBusHandlerResult application_object_manager_handler(DBusConnection *c, DBusMessage *m, void *userdata)
814 {
815 	struct impl *backend = userdata;
816 	const char *path, *interface, *member;
817 	DBusMessage *r;
818 
819 	path = dbus_message_get_path(m);
820 	interface = dbus_message_get_interface(m);
821 	member = dbus_message_get_member(m);
822 
823 	spa_log_debug(backend->log, "dbus: path=%s, interface=%s, member=%s", path, interface, member);
824 
825 	if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
826 		const char *xml = APPLICATION_OBJECT_MANAGER_INTROSPECT_XML;
827 
828 		if ((r = dbus_message_new_method_return(m)) == NULL)
829 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
830 		if (!dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID))
831 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
832 	} else if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects")) {
833 		DBusMessageIter iter, array;
834 
835 		if ((r = dbus_message_new_method_return(m)) == NULL)
836 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
837 
838 		dbus_message_iter_init_append(r, &iter);
839 		dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{oa{sa{sv}}}", &array);
840 
841 		append_audio_agent_object(&array, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ, HSPHFP_AGENT_CODEC_PCM);
842 		if (backend->msbc_supported)
843 			append_audio_agent_object(&array, HSPHFP_AUDIO_CLIENT_MSBC, HSPHFP_AGENT_CODEC_MSBC);
844 
845 		dbus_message_iter_close_container(&iter, &array);
846 	} else
847 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
848 
849 	if (!dbus_connection_send(backend->conn, r, NULL))
850 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
851 	dbus_message_unref(r);
852 
853   return DBUS_HANDLER_RESULT_HANDLED;
854 }
855 
hsphfpd_audio_acquire_reply(DBusPendingCall * pending,void * user_data)856 static void hsphfpd_audio_acquire_reply(DBusPendingCall *pending, void *user_data)
857 {
858 	struct impl *backend = user_data;
859 	DBusMessage *r;
860 	const char *transport_path;
861 	const char *service_id;
862 	const char *agent_path;
863 	DBusError error;
864 
865 	dbus_error_init(&error);
866 
867 	backend->acquire_in_progress = false;
868 
869 	r = dbus_pending_call_steal_reply(pending);
870 	if (r == NULL)
871 		return;
872 
873 	if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
874 		spa_log_error(backend->log, "RegisterApplication() failed: %s",
875 				dbus_message_get_error_name(r));
876 		goto finish;
877 	}
878 
879 	if (!spa_streq(dbus_message_get_sender(r), backend->hsphfpd_service_id)) {
880 		spa_log_error(backend->log, "Reply for " HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio() from invalid sender");
881 		goto finish;
882 	}
883 
884 	if (!check_signature(r, "oso")) {
885 		spa_log_error(backend->log, "Invalid reply signature for " HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio()");
886 		goto finish;
887 	}
888 
889 	if (dbus_message_get_args(r, &error,
890 	                          DBUS_TYPE_OBJECT_PATH, &transport_path,
891 	                          DBUS_TYPE_STRING, &service_id,
892 	                          DBUS_TYPE_OBJECT_PATH, &agent_path,
893 	                          DBUS_TYPE_INVALID) == FALSE) {
894 		spa_log_error(backend->log, "Failed to parse " HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio() reply: %s", error.message);
895 		goto finish;
896 	}
897 
898 	if (!spa_streq(service_id, dbus_bus_get_unique_name(backend->conn))) {
899 		spa_log_warn(backend->log, HSPHFPD_ENDPOINT_INTERFACE ".ConnectAudio() failed: Other audio application took audio socket");
900 		goto finish;
901 	}
902 
903 	spa_log_debug(backend->log, "hsphfpd audio acquired");
904 
905 finish:
906 	dbus_message_unref(r);
907 	dbus_pending_call_unref(pending);
908 }
909 
hsphfpd_audio_acquire(void * data,bool optional)910 static int hsphfpd_audio_acquire(void *data, bool optional)
911 {
912 	struct spa_bt_transport *transport = data;
913 	struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this);
914 	DBusMessage *m;
915 	const char *air_codec = HSPHFP_AIR_CODEC_CVSD;
916 	const char *agent_codec = HSPHFP_AGENT_CODEC_PCM;
917 	DBusPendingCall *call;
918 	DBusError err;
919 
920 	spa_log_debug(backend->log, "transport %p: Acquire %s",
921 			transport, transport->path);
922 
923 	if (backend->acquire_in_progress)
924 		return -EINPROGRESS;
925 
926 	if (transport->codec == HFP_AUDIO_CODEC_MSBC) {
927 		air_codec = HSPHFP_AIR_CODEC_MSBC;
928 		agent_codec = HSPHFP_AGENT_CODEC_MSBC;
929 	}
930 
931 	m = dbus_message_new_method_call(HSPHFPD_SERVICE,
932 					 transport->path,
933 					 HSPHFPD_ENDPOINT_INTERFACE,
934 					 "ConnectAudio");
935 	if (m == NULL)
936 		return -ENOMEM;
937 	dbus_message_append_args(m, DBUS_TYPE_STRING, &air_codec, DBUS_TYPE_STRING, &agent_codec, DBUS_TYPE_INVALID);
938 
939 	dbus_error_init(&err);
940 
941 	dbus_connection_send_with_reply(backend->conn, m, &call, -1);
942 	dbus_pending_call_set_notify(call, hsphfpd_audio_acquire_reply, backend, NULL);
943 	dbus_message_unref(m);
944 
945 	/* The ConnectAudio method triggers Introspect and NewConnection calls,
946 	   which will set the fd to use for the SCO data.
947 	   We need to run the DBus loop to be able to reply to those method calls */
948 	backend->acquire_in_progress = true;
949 	while (backend->acquire_in_progress && dbus_connection_read_write_dispatch(backend->conn, -1))
950 		; // empty loop body
951 
952 	return 0;
953 }
954 
hsphfpd_audio_release(void * data)955 static int hsphfpd_audio_release(void *data)
956 {
957 	struct spa_bt_transport *transport = data;
958 	struct impl *backend = SPA_CONTAINER_OF(transport->backend, struct impl, this);
959 	struct hsphfpd_transport_data *transport_data = transport->user_data;
960 
961 	spa_log_debug(backend->log, "transport %p: Release %s",
962 			transport, transport->path);
963 
964 	if (transport->sco_io) {
965 		spa_bt_sco_io_destroy(transport->sco_io);
966 		transport->sco_io = NULL;
967 	}
968 
969 	/* shutdown to make sure connection is dropped immediately */
970 	shutdown(transport->fd, SHUT_RDWR);
971 	close(transport->fd);
972 	if (transport_data->transport_path) {
973 		free(transport_data->transport_path);
974 		transport_data->transport_path = NULL;
975 	}
976 	transport->fd = -1;
977 
978 	return 0;
979 }
980 
hsphfpd_audio_destroy(void * data)981 static int hsphfpd_audio_destroy(void *data)
982 {
983 	struct spa_bt_transport *transport = data;
984 	struct hsphfpd_transport_data *transport_data = transport->user_data;
985 
986 	if (transport_data->transport_path) {
987 		free(transport_data->transport_path);
988 		transport_data->transport_path = NULL;
989 	}
990 
991 	return 0;
992 }
993 
994 static const struct spa_bt_transport_implementation hsphfpd_transport_impl = {
995 	SPA_VERSION_BT_TRANSPORT_IMPLEMENTATION,
996 	.acquire = hsphfpd_audio_acquire,
997 	.release = hsphfpd_audio_release,
998 	.destroy = hsphfpd_audio_destroy,
999 };
1000 
hsphfpd_parse_endpoint_properties(struct impl * backend,struct hsphfpd_endpoint * endpoint,DBusMessageIter * i)1001 static DBusHandlerResult hsphfpd_parse_endpoint_properties(struct impl *backend, struct hsphfpd_endpoint *endpoint, DBusMessageIter *i)
1002 {
1003 	DBusMessageIter element_i;
1004 	struct spa_bt_device *d;
1005 	struct spa_bt_transport *t;
1006 
1007 	dbus_message_iter_recurse(i, &element_i);
1008 	while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1009 		DBusMessageIter dict_i, value_i;
1010 		const char *key;
1011 
1012 		dbus_message_iter_recurse(&element_i, &dict_i);
1013 		dbus_message_iter_get_basic(&dict_i, &key);
1014 		dbus_message_iter_next(&dict_i);
1015 		dbus_message_iter_recurse(&dict_i, &value_i);
1016 		switch (dbus_message_iter_get_arg_type(&value_i)) {
1017 			case DBUS_TYPE_STRING:
1018 				{
1019 					const char *value;
1020 					dbus_message_iter_get_basic(&value_i, &value);
1021 					if (spa_streq(key, "RemoteAddress"))
1022 						endpoint->remote_address = strdup(value);
1023 					else if (spa_streq(key, "LocalAddress"))
1024 						endpoint->local_address = strdup(value);
1025 					else if (spa_streq(key, "Profile")) {
1026 						if (endpoint->profile)
1027 							spa_log_warn(backend->log, "Endpoint %s received a duplicate '%s' property, ignoring", endpoint->path, key);
1028 						else if (spa_streq(value, "headset"))
1029 							endpoint->profile = HSPHFPD_PROFILE_HEADSET;
1030 						else if (spa_streq(value, "handsfree"))
1031 							endpoint->profile = HSPHFPD_PROFILE_HANDSFREE;
1032 						else
1033 							spa_log_warn(backend->log, "Endpoint %s received invalid '%s' property value '%s', ignoring", endpoint->path, key, value);
1034 					} else if (spa_streq(key, "Role")) {
1035 						if (endpoint->role)
1036 							spa_log_warn(backend->log, "Endpoint %s received a duplicate '%s' property, ignoring", endpoint->path, key);
1037 						else if (spa_streq(value, "client"))
1038 							endpoint->role = HSPHFPD_ROLE_CLIENT;
1039 						else if (spa_streq(value, "gateway"))
1040 							endpoint->role = HSPHFPD_ROLE_GATEWAY;
1041 						else
1042 							spa_log_warn(backend->log, "Endpoint %s received invalid '%s' property value '%s', ignoring", endpoint->path, key, value);
1043 					}
1044 					spa_log_trace(backend->log, "  %s: %s (%p)", key, value, endpoint);
1045 				}
1046 				break;
1047 
1048 			case DBUS_TYPE_BOOLEAN:
1049 				{
1050 					bool value;
1051 					dbus_message_iter_get_basic(&value_i, &value);
1052 					if (spa_streq(key, "Connected"))
1053 						endpoint->connected = value;
1054 					spa_log_trace(backend->log, "  %s: %d", key, value);
1055 				}
1056 				break;
1057 
1058 			case DBUS_TYPE_ARRAY:
1059 				{
1060 					if (spa_streq(key, "AudioCodecs")) {
1061 						DBusMessageIter array_i;
1062 						const char *value;
1063 
1064 						endpoint->air_codecs = 0;
1065 						dbus_message_iter_recurse(&value_i, &array_i);
1066 						while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) {
1067 							dbus_message_iter_get_basic(&array_i, &value);
1068 							if (spa_streq(value, HSPHFP_AIR_CODEC_CVSD))
1069 								endpoint->air_codecs |= HFP_AUDIO_CODEC_CVSD;
1070 							if (spa_streq(value, HSPHFP_AIR_CODEC_MSBC))
1071 								endpoint->air_codecs |= HFP_AUDIO_CODEC_MSBC;
1072 							dbus_message_iter_next(&array_i);
1073 						}
1074 					}
1075 				}
1076 				break;
1077 		}
1078 
1079 		dbus_message_iter_next(&element_i);
1080 	}
1081 
1082 	if (!endpoint->valid && endpoint->local_address && endpoint->remote_address && endpoint->profile && endpoint->role)
1083 		endpoint->valid = true;
1084 
1085 	if (!endpoint->remote_address || !endpoint->local_address) {
1086 		spa_log_debug(backend->log, "Missing addresses for %s", endpoint->path);
1087 		return DBUS_HANDLER_RESULT_HANDLED;
1088 	}
1089 
1090 	d = spa_bt_device_find_by_address(backend->monitor, endpoint->remote_address, endpoint->local_address);
1091 	if (!d) {
1092 		spa_log_debug(backend->log, "No device for %s", endpoint->path);
1093 		return DBUS_HANDLER_RESULT_HANDLED;
1094 	}
1095 
1096 	if ((t = spa_bt_transport_find(backend->monitor, endpoint->path)) != NULL) {
1097 		/* Release transport on disconnection, or when mSBC is supported if there
1098 		   is an update of the remote codecs */
1099 		if (!endpoint->connected || (backend->msbc_supported && (endpoint->air_codecs & HFP_AUDIO_CODEC_MSBC) && t->codec == HFP_AUDIO_CODEC_CVSD)) {
1100 			spa_bt_transport_free(t);
1101 			spa_bt_device_check_profiles(d, false);
1102 			spa_log_debug(backend->log, "Transport released for %s", endpoint->path);
1103 		} else {
1104 			spa_log_debug(backend->log, "Transport already configured for %s", endpoint->path);
1105 			return DBUS_HANDLER_RESULT_HANDLED;
1106 		}
1107 	}
1108 
1109 	if (!endpoint->valid || !endpoint->connected)
1110 		return DBUS_HANDLER_RESULT_HANDLED;
1111 
1112 	char *t_path = strdup(endpoint->path);
1113 	t = spa_bt_transport_create(backend->monitor, t_path, sizeof(struct hsphfpd_transport_data));
1114 	if (t == NULL) {
1115 		spa_log_warn(backend->log, "can't create transport: %m");
1116 		free(t_path);
1117 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
1118 	}
1119 	spa_bt_transport_set_implementation(t, &hsphfpd_transport_impl, t);
1120 
1121 	t->device = d;
1122 	spa_list_append(&t->device->transport_list, &t->device_link);
1123 	t->backend = &backend->this;
1124 	t->profile = SPA_BT_PROFILE_NULL;
1125 	if (endpoint->profile == HSPHFPD_PROFILE_HEADSET) {
1126 		if (endpoint->role == HSPHFPD_ROLE_CLIENT)
1127 			t->profile = SPA_BT_PROFILE_HSP_HS;
1128 		else if (endpoint->role == HSPHFPD_ROLE_GATEWAY)
1129 			t->profile = SPA_BT_PROFILE_HSP_AG;
1130 	} else if (endpoint->profile == HSPHFPD_PROFILE_HANDSFREE) {
1131 		if (endpoint->role == HSPHFPD_ROLE_CLIENT)
1132 			t->profile = SPA_BT_PROFILE_HFP_HF;
1133 		else if (endpoint->role == HSPHFPD_ROLE_GATEWAY)
1134 			t->profile = SPA_BT_PROFILE_HFP_AG;
1135 	}
1136 	if (backend->msbc_supported && (endpoint->air_codecs & HFP_AUDIO_CODEC_MSBC))
1137 		t->codec = HFP_AUDIO_CODEC_MSBC;
1138 	else
1139 		t->codec = HFP_AUDIO_CODEC_CVSD;
1140 
1141 	t->n_channels = 1;
1142 	t->channels[0] = SPA_AUDIO_CHANNEL_MONO;
1143 
1144 	spa_bt_device_add_profile(d, t->profile);
1145 	spa_bt_device_connect_profile(t->device, t->profile);
1146 
1147 	spa_log_debug(backend->log, "Transport %s available for hsphfpd", endpoint->path);
1148 
1149 	return DBUS_HANDLER_RESULT_HANDLED;
1150 }
1151 
hsphfpd_parse_interfaces(struct impl * backend,DBusMessageIter * dict_i)1152 static DBusHandlerResult hsphfpd_parse_interfaces(struct impl *backend, DBusMessageIter *dict_i)
1153 {
1154 	DBusMessageIter element_i;
1155 	const char *path;
1156 
1157 	spa_assert(backend);
1158 	spa_assert(dict_i);
1159 
1160 	dbus_message_iter_get_basic(dict_i, &path);
1161 	dbus_message_iter_next(dict_i);
1162 	dbus_message_iter_recurse(dict_i, &element_i);
1163 
1164 	while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1165 		DBusMessageIter iface_i;
1166 		const char *interface;
1167 
1168 		dbus_message_iter_recurse(&element_i, &iface_i);
1169 		dbus_message_iter_get_basic(&iface_i, &interface);
1170 		dbus_message_iter_next(&iface_i);
1171 
1172 		if (spa_streq(interface, HSPHFPD_ENDPOINT_INTERFACE)) {
1173 			struct hsphfpd_endpoint *endpoint;
1174 
1175 			endpoint = endpoint_find(backend, path);
1176 			if (!endpoint) {
1177 				endpoint = calloc(1, sizeof(struct hsphfpd_endpoint));
1178 				endpoint->path = strdup(path);
1179 				spa_list_append(&backend->endpoint_list, &endpoint->link);
1180 				spa_log_debug(backend->log, "Found endpoint %s", path);
1181 			}
1182 			hsphfpd_parse_endpoint_properties(backend, endpoint, &iface_i);
1183 		} else
1184 			spa_log_debug(backend->log, "Unknown interface %s found, skipping", interface);
1185 
1186 		dbus_message_iter_next(&element_i);
1187 	}
1188 
1189 	return DBUS_HANDLER_RESULT_HANDLED;
1190 }
1191 
hsphfpd_get_endpoints_reply(DBusPendingCall * pending,void * user_data)1192 static void hsphfpd_get_endpoints_reply(DBusPendingCall *pending, void *user_data)
1193 {
1194 	struct impl *backend = user_data;
1195 	DBusMessage *r;
1196 	DBusMessageIter i, array_i;
1197 
1198 	r = dbus_pending_call_steal_reply(pending);
1199 	if (r == NULL)
1200 		return;
1201 
1202 	if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1203 		spa_log_error(backend->log, "Failed to get a list of endpoints from hsphfpd: %s",
1204 				dbus_message_get_error_name(r));
1205 		goto finish;
1206 	}
1207 
1208 	if (!spa_streq(dbus_message_get_sender(r), backend->hsphfpd_service_id)) {
1209 		spa_log_error(backend->log, "Reply for GetManagedObjects() from invalid sender");
1210 		goto finish;
1211 	}
1212 
1213 	if (!dbus_message_iter_init(r, &i) || !check_signature(r, "a{oa{sa{sv}}}")) {
1214 		spa_log_error(backend->log, "Invalid arguments in GetManagedObjects() reply");
1215 		goto finish;
1216 	}
1217 
1218 	dbus_message_iter_recurse(&i, &array_i);
1219 	while (dbus_message_iter_get_arg_type(&array_i) != DBUS_TYPE_INVALID) {
1220 			DBusMessageIter dict_i;
1221 
1222 			dbus_message_iter_recurse(&array_i, &dict_i);
1223 			hsphfpd_parse_interfaces(backend, &dict_i);
1224 			dbus_message_iter_next(&array_i);
1225 	}
1226 
1227 	backend->endpoints_listed = true;
1228 
1229 finish:
1230 	dbus_message_unref(r);
1231 	dbus_pending_call_unref(pending);
1232 }
1233 
backend_hsphfpd_register(void * data)1234 static int backend_hsphfpd_register(void *data)
1235 {
1236 	struct impl *backend = data;
1237 	DBusMessage *m, *r;
1238 	const char *path = APPLICATION_OBJECT_MANAGER_PATH;
1239 	DBusPendingCall *call;
1240 	DBusError err;
1241 	int res;
1242 
1243 	spa_log_debug(backend->log, "Registering to hsphfpd");
1244 
1245 	m = dbus_message_new_method_call(HSPHFPD_SERVICE, "/",
1246 			HSPHFPD_APPLICATION_MANAGER_INTERFACE, "RegisterApplication");
1247 	if (m == NULL)
1248 		return -ENOMEM;
1249 
1250 	dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
1251 
1252 	dbus_error_init(&err);
1253 
1254 	r = dbus_connection_send_with_reply_and_block(backend->conn, m, -1, &err);
1255 	dbus_message_unref(m);
1256 
1257 	if (r == NULL) {
1258 		if (dbus_error_has_name(&err, "org.freedesktop.DBus.Error.ServiceUnknown")) {
1259 			spa_log_info(backend->log, "hsphfpd not available: %s",
1260 					err.message);
1261 			res = -ENOTSUP;
1262 		} else {
1263 			spa_log_warn(backend->log, "Registering application %s failed: %s (%s)",
1264 					path, err.message, err.name);
1265 			res = -EIO;
1266 		}
1267 		dbus_error_free(&err);
1268 		return res;
1269 	}
1270 
1271 	if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1272 		spa_log_error(backend->log, "RegisterApplication() failed: %s",
1273 				dbus_message_get_error_name(r));
1274 		goto finish;
1275 	}
1276 	dbus_message_unref(r);
1277 
1278 	backend->hsphfpd_service_id = strdup(dbus_message_get_sender(r));
1279 
1280 	spa_log_debug(backend->log, "Registered to hsphfpd");
1281 
1282 	m = dbus_message_new_method_call(HSPHFPD_SERVICE, "/",
1283 			DBUS_INTERFACE_OBJECTMANAGER, "GetManagedObjects");
1284 	if (m == NULL)
1285 		goto finish;
1286 
1287 	dbus_connection_send_with_reply(backend->conn, m, &call, -1);
1288 	dbus_pending_call_set_notify(call, hsphfpd_get_endpoints_reply, backend, NULL);
1289 	dbus_message_unref(m);
1290 
1291 	return 0;
1292 
1293 finish:
1294 	dbus_message_unref(r);
1295 	return -EIO;
1296 }
1297 
backend_hsphfpd_unregistered(void * data)1298 static int backend_hsphfpd_unregistered(void *data)
1299 {
1300 	struct impl *backend = data;
1301 	struct hsphfpd_endpoint *endpoint;
1302 
1303 	if (backend->hsphfpd_service_id) {
1304 		free(backend->hsphfpd_service_id);
1305 		backend->hsphfpd_service_id = NULL;
1306 	}
1307 	backend->endpoints_listed = false;
1308 	spa_list_consume(endpoint, &backend->endpoint_list, link)
1309 		endpoint_free(endpoint);
1310 
1311 	return 0;
1312 }
1313 
hsphfpd_filter_cb(DBusConnection * bus,DBusMessage * m,void * user_data)1314 static DBusHandlerResult hsphfpd_filter_cb(DBusConnection *bus, DBusMessage *m, void *user_data)
1315 {
1316 	const char *sender;
1317 	struct impl *backend = user_data;
1318 	DBusError err;
1319 
1320 	dbus_error_init(&err);
1321 
1322 	sender = dbus_message_get_sender(m);
1323 
1324 	if (backend->hsphfpd_service_id && spa_streq(sender, backend->hsphfpd_service_id)) {
1325 		if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECTMANAGER, "InterfacesAdded")) {
1326 			DBusMessageIter arg_i;
1327 
1328 			spa_log_warn(backend->log, "sender: %s", dbus_message_get_sender(m));
1329 
1330 			if (!backend->endpoints_listed)
1331 				goto finish;
1332 
1333 			if (!dbus_message_iter_init(m, &arg_i) || !check_signature(m, "oa{sa{sv}}")) {
1334 					spa_log_error(backend->log, "Invalid signature found in InterfacesAdded");
1335 					goto finish;
1336 			}
1337 
1338 			hsphfpd_parse_interfaces(backend, &arg_i);
1339 		} else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECTMANAGER, "InterfacesRemoved")) {
1340 			const char *path;
1341 			DBusMessageIter arg_i, element_i;
1342 
1343 			if (!backend->endpoints_listed)
1344 				goto finish;
1345 
1346 			if (!dbus_message_iter_init(m, &arg_i) || !check_signature(m, "oas")) {
1347 					spa_log_error(backend->log, "Invalid signature found in InterfacesRemoved");
1348 					goto finish;
1349 			}
1350 
1351 			dbus_message_iter_get_basic(&arg_i, &path);
1352 			dbus_message_iter_next(&arg_i);
1353 			dbus_message_iter_recurse(&arg_i, &element_i);
1354 
1355 			while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
1356 					const char *iface;
1357 
1358 					dbus_message_iter_get_basic(&element_i, &iface);
1359 
1360 					if (spa_streq(iface, HSPHFPD_ENDPOINT_INTERFACE)) {
1361 							struct hsphfpd_endpoint *endpoint;
1362 							struct spa_bt_transport *transport = spa_bt_transport_find(backend->monitor, path);
1363 
1364 							if (transport)
1365 								spa_bt_transport_free(transport);
1366 
1367 							spa_log_debug(backend->log, "Remove endpoint %s", path);
1368 							endpoint = endpoint_find(backend, path);
1369 							if (endpoint)
1370 								endpoint_free(endpoint);
1371 					}
1372 
1373 					dbus_message_iter_next(&element_i);
1374 			}
1375 		} else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged")) {
1376 			DBusMessageIter arg_i;
1377 			const char *iface;
1378 			const char *path;
1379 
1380 			if (!backend->endpoints_listed)
1381 				goto finish;
1382 
1383 			if (!dbus_message_iter_init(m, &arg_i) || !check_signature(m, "sa{sv}as")) {
1384 					spa_log_error(backend->log, "Invalid signature found in PropertiesChanged");
1385 					goto finish;
1386 			}
1387 
1388 			dbus_message_iter_get_basic(&arg_i, &iface);
1389 			dbus_message_iter_next(&arg_i);
1390 
1391 			path = dbus_message_get_path(m);
1392 
1393 			if (spa_streq(iface, HSPHFPD_ENDPOINT_INTERFACE)) {
1394 				struct hsphfpd_endpoint *endpoint = endpoint_find(backend, path);
1395 				if (!endpoint) {
1396 					spa_log_warn(backend->log, "Properties changed on unknown endpoint %s", path);
1397 					goto finish;
1398 				}
1399 				spa_log_debug(backend->log, "Properties changed on endpoint %s", path);
1400 				hsphfpd_parse_endpoint_properties(backend, endpoint, &arg_i);
1401 			} else if (spa_streq(iface, HSPHFPD_AUDIO_TRANSPORT_INTERFACE)) {
1402 				struct spa_bt_transport *transport = spa_bt_transport_find_full(backend->monitor,
1403 				                                                                hsphfpd_cmp_transport_path,
1404 				                                                                (const void *)path);
1405 				if (!transport) {
1406 					spa_log_warn(backend->log, "Properties changed on unknown transport %s", path);
1407 					goto finish;
1408 				}
1409 				spa_log_debug(backend->log, "Properties changed on transport %s", path);
1410 				hsphfpd_parse_transport_properties(backend, transport, &arg_i);
1411 			}
1412 		}
1413 	}
1414 
1415 finish:
1416 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1417 }
1418 
add_filters(void * data)1419 static int add_filters(void *data)
1420 {
1421 	struct impl *backend = data;
1422 	DBusError err;
1423 
1424 	if (backend->filters_added)
1425 		return 0;
1426 
1427 	dbus_error_init(&err);
1428 
1429 	if (!dbus_connection_add_filter(backend->conn, hsphfpd_filter_cb, backend, NULL)) {
1430 		spa_log_error(backend->log, "failed to add filter function");
1431 		goto fail;
1432 	}
1433 
1434 	dbus_bus_add_match(backend->conn,
1435 			"type='signal',sender='" HSPHFPD_SERVICE "',"
1436 			"interface='" DBUS_INTERFACE_OBJECTMANAGER "',member='InterfacesAdded'", &err);
1437 	dbus_bus_add_match(backend->conn,
1438 			"type='signal',sender='" HSPHFPD_SERVICE "',"
1439 			"interface='" DBUS_INTERFACE_OBJECTMANAGER "',member='InterfacesRemoved'", &err);
1440 	dbus_bus_add_match(backend->conn,
1441 			"type='signal',sender='" HSPHFPD_SERVICE "',"
1442 			"interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged',"
1443 			"arg0='" HSPHFPD_ENDPOINT_INTERFACE "'", &err);
1444 	dbus_bus_add_match(backend->conn,
1445 			"type='signal',sender='" HSPHFPD_SERVICE "',"
1446 			"interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged',"
1447 			"arg0='" HSPHFPD_AUDIO_TRANSPORT_INTERFACE "'", &err);
1448 
1449 	backend->filters_added = true;
1450 
1451 	return 0;
1452 
1453 fail:
1454 	dbus_error_free(&err);
1455 	return -EIO;
1456 }
1457 
backend_hsphfpd_free(void * data)1458 static int backend_hsphfpd_free(void *data)
1459 {
1460 	struct impl *backend = data;
1461 	struct hsphfpd_endpoint *endpoint;
1462 
1463 	if (backend->filters_added) {
1464 		dbus_connection_remove_filter(backend->conn, hsphfpd_filter_cb, backend);
1465 		backend->filters_added = false;
1466 	}
1467 
1468 	if (backend->msbc_supported)
1469 		dbus_connection_unregister_object_path(backend->conn, HSPHFP_AUDIO_CLIENT_MSBC);
1470 	dbus_connection_unregister_object_path(backend->conn, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ);
1471 	dbus_connection_unregister_object_path(backend->conn, APPLICATION_OBJECT_MANAGER_PATH);
1472 
1473 	spa_list_consume(endpoint, &backend->endpoint_list, link)
1474 		endpoint_free(endpoint);
1475 
1476 	free(backend);
1477 
1478 	return 0;
1479 }
1480 
1481 static const struct spa_bt_backend_implementation backend_impl = {
1482 	SPA_VERSION_BT_BACKEND_IMPLEMENTATION,
1483 	.free = backend_hsphfpd_free,
1484 	.register_profiles = backend_hsphfpd_register,
1485 	.unregister_profiles = backend_hsphfpd_unregistered,
1486 };
1487 
is_available(struct impl * backend)1488 static bool is_available(struct impl *backend)
1489 {
1490 	DBusMessage *m, *r;
1491 	DBusError err;
1492 	bool success = false;
1493 
1494 	m = dbus_message_new_method_call(HSPHFPD_SERVICE, "/",
1495 			DBUS_INTERFACE_INTROSPECTABLE, "Introspect");
1496 	if (m == NULL)
1497 		return false;
1498 
1499 	dbus_error_init(&err);
1500 	r = dbus_connection_send_with_reply_and_block(backend->conn, m, -1, &err);
1501 	dbus_message_unref(m);
1502 
1503 	if (r && dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
1504 		success = true;
1505 
1506 	if (r)
1507 		dbus_message_unref(r);
1508 	else
1509 		dbus_error_free(&err);
1510 
1511 	return success;
1512 }
1513 
backend_hsphfpd_new(struct spa_bt_monitor * monitor,void * dbus_connection,const struct spa_dict * info,const struct spa_bt_quirks * quirks,const struct spa_support * support,uint32_t n_support)1514 struct spa_bt_backend *backend_hsphfpd_new(struct spa_bt_monitor *monitor,
1515 		void *dbus_connection,
1516 		const struct spa_dict *info,
1517 		const struct spa_bt_quirks *quirks,
1518 		const struct spa_support *support,
1519 	  uint32_t n_support)
1520 {
1521 	struct impl *backend;
1522 	const char *str;
1523 	static const DBusObjectPathVTable vtable_application_object_manager = {
1524 		.message_function = application_object_manager_handler,
1525 	};
1526 	static const DBusObjectPathVTable vtable_audio_agent_endpoint = {
1527 		.message_function = audio_agent_endpoint_handler,
1528 	};
1529 
1530 	backend = calloc(1, sizeof(struct impl));
1531 	if (backend == NULL)
1532 		return NULL;
1533 
1534 	spa_bt_backend_set_implementation(&backend->this, &backend_impl, backend);
1535 
1536 	backend->this.name = "hsphfpd";
1537 	backend->this.exclusive = true;
1538 	backend->monitor = monitor;
1539 	backend->quirks = quirks;
1540 	backend->log = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Log);
1541 	backend->dbus = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_DBus);
1542 	backend->main_loop = spa_support_find(support, n_support, SPA_TYPE_INTERFACE_Loop);
1543 	backend->conn = dbus_connection;
1544 	if (info && (str = spa_dict_lookup(info, "bluez5.enable-msbc")))
1545 		backend->msbc_supported = spa_atob(str);
1546 	else
1547 		backend->msbc_supported = false;
1548 
1549 	spa_log_topic_init(backend->log, &log_topic);
1550 
1551 	spa_list_init(&backend->endpoint_list);
1552 
1553 	if (!dbus_connection_register_object_path(backend->conn,
1554 	            APPLICATION_OBJECT_MANAGER_PATH,
1555 	            &vtable_application_object_manager, backend)) {
1556 		free(backend);
1557 		return NULL;
1558 	}
1559 
1560 	if (!dbus_connection_register_object_path(backend->conn,
1561 	            HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ,
1562 	            &vtable_audio_agent_endpoint, backend)) {
1563 		dbus_connection_unregister_object_path(backend->conn, APPLICATION_OBJECT_MANAGER_PATH);
1564 		free(backend);
1565 		return NULL;
1566 	}
1567 
1568 	if (backend->msbc_supported && !dbus_connection_register_object_path(backend->conn,
1569 	            HSPHFP_AUDIO_CLIENT_MSBC,
1570 	            &vtable_audio_agent_endpoint, backend)) {
1571 		dbus_connection_unregister_object_path(backend->conn, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ);
1572 		dbus_connection_unregister_object_path(backend->conn, APPLICATION_OBJECT_MANAGER_PATH);
1573 		free(backend);
1574 		return NULL;
1575 	}
1576 
1577 	if (add_filters(backend) < 0) {
1578 		dbus_connection_unregister_object_path(backend->conn, HSPHFP_AUDIO_CLIENT_MSBC);
1579 		dbus_connection_unregister_object_path(backend->conn, HSPHFP_AUDIO_CLIENT_PCM_S16LE_8KHZ);
1580 		dbus_connection_unregister_object_path(backend->conn, APPLICATION_OBJECT_MANAGER_PATH);
1581 		free(backend);
1582 		return NULL;
1583 	}
1584 
1585 	backend->this.available = is_available(backend);
1586 
1587 	return &backend->this;
1588 }
1589