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