service.c revision 694f01921120bb2584b09b4ba502703cd8afd3f6
18e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project/*
28e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
38e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  BlueZ - Bluetooth protocol stack for Linux
48e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
58e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  Copyright (C) 2006-2007  Nokia Corporation
68e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  Copyright (C) 2004-2008  Marcel Holtmann <marcel@holtmann.org>
78e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
88e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
98e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  This program is free software; you can redistribute it and/or modify
108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  it under the terms of the GNU General Public License as published by
118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  the Free Software Foundation; either version 2 of the License, or
128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  (at your option) any later version.
138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  This program is distributed in the hope that it will be useful,
158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  but WITHOUT ANY WARRANTY; without even the implied warranty of
168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  GNU General Public License for more details.
188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  You should have received a copy of the GNU General Public License
208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  along with this program; if not, write to the Free Software
218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project */
248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#ifdef HAVE_CONFIG_H
268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include <config.h>
278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#endif
288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include <errno.h>
308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include <stdlib.h>
318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include <string.h>
328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
3328040489d744e0c5d475a88663056c9040ed5320Teng-Hui Zhu#include <bluetooth/bluetooth.h>
3428040489d744e0c5d475a88663056c9040ed5320Teng-Hui Zhu#include <bluetooth/hci.h>
358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include <bluetooth/hci_lib.h>
368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include <bluetooth/sdp.h>
378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include <bluetooth/sdp_lib.h>
388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include <gdbus.h>
408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
418f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian#include "sdpd.h"
428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "sdp-xml.h"
438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "plugin.h"
448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "adapter.h"
458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "error.h"
468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "logging.h"
478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
488f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian#define SERVICE_INTERFACE "org.bluez.Service"
498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectstatic DBusConnection *connection = NULL;
518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectstatic GSList *records = NULL;
528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectstruct record_data {
548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	uint32_t handle;
558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	char *sender;
56dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block	guint listener_id;
578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project};
588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectstruct context_data {
608e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	sdp_record_t *record;
618e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	sdp_data_t attr_data;
628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	struct sdp_xml_data *stack_head;
638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	uint16_t attr_id;
648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project};
658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectstatic int compute_seq_size(sdp_data_t *data)
678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
68635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project	int unit_size = data->unitSize;
69635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Project	sdp_data_t *seq = data->val.dataseq;
708e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	for (; seq; seq = seq->next)
728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		unit_size += seq->unitSize;
738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	return unit_size;
758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
77635860845790a19bf50bbc51ba8fb66a96dde068The Android Open Source Projectstatic void element_start(GMarkupParseContext *context,
785f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian		const gchar *element_name, const gchar **attribute_names,
798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		const gchar **attribute_values, gpointer user_data, GError **err)
808e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
818f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian	struct context_data *ctx_data = user_data;
828e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
838e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	if (!strcmp(element_name, "record"))
848e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		return;
858e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
868e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	if (!strcmp(element_name, "attribute")) {
878e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		int i;
888e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		for (i = 0; attribute_names[i]; i++) {
898e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project			if (!strcmp(attribute_names[i], "id")) {
908e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project				ctx_data->attr_id = strtol(attribute_values[i], 0, 0);
9128040489d744e0c5d475a88663056c9040ed5320Teng-Hui Zhu				break;
9228040489d744e0c5d475a88663056c9040ed5320Teng-Hui Zhu			}
938e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		}
948e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		debug("New attribute 0x%04x", ctx_data->attr_id);
958e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		return;
968e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	}
975f1ab04193ad0130ca8204aadaceae083aca9881Feng Qian
988e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	if (ctx_data->stack_head) {
998e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		struct sdp_xml_data *newelem = sdp_xml_data_alloc();
1008e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		newelem->next = ctx_data->stack_head;
1018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project		ctx_data->stack_head = newelem;
1028e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project	} else {
103		ctx_data->stack_head = sdp_xml_data_alloc();
104		ctx_data->stack_head->next = NULL;
105	}
106
107	if (!strcmp(element_name, "sequence"))
108		ctx_data->stack_head->data = sdp_data_alloc(SDP_SEQ8, NULL);
109	else if (!strcmp(element_name, "alternate"))
110		ctx_data->stack_head->data = sdp_data_alloc(SDP_ALT8, NULL);
111	else {
112		int i;
113		/* Parse value, name, encoding */
114		for (i = 0; attribute_names[i]; i++) {
115			if (!strcmp(attribute_names[i], "value")) {
116				int curlen = strlen(ctx_data->stack_head->text);
117				int attrlen = strlen(attribute_values[i]);
118
119				/* Ensure we're big enough */
120				while ((curlen + 1 + attrlen) > ctx_data->stack_head->size) {
121					sdp_xml_data_expand(ctx_data->stack_head);
122				}
123
124				memcpy(ctx_data->stack_head->text + curlen,
125						attribute_values[i], attrlen);
126				ctx_data->stack_head->text[curlen + attrlen] = '\0';
127			}
128
129			if (!strcmp(attribute_names[i], "encoding")) {
130				if (!strcmp(attribute_values[i], "hex"))
131					ctx_data->stack_head->type = 1;
132			}
133
134			if (!strcmp(attribute_names[i], "name")) {
135				ctx_data->stack_head->name = strdup(attribute_values[i]);
136			}
137		}
138
139		ctx_data->stack_head->data = sdp_xml_parse_datatype(element_name,
140				ctx_data->stack_head, ctx_data->record);
141
142		if (ctx_data->stack_head->data == NULL)
143			error("Can't parse element %s", element_name);
144	}
145}
146
147static void element_end(GMarkupParseContext *context,
148		const gchar *element_name, gpointer user_data, GError **err)
149{
150	struct context_data *ctx_data = user_data;
151	struct sdp_xml_data *elem;
152
153	if (!strcmp(element_name, "record"))
154		return;
155
156	if (!strcmp(element_name, "attribute")) {
157		if (ctx_data->stack_head && ctx_data->stack_head->data) {
158			int ret = sdp_attr_add(ctx_data->record, ctx_data->attr_id,
159							ctx_data->stack_head->data);
160			if (ret == -1)
161				debug("Trouble adding attribute\n");
162
163			ctx_data->stack_head->data = NULL;
164			sdp_xml_data_free(ctx_data->stack_head);
165			ctx_data->stack_head = NULL;
166		} else {
167			debug("No data for attribute 0x%04x\n", ctx_data->attr_id);
168		}
169		return;
170	}
171
172	if (!strcmp(element_name, "sequence")) {
173		ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
174
175		if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
176			ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
177			ctx_data->stack_head->data->dtd = SDP_SEQ32;
178		} else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
179			ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
180			ctx_data->stack_head->data->dtd = SDP_SEQ16;
181		} else {
182			ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
183		}
184	} else if (!strcmp(element_name, "alternate")) {
185		ctx_data->stack_head->data->unitSize = compute_seq_size(ctx_data->stack_head->data);
186
187		if (ctx_data->stack_head->data->unitSize > USHRT_MAX) {
188			ctx_data->stack_head->data->unitSize += sizeof(uint32_t);
189			ctx_data->stack_head->data->dtd = SDP_ALT32;
190		} else if (ctx_data->stack_head->data->unitSize > UCHAR_MAX) {
191			ctx_data->stack_head->data->unitSize += sizeof(uint16_t);
192			ctx_data->stack_head->data->dtd = SDP_ALT16;
193		} else {
194			ctx_data->stack_head->data->unitSize += sizeof(uint8_t);
195		}
196	}
197
198	if (ctx_data->stack_head->next && ctx_data->stack_head->data &&
199					ctx_data->stack_head->next->data) {
200		switch (ctx_data->stack_head->next->data->dtd) {
201		case SDP_SEQ8:
202		case SDP_SEQ16:
203		case SDP_SEQ32:
204		case SDP_ALT8:
205		case SDP_ALT16:
206		case SDP_ALT32:
207			ctx_data->stack_head->next->data->val.dataseq =
208				sdp_seq_append(ctx_data->stack_head->next->data->val.dataseq,
209								ctx_data->stack_head->data);
210			ctx_data->stack_head->data = NULL;
211			break;
212		}
213
214		elem = ctx_data->stack_head;
215		ctx_data->stack_head = ctx_data->stack_head->next;
216
217		sdp_xml_data_free(elem);
218	}
219}
220
221static GMarkupParser parser = {
222	element_start, element_end, NULL, NULL, NULL
223};
224
225static sdp_record_t *sdp_xml_parse_record(const char *data, int size)
226{
227	GMarkupParseContext *ctx;
228	struct context_data *ctx_data;
229	sdp_record_t *record;
230
231	ctx_data = malloc(sizeof(*ctx_data));
232	if (!ctx_data)
233		return NULL;
234
235	record = sdp_record_alloc();
236	if (!record) {
237		free(ctx_data);
238		return NULL;
239	}
240
241	memset(ctx_data, 0, sizeof(*ctx_data));
242	ctx_data->record = record;
243
244	ctx = g_markup_parse_context_new(&parser, 0, ctx_data, NULL);
245
246	if (g_markup_parse_context_parse(ctx, data, size, NULL) == FALSE) {
247		error("XML parsing error");
248		g_markup_parse_context_free(ctx);
249		sdp_record_free(record);
250		free(ctx_data);
251		return NULL;
252	}
253
254	g_markup_parse_context_free(ctx);
255
256	free(ctx_data);
257
258	return record;
259}
260
261static struct record_data *find_record(uint32_t handle, const char *sender)
262{
263	GSList *list;
264
265	for (list = records; list; list = list->next) {
266		struct record_data *data = list->data;
267		if (handle == data->handle && !strcmp(sender, data->sender))
268			return data;
269	}
270
271	return NULL;
272}
273
274static void exit_callback(void *user_data)
275{
276	struct record_data *user_record = user_data;
277
278	debug("remove record");
279
280	records = g_slist_remove(records, user_record);
281
282	remove_record_from_server(user_record->handle);
283
284	g_free(user_record->sender);
285	g_free(user_record);
286}
287
288static inline DBusMessage *invalid_arguments(DBusMessage *msg)
289{
290	return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
291					"Invalid arguments in method call");
292}
293
294static inline DBusMessage *not_available(DBusMessage *msg)
295{
296	return g_dbus_create_error(msg, ERROR_INTERFACE ".NotAvailable",
297							"Not Available");
298}
299
300static inline DBusMessage *failed(DBusMessage *msg)
301{
302	return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed", "Failed");
303}
304
305static inline DBusMessage *failed_strerror(DBusMessage *msg, int err)
306{
307	return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
308			strerror(err));
309}
310
311static int add_xml_record(DBusConnection *conn, const char *sender,
312			bdaddr_t *src, const char *record,
313			dbus_uint32_t *handle)
314{
315	struct record_data *user_record;
316	sdp_record_t *sdp_record;
317
318	sdp_record = sdp_xml_parse_record(record, strlen(record));
319	if (!sdp_record) {
320		error("Parsing of XML service record failed");
321		return -EIO;
322	}
323
324	if (add_record_to_server(src, sdp_record) < 0) {
325		error("Failed to register service record");
326		sdp_record_free(sdp_record);
327		return -EIO;
328	}
329
330	user_record = g_new0(struct record_data, 1);
331
332	user_record->handle = sdp_record->handle;
333
334	user_record->sender = g_strdup(sender);
335
336	records = g_slist_append(records, user_record);
337
338	user_record->listener_id = g_dbus_add_disconnect_watch(conn, sender,
339					exit_callback, user_record, NULL);
340
341	debug("listener_id %d", user_record->listener_id);
342
343	*handle = user_record->handle;
344
345	return 0;
346}
347
348static DBusMessage *update_record(DBusConnection *conn, DBusMessage *msg,
349		bdaddr_t *src, dbus_uint32_t handle, sdp_record_t *sdp_record)
350{
351	int err;
352
353	if (remove_record_from_server(handle) < 0) {
354		sdp_record_free(sdp_record);
355		return g_dbus_create_error(msg,
356				ERROR_INTERFACE ".NotAvailable",
357				"Not Available");
358	}
359
360	sdp_record->handle = handle;
361	err = add_record_to_server(src, sdp_record);
362	if (err < 0) {
363		sdp_record_free(sdp_record);
364		error("Failed to update the service record");
365		return g_dbus_create_error(msg,
366				ERROR_INTERFACE ".Failed",
367				strerror(EIO));
368	}
369
370	return dbus_message_new_method_return(msg);
371}
372
373static DBusMessage *update_xml_record(DBusConnection *conn,
374				DBusMessage *msg, bdaddr_t *src)
375{
376	struct record_data *user_record;
377	sdp_record_t *sdp_record;
378	const char *record;
379	dbus_uint32_t handle;
380	int len;
381
382	if (dbus_message_get_args(msg, NULL,
383				DBUS_TYPE_UINT32, &handle,
384				DBUS_TYPE_STRING, &record,
385				DBUS_TYPE_INVALID) == FALSE)
386		return NULL;
387
388	len = (record ? strlen(record) : 0);
389	if (len == 0)
390		return invalid_arguments(msg);
391
392	user_record = find_record(handle, dbus_message_get_sender(msg));
393	if (!user_record)
394		return g_dbus_create_error(msg,
395				ERROR_INTERFACE ".NotAvailable",
396				"Not Available");
397
398	sdp_record = sdp_xml_parse_record(record, len);
399	if (!sdp_record) {
400		error("Parsing of XML service record failed");
401		sdp_record_free(sdp_record);
402		return g_dbus_create_error(msg,
403				ERROR_INTERFACE ".Failed",
404				strerror(EIO));
405	}
406
407	return update_record(conn, msg, src, handle, sdp_record);
408}
409
410static int remove_record(DBusConnection *conn, const char *sender,
411			dbus_uint32_t handle)
412{
413	struct record_data *user_record;
414
415	debug("remove record 0x%x", handle);
416
417	user_record = find_record(handle, sender);
418	if (!user_record)
419		return -1;
420
421	debug("listner_id %d", user_record->listener_id);
422
423	g_dbus_remove_watch(conn, user_record->listener_id);
424
425	exit_callback(user_record);
426
427	return 0;
428}
429
430static DBusMessage *add_service_record(DBusConnection *conn,
431					DBusMessage *msg, void *data)
432{
433	struct adapter *adapter = data;
434	DBusMessage *reply;
435	const char *sender, *record;
436	dbus_uint32_t handle;
437	bdaddr_t src;
438	int err;
439
440	if (dbus_message_get_args(msg, NULL,
441			DBUS_TYPE_STRING, &record, DBUS_TYPE_INVALID) == FALSE)
442		return NULL;
443
444	sender = dbus_message_get_sender(msg);
445	str2ba(adapter->address, &src);
446	err = add_xml_record(conn, sender, &src, record, &handle);
447	if (err < 0)
448		return failed_strerror(msg, err);
449
450	reply = dbus_message_new_method_return(msg);
451	if (!reply)
452		return NULL;
453
454	dbus_message_append_args(reply, DBUS_TYPE_UINT32, &handle,
455							DBUS_TYPE_INVALID);
456
457	return reply;
458}
459
460static DBusMessage *update_service_record(DBusConnection *conn,
461					DBusMessage *msg, void *data)
462{
463	struct adapter *adapter = data;
464	bdaddr_t src;
465
466	str2ba(adapter->address, &src);
467
468	return update_xml_record(conn, msg, &src);
469}
470
471static DBusMessage *remove_service_record(DBusConnection *conn,
472					DBusMessage *msg, void *data)
473{
474	dbus_uint32_t handle;
475	const char *sender;
476
477	if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &handle,
478						DBUS_TYPE_INVALID) == FALSE)
479		return NULL;
480
481	sender = dbus_message_get_sender(msg);
482
483	if (remove_record(conn, sender, handle) < 0)
484		return not_available(msg);
485
486	return dbus_message_new_method_return(msg);
487}
488
489static DBusMessage *request_authorization(DBusConnection *conn,
490						DBusMessage *msg, void *data)
491{
492	/* FIXME implement the request */
493
494	return NULL;
495}
496
497static DBusMessage *cancel_authorization(DBusConnection *conn,
498						DBusMessage *msg, void *data)
499{
500	/* FIXME implement cancel request */
501
502	return dbus_message_new_method_return(msg);
503}
504
505static GDBusMethodTable service_methods[] = {
506	{ "AddRecord",		"s",	"u",	add_service_record	},
507	{ "UpdateRecord",	"us",	"",	update_service_record	},
508	{ "RemoveRecord",	"u",	"",	remove_service_record	},
509	{ "RequestAuthorization","su",	"",	request_authorization,
510						G_DBUS_METHOD_FLAG_ASYNC},
511	{ "CancelAuthorization", "",	"",	cancel_authorization	},
512	{ }
513};
514
515static void path_unregister(void *data)
516{
517	g_slist_foreach(records, (GFunc) exit_callback, NULL);
518}
519
520static int service_probe(struct adapter *adapter)
521{
522	const char *path = adapter_get_path(adapter);
523
524	DBG("path %s", path);
525
526	if (!g_dbus_register_interface(connection, path,
527					SERVICE_INTERFACE,
528					service_methods, NULL, NULL,
529					adapter, path_unregister)) {
530		error("D-Bus failed to register %s interface",
531				SERVICE_INTERFACE);
532		return -1;
533	}
534
535	info("Registered interface %s on path %s", SERVICE_INTERFACE, path);
536
537	return 0;
538}
539
540static void service_remove(struct adapter *adapter)
541{
542	const char *path = adapter_get_path(adapter);
543
544	DBG("path %s", path);
545
546	g_dbus_unregister_interface(connection, path, SERVICE_INTERFACE);
547}
548
549static struct btd_adapter_driver service_driver = {
550	.name	= "service",
551	.probe	= service_probe,
552	.remove	= service_remove,
553};
554
555static int service_init(void)
556{
557	connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
558	if (connection == NULL)
559		return -EIO;
560
561	return btd_register_adapter_driver(&service_driver);
562}
563
564static void service_exit(void)
565{
566	btd_unregister_adapter_driver(&service_driver);
567}
568
569BLUETOOTH_PLUGIN_DEFINE("service", service_init, service_exit)
570