Fix build break for rpm
[framework/connectivity/bluez.git] / proximity / monitor.c
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2011  Nokia Corporation
6  *  Copyright (C) 2011  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <gdbus.h>
32 #include <stdint.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/stat.h>
36
37 #include <bluetooth/bluetooth.h>
38 #include <bluetooth/uuid.h>
39
40 #include "dbus-common.h"
41 #include "adapter.h"
42 #include "device.h"
43 #include "error.h"
44 #include "log.h"
45 #include "att.h"
46 #include "gattrib.h"
47 #include "gatt.h"
48 #include "attio.h"
49 #include "monitor.h"
50 #include "textfile.h"
51
52 #define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor"
53
54 #define ALERT_LEVEL_CHR_UUID 0x2A06
55 #define POWER_LEVEL_CHR_UUID 0x2A07
56
57 #define IMMEDIATE_TIMEOUT       5
58
59 enum {
60         ALERT_NONE = 0,
61         ALERT_MILD,
62         ALERT_HIGH,
63 };
64
65 struct monitor {
66         struct btd_device *device;
67         GAttrib *attrib;
68         DBusConnection *conn;
69         struct att_range *linkloss;
70         struct att_range *txpower;
71         struct att_range *immediate;
72         struct enabled enabled;
73         char *linklosslevel;            /* Link Loss Alert Level */
74         char *fallbacklevel;            /* Immediate fallback alert level */
75         char *immediatelevel;           /* Immediate Alert Level */
76         char *signallevel;              /* Path Loss RSSI level */
77         uint16_t linklosshandle;        /* Link Loss Characteristic
78                                          * Value Handle */
79         uint16_t txpowerhandle;         /* Tx Characteristic Value Handle */
80         uint16_t immediatehandle;       /* Immediate Alert Value Handle */
81         guint immediateto;              /* Reset Immediate Alert to "none" */
82         guint attioid;
83 };
84
85 static inline int create_filename(char *buf, size_t size,
86                                 const bdaddr_t *bdaddr, const char *name)
87 {
88         char addr[18];
89
90         ba2str(bdaddr, addr);
91
92         return create_name(buf, size, STORAGEDIR, addr, name);
93 }
94
95 static int write_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
96                                         const char *alert, const char *level)
97 {
98         char filename[PATH_MAX + 1], addr[18], key[38];
99
100         create_filename(filename, PATH_MAX, sba, "proximity");
101
102         create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
103
104         ba2str(dba, addr);
105
106         snprintf(key, sizeof(key), "%17s#%s", addr, alert);
107
108         return textfile_put(filename, key, level);
109 }
110
111 static char *read_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
112                                                         const char *alert)
113 {
114         char filename[PATH_MAX + 1], addr[18], key[38];
115         char *str, *strnew;
116
117         create_filename(filename, PATH_MAX, sba, "proximity");
118
119         ba2str(dba, addr);
120         snprintf(key, sizeof(key), "%17s#%s", addr, alert);
121
122         str = textfile_caseget(filename, key);
123         if (str == NULL)
124                 return NULL;
125
126         strnew = g_strdup(str);
127         free(str);
128
129         return strnew;
130 }
131
132 static uint8_t str2level(const char *level)
133 {
134         if (g_strcmp0("high", level) == 0)
135                 return ALERT_HIGH;
136         else if (g_strcmp0("mild", level) == 0)
137                 return ALERT_MILD;
138
139         return ALERT_NONE;
140 }
141
142 static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
143                                                         gpointer user_data)
144 {
145         struct monitor *monitor = user_data;
146         struct btd_device *device = monitor->device;
147         const char *path = device_get_path(device);
148
149         if (status != 0) {
150                 error("Link Loss Write Request failed: %s",
151                                                         att_ecode2str(status));
152                 return;
153         }
154
155         if (!dec_write_resp(pdu, plen)) {
156                 error("Link Loss Write Request: protocol error");
157                 return;
158         }
159
160         DBG("Link Loss Alert Level written");
161
162         emit_property_changed(monitor->conn, path,
163                                 PROXIMITY_INTERFACE, "LinkLossAlertLevel",
164                                 DBUS_TYPE_STRING, &monitor->linklosslevel);
165 }
166
167 static void char_discovered_cb(GSList *characteristics, guint8 status,
168                                                         gpointer user_data)
169 {
170         struct monitor *monitor = user_data;
171         struct gatt_char *chr;
172         uint8_t value = str2level(monitor->linklosslevel);
173
174         if (status) {
175                 error("Discover Link Loss handle: %s", att_ecode2str(status));
176                 return;
177         }
178
179         DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel);
180
181         /* Assume there is a single Alert Level characteristic */
182         chr = characteristics->data;
183         monitor->linklosshandle = chr->value_handle;
184
185         gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1,
186                                                 linkloss_written, monitor);
187 }
188
189 static int write_alert_level(struct monitor *monitor)
190 {
191         struct att_range *linkloss = monitor->linkloss;
192         bt_uuid_t uuid;
193
194         if (monitor->linklosshandle) {
195                 uint8_t value = str2level(monitor->linklosslevel);
196
197                 gatt_write_char(monitor->attrib, monitor->linklosshandle,
198                                         &value, 1, linkloss_written, monitor);
199                 return 0;
200         }
201
202         bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
203
204         /* FIXME: use cache (requires service changed support) ? */
205         gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end,
206                                         &uuid, char_discovered_cb, monitor);
207
208         return 0;
209 }
210
211 static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
212                                                         gpointer user_data)
213 {
214         uint8_t value[ATT_MAX_MTU];
215         int vlen;
216
217         if (status != 0) {
218                 DBG("Tx Power Level read failed: %s", att_ecode2str(status));
219                 return;
220         }
221
222         if (!dec_read_resp(pdu, plen, value, &vlen)) {
223                 DBG("Protocol error");
224                 return;
225         }
226
227         if (vlen != 1) {
228                 DBG("Invalid length for TX Power value: %d", vlen);
229                 return;
230         }
231
232         DBG("Tx Power Level: %02x", (int8_t) value[0]);
233 }
234
235 static void tx_power_handle_cb(GSList *characteristics, guint8 status,
236                                                         gpointer user_data)
237 {
238         struct monitor *monitor = user_data;
239         struct gatt_char *chr;
240
241         if (status) {
242                 error("Discover Tx Power handle: %s", att_ecode2str(status));
243                 return;
244         }
245
246         chr = characteristics->data;
247         monitor->txpowerhandle = chr->value_handle;
248
249         DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle);
250
251         gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
252                                                         tx_power_read_cb, monitor);
253 }
254
255 static void read_tx_power(struct monitor *monitor)
256 {
257         struct att_range *txpower = monitor->txpower;
258         bt_uuid_t uuid;
259
260         if (monitor->txpowerhandle != 0) {
261                 gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
262                                                 tx_power_read_cb, monitor);
263                 return;
264         }
265
266         bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
267
268         gatt_discover_char(monitor->attrib, txpower->start, txpower->end,
269                                 &uuid, tx_power_handle_cb, monitor);
270 }
271
272 static gboolean immediate_timeout(gpointer user_data)
273 {
274         struct monitor *monitor = user_data;
275         const char *path = device_get_path(monitor->device);
276
277         monitor->immediateto = 0;
278
279         if (g_strcmp0(monitor->immediatelevel, "none") == 0)
280                 return FALSE;
281
282         if (monitor->attrib) {
283                 uint8_t value = ALERT_NONE;
284                 gatt_write_cmd(monitor->attrib, monitor->immediatehandle,
285                                 &value, 1, NULL, NULL);
286         }
287
288         g_free(monitor->immediatelevel);
289         monitor->immediatelevel = g_strdup("none");
290         emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
291                                         "ImmediateAlertLevel", DBUS_TYPE_STRING,
292                                         &monitor->immediatelevel);
293
294         return FALSE;
295 }
296
297 static void immediate_written(gpointer user_data)
298 {
299         struct monitor *monitor = user_data;
300         const char *path = device_get_path(monitor->device);
301
302         g_free(monitor->fallbacklevel);
303         monitor->fallbacklevel = NULL;
304
305         emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
306                                 "ImmediateAlertLevel",
307                                 DBUS_TYPE_STRING, &monitor->immediatelevel);
308
309         monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT,
310                                                 immediate_timeout, monitor);
311 }
312
313 static void write_immediate_alert(struct monitor *monitor)
314 {
315         uint8_t value = str2level(monitor->immediatelevel);
316
317         gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1,
318                                                 immediate_written, monitor);
319 }
320
321 static void immediate_handle_cb(GSList *characteristics, guint8 status,
322                                                         gpointer user_data)
323 {
324         struct monitor *monitor = user_data;
325         struct gatt_char *chr;
326
327         if (status) {
328                 error("Discover Immediate Alert handle: %s",
329                                                 att_ecode2str(status));
330                 return;
331         }
332
333         chr = characteristics->data;
334         monitor->immediatehandle = chr->value_handle;
335
336         DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle);
337
338         if (monitor->fallbacklevel)
339                 write_immediate_alert(monitor);
340 }
341
342 static void discover_immediate_handle(struct monitor *monitor)
343 {
344         struct att_range *immediate = monitor->immediate;
345         bt_uuid_t uuid;
346
347         bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
348
349         gatt_discover_char(monitor->attrib, immediate->start, immediate->end,
350                                         &uuid, immediate_handle_cb, monitor);
351 }
352
353 static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
354 {
355         struct monitor *monitor = user_data;
356
357         monitor->attrib = g_attrib_ref(attrib);
358
359         if (monitor->enabled.linkloss)
360                 write_alert_level(monitor);
361
362         if (monitor->enabled.pathloss)
363                 read_tx_power(monitor);
364
365         if (monitor->immediatehandle == 0) {
366                 if(monitor->enabled.pathloss || monitor->enabled.findme)
367                         discover_immediate_handle(monitor);
368         } else if (monitor->fallbacklevel)
369                 write_immediate_alert(monitor);
370 }
371
372 static void attio_disconnected_cb(gpointer user_data)
373 {
374         struct monitor *monitor = user_data;
375         const char *path = device_get_path(monitor->device);
376
377         g_attrib_unref(monitor->attrib);
378         monitor->attrib = NULL;
379
380         if (monitor->immediateto == 0)
381                 return;
382
383         g_source_remove(monitor->immediateto);
384         monitor->immediateto = 0;
385
386         if (g_strcmp0(monitor->immediatelevel, "none") == 0)
387                 return;
388
389         g_free(monitor->immediatelevel);
390         monitor->immediatelevel = g_strdup("none");
391         emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
392                                         "ImmediateAlertLevel", DBUS_TYPE_STRING,
393                                         &monitor->immediatelevel);
394 }
395
396 static gboolean level_is_valid(const char *level)
397 {
398         return (g_str_equal("none", level) ||
399                         g_str_equal("mild", level) ||
400                         g_str_equal("high", level));
401 }
402
403 static DBusMessage *set_link_loss_alert(DBusConnection *conn, DBusMessage *msg,
404                                                 const char *level, void *data)
405 {
406         struct monitor *monitor = data;
407         struct btd_device *device = monitor->device;
408         bdaddr_t sba, dba;
409
410         if (!level_is_valid(level))
411                 return btd_error_invalid_args(msg);
412
413         if (g_strcmp0(monitor->linklosslevel, level) == 0)
414                 return dbus_message_new_method_return(msg);
415
416         g_free(monitor->linklosslevel);
417         monitor->linklosslevel = g_strdup(level);
418
419         adapter_get_address(device_get_adapter(device), &sba);
420         device_get_address(device, &dba, NULL);
421
422         write_proximity_config(&sba, &dba, "LinkLossAlertLevel", level);
423
424         if (monitor->attrib)
425                 write_alert_level(monitor);
426
427         return dbus_message_new_method_return(msg);
428 }
429
430 static DBusMessage *set_immediate_alert(DBusConnection *conn, DBusMessage *msg,
431                                                 const char *level, void *data)
432 {
433         struct monitor *monitor = data;
434
435         if (!level_is_valid(level))
436                 return btd_error_invalid_args(msg);
437
438         if (g_strcmp0(monitor->immediatelevel, level) == 0)
439                 return dbus_message_new_method_return(msg);
440
441         if (monitor->immediateto) {
442                 g_source_remove(monitor->immediateto);
443                 monitor->immediateto = 0;
444         }
445
446         /* Previous Immediate Alert level if connection/write fails */
447         g_free(monitor->fallbacklevel);
448         monitor->fallbacklevel = monitor->immediatelevel;
449
450         monitor->immediatelevel = g_strdup(level);
451
452         /*
453          * Means that Link/Path Loss are disabled or there is a pending
454          * writting for Find Me(Immediate Alert characteristic value).
455          * If enabled, Path Loss always registers a connection callback
456          * when the Proximity Monitor starts.
457          */
458         if (monitor->attioid == 0)
459                 monitor->attioid = btd_device_add_attio_callback(monitor->device,
460                                                         attio_connected_cb,
461                                                         attio_disconnected_cb,
462                                                         monitor);
463         else if (monitor->attrib)
464                 write_immediate_alert(monitor);
465
466         return dbus_message_new_method_return(msg);
467 }
468
469 static DBusMessage *get_properties(DBusConnection *conn,
470                                         DBusMessage *msg, void *data)
471 {
472         struct monitor *monitor = data;
473         DBusMessageIter iter;
474         DBusMessageIter dict;
475         DBusMessage *reply;
476
477         reply = dbus_message_new_method_return(msg);
478         if (!reply)
479                 return NULL;
480
481         dbus_message_iter_init_append(reply, &iter);
482
483         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
484                         DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
485                         DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
486                         DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
487
488         if (monitor->enabled.linkloss)
489                 dict_append_entry(&dict, "LinkLossAlertLevel",
490                                 DBUS_TYPE_STRING, &monitor->linklosslevel);
491
492         if (monitor->enabled.findme || monitor->enabled.pathloss)
493                 dict_append_entry(&dict, "ImmediateAlertLevel",
494                                 DBUS_TYPE_STRING, &monitor->immediatelevel);
495
496         if (monitor->enabled.pathloss)
497                 dict_append_entry(&dict, "SignalLevel",
498                                 DBUS_TYPE_STRING, &monitor->signallevel);
499
500         dbus_message_iter_close_container(&iter, &dict);
501
502         return reply;
503 }
504
505 static DBusMessage *set_property(DBusConnection *conn,
506                                         DBusMessage *msg, void *data)
507 {
508         struct monitor *monitor = data;
509         const char *property;
510         DBusMessageIter iter;
511         DBusMessageIter sub;
512         const char *level;
513
514         if (!dbus_message_iter_init(msg, &iter))
515                 return btd_error_invalid_args(msg);
516
517         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
518                 return btd_error_invalid_args(msg);
519
520         dbus_message_iter_get_basic(&iter, &property);
521         dbus_message_iter_next(&iter);
522
523         if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
524                 return btd_error_invalid_args(msg);
525
526         dbus_message_iter_recurse(&iter, &sub);
527
528         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
529                 return btd_error_invalid_args(msg);
530
531         dbus_message_iter_get_basic(&sub, &level);
532
533         if (g_str_equal("ImmediateAlertLevel", property)) {
534                 if (monitor->enabled.findme == FALSE &&
535                                 monitor->enabled.pathloss == FALSE)
536                         return btd_error_not_available(msg);
537
538                 return set_immediate_alert(conn, msg, level, data);
539         } else if (g_str_equal("LinkLossAlertLevel", property)) {
540                 if (monitor->enabled.linkloss == FALSE)
541                         return btd_error_not_available(msg);
542
543                 return set_link_loss_alert(conn, msg, level, data);
544         }
545
546         return btd_error_invalid_args(msg);
547 }
548
549 static const GDBusMethodTable monitor_methods[] = {
550         { GDBUS_METHOD("GetProperties",
551                         NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
552                         get_properties) },
553         { GDBUS_ASYNC_METHOD("SetProperty",
554                         GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
555                         set_property) },
556         { }
557 };
558
559 static const GDBusSignalTable monitor_signals[] = {
560         { GDBUS_SIGNAL("PropertyChanged",
561                         GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
562         { }
563 };
564
565 static void monitor_destroy(gpointer user_data)
566 {
567         struct monitor *monitor = user_data;
568
569         if (monitor->immediateto)
570                 g_source_remove(monitor->immediateto);
571
572         if (monitor->attioid)
573                 btd_device_remove_attio_callback(monitor->device,
574                                                 monitor->attioid);
575         if (monitor->attrib)
576                 g_attrib_unref(monitor->attrib);
577
578         dbus_connection_unref(monitor->conn);
579         btd_device_unref(monitor->device);
580         g_free(monitor->linkloss);
581         g_free(monitor->immediate);
582         g_free(monitor->txpower);
583         g_free(monitor->linklosslevel);
584         g_free(monitor->immediatelevel);
585         g_free(monitor->signallevel);
586         g_free(monitor);
587 }
588
589 int monitor_register(DBusConnection *conn, struct btd_device *device,
590                 struct gatt_primary *linkloss, struct gatt_primary *txpower,
591                 struct gatt_primary *immediate, struct enabled *enabled)
592 {
593         const char *path = device_get_path(device);
594         struct monitor *monitor;
595         bdaddr_t sba, dba;
596         char *level;
597
598         adapter_get_address(device_get_adapter(device), &sba);
599         device_get_address(device, &dba, NULL);
600
601         level = read_proximity_config(&sba, &dba, "LinkLossAlertLevel");
602
603         monitor = g_new0(struct monitor, 1);
604         monitor->device = btd_device_ref(device);
605         monitor->conn = dbus_connection_ref(conn);
606         monitor->linklosslevel = (level ? : g_strdup("high"));
607         monitor->signallevel = g_strdup("unknown");
608         monitor->immediatelevel = g_strdup("none");
609
610         if (g_dbus_register_interface(conn, path,
611                                 PROXIMITY_INTERFACE,
612                                 monitor_methods, monitor_signals,
613                                 NULL, monitor, monitor_destroy) == FALSE) {
614                 error("D-Bus failed to register %s interface",
615                                                 PROXIMITY_INTERFACE);
616                 monitor_destroy(monitor);
617                 return -1;
618         }
619
620         DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
621
622         if (linkloss && enabled->linkloss) {
623                 monitor->linkloss = g_new0(struct att_range, 1);
624                 monitor->linkloss->start = linkloss->range.start;
625                 monitor->linkloss->end = linkloss->range.end;
626
627                 monitor->enabled.linkloss = TRUE;
628         }
629
630         if (immediate) {
631                 if (txpower && enabled->pathloss) {
632                         monitor->txpower = g_new0(struct att_range, 1);
633                         monitor->txpower->start = txpower->range.start;
634                         monitor->txpower->end = txpower->range.end;
635
636                         monitor->enabled.pathloss = TRUE;
637                 }
638
639                 if (enabled->pathloss || enabled->findme) {
640                         monitor->immediate = g_new0(struct att_range, 1);
641                         monitor->immediate->start = immediate->range.start;
642                         monitor->immediate->end = immediate->range.end;
643                 }
644
645                 monitor->enabled.findme = enabled->findme;
646         }
647
648         DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
649                                 monitor->enabled.linkloss ? "TRUE" : "FALSE",
650                                 monitor->enabled.pathloss ? "TRUE" : "FALSE",
651                                 monitor->enabled.findme ? "TRUE" : "FALSE");
652
653         if (monitor->enabled.linkloss || monitor->enabled.pathloss)
654                 monitor->attioid = btd_device_add_attio_callback(device,
655                                                         attio_connected_cb,
656                                                         attio_disconnected_cb,
657                                                         monitor);
658
659         return 0;
660 }
661
662 void monitor_unregister(DBusConnection *conn, struct btd_device *device)
663 {
664         const char *path = device_get_path(device);
665
666         g_dbus_unregister_interface(conn, path, PROXIMITY_INTERFACE);
667 }