bluetooth: Support port availability flag
[profile/ivi/pulseaudio.git] / src / modules / reserve-monitor.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
2
3 /***
4   Copyright 2009 Lennart Poettering
5
6   Permission is hereby granted, free of charge, to any person
7   obtaining a copy of this software and associated documentation files
8   (the "Software"), to deal in the Software without restriction,
9   including without limitation the rights to use, copy, modify, merge,
10   publish, distribute, sublicense, and/or sell copies of the Software,
11   and to permit persons to whom the Software is furnished to do so,
12   subject to the following conditions:
13
14   The above copyright notice and this permission notice shall be
15   included in all copies or substantial portions of the Software.
16
17   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21   BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22   ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24   SOFTWARE.
25 ***/
26
27 #include <string.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <assert.h>
33
34 #include "reserve-monitor.h"
35
36 struct rm_monitor {
37         int ref;
38
39         char *device_name;
40         char *service_name;
41         char *match;
42
43         DBusConnection *connection;
44
45         unsigned busy:1;
46         unsigned filtering:1;
47         unsigned matching:1;
48
49         rm_change_cb_t change_cb;
50         void *userdata;
51 };
52
53 #define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
54
55 #define SERVICE_FILTER                          \
56         "type='signal',"                        \
57         "sender='" DBUS_SERVICE_DBUS "',"       \
58         "interface='" DBUS_INTERFACE_DBUS "',"  \
59         "member='NameOwnerChanged',"            \
60         "arg0='%s'"
61
62 static DBusHandlerResult filter_handler(
63         DBusConnection *c,
64         DBusMessage *s,
65         void *userdata) {
66
67         rm_monitor *m;
68         DBusError error;
69
70         dbus_error_init(&error);
71
72         m = userdata;
73         assert(m->ref >= 1);
74
75         if (dbus_message_is_signal(s, "org.freedesktop.DBus", "NameOwnerChanged")) {
76                 const char *name, *old, *new;
77
78                 if (!dbus_message_get_args(
79                             s,
80                             &error,
81                             DBUS_TYPE_STRING, &name,
82                             DBUS_TYPE_STRING, &old,
83                             DBUS_TYPE_STRING, &new,
84                             DBUS_TYPE_INVALID))
85                         goto invalid;
86
87                 if (strcmp(name, m->service_name) == 0) {
88                         m->busy = !!(new && *new);
89
90                         /* If we ourselves own the device, then don't consider this 'busy' */
91                         if (m->busy) {
92                                 const char *un;
93
94                                 if ((un = dbus_bus_get_unique_name(c)))
95                                         if (strcmp(new, un) == 0)
96                                                 m->busy = FALSE;
97                         }
98
99                         if (m->change_cb) {
100                                 m->ref++;
101                                 m->change_cb(m);
102                                 rm_release(m);
103                         }
104                 }
105         }
106
107 invalid:
108         dbus_error_free(&error);
109
110         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
111 }
112
113 int rm_watch(
114         rm_monitor **_m,
115         DBusConnection *connection,
116         const char*device_name,
117         rm_change_cb_t change_cb,
118         DBusError *error)  {
119
120         rm_monitor *m = NULL;
121         int r;
122         DBusError _error;
123
124         if (!error)
125                 error = &_error;
126
127         dbus_error_init(error);
128
129         if (!_m)
130                 return -EINVAL;
131
132         if (!connection)
133                 return -EINVAL;
134
135         if (!device_name)
136                 return -EINVAL;
137
138         if (!(m = calloc(sizeof(rm_monitor), 1)))
139                 return -ENOMEM;
140
141         m->ref = 1;
142
143         if (!(m->device_name = strdup(device_name))) {
144                 r = -ENOMEM;
145                 goto fail;
146         }
147
148         m->connection = dbus_connection_ref(connection);
149         m->change_cb = change_cb;
150
151         if (!(m->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
152                 r = -ENOMEM;
153                 goto fail;
154         }
155         sprintf(m->service_name, SERVICE_PREFIX "%s", m->device_name);
156
157         if (!(dbus_connection_add_filter(m->connection, filter_handler, m, NULL))) {
158                 r = -ENOMEM;
159                 goto fail;
160         }
161
162         m->filtering = 1;
163
164         if (!(m->match = malloc(sizeof(SERVICE_FILTER) - 2 + strlen(m->service_name)))) {
165                 r = -ENOMEM;
166                 goto fail;
167         }
168
169         sprintf(m->match, SERVICE_FILTER, m->service_name);
170         dbus_bus_add_match(m->connection, m->match, error);
171
172         if (dbus_error_is_set(error)) {
173                 r = -EIO;
174                 goto fail;
175         }
176
177         m->matching = 1;
178
179         m->busy = dbus_bus_name_has_owner(m->connection, m->service_name, error);
180
181         if (dbus_error_is_set(error)) {
182                 r = -EIO;
183                 goto fail;
184         }
185
186         *_m = m;
187         return 0;
188
189 fail:
190         if (&_error == error)
191                 dbus_error_free(&_error);
192
193         if (m)
194                 rm_release(m);
195
196         return r;
197 }
198
199 void rm_release(rm_monitor *m) {
200         if (!m)
201                 return;
202
203         assert(m->ref > 0);
204
205         if (--m->ref > 0)
206                 return;
207
208         if (m->matching)
209                 dbus_bus_remove_match(
210                         m->connection,
211                         m->match,
212                         NULL);
213
214         if (m->filtering)
215                 dbus_connection_remove_filter(
216                         m->connection,
217                         filter_handler,
218                         m);
219
220         free(m->device_name);
221         free(m->service_name);
222         free(m->match);
223
224         if (m->connection)
225                 dbus_connection_unref(m->connection);
226
227         free(m);
228 }
229
230 int rm_busy(rm_monitor *m) {
231         if (!m)
232                 return -EINVAL;
233
234         assert(m->ref > 0);
235
236         return m->busy;
237 }
238
239 void rm_set_userdata(rm_monitor *m, void *userdata) {
240
241         if (!m)
242                 return;
243
244         assert(m->ref > 0);
245         m->userdata = userdata;
246 }
247
248 void* rm_get_userdata(rm_monitor *m) {
249
250         if (!m)
251                 return NULL;
252
253         assert(m->ref > 0);
254
255         return m->userdata;
256 }