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