• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2013 The Chromium Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 
6 #include <dbus/dbus.h>
7 #include <errno.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <syslog.h>
12 
13 #include "cras_bt_constants.h"
14 #include "cras_bt_manager.h"
15 #include "cras_bt_adapter.h"
16 #include "cras_bt_device.h"
17 #include "cras_bt_endpoint.h"
18 #include "cras_bt_player.h"
19 #include "cras_bt_profile.h"
20 #include "cras_bt_transport.h"
21 #include "utlist.h"
22 
23 
cras_bt_interface_added(DBusConnection * conn,const char * object_path,const char * interface_name,DBusMessageIter * properties_array_iter)24 static void cras_bt_interface_added(DBusConnection *conn,
25 				    const char *object_path,
26 				    const char *interface_name,
27 				    DBusMessageIter *properties_array_iter)
28 {
29 	if (strcmp(interface_name, BLUEZ_INTERFACE_ADAPTER) == 0) {
30 		struct cras_bt_adapter *adapter;
31 
32 		adapter = cras_bt_adapter_get(object_path);
33 		if (adapter) {
34 			cras_bt_adapter_update_properties(
35 				adapter, properties_array_iter, NULL);
36 		} else {
37 			adapter = cras_bt_adapter_create(object_path);
38 			if (adapter) {
39 				cras_bt_adapter_update_properties(
40 					adapter, properties_array_iter, NULL);
41 				cras_bt_register_endpoints(conn, adapter);
42 				cras_bt_register_player(conn, adapter);
43 				cras_bt_register_profiles(conn);
44 
45 				syslog(LOG_INFO, "Bluetooth Adapter: %s added",
46 				       cras_bt_adapter_address(adapter));
47 			} else {
48 				syslog(LOG_WARNING,
49 				       "Failed to create Bluetooth Adapter: %s",
50 				       object_path);
51 			}
52 		}
53 
54 	} else if (strcmp(interface_name, BLUEZ_INTERFACE_DEVICE) == 0) {
55 		struct cras_bt_device *device;
56 
57 		device = cras_bt_device_get(object_path);
58 		if (device) {
59 			cras_bt_device_update_properties(
60 				device, properties_array_iter, NULL);
61 		} else {
62 			device = cras_bt_device_create(conn, object_path);
63 			if (device) {
64 				cras_bt_device_update_properties(
65 					device, properties_array_iter, NULL);
66 
67 				syslog(LOG_INFO, "Bluetooth Device: %s added",
68 				       cras_bt_device_address(device));
69 			} else {
70 				syslog(LOG_WARNING,
71 				       "Failed to create Bluetooth Device: %s",
72 				       object_path);
73 			}
74 		}
75 
76 	} else if (strcmp(interface_name,
77 			  BLUEZ_INTERFACE_MEDIA_TRANSPORT) == 0) {
78 		struct cras_bt_transport *transport;
79 
80 		transport = cras_bt_transport_get(object_path);
81 		if (transport) {
82 			cras_bt_transport_update_properties(
83 				transport, properties_array_iter, NULL);
84 		} else {
85 			transport = cras_bt_transport_create(conn, object_path);
86 			if (transport) {
87 				cras_bt_transport_update_properties(
88 					transport, properties_array_iter, NULL);
89 
90 				syslog(LOG_INFO,
91 				      "Bluetooth Transport: %s added",
92 				      cras_bt_transport_object_path(transport));
93 			} else {
94 				syslog(LOG_WARNING,
95 				       "Failed to create Bluetooth Transport: "
96 				       "%s",
97 				       object_path);
98 			}
99 		}
100 
101 	}
102 }
103 
cras_bt_interface_removed(DBusConnection * conn,const char * object_path,const char * interface_name)104 static void cras_bt_interface_removed(DBusConnection *conn,
105 				      const char *object_path,
106 				      const char *interface_name)
107 {
108 	if (strcmp(interface_name, BLUEZ_INTERFACE_ADAPTER) == 0) {
109 		struct cras_bt_adapter *adapter;
110 
111 		adapter = cras_bt_adapter_get(object_path);
112 		if (adapter) {
113 			syslog(LOG_INFO, "Bluetooth Adapter: %s removed",
114 			       cras_bt_adapter_address(adapter));
115 			cras_bt_adapter_destroy(adapter);
116 		}
117 
118 	} else if (strcmp(interface_name, BLUEZ_INTERFACE_DEVICE) == 0) {
119 		struct cras_bt_device *device;
120 
121 		device = cras_bt_device_get(object_path);
122 		if (device) {
123 			syslog(LOG_INFO, "Bluetooth Device: %s removed",
124 			       cras_bt_device_address(device));
125 			cras_bt_device_destroy(device);
126 		}
127 
128 	} else if (strcmp(interface_name,
129 			  BLUEZ_INTERFACE_MEDIA_TRANSPORT) == 0) {
130 		struct cras_bt_transport *transport;
131 
132 		transport = cras_bt_transport_get(object_path);
133 		if (transport) {
134 			syslog(LOG_INFO, "Bluetooth Transport: %s removed",
135 			       cras_bt_transport_object_path(transport));
136 			cras_bt_transport_destroy(transport);
137 		}
138 
139 	}
140 }
141 
cras_bt_update_properties(DBusConnection * conn,const char * object_path,const char * interface_name,DBusMessageIter * properties_array_iter,DBusMessageIter * invalidated_array_iter)142 static void cras_bt_update_properties(DBusConnection *conn,
143 				      const char *object_path,
144 				      const char *interface_name,
145 				      DBusMessageIter *properties_array_iter,
146 				      DBusMessageIter *invalidated_array_iter)
147 {
148 	if (strcmp(interface_name, BLUEZ_INTERFACE_ADAPTER) == 0) {
149 		struct cras_bt_adapter *adapter;
150 
151 		adapter = cras_bt_adapter_get(object_path);
152 		if (adapter) {
153 			cras_bt_adapter_update_properties(
154 				adapter, properties_array_iter,
155 				invalidated_array_iter);
156 		}
157 
158 	} else if (strcmp(interface_name, BLUEZ_INTERFACE_DEVICE) == 0) {
159 		struct cras_bt_device *device;
160 
161 		device = cras_bt_device_get(object_path);
162 		if (device) {
163 			cras_bt_device_update_properties(
164 				device, properties_array_iter,
165 				invalidated_array_iter);
166 		}
167 
168 	} else if (strcmp(interface_name,
169 			  BLUEZ_INTERFACE_MEDIA_TRANSPORT) == 0) {
170 		struct cras_bt_transport *transport;
171 
172 		transport = cras_bt_transport_get(object_path);
173 		if (transport) {
174 			cras_bt_transport_update_properties(
175 				transport, properties_array_iter,
176 				invalidated_array_iter);
177 		}
178 
179 	}
180 }
181 
182 /* Destroys all bt related stuff. The reset functions must be called in
183  * reverse order of the adapter -> device -> profile(s) hierarchy.
184  */
cras_bt_reset()185 static void cras_bt_reset()
186 {
187 	cras_bt_endpoint_reset();
188 	cras_bt_transport_reset();
189 	cras_bt_profile_reset();
190 	cras_bt_device_reset();
191 	cras_bt_adapter_reset();
192 }
193 
194 
cras_bt_on_get_managed_objects(DBusPendingCall * pending_call,void * data)195 static void cras_bt_on_get_managed_objects(DBusPendingCall *pending_call,
196 					   void *data)
197 {
198 	DBusConnection *conn = (DBusConnection *)data;
199 	DBusMessage *reply;
200 	DBusMessageIter message_iter, object_array_iter;
201 
202 	reply = dbus_pending_call_steal_reply(pending_call);
203 	dbus_pending_call_unref(pending_call);
204 
205 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
206 		syslog(LOG_WARNING, "GetManagedObjects returned error: %s",
207 		       dbus_message_get_error_name(reply));
208 		dbus_message_unref(reply);
209 		return;
210 	}
211 
212 	if (!dbus_message_has_signature(reply, "a{oa{sa{sv}}}")) {
213 		syslog(LOG_WARNING, "Bad GetManagedObjects reply received");
214 		dbus_message_unref(reply);
215 		return;
216 	}
217 
218 	dbus_message_iter_init(reply, &message_iter);
219 	dbus_message_iter_recurse(&message_iter, &object_array_iter);
220 
221 	while (dbus_message_iter_get_arg_type(&object_array_iter) !=
222 		       DBUS_TYPE_INVALID) {
223 		DBusMessageIter object_dict_iter, interface_array_iter;
224 		const char *object_path;
225 
226 		dbus_message_iter_recurse(&object_array_iter,
227 					  &object_dict_iter);
228 
229 		dbus_message_iter_get_basic(&object_dict_iter, &object_path);
230 		dbus_message_iter_next(&object_dict_iter);
231 
232 		dbus_message_iter_recurse(&object_dict_iter,
233 					  &interface_array_iter);
234 
235 		while (dbus_message_iter_get_arg_type(&interface_array_iter) !=
236 			       DBUS_TYPE_INVALID) {
237 			DBusMessageIter interface_dict_iter;
238 			DBusMessageIter properties_array_iter;
239 			const char *interface_name;
240 
241 			dbus_message_iter_recurse(&interface_array_iter,
242 						  &interface_dict_iter);
243 
244 			dbus_message_iter_get_basic(&interface_dict_iter,
245 						    &interface_name);
246 			dbus_message_iter_next(&interface_dict_iter);
247 
248 			dbus_message_iter_recurse(&interface_dict_iter,
249 						  &properties_array_iter);
250 
251 			cras_bt_interface_added(conn,
252 						object_path, interface_name,
253 						&properties_array_iter);
254 
255 			dbus_message_iter_next(&interface_array_iter);
256 		}
257 
258 		dbus_message_iter_next(&object_array_iter);
259 	}
260 
261 	dbus_message_unref(reply);
262 }
263 
cras_bt_get_managed_objects(DBusConnection * conn)264 static int cras_bt_get_managed_objects(DBusConnection *conn)
265 {
266 	DBusMessage *method_call;
267 	DBusPendingCall *pending_call;
268 
269 	method_call = dbus_message_new_method_call(
270 		BLUEZ_SERVICE,
271 		"/",
272 		DBUS_INTERFACE_OBJECT_MANAGER,
273 		"GetManagedObjects");
274 	if (!method_call)
275 		return -ENOMEM;
276 
277 	pending_call = NULL;
278 	if (!dbus_connection_send_with_reply(conn, method_call,
279 					     &pending_call,
280 					     DBUS_TIMEOUT_USE_DEFAULT)) {
281 		dbus_message_unref(method_call);
282 		return -ENOMEM;
283 	}
284 
285 	dbus_message_unref(method_call);
286 	if (!pending_call)
287 		return -EIO;
288 
289 	if (!dbus_pending_call_set_notify(pending_call,
290 					  cras_bt_on_get_managed_objects,
291 					  conn, NULL)) {
292 		dbus_pending_call_cancel(pending_call);
293 		dbus_pending_call_unref(pending_call);
294 		return -ENOMEM;
295 	}
296 
297 	return 0;
298 }
299 
300 
cras_bt_handle_name_owner_changed(DBusConnection * conn,DBusMessage * message,void * arg)301 static DBusHandlerResult cras_bt_handle_name_owner_changed(DBusConnection *conn,
302 							   DBusMessage *message,
303 							   void *arg)
304 {
305 	DBusError dbus_error;
306 	const char *service_name, *old_owner, *new_owner;
307 
308 	if (!dbus_message_is_signal(message, DBUS_INTERFACE_DBUS,
309 		    "NameOwnerChanged"))
310 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
311 
312 	dbus_error_init(&dbus_error);
313 	if (!dbus_message_get_args(message, &dbus_error,
314 				   DBUS_TYPE_STRING, &service_name,
315 				   DBUS_TYPE_STRING, &old_owner,
316 				   DBUS_TYPE_STRING, &new_owner,
317 				   DBUS_TYPE_INVALID)) {
318 		syslog(LOG_WARNING, "Bad NameOwnerChanged signal received: %s",
319 		       dbus_error.message);
320 		dbus_error_free(&dbus_error);
321 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
322 	}
323 
324 	syslog(LOG_INFO, "Bluetooth daemon disconnected from the bus.");
325 	cras_bt_reset();
326 
327 	if (strlen(new_owner) > 0)
328 		cras_bt_get_managed_objects(conn);
329 
330 	return DBUS_HANDLER_RESULT_HANDLED;
331 }
332 
cras_bt_handle_interfaces_added(DBusConnection * conn,DBusMessage * message,void * arg)333 static DBusHandlerResult cras_bt_handle_interfaces_added(DBusConnection *conn,
334 							 DBusMessage *message,
335 							 void *arg)
336 {
337 	DBusMessageIter message_iter, interface_array_iter;
338 	const char *object_path;
339 
340 	if (!dbus_message_is_signal(message, DBUS_INTERFACE_OBJECT_MANAGER,
341 			    "InterfacesAdded"))
342 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
343 
344 	if (!dbus_message_has_signature(message, "oa{sa{sv}}")) {
345 		syslog(LOG_WARNING, "Bad InterfacesAdded signal received");
346 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
347 	}
348 
349 	dbus_message_iter_init(message, &message_iter);
350 
351 	dbus_message_iter_get_basic(&message_iter, &object_path);
352 	dbus_message_iter_next(&message_iter);
353 
354 	dbus_message_iter_recurse(&message_iter, &interface_array_iter);
355 
356 	while (dbus_message_iter_get_arg_type(&interface_array_iter) !=
357 		       DBUS_TYPE_INVALID) {
358 		DBusMessageIter interface_dict_iter, properties_array_iter;
359 		const char *interface_name;
360 
361 		dbus_message_iter_recurse(&interface_array_iter,
362 					  &interface_dict_iter);
363 
364 		dbus_message_iter_get_basic(&interface_dict_iter,
365 					    &interface_name);
366 		dbus_message_iter_next(&interface_dict_iter);
367 
368 		dbus_message_iter_recurse(&interface_dict_iter,
369 					  &properties_array_iter);
370 
371 		cras_bt_interface_added(conn, object_path, interface_name,
372 					&properties_array_iter);
373 
374 		dbus_message_iter_next(&interface_array_iter);
375 	}
376 
377 	return DBUS_HANDLER_RESULT_HANDLED;
378 }
379 
cras_bt_handle_interfaces_removed(DBusConnection * conn,DBusMessage * message,void * arg)380 static DBusHandlerResult cras_bt_handle_interfaces_removed(DBusConnection *conn,
381 							   DBusMessage *message,
382 							   void *arg)
383 {
384 	DBusMessageIter message_iter, interface_array_iter;
385 	const char *object_path;
386 
387 	if (!dbus_message_is_signal(message, DBUS_INTERFACE_OBJECT_MANAGER,
388 			    "InterfacesRemoved"))
389 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
390 
391 	if (!dbus_message_has_signature(message, "oas")) {
392 		syslog(LOG_WARNING, "Bad InterfacesRemoved signal received");
393 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
394 	}
395 
396 	dbus_message_iter_init(message, &message_iter);
397 
398 	dbus_message_iter_get_basic(&message_iter, &object_path);
399 	dbus_message_iter_next(&message_iter);
400 
401 	dbus_message_iter_recurse(&message_iter, &interface_array_iter);
402 
403 	while (dbus_message_iter_get_arg_type(&interface_array_iter) !=
404 	       DBUS_TYPE_INVALID) {
405 		const char *interface_name;
406 
407 		dbus_message_iter_get_basic(&interface_array_iter,
408 					    &interface_name);
409 
410 		cras_bt_interface_removed(conn, object_path, interface_name);
411 
412 		dbus_message_iter_next(&interface_array_iter);
413 	}
414 
415 	return DBUS_HANDLER_RESULT_HANDLED;
416 }
417 
cras_bt_handle_properties_changed(DBusConnection * conn,DBusMessage * message,void * arg)418 static DBusHandlerResult cras_bt_handle_properties_changed(DBusConnection *conn,
419 							   DBusMessage *message,
420 							   void *arg)
421 {
422 	DBusMessageIter message_iter, properties_array_iter;
423 	DBusMessageIter invalidated_array_iter;
424 	const char *object_path, *interface_name;
425 
426 	if (!dbus_message_is_signal(message, DBUS_INTERFACE_PROPERTIES,
427 				    "PropertiesChanged"))
428 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
429 
430 	if (!dbus_message_has_signature(message, "sa{sv}as")) {
431 		syslog(LOG_WARNING, "Bad PropertiesChanged signal received");
432 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
433 	}
434 
435 	object_path = dbus_message_get_path(message);
436 
437 	dbus_message_iter_init(message, &message_iter);
438 
439 	dbus_message_iter_get_basic(&message_iter, &interface_name);
440 	dbus_message_iter_next(&message_iter);
441 
442 	dbus_message_iter_recurse(&message_iter, &properties_array_iter);
443 	dbus_message_iter_next(&message_iter);
444 
445 	dbus_message_iter_recurse(&message_iter, &invalidated_array_iter);
446 
447 	cras_bt_update_properties(conn, object_path, interface_name,
448 				  &properties_array_iter,
449 				  &invalidated_array_iter);
450 
451 	return DBUS_HANDLER_RESULT_HANDLED;
452 }
453 
454 
cras_bt_start(DBusConnection * conn)455 void cras_bt_start(DBusConnection *conn)
456 {
457 	DBusError dbus_error;
458 
459 	dbus_error_init(&dbus_error);
460 
461 	/* Inform the bus daemon which signals we wish to receive. */
462 	dbus_bus_add_match(conn,
463 			   "type='signal',"
464 			   "sender='" DBUS_SERVICE_DBUS "',"
465 			   "interface='" DBUS_INTERFACE_DBUS "',"
466 			   "member='NameOwnerChanged',"
467 			   "arg0='" BLUEZ_SERVICE "'",
468 			   &dbus_error);
469 	if (dbus_error_is_set(&dbus_error))
470 		goto add_match_error;
471 
472 	dbus_bus_add_match(conn,
473 			   "type='signal',"
474 			   "sender='" BLUEZ_SERVICE "',"
475 			   "interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
476 			   "member='InterfacesAdded'",
477 			   &dbus_error);
478 	if (dbus_error_is_set(&dbus_error))
479 		goto add_match_error;
480 
481 	dbus_bus_add_match(conn,
482 			   "type='signal',"
483 			   "sender='" BLUEZ_SERVICE "',"
484 			   "interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
485 			   "member='InterfacesRemoved'",
486 			   &dbus_error);
487 	if (dbus_error_is_set(&dbus_error))
488 		goto add_match_error;
489 
490 	dbus_bus_add_match(conn,
491 			   "type='signal',"
492 			   "sender='" BLUEZ_SERVICE "',"
493 			   "interface='" DBUS_INTERFACE_PROPERTIES "',"
494 			   "member='PropertiesChanged'",
495 			   &dbus_error);
496 	if (dbus_error_is_set(&dbus_error))
497 		goto add_match_error;
498 
499 	/* Install filter functions to handle the signals we receive. */
500 	if (!dbus_connection_add_filter(conn, cras_bt_handle_name_owner_changed,
501 					NULL, NULL))
502 		goto add_filter_error;
503 
504 	if (!dbus_connection_add_filter(conn, cras_bt_handle_interfaces_added,
505 					NULL, NULL))
506 		goto add_filter_error;
507 
508 	if (!dbus_connection_add_filter(conn, cras_bt_handle_interfaces_removed,
509 					NULL, NULL))
510 		goto add_filter_error;
511 
512 	if (!dbus_connection_add_filter(conn, cras_bt_handle_properties_changed,
513 					NULL, NULL))
514 		goto add_filter_error;
515 
516 	cras_bt_get_managed_objects(conn);
517 	return;
518 
519 add_match_error:
520 	syslog(LOG_WARNING, "Couldn't setup Bluetooth device monitoring: %s",
521 	       dbus_error.message);
522 	dbus_error_free(&dbus_error);
523 	cras_bt_stop(conn);
524 	return;
525 
526 add_filter_error:
527 	syslog(LOG_WARNING, "Couldn't setup Bluetooth device monitoring: %s",
528 	       strerror(ENOMEM));
529 	cras_bt_stop(conn);
530 	return;
531 }
532 
cras_bt_stop(DBusConnection * conn)533 void cras_bt_stop(DBusConnection *conn)
534 {
535 	cras_bt_reset();
536 
537 	dbus_bus_remove_match(conn,
538 			      "type='signal',"
539 			      "sender='" DBUS_SERVICE_DBUS "',"
540 			      "interface='" DBUS_INTERFACE_DBUS "',"
541 			      "member='NameOwnerChanged',"
542 			      "arg0='" BLUEZ_SERVICE "'",
543 			      NULL);
544 	dbus_bus_remove_match(conn,
545 			      "type='signal',"
546 			      "sender='" BLUEZ_SERVICE "',"
547 			      "interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
548 			      "member='InterfacesAdded'",
549 			      NULL);
550 	dbus_bus_remove_match(conn,
551 			      "type='signal',"
552 			      "sender='" BLUEZ_SERVICE "',"
553 			      "interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
554 			      "member='InterfacesRemoved'",
555 			      NULL);
556 	dbus_bus_remove_match(conn,
557 			      "type='signal',"
558 			      "sender='" BLUEZ_SERVICE "',"
559 			      "interface='" DBUS_INTERFACE_PROPERTIES "',"
560 			      "member='PropertiesChanged'",
561 			      NULL);
562 
563 	dbus_connection_remove_filter(conn, cras_bt_handle_name_owner_changed,
564 				      NULL);
565 	dbus_connection_remove_filter(conn, cras_bt_handle_interfaces_added,
566 				      NULL);
567 	dbus_connection_remove_filter(conn, cras_bt_handle_interfaces_removed,
568 				      NULL);
569 	dbus_connection_remove_filter(conn, cras_bt_handle_properties_changed,
570 				      NULL);
571 }
572