Home | History | Annotate | Download | only in server
      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