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 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 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 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 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 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 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 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 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 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 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 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 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