• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2013 The Chromium OS 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 <stdlib.h>
9 #include <string.h>
10 #include <syslog.h>
11 #include <unistd.h>
12 
13 #include "cras_bt_constants.h"
14 #include "cras_bt_device.h"
15 #include "cras_bt_profile.h"
16 #include "cras_dbus_util.h"
17 #include "utlist.h"
18 
19 #define PROFILE_INTROSPECT_XML                                                 \
20 	DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                              \
21 	"<node>\n"                                                             \
22 	"  <interface name=\"org.bluez.Profile1\">\n"                          \
23 	"    <method name=\"Release\">\n"                                      \
24 	"    </method>\n"                                                      \
25 	"    <method name=\"NewConnection\">\n"                                \
26 	"      <arg name=\"device\" type=\"o\" direction=\"in\">\n"            \
27 	"      <arg name=\"fd\" type=\"h\" direction=\"in\">\n"                \
28 	"      <arg name=\"fd_properties\" type=\"a{sv}\" direction=\"in\">\n" \
29 	"    </method>\n"                                                      \
30 	"    <method name=\"RequestDisconnection\">\n"                         \
31 	"      <arg name=\"device\" type=\"o\" direction=\"in\">\n"            \
32 	"    </method>\n"                                                      \
33 	"    <method name=\"Cancel\">\n"                                       \
34 	"    </method>\n"                                                      \
35 	"  <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"           \
36 	"    <method name=\"Introspect\">\n"                                   \
37 	"      <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"            \
38 	"    </method>\n"                                                      \
39 	"  </interface>\n"                                                     \
40 	"</node>\n"
41 
42 /* Profiles */
43 static struct cras_bt_profile *profiles;
44 
cras_bt_profile_handle_release(DBusConnection * conn,DBusMessage * message,void * arg)45 static DBusHandlerResult cras_bt_profile_handle_release(DBusConnection *conn,
46 							DBusMessage *message,
47 							void *arg)
48 {
49 	DBusMessage *reply;
50 	const char *profile_path;
51 	struct cras_bt_profile *profile;
52 
53 	profile_path = dbus_message_get_path(message);
54 
55 	profile = cras_bt_profile_get(profile_path);
56 	if (!profile)
57 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
58 
59 	syslog(LOG_ERR, "Profile %s released by bluetoothd", profile->name);
60 	profile->release(profile);
61 
62 	reply = dbus_message_new_method_return(message);
63 	if (!reply)
64 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
65 	if (!dbus_connection_send(conn, reply, NULL)) {
66 		dbus_message_unref(reply);
67 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
68 	}
69 
70 	dbus_message_unref(reply);
71 
72 	return DBUS_HANDLER_RESULT_HANDLED;
73 }
74 
75 static DBusHandlerResult
cras_bt_profile_handle_new_connection(DBusConnection * conn,DBusMessage * message,void * arg)76 cras_bt_profile_handle_new_connection(DBusConnection *conn,
77 				      DBusMessage *message, void *arg)
78 {
79 	DBusMessageIter message_iter;
80 	DBusMessage *reply;
81 	const char *profile_path, *object_path;
82 	int fd = -1;
83 	int err;
84 	struct cras_bt_profile *profile;
85 	struct cras_bt_device *device;
86 
87 	profile_path = dbus_message_get_path(message);
88 
89 	dbus_message_iter_init(message, &message_iter);
90 	dbus_message_iter_get_basic(&message_iter, &object_path);
91 	dbus_message_iter_next(&message_iter);
92 
93 	if (dbus_message_iter_get_arg_type(&message_iter) !=
94 	    DBUS_TYPE_UNIX_FD) {
95 		syslog(LOG_ERR, "Argument not a valid unix file descriptor");
96 		goto invalid;
97 	}
98 
99 	dbus_message_iter_get_basic(&message_iter, &fd);
100 	dbus_message_iter_next(&message_iter);
101 	if (fd < 0)
102 		goto invalid;
103 
104 	profile = cras_bt_profile_get(profile_path);
105 	if (!profile)
106 		goto invalid;
107 
108 	device = cras_bt_device_get(object_path);
109 	if (!device) {
110 		syslog(LOG_ERR, "Device %s not found at %s new connection",
111 		       object_path, profile_path);
112 		device = cras_bt_device_create(conn, object_path);
113 	}
114 
115 	err = profile->new_connection(conn, profile, device, fd);
116 	if (err) {
117 		syslog(LOG_INFO, "%s new connection rejected", profile->name);
118 		close(fd);
119 		reply = dbus_message_new_error(
120 			message, "org.chromium.Cras.Error.RejectNewConnection",
121 			"Possibly another headset already in use");
122 		if (!dbus_connection_send(conn, reply, NULL))
123 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
124 
125 		dbus_message_unref(reply);
126 		return DBUS_HANDLER_RESULT_HANDLED;
127 	}
128 
129 	reply = dbus_message_new_method_return(message);
130 	if (!reply)
131 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
132 	if (!dbus_connection_send(conn, reply, NULL)) {
133 		dbus_message_unref(reply);
134 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
135 	}
136 
137 	dbus_message_unref(reply);
138 	return DBUS_HANDLER_RESULT_HANDLED;
139 
140 invalid:
141 	if (fd >= 0)
142 		close(fd);
143 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
144 }
145 
146 static DBusHandlerResult
cras_bt_profile_handle_request_disconnection(DBusConnection * conn,DBusMessage * message,void * arg)147 cras_bt_profile_handle_request_disconnection(DBusConnection *conn,
148 					     DBusMessage *message, void *arg)
149 {
150 	DBusMessageIter message_iter;
151 	DBusMessage *reply;
152 	const char *prpofile_path, *object_path;
153 	struct cras_bt_profile *profile;
154 	struct cras_bt_device *device;
155 
156 	prpofile_path = dbus_message_get_path(message);
157 
158 	dbus_message_iter_init(message, &message_iter);
159 	dbus_message_iter_get_basic(&message_iter, &object_path);
160 	dbus_message_iter_next(&message_iter);
161 
162 	profile = cras_bt_profile_get(prpofile_path);
163 	if (!profile)
164 		goto invalid;
165 
166 	device = cras_bt_device_get(object_path);
167 	if (!device)
168 		goto invalid;
169 
170 	profile->request_disconnection(profile, device);
171 
172 	reply = dbus_message_new_method_return(message);
173 	if (!reply)
174 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
175 	if (!dbus_connection_send(conn, reply, NULL)) {
176 		dbus_message_unref(reply);
177 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
178 	}
179 
180 	dbus_message_unref(reply);
181 
182 	return DBUS_HANDLER_RESULT_HANDLED;
183 
184 invalid:
185 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
186 }
187 
cras_bt_profile_handle_cancel(DBusConnection * conn,DBusMessage * message,void * arg)188 static DBusHandlerResult cras_bt_profile_handle_cancel(DBusConnection *conn,
189 						       DBusMessage *message,
190 						       void *arg)
191 {
192 	DBusMessage *reply;
193 	const char *profile_path;
194 	struct cras_bt_profile *profile;
195 
196 	profile_path = dbus_message_get_path(message);
197 
198 	profile = cras_bt_profile_get(profile_path);
199 	if (!profile)
200 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
201 
202 	profile->cancel(profile);
203 
204 	reply = dbus_message_new_method_return(message);
205 	if (!reply)
206 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
207 	if (!dbus_connection_send(conn, reply, NULL)) {
208 		dbus_message_unref(reply);
209 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
210 	}
211 
212 	dbus_message_unref(reply);
213 
214 	return DBUS_HANDLER_RESULT_HANDLED;
215 }
216 
cras_bt_handle_profile_messages(DBusConnection * conn,DBusMessage * message,void * arg)217 static DBusHandlerResult cras_bt_handle_profile_messages(DBusConnection *conn,
218 							 DBusMessage *message,
219 							 void *arg)
220 {
221 	if (dbus_message_is_method_call(message, DBUS_INTERFACE_INTROSPECTABLE,
222 					"Introspect")) {
223 		DBusMessage *reply;
224 		const char *xml = PROFILE_INTROSPECT_XML;
225 
226 		reply = dbus_message_new_method_return(message);
227 		if (!reply)
228 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
229 		if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &xml,
230 					      DBUS_TYPE_INVALID)) {
231 			dbus_message_unref(reply);
232 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
233 		}
234 		if (!dbus_connection_send(conn, reply, NULL)) {
235 			dbus_message_unref(reply);
236 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
237 		}
238 
239 		dbus_message_unref(reply);
240 		return DBUS_HANDLER_RESULT_HANDLED;
241 	} else if (dbus_message_is_method_call(message, BLUEZ_INTERFACE_PROFILE,
242 					       "Release")) {
243 		return cras_bt_profile_handle_release(conn, message, arg);
244 	} else if (dbus_message_is_method_call(message, BLUEZ_INTERFACE_PROFILE,
245 					       "NewConnection")) {
246 		return cras_bt_profile_handle_new_connection(conn, message,
247 							     arg);
248 	} else if (dbus_message_is_method_call(message, BLUEZ_INTERFACE_PROFILE,
249 					       "RequestDisconnection")) {
250 		return cras_bt_profile_handle_request_disconnection(
251 			conn, message, arg);
252 	} else if (dbus_message_is_method_call(message, BLUEZ_INTERFACE_PROFILE,
253 					       "Cancel")) {
254 		return cras_bt_profile_handle_cancel(conn, message, arg);
255 	} else {
256 		syslog(LOG_ERR, "Unknown Profile message");
257 	}
258 
259 	return DBUS_HANDLER_RESULT_HANDLED;
260 }
261 
cras_bt_on_register_profile(DBusPendingCall * pending_call,void * data)262 static void cras_bt_on_register_profile(DBusPendingCall *pending_call,
263 					void *data)
264 {
265 	DBusMessage *reply;
266 
267 	reply = dbus_pending_call_steal_reply(pending_call);
268 	dbus_pending_call_unref(pending_call);
269 
270 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR)
271 		syslog(LOG_ERR, "RegisterProfile returned error: %s",
272 		       dbus_message_get_error_name(reply));
273 	dbus_message_unref(reply);
274 }
275 
cras_bt_register_profile(DBusConnection * conn,struct cras_bt_profile * profile)276 int cras_bt_register_profile(DBusConnection *conn,
277 			     struct cras_bt_profile *profile)
278 {
279 	DBusMessage *method_call;
280 	DBusMessageIter message_iter;
281 	DBusMessageIter properties_array_iter;
282 	DBusPendingCall *pending_call;
283 
284 	method_call = dbus_message_new_method_call(BLUEZ_SERVICE,
285 						   PROFILE_MANAGER_OBJ_PATH,
286 						   BLUEZ_PROFILE_MGMT_INTERFACE,
287 						   "RegisterProfile");
288 
289 	if (!method_call)
290 		return -ENOMEM;
291 
292 	dbus_message_iter_init_append(method_call, &message_iter);
293 	dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_OBJECT_PATH,
294 				       &profile->object_path);
295 	dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_STRING,
296 				       &profile->uuid);
297 
298 	dbus_message_iter_open_container(
299 		&message_iter, DBUS_TYPE_ARRAY,
300 		DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
301 			DBUS_TYPE_VARIANT_AS_STRING
302 				DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
303 		&properties_array_iter);
304 
305 	if (!append_key_value(&properties_array_iter, "Name", DBUS_TYPE_STRING,
306 			      DBUS_TYPE_STRING_AS_STRING, &profile->name)) {
307 		dbus_message_unref(method_call);
308 		return -ENOMEM;
309 	}
310 
311 	if (profile->record &&
312 	    !append_key_value(&properties_array_iter, "ServiceRecord",
313 			      DBUS_TYPE_STRING, DBUS_TYPE_STRING_AS_STRING,
314 			      &profile->record)) {
315 		dbus_message_unref(method_call);
316 		return -ENOMEM;
317 	}
318 
319 	if (!append_key_value(&properties_array_iter, "Version",
320 			      DBUS_TYPE_UINT16, DBUS_TYPE_UINT16_AS_STRING,
321 			      &profile->version)) {
322 		dbus_message_unref(method_call);
323 		return -ENOMEM;
324 	}
325 
326 	if (profile->role &&
327 	    !append_key_value(&properties_array_iter, "Role", DBUS_TYPE_STRING,
328 			      DBUS_TYPE_STRING_AS_STRING, &profile->role)) {
329 		dbus_message_unref(method_call);
330 		return -ENOMEM;
331 	}
332 
333 	if (profile->features &&
334 	    !append_key_value(&properties_array_iter, "Features",
335 			      DBUS_TYPE_UINT16, DBUS_TYPE_UINT16_AS_STRING,
336 			      &profile->features)) {
337 		dbus_message_unref(method_call);
338 		return -ENOMEM;
339 	}
340 
341 	dbus_message_iter_close_container(&message_iter,
342 					  &properties_array_iter);
343 
344 	if (!dbus_connection_send_with_reply(conn, method_call, &pending_call,
345 					     DBUS_TIMEOUT_USE_DEFAULT)) {
346 		dbus_message_unref(method_call);
347 		return -ENOMEM;
348 	}
349 
350 	dbus_message_unref(method_call);
351 	if (!pending_call)
352 		return -EIO;
353 
354 	if (!dbus_pending_call_set_notify(
355 		    pending_call, cras_bt_on_register_profile, NULL, NULL)) {
356 		dbus_pending_call_cancel(pending_call);
357 		dbus_pending_call_unref(pending_call);
358 		syslog(LOG_ERR, "register profile fail on set notify");
359 		return -ENOMEM;
360 	}
361 
362 	return 0;
363 }
364 
cras_bt_unregister_profile(DBusConnection * conn,struct cras_bt_profile * profile)365 int cras_bt_unregister_profile(DBusConnection *conn,
366 			       struct cras_bt_profile *profile)
367 {
368 	DBusMessage *method_call;
369 	DBusMessageIter message_iter;
370 	DBusError dbus_error;
371 	DBusMessage *reply;
372 
373 	method_call = dbus_message_new_method_call(BLUEZ_SERVICE,
374 						   PROFILE_MANAGER_OBJ_PATH,
375 						   BLUEZ_PROFILE_MGMT_INTERFACE,
376 						   "UnregisterProfile");
377 
378 	if (!method_call)
379 		return -ENOMEM;
380 	dbus_error_init(&dbus_error);
381 	dbus_message_iter_init_append(method_call, &message_iter);
382 	dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_OBJECT_PATH,
383 				       &profile->object_path);
384 	reply = dbus_connection_send_with_reply_and_block(
385 		conn, method_call, DBUS_TIMEOUT_USE_DEFAULT, &dbus_error);
386 
387 	if (!reply) {
388 		dbus_error_free(&dbus_error);
389 		dbus_message_unref(method_call);
390 		return -EIO;
391 	}
392 
393 	dbus_message_unref(method_call);
394 
395 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
396 		syslog(LOG_ERR, "Unregister profile returned error: %s",
397 		       dbus_message_get_error_name(reply));
398 		dbus_message_unref(reply);
399 		return -EIO;
400 	}
401 	dbus_message_unref(reply);
402 	return 0;
403 }
404 
cras_bt_register_profiles(DBusConnection * conn)405 int cras_bt_register_profiles(DBusConnection *conn)
406 {
407 	struct cras_bt_profile *profile;
408 	int err;
409 
410 	DL_FOREACH (profiles, profile) {
411 		err = cras_bt_register_profile(conn, profile);
412 		if (err)
413 			return err;
414 	}
415 
416 	return 0;
417 }
418 
cras_bt_add_profile(DBusConnection * conn,struct cras_bt_profile * profile)419 int cras_bt_add_profile(DBusConnection *conn, struct cras_bt_profile *profile)
420 {
421 	static const DBusObjectPathVTable profile_vtable = {
422 		NULL, cras_bt_handle_profile_messages, NULL, NULL, NULL, NULL
423 	};
424 
425 	DBusError dbus_error;
426 
427 	dbus_error_init(&dbus_error);
428 
429 	if (!dbus_connection_register_object_path(
430 		    conn, profile->object_path, &profile_vtable, &dbus_error)) {
431 		syslog(LOG_ERR, "Could not register BT profile %s: %s",
432 		       profile->object_path, dbus_error.message);
433 		dbus_error_free(&dbus_error);
434 		return -ENOMEM;
435 	}
436 
437 	DL_APPEND(profiles, profile);
438 
439 	return 0;
440 }
441 
cras_bt_rm_profile(DBusConnection * conn,struct cras_bt_profile * profile)442 int cras_bt_rm_profile(DBusConnection *conn, struct cras_bt_profile *profile)
443 {
444 	DL_DELETE(profiles, profile);
445 
446 	if (!dbus_connection_unregister_object_path(conn,
447 						    profile->object_path)) {
448 		syslog(LOG_ERR, "Could not unregister BT profile %s",
449 		       profile->object_path);
450 		return -ENOMEM;
451 	}
452 	return 0;
453 }
454 
cras_bt_profile_reset()455 void cras_bt_profile_reset()
456 {
457 	struct cras_bt_profile *profile;
458 
459 	DL_FOREACH (profiles, profile)
460 		profile->release(profile);
461 }
462 
cras_bt_profile_get(const char * path)463 struct cras_bt_profile *cras_bt_profile_get(const char *path)
464 {
465 	struct cras_bt_profile *profile;
466 	DL_FOREACH (profiles, profile) {
467 		if (strcmp(profile->object_path, path) == 0)
468 			return profile;
469 	}
470 
471 	return NULL;
472 }
473 
cras_bt_profile_on_device_disconnected(struct cras_bt_device * device)474 void cras_bt_profile_on_device_disconnected(struct cras_bt_device *device)
475 {
476 	struct cras_bt_profile *profile;
477 	DL_FOREACH (profiles, profile)
478 		profile->request_disconnection(profile, device);
479 }
480