OSDN Git Service

build: Add --enable-midi to bootstrap-configure
[android-x86/external-bluetooth-bluez.git] / src / advertising.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2015  Google Inc.
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  */
19
20 #include "advertising.h"
21
22 #include <stdint.h>
23 #include <stdbool.h>
24
25 #include <dbus/dbus.h>
26 #include <gdbus/gdbus.h>
27
28 #include "lib/bluetooth.h"
29 #include "lib/mgmt.h"
30 #include "lib/sdp.h"
31
32 #include "adapter.h"
33 #include "dbus-common.h"
34 #include "error.h"
35 #include "log.h"
36 #include "src/shared/ad.h"
37 #include "src/shared/mgmt.h"
38 #include "src/shared/queue.h"
39 #include "src/shared/util.h"
40
41 #define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1"
42 #define LE_ADVERTISEMENT_IFACE "org.bluez.LEAdvertisement1"
43
44 struct btd_adv_manager {
45         struct btd_adapter *adapter;
46         struct queue *clients;
47         struct mgmt *mgmt;
48         uint16_t mgmt_index;
49         uint8_t max_adv_len;
50         uint8_t max_ads;
51         unsigned int instance_bitmap;
52 };
53
54 #define AD_TYPE_BROADCAST 0
55 #define AD_TYPE_PERIPHERAL 1
56
57 struct btd_adv_client {
58         struct btd_adv_manager *manager;
59         char *owner;
60         char *path;
61         GDBusClient *client;
62         GDBusProxy *proxy;
63         DBusMessage *reg;
64         uint8_t type; /* Advertising type */
65         bool include_tx_power;
66         struct bt_ad *data;
67         uint8_t instance;
68 };
69
70 struct dbus_obj_match {
71         const char *owner;
72         const char *path;
73 };
74
75 static bool match_client(const void *a, const void *b)
76 {
77         const struct btd_adv_client *client = a;
78         const struct dbus_obj_match *match = b;
79
80         if (match->owner && g_strcmp0(client->owner, match->owner))
81                 return false;
82
83         if (match->path && g_strcmp0(client->path, match->path))
84                 return false;
85
86         return true;
87 }
88
89 static void client_free(void *data)
90 {
91         struct btd_adv_client *client = data;
92
93         if (client->client) {
94                 g_dbus_client_set_disconnect_watch(client->client, NULL, NULL);
95                 g_dbus_client_unref(client->client);
96         }
97
98         if (client->instance)
99                 util_clear_uid(&client->manager->instance_bitmap,
100                                                 client->instance);
101
102         bt_ad_unref(client->data);
103
104         g_dbus_proxy_unref(client->proxy);
105
106         if (client->owner)
107                 g_free(client->owner);
108
109         if (client->path)
110                 g_free(client->path);
111
112         free(client);
113 }
114
115 static gboolean client_free_idle_cb(void *data)
116 {
117         client_free(data);
118
119         return FALSE;
120 }
121
122 static void client_release(void *data)
123 {
124         struct btd_adv_client *client = data;
125         DBusMessage *message;
126
127         DBG("Releasing advertisement %s, %s", client->owner, client->path);
128
129         message = dbus_message_new_method_call(client->owner, client->path,
130                                                         LE_ADVERTISEMENT_IFACE,
131                                                         "Release");
132
133         if (!message) {
134                 error("Couldn't allocate D-Bus message");
135                 return;
136         }
137
138         g_dbus_send_message(btd_get_dbus_connection(), message);
139 }
140
141 static void client_destroy(void *data)
142 {
143         client_release(data);
144         client_free(data);
145 }
146
147 static void client_remove(void *data)
148 {
149         struct btd_adv_client *client = data;
150         struct mgmt_cp_remove_advertising cp;
151
152         g_dbus_client_set_disconnect_watch(client->client, NULL, NULL);
153
154         cp.instance = client->instance;
155
156         mgmt_send(client->manager->mgmt, MGMT_OP_REMOVE_ADVERTISING,
157                         client->manager->mgmt_index, sizeof(cp), &cp,
158                         NULL, NULL, NULL);
159
160         queue_remove(client->manager->clients, client);
161
162         g_idle_add(client_free_idle_cb, client);
163 }
164
165 static void client_disconnect_cb(DBusConnection *conn, void *user_data)
166 {
167         DBG("Client disconnected");
168
169         client_remove(user_data);
170 }
171
172 static bool parse_type(GDBusProxy *proxy, uint8_t *type)
173 {
174         DBusMessageIter iter;
175         const char *msg_type;
176
177         if (!g_dbus_proxy_get_property(proxy, "Type", &iter))
178                 return false;
179
180         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
181                 return false;
182
183         dbus_message_iter_get_basic(&iter, &msg_type);
184
185         if (!g_strcmp0(msg_type, "broadcast")) {
186                 *type = AD_TYPE_BROADCAST;
187                 return true;
188         }
189
190         if (!g_strcmp0(msg_type, "peripheral")) {
191                 *type = AD_TYPE_PERIPHERAL;
192                 return true;
193         }
194
195         return false;
196 }
197
198 static bool parse_service_uuids(GDBusProxy *proxy, struct bt_ad *data)
199 {
200         DBusMessageIter iter, ariter;
201
202         if (!g_dbus_proxy_get_property(proxy, "ServiceUUIDs", &iter))
203                 return true;
204
205         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
206                 return false;
207
208         dbus_message_iter_recurse(&iter, &ariter);
209
210         bt_ad_clear_service_uuid(data);
211
212         while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
213                 const char *uuid_str;
214                 bt_uuid_t uuid;
215
216                 dbus_message_iter_get_basic(&ariter, &uuid_str);
217
218                 DBG("Adding ServiceUUID: %s", uuid_str);
219
220                 if (bt_string_to_uuid(&uuid, uuid_str) < 0)
221                         goto fail;
222
223                 if (!bt_ad_add_service_uuid(data, &uuid))
224                         goto fail;
225
226                 dbus_message_iter_next(&ariter);
227         }
228
229         return true;
230
231 fail:
232         bt_ad_clear_service_uuid(data);
233         return false;
234 }
235
236 static bool parse_solicit_uuids(GDBusProxy *proxy, struct bt_ad *data)
237 {
238         DBusMessageIter iter, ariter;
239
240         if (!g_dbus_proxy_get_property(proxy, "SolicitUUIDs", &iter))
241                 return true;
242
243         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
244                 return false;
245
246         dbus_message_iter_recurse(&iter, &ariter);
247
248         bt_ad_clear_solicit_uuid(data);
249
250         while (dbus_message_iter_get_arg_type(&ariter) == DBUS_TYPE_STRING) {
251                 const char *uuid_str;
252                 bt_uuid_t uuid;
253
254                 dbus_message_iter_get_basic(&ariter, &uuid_str);
255
256                 DBG("Adding SolicitUUID: %s", uuid_str);
257
258                 if (bt_string_to_uuid(&uuid, uuid_str) < 0)
259                         goto fail;
260
261                 if (!bt_ad_add_solicit_uuid(data, &uuid))
262                         goto fail;
263
264                 dbus_message_iter_next(&ariter);
265         }
266
267         return true;
268
269 fail:
270         bt_ad_clear_solicit_uuid(data);
271         return false;
272 }
273
274 static bool parse_manufacturer_data(GDBusProxy *proxy, struct bt_ad *data)
275 {
276         DBusMessageIter iter, entries;
277
278         if (!g_dbus_proxy_get_property(proxy, "ManufacturerData", &iter))
279                 return true;
280
281         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
282                 return false;
283
284         dbus_message_iter_recurse(&iter, &entries);
285
286         bt_ad_clear_manufacturer_data(data);
287
288         while (dbus_message_iter_get_arg_type(&entries)
289                                                 == DBUS_TYPE_DICT_ENTRY) {
290                 DBusMessageIter value, entry, array;
291                 uint16_t manuf_id;
292                 uint8_t *manuf_data;
293                 int len;
294
295                 dbus_message_iter_recurse(&entries, &entry);
296                 dbus_message_iter_get_basic(&entry, &manuf_id);
297
298                 dbus_message_iter_next(&entry);
299
300                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
301                         goto fail;
302
303                 dbus_message_iter_recurse(&entry, &value);
304
305                 if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
306                         goto fail;
307
308                 dbus_message_iter_recurse(&value, &array);
309
310                 if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
311                         goto fail;
312
313                 dbus_message_iter_get_fixed_array(&array, &manuf_data, &len);
314
315                 DBG("Adding ManufacturerData for %04x", manuf_id);
316
317                 if (!bt_ad_add_manufacturer_data(data, manuf_id, manuf_data,
318                                                                         len))
319                         goto fail;
320
321                 dbus_message_iter_next(&entries);
322         }
323
324         return true;
325
326 fail:
327         bt_ad_clear_manufacturer_data(data);
328         return false;
329 }
330
331 static bool parse_service_data(GDBusProxy *proxy, struct bt_ad *data)
332 {
333         DBusMessageIter iter, entries;
334
335         if (!g_dbus_proxy_get_property(proxy, "ServiceData", &iter))
336                 return true;
337
338         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
339                 return false;
340
341         dbus_message_iter_recurse(&iter, &entries);
342
343         bt_ad_clear_service_data(data);
344
345         while (dbus_message_iter_get_arg_type(&entries)
346                                                 == DBUS_TYPE_DICT_ENTRY) {
347                 DBusMessageIter value, entry, array;
348                 const char *uuid_str;
349                 bt_uuid_t uuid;
350                 uint8_t *service_data;
351                 int len;
352
353                 dbus_message_iter_recurse(&entries, &entry);
354                 dbus_message_iter_get_basic(&entry, &uuid_str);
355
356                 if (bt_string_to_uuid(&uuid, uuid_str) < 0)
357                         goto fail;
358
359                 dbus_message_iter_next(&entry);
360
361                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
362                         goto fail;
363
364                 dbus_message_iter_recurse(&entry, &value);
365
366                 if (dbus_message_iter_get_arg_type(&value) != DBUS_TYPE_ARRAY)
367                         goto fail;
368
369                 dbus_message_iter_recurse(&value, &array);
370
371                 if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE)
372                         goto fail;
373
374                 dbus_message_iter_get_fixed_array(&array, &service_data, &len);
375
376                 DBG("Adding ServiceData for %s", uuid_str);
377
378                 if (!bt_ad_add_service_data(data, &uuid, service_data, len))
379                         goto fail;
380
381                 dbus_message_iter_next(&entries);
382         }
383
384         return true;
385
386 fail:
387         bt_ad_clear_service_data(data);
388         return false;
389 }
390
391 static bool parse_include_tx_power(GDBusProxy *proxy, bool *included)
392 {
393         DBusMessageIter iter;
394         dbus_bool_t b;
395
396         if (!g_dbus_proxy_get_property(proxy, "IncludeTxPower", &iter))
397                 return true;
398
399         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
400                 return false;
401
402         dbus_message_iter_get_basic(&iter, &b);
403
404         *included = b;
405
406         return true;
407 }
408
409 static void add_client_complete(struct btd_adv_client *client, uint8_t status)
410 {
411         DBusMessage *reply;
412
413         if (status) {
414                 error("Failed to add advertisement: %s (0x%02x)",
415                                                 mgmt_errstr(status), status);
416                 reply = btd_error_failed(client->reg,
417                                         "Failed to register advertisement");
418                 queue_remove(client->manager->clients, client);
419                 g_idle_add(client_free_idle_cb, client);
420
421         } else
422                 reply = dbus_message_new_method_return(client->reg);
423
424         g_dbus_send_message(btd_get_dbus_connection(), reply);
425         dbus_message_unref(client->reg);
426         client->reg = NULL;
427 }
428
429 static void add_adv_callback(uint8_t status, uint16_t length,
430                                           const void *param, void *user_data)
431 {
432         struct btd_adv_client *client = user_data;
433         const struct mgmt_rp_add_advertising *rp = param;
434
435         if (status)
436                 goto done;
437
438         if (!param || length < sizeof(*rp)) {
439                 status = MGMT_STATUS_FAILED;
440                 goto done;
441         }
442
443         client->instance = rp->instance;
444
445         g_dbus_client_set_disconnect_watch(client->client, client_disconnect_cb,
446                                                                         client);
447         DBG("Advertisement registered: %s", client->path);
448
449 done:
450         add_client_complete(client, status);
451 }
452
453 static size_t calc_max_adv_len(struct btd_adv_client *client, uint32_t flags)
454 {
455         size_t max = client->manager->max_adv_len;
456
457         /*
458          * Flags which reduce the amount of space available for advertising.
459          * See doc/mgmt-api.txt
460          */
461         if (flags & MGMT_ADV_FLAG_TX_POWER)
462                 max -= 3;
463
464         if (flags & (MGMT_ADV_FLAG_DISCOV | MGMT_ADV_FLAG_LIMITED_DISCOV |
465                                                 MGMT_ADV_FLAG_MANAGED_FLAGS))
466                 max -= 3;
467
468         if (flags & MGMT_ADV_FLAG_APPEARANCE)
469                 max -= 4;
470
471         return max;
472 }
473
474 static DBusMessage *refresh_advertisement(struct btd_adv_client *client)
475 {
476         struct mgmt_cp_add_advertising *cp;
477         uint8_t param_len;
478         uint8_t *adv_data;
479         size_t adv_data_len;
480         uint32_t flags = 0;
481
482         DBG("Refreshing advertisement: %s", client->path);
483
484         if (client->type == AD_TYPE_PERIPHERAL)
485                 flags = MGMT_ADV_FLAG_CONNECTABLE | MGMT_ADV_FLAG_DISCOV;
486
487         if (client->include_tx_power)
488                 flags |= MGMT_ADV_FLAG_TX_POWER;
489
490         adv_data = bt_ad_generate(client->data, &adv_data_len);
491
492         if (!adv_data || (adv_data_len > calc_max_adv_len(client, flags))) {
493                 error("Advertising data too long or couldn't be generated.");
494
495                 return g_dbus_create_error(client->reg, ERROR_INTERFACE
496                                                 ".InvalidLength",
497                                                 "Advertising data too long.");
498         }
499
500         param_len = sizeof(struct mgmt_cp_add_advertising) + adv_data_len;
501
502         cp = malloc0(param_len);
503
504         if (!cp) {
505                 error("Couldn't allocate for MGMT!");
506
507                 free(adv_data);
508
509                 return btd_error_failed(client->reg, "Failed");
510         }
511
512         cp->flags = htobl(flags);
513         cp->instance = client->instance;
514         cp->adv_data_len = adv_data_len;
515         memcpy(cp->data, adv_data, adv_data_len);
516
517         free(adv_data);
518
519         if (!mgmt_send(client->manager->mgmt, MGMT_OP_ADD_ADVERTISING,
520                                 client->manager->mgmt_index, param_len, cp,
521                                 add_adv_callback, client, NULL)) {
522                 error("Failed to add Advertising Data");
523
524                 free(cp);
525
526                 return btd_error_failed(client->reg, "Failed");
527         }
528
529         free(cp);
530
531         return NULL;
532 }
533
534 static DBusMessage *parse_advertisement(struct btd_adv_client *client)
535 {
536         if (!parse_type(client->proxy, &client->type)) {
537                 error("Failed to read \"Type\" property of advertisement");
538                 goto fail;
539         }
540
541         if (!parse_service_uuids(client->proxy, client->data)) {
542                 error("Property \"ServiceUUIDs\" failed to parse");
543                 goto fail;
544         }
545
546         if (!parse_solicit_uuids(client->proxy, client->data)) {
547                 error("Property \"SolicitUUIDs\" failed to parse");
548                 goto fail;
549         }
550
551         if (!parse_manufacturer_data(client->proxy, client->data)) {
552                 error("Property \"ManufacturerData\" failed to parse");
553                 goto fail;
554         }
555
556         if (!parse_service_data(client->proxy, client->data)) {
557                 error("Property \"ServiceData\" failed to parse");
558                 goto fail;
559         }
560
561         if (!parse_include_tx_power(client->proxy, &client->include_tx_power)) {
562                 error("Property \"IncludeTxPower\" failed to parse");
563                 goto fail;
564         }
565
566         return refresh_advertisement(client);
567
568 fail:
569         return btd_error_failed(client->reg, "Failed to parse advertisement.");
570 }
571
572 static void client_proxy_added(GDBusProxy *proxy, void *data)
573 {
574         struct btd_adv_client *client = data;
575         DBusMessage *reply;
576
577         reply = parse_advertisement(client);
578         if (!reply)
579                 return;
580
581         /* Failed to publish for some reason, remove. */
582         queue_remove(client->manager->clients, client);
583
584         g_idle_add(client_free_idle_cb, client);
585
586         g_dbus_send_message(btd_get_dbus_connection(), reply);
587
588         dbus_message_unref(client->reg);
589         client->reg = NULL;
590 }
591
592 static struct btd_adv_client *client_create(struct btd_adv_manager *manager,
593                                         DBusConnection *conn,
594                                         DBusMessage *msg, const char *path)
595 {
596         struct btd_adv_client *client;
597         const char *sender = dbus_message_get_sender(msg);
598
599         if (!path || !g_str_has_prefix(path, "/"))
600                 return NULL;
601
602         client = new0(struct btd_adv_client, 1);
603         client->client = g_dbus_client_new_full(conn, sender, path, path);
604         if (!client->client)
605                 goto fail;
606
607         client->owner = g_strdup(sender);
608         if (!client->owner)
609                 goto fail;
610
611         client->path = g_strdup(path);
612         if (!client->path)
613                 goto fail;
614
615         DBG("Adding proxy for %s", path);
616         client->proxy = g_dbus_proxy_new(client->client, path,
617                                                 LE_ADVERTISEMENT_IFACE);
618         if (!client->proxy)
619                 goto fail;
620
621         g_dbus_client_set_proxy_handlers(client->client, client_proxy_added,
622                                                         NULL, NULL, client);
623
624         client->reg = dbus_message_ref(msg);
625
626         client->data = bt_ad_new();
627         if (!client->data)
628                 goto fail;
629
630         client->instance = util_get_uid(&manager->instance_bitmap,
631                                                         manager->max_ads);
632         if (!client->instance)
633                 goto fail;
634
635         client->manager = manager;
636
637         return client;
638
639 fail:
640         client_free(client);
641         return NULL;
642 }
643
644 static DBusMessage *register_advertisement(DBusConnection *conn,
645                                                 DBusMessage *msg,
646                                                 void *user_data)
647 {
648         struct btd_adv_manager *manager = user_data;
649         DBusMessageIter args;
650         struct btd_adv_client *client;
651         struct dbus_obj_match match;
652
653         DBG("RegisterAdvertisement");
654
655         if (!dbus_message_iter_init(msg, &args))
656                 return btd_error_invalid_args(msg);
657
658         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
659                 return btd_error_invalid_args(msg);
660
661         dbus_message_iter_get_basic(&args, &match.path);
662
663         match.owner = dbus_message_get_sender(msg);
664
665         if (queue_find(manager->clients, match_client, &match))
666                 return btd_error_already_exists(msg);
667
668         dbus_message_iter_next(&args);
669
670         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
671                 return btd_error_invalid_args(msg);
672
673         client = client_create(manager, conn, msg, match.path);
674         if (!client)
675                 return btd_error_failed(msg,
676                                         "Failed to register advertisement");
677
678         DBG("Registered advertisement at path %s", match.path);
679
680         queue_push_tail(manager->clients, client);
681
682         return NULL;
683 }
684
685 static DBusMessage *unregister_advertisement(DBusConnection *conn,
686                                                 DBusMessage *msg,
687                                                 void *user_data)
688 {
689         struct btd_adv_manager *manager = user_data;
690         DBusMessageIter args;
691         struct btd_adv_client *client;
692         struct dbus_obj_match match;
693
694         DBG("UnregisterAdvertisement");
695
696         if (!dbus_message_iter_init(msg, &args))
697                 return btd_error_invalid_args(msg);
698
699         if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
700                 return btd_error_invalid_args(msg);
701
702         dbus_message_iter_get_basic(&args, &match.path);
703
704         match.owner = dbus_message_get_sender(msg);
705
706         client = queue_find(manager->clients, match_client, &match);
707         if (!client)
708                 return btd_error_does_not_exist(msg);
709
710         client_remove(client);
711
712         return dbus_message_new_method_return(msg);
713 }
714
715 static const GDBusMethodTable methods[] = {
716         { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterAdvertisement",
717                                         GDBUS_ARGS({ "advertisement", "o" },
718                                                         { "options", "a{sv}" }),
719                                         NULL, register_advertisement) },
720         { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterAdvertisement",
721                                                 GDBUS_ARGS({ "service", "o" }),
722                                                 NULL,
723                                                 unregister_advertisement) },
724         { }
725 };
726
727 static void manager_destroy(void *user_data)
728 {
729         struct btd_adv_manager *manager = user_data;
730
731         queue_destroy(manager->clients, client_destroy);
732
733         mgmt_unref(manager->mgmt);
734
735         free(manager);
736 }
737
738 static void read_adv_features_callback(uint8_t status, uint16_t length,
739                                         const void *param, void *user_data)
740 {
741         struct btd_adv_manager *manager = user_data;
742         const struct mgmt_rp_read_adv_features *feat = param;
743
744         if (status || !param) {
745                 error("Failed to read advertising features: %s (0x%02x)",
746                                                 mgmt_errstr(status), status);
747                 return;
748         }
749
750         if (length < sizeof(*feat)) {
751                 error("Wrong size of read adv features response");
752                 return;
753         }
754
755         manager->max_adv_len = feat->max_adv_data_len;
756         manager->max_ads = feat->max_instances;
757
758         if (manager->max_ads == 0)
759                 return;
760
761         if (!g_dbus_register_interface(btd_get_dbus_connection(),
762                                         adapter_get_path(manager->adapter),
763                                         LE_ADVERTISING_MGR_IFACE,
764                                         methods, NULL, NULL, manager, NULL))
765                 error("Failed to register " LE_ADVERTISING_MGR_IFACE);
766 }
767
768 static struct btd_adv_manager *manager_create(struct btd_adapter *adapter)
769 {
770         struct btd_adv_manager *manager;
771
772         manager = new0(struct btd_adv_manager, 1);
773         manager->adapter = adapter;
774
775         manager->mgmt = mgmt_new_default();
776
777         if (!manager->mgmt) {
778                 error("Failed to access management interface");
779                 free(manager);
780                 return NULL;
781         }
782
783         manager->mgmt_index = btd_adapter_get_index(adapter);
784
785         if (!mgmt_send(manager->mgmt, MGMT_OP_READ_ADV_FEATURES,
786                                 manager->mgmt_index, 0, NULL,
787                                 read_adv_features_callback, manager, NULL)) {
788                 error("Failed to read advertising features");
789                 manager_destroy(manager);
790                 return NULL;
791         }
792
793         manager->clients = queue_new();
794
795         return manager;
796 }
797
798 struct btd_adv_manager *btd_adv_manager_new(struct btd_adapter *adapter)
799 {
800         struct btd_adv_manager *manager;
801
802         if (!adapter)
803                 return NULL;
804
805         manager = manager_create(adapter);
806         if (!manager)
807                 return NULL;
808
809         DBG("LE Advertising Manager created for adapter: %s",
810                                                 adapter_get_path(adapter));
811
812         return manager;
813 }
814
815 void btd_adv_manager_destroy(struct btd_adv_manager *manager)
816 {
817         if (!manager)
818                 return;
819
820         g_dbus_unregister_interface(btd_get_dbus_connection(),
821                                         adapter_get_path(manager->adapter),
822                                         LE_ADVERTISING_MGR_IFACE);
823
824         manager_destroy(manager);
825 }