26
26
#include <config.h>
37
#include <bluetooth/bluetooth.h>
38
#include <bluetooth/uuid.h>
40
#include "dbus-common.h"
33
49
#include "monitor.h"
35
52
#define PROXIMITY_INTERFACE "org.bluez.Proximity"
36
#define PROXIMITY_PATH "/org/bluez/proximity"
54
#define ALERT_LEVEL_CHR_UUID 0x2A06
55
#define POWER_LEVEL_CHR_UUID 0x2A07
57
#define IMMEDIATE_TIMEOUT 5
66
struct btd_device *device;
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
79
uint16_t txpowerhandle; /* Tx Characteristic Value Handle */
80
uint16_t immediatehandle; /* Immediate Alert Value Handle */
81
guint immediateto; /* Reset Immediate Alert to "none" */
85
static inline int create_filename(char *buf, size_t size,
86
const bdaddr_t *bdaddr, const char *name)
92
return create_name(buf, size, STORAGEDIR, addr, name);
95
static int write_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
96
const char *alert, const char *level)
98
char filename[PATH_MAX + 1], addr[18], key[38];
100
create_filename(filename, PATH_MAX, sba, "proximity");
102
create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
106
snprintf(key, sizeof(key), "%17s#%s", addr, alert);
108
return textfile_put(filename, key, level);
111
static char *read_proximity_config(bdaddr_t *sba, bdaddr_t *dba,
114
char filename[PATH_MAX + 1], addr[18], key[38];
117
create_filename(filename, PATH_MAX, sba, "proximity");
120
snprintf(key, sizeof(key), "%17s#%s", addr, alert);
122
str = textfile_caseget(filename, key);
126
strnew = g_strdup(str);
132
static uint8_t str2level(const char *level)
134
if (g_strcmp0("high", level) == 0)
136
else if (g_strcmp0("mild", level) == 0)
142
static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
145
struct monitor *monitor = user_data;
146
struct btd_device *device = monitor->device;
147
const char *path = device_get_path(device);
150
error("Link Loss Write Request failed: %s",
151
att_ecode2str(status));
155
if (!dec_write_resp(pdu, plen)) {
156
error("Link Loss Write Request: protocol error");
160
DBG("Link Loss Alert Level written");
162
emit_property_changed(monitor->conn, path,
163
PROXIMITY_INTERFACE, "LinkLossAlertLevel",
164
DBUS_TYPE_STRING, &monitor->linklosslevel);
167
static void char_discovered_cb(GSList *characteristics, guint8 status,
170
struct monitor *monitor = user_data;
171
struct att_char *chr;
172
uint8_t value = str2level(monitor->linklosslevel);
175
error("Discover Link Loss handle: %s", att_ecode2str(status));
179
DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel);
181
/* Assume there is a single Alert Level characteristic */
182
chr = characteristics->data;
183
monitor->linklosshandle = chr->value_handle;
185
gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1,
186
linkloss_written, monitor);
189
static int write_alert_level(struct monitor *monitor)
191
struct att_range *linkloss = monitor->linkloss;
194
if (monitor->linklosshandle) {
195
uint8_t value = str2level(monitor->linklosslevel);
197
gatt_write_char(monitor->attrib, monitor->linklosshandle,
198
&value, 1, linkloss_written, monitor);
202
bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
204
/* FIXME: use cache (requires service changed support) ? */
205
gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end,
206
&uuid, char_discovered_cb, monitor);
211
static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
214
uint8_t value[ATT_MAX_MTU];
218
DBG("Tx Power Level read failed: %s", att_ecode2str(status));
222
if (!dec_read_resp(pdu, plen, value, &vlen)) {
223
DBG("Protocol error");
228
DBG("Invalid length for TX Power value: %d", vlen);
232
DBG("Tx Power Level: %02x", (int8_t) value[0]);
235
static void tx_power_handle_cb(GSList *characteristics, guint8 status,
238
struct monitor *monitor = user_data;
239
struct att_char *chr;
242
error("Discover Tx Power handle: %s", att_ecode2str(status));
246
chr = characteristics->data;
247
monitor->txpowerhandle = chr->value_handle;
249
DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle);
251
gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
252
tx_power_read_cb, monitor);
255
static void read_tx_power(struct monitor *monitor)
257
struct att_range *txpower = monitor->txpower;
260
if (monitor->txpowerhandle != 0) {
261
gatt_read_char(monitor->attrib, monitor->txpowerhandle, 0,
262
tx_power_read_cb, monitor);
266
bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
268
gatt_discover_char(monitor->attrib, txpower->start, txpower->end,
269
&uuid, tx_power_handle_cb, monitor);
272
static gboolean immediate_timeout(gpointer user_data)
274
struct monitor *monitor = user_data;
275
const char *path = device_get_path(monitor->device);
277
monitor->immediateto = 0;
279
if (g_strcmp0(monitor->immediatelevel, "none") == 0)
282
if (monitor->attrib) {
283
uint8_t value = ALERT_NONE;
284
gatt_write_cmd(monitor->attrib, monitor->immediatehandle,
285
&value, 1, NULL, NULL);
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);
297
static void immediate_written(gpointer user_data)
299
struct monitor *monitor = user_data;
300
const char *path = device_get_path(monitor->device);
302
g_free(monitor->fallbacklevel);
303
monitor->fallbacklevel = NULL;
305
emit_property_changed(monitor->conn, path, PROXIMITY_INTERFACE,
306
"ImmediateAlertLevel",
307
DBUS_TYPE_STRING, &monitor->immediatelevel);
309
monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT,
310
immediate_timeout, monitor);
313
static void write_immediate_alert(struct monitor *monitor)
315
uint8_t value = str2level(monitor->immediatelevel);
317
gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1,
318
immediate_written, monitor);
321
static void immediate_handle_cb(GSList *characteristics, guint8 status,
324
struct monitor *monitor = user_data;
325
struct att_char *chr;
328
error("Discover Immediate Alert handle: %s",
329
att_ecode2str(status));
333
chr = characteristics->data;
334
monitor->immediatehandle = chr->value_handle;
336
DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle);
338
if (monitor->fallbacklevel)
339
write_immediate_alert(monitor);
342
static void discover_immediate_handle(struct monitor *monitor)
344
struct att_range *immediate = monitor->immediate;
347
bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
349
gatt_discover_char(monitor->attrib, immediate->start, immediate->end,
350
&uuid, immediate_handle_cb, monitor);
353
static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
355
struct monitor *monitor = user_data;
357
monitor->attrib = g_attrib_ref(attrib);
359
if (monitor->enabled.linkloss)
360
write_alert_level(monitor);
362
if (monitor->enabled.pathloss)
363
read_tx_power(monitor);
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);
372
static void attio_disconnected_cb(gpointer user_data)
374
struct monitor *monitor = user_data;
375
const char *path = device_get_path(monitor->device);
377
g_attrib_unref(monitor->attrib);
378
monitor->attrib = NULL;
380
if (monitor->immediateto == 0)
383
g_source_remove(monitor->immediateto);
384
monitor->immediateto = 0;
386
if (g_strcmp0(monitor->immediatelevel, "none") == 0)
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);
396
static gboolean level_is_valid(const char *level)
398
return (g_str_equal("none", level) ||
399
g_str_equal("mild", level) ||
400
g_str_equal("high", level));
403
static DBusMessage *set_link_loss_alert(DBusConnection *conn, DBusMessage *msg,
404
const char *level, void *data)
406
struct monitor *monitor = data;
407
struct btd_device *device = monitor->device;
410
if (!level_is_valid(level))
411
return btd_error_invalid_args(msg);
413
if (g_strcmp0(monitor->linklosslevel, level) == 0)
414
return dbus_message_new_method_return(msg);
416
g_free(monitor->linklosslevel);
417
monitor->linklosslevel = g_strdup(level);
419
adapter_get_address(device_get_adapter(device), &sba);
420
device_get_address(device, &dba, NULL);
422
write_proximity_config(&sba, &dba, "LinkLossAlertLevel", level);
425
write_alert_level(monitor);
427
return dbus_message_new_method_return(msg);
430
static DBusMessage *set_immediate_alert(DBusConnection *conn, DBusMessage *msg,
431
const char *level, void *data)
433
struct monitor *monitor = data;
435
if (!level_is_valid(level))
436
return btd_error_invalid_args(msg);
438
if (g_strcmp0(monitor->immediatelevel, level) == 0)
439
return dbus_message_new_method_return(msg);
441
if (monitor->immediateto) {
442
g_source_remove(monitor->immediateto);
443
monitor->immediateto = 0;
446
/* Previous Immediate Alert level if connection/write fails */
447
g_free(monitor->fallbacklevel);
448
monitor->fallbacklevel = monitor->immediatelevel;
450
monitor->immediatelevel = g_strdup(level);
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.
458
if (monitor->attioid == 0)
459
monitor->attioid = btd_device_add_attio_callback(monitor->device,
461
attio_disconnected_cb,
463
else if (monitor->attrib)
464
write_immediate_alert(monitor);
466
return dbus_message_new_method_return(msg);
38
469
static DBusMessage *get_properties(DBusConnection *conn,
39
470
DBusMessage *msg, void *data)
41
return dbus_message_new_method_return(msg);
472
struct monitor *monitor = data;
473
DBusMessageIter iter;
474
DBusMessageIter dict;
477
reply = dbus_message_new_method_return(msg);
481
dbus_message_iter_init_append(reply, &iter);
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);
488
if (monitor->enabled.linkloss)
489
dict_append_entry(&dict, "LinkLossAlertLevel",
490
DBUS_TYPE_STRING, &monitor->linklosslevel);
492
if (monitor->enabled.findme || monitor->enabled.pathloss)
493
dict_append_entry(&dict, "ImmediateAlertLevel",
494
DBUS_TYPE_STRING, &monitor->immediatelevel);
496
if (monitor->enabled.pathloss)
497
dict_append_entry(&dict, "SignalLevel",
498
DBUS_TYPE_STRING, &monitor->signallevel);
500
dbus_message_iter_close_container(&iter, &dict);
44
505
static DBusMessage *set_property(DBusConnection *conn,
45
506
DBusMessage *msg, void *data)
47
return dbus_message_new_method_return(msg);
508
struct monitor *monitor = data;
509
const char *property;
510
DBusMessageIter iter;
514
if (!dbus_message_iter_init(msg, &iter))
515
return btd_error_invalid_args(msg);
517
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
518
return btd_error_invalid_args(msg);
520
dbus_message_iter_get_basic(&iter, &property);
521
dbus_message_iter_next(&iter);
523
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
524
return btd_error_invalid_args(msg);
526
dbus_message_iter_recurse(&iter, &sub);
528
if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
529
return btd_error_invalid_args(msg);
531
dbus_message_iter_get_basic(&sub, &level);
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);
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);
543
return set_link_loss_alert(conn, msg, level, data);
546
return btd_error_invalid_args(msg);
50
549
static GDBusMethodTable monitor_methods[] = {
62
int monitor_register(DBusConnection *conn)
66
if (g_dbus_register_interface(conn, PROXIMITY_PATH,
68
monitor_methods, monitor_signals,
69
NULL, NULL, NULL) == TRUE) {
70
DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE,
76
error("D-Bus failed to register %s interface", PROXIMITY_INTERFACE);
81
void monitor_unregister(DBusConnection *conn)
83
g_dbus_unregister_interface(conn, PROXIMITY_PATH, PROXIMITY_INTERFACE);
561
static void monitor_destroy(gpointer user_data)
563
struct monitor *monitor = user_data;
565
if (monitor->immediateto)
566
g_source_remove(monitor->immediateto);
568
if (monitor->attioid)
569
btd_device_remove_attio_callback(monitor->device,
572
g_attrib_unref(monitor->attrib);
574
dbus_connection_unref(monitor->conn);
575
btd_device_unref(monitor->device);
576
g_free(monitor->linkloss);
577
g_free(monitor->immediate);
578
g_free(monitor->txpower);
579
g_free(monitor->linklosslevel);
580
g_free(monitor->immediatelevel);
581
g_free(monitor->signallevel);
585
int monitor_register(DBusConnection *conn, struct btd_device *device,
586
struct att_primary *linkloss, struct att_primary *txpower,
587
struct att_primary *immediate, struct enabled *enabled)
589
const char *path = device_get_path(device);
590
struct monitor *monitor;
594
adapter_get_address(device_get_adapter(device), &sba);
595
device_get_address(device, &dba, NULL);
597
level = read_proximity_config(&sba, &dba, "LinkLossAlertLevel");
599
monitor = g_new0(struct monitor, 1);
600
monitor->device = btd_device_ref(device);
601
monitor->conn = dbus_connection_ref(conn);
602
monitor->linklosslevel = (level ? : g_strdup("high"));
603
monitor->signallevel = g_strdup("unknown");
604
monitor->immediatelevel = g_strdup("none");
606
if (g_dbus_register_interface(conn, path,
608
monitor_methods, monitor_signals,
609
NULL, monitor, monitor_destroy) == FALSE) {
610
error("D-Bus failed to register %s interface",
611
PROXIMITY_INTERFACE);
612
monitor_destroy(monitor);
616
DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
618
if (linkloss && enabled->linkloss) {
619
monitor->linkloss = g_new0(struct att_range, 1);
620
monitor->linkloss->start = linkloss->start;
621
monitor->linkloss->end = linkloss->end;
623
monitor->enabled.linkloss = TRUE;
627
if (txpower && enabled->pathloss) {
628
monitor->txpower = g_new0(struct att_range, 1);
629
monitor->txpower->start = txpower->start;
630
monitor->txpower->end = txpower->end;
632
monitor->enabled.pathloss = TRUE;
635
if (enabled->pathloss || enabled->findme) {
636
monitor->immediate = g_new0(struct att_range, 1);
637
monitor->immediate->start = immediate->start;
638
monitor->immediate->end = immediate->end;
641
monitor->enabled.findme = enabled->findme;
644
DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
645
monitor->enabled.linkloss ? "TRUE" : "FALSE",
646
monitor->enabled.pathloss ? "TRUE" : "FALSE",
647
monitor->enabled.findme ? "TRUE" : "FALSE");
649
if (monitor->enabled.linkloss || monitor->enabled.pathloss)
650
monitor->attioid = btd_device_add_attio_callback(device,
652
attio_disconnected_cb,
658
void monitor_unregister(DBusConnection *conn, struct btd_device *device)
660
const char *path = device_get_path(device);
662
g_dbus_unregister_interface(conn, path, PROXIMITY_INTERFACE);