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