/* * * neard - Near Field Communication manager * * Copyright (C) 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "near.h" #define BLUEZ_SERVICE "org.bluez" #define MANAGER_INTF BLUEZ_SERVICE ".Manager" #define ADAPTER_INTF BLUEZ_SERVICE ".Adapter" #define OOB_INTF BLUEZ_SERVICE ".OutOfBand" #define DEFAULT_ADAPTER "DefaultAdapter" #define ADAPTER_REMOVED "AdapterRemoved" #define DEFAULT_ADAPTER_CHANGED "DefaultAdapterChanged" #define ADAPTER_PROPERTY_CHANGED "PropertyChanged" #define MANAGER_PATH "/" #define OOB_AGENT "/org/neard/agent/neard_oob" #define BT_NOINPUTOUTPUT "NoInputNoOutput" #define BT_DISPLAY_YESNO "DisplayYesNo" #define DBUS_MANAGER_INTF "org.freedesktop.DBus.ObjectManager" #define AGENT_REGISTER_TIMEOUT 2 /* BT EIR list */ #define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ #define EIR_NAME_SHORT 0x08 /* shortened local name */ #define EIR_NAME_COMPLETE 0x09 /* complete local name */ /* Specific OOB EIRs */ #define EIR_CLASS_OF_DEVICE 0x0D /* class of device */ #define EIR_SP_HASH 0x0E /* simple pairing hash C */ #define EIR_SP_RANDOMIZER 0x0F /* simple pairing randomizer R */ /* Optional EIRs */ #define EIR_DEVICE_ID 0x10 /* device ID */ #define EIR_SECURITY_MGR_FLAGS 0x11 /* security manager flags */ #define EIR_SIZE_LEN 1 #define EIR_HEADER_LEN (EIR_SIZE_LEN + 1) #define BT_ADDRESS_SIZE 6 #define COD_SIZE 3 #define OOB_SP_SIZE 16 #define EIR_SIZE_MAX 255 struct near_oob_data { char *def_adapter; char *bd_addr; /* oob mandatory */ /* optional */ char *bt_name; /* short or long name */ uint8_t bt_name_len; int class_of_device; /* Class of device */ near_bool_t powered; near_bool_t pairable; near_bool_t discoverable; uint8_t *uuids; int uuids_len; uint8_t *spair_hash; /* OOB hash Key */ uint8_t *spair_randomizer; /* OOB randomizer key */ uint8_t authentication[OOB_SP_SIZE]; /* On BT 2.0 */ uint8_t security_manager_oob_flags; /* see BT Core 4.0 */ }; static DBusConnection *bt_conn; static struct near_oob_data bt_def_oob_data; static guint watch; static guint removed_watch; static guint adapter_watch; static guint adapter_props_watch; static guint register_bluez_timer; static void __bt_eir_free(struct near_oob_data *oob) { DBG(""); if (oob->def_adapter != NULL) { g_free(oob->def_adapter); oob->def_adapter = NULL; } if (oob->bd_addr != NULL) { g_free(oob->bd_addr); oob->bd_addr = NULL; } if (oob->bt_name != NULL) { g_free(oob->bt_name); oob->bt_name = NULL; } if (oob->spair_hash != NULL) { g_free(oob->spair_hash); oob->spair_hash = NULL; } if (oob->spair_randomizer != NULL) { g_free(oob->spair_randomizer); oob->spair_randomizer = NULL; } } static void bt_eir_free(struct near_oob_data *oob) { __bt_eir_free(oob); g_free(oob); } /* D-Bus helper functions */ static int bt_generic_call(DBusConnection *conn, struct near_oob_data *oob, /* user data */ const char *dest, /* method call */ const char *path, const char *interface, const char *method, DBusPendingCallNotifyFunction bt_cb, /* callback */ int type, ...) /* params */ { DBusMessage *msg; DBusPendingCall *pending; va_list args; int err; DBG("%s", method); msg = dbus_message_new_method_call(dest, path, interface, method); if (msg == NULL) { near_error("Unable to allocate new D-Bus %s message", method); err = -ENOMEM; } va_start(args, type); if (!dbus_message_append_args_valist(msg, type, args)) { va_end(args); err = -EIO; goto error_done; } va_end(args); if (!dbus_connection_send_with_reply(conn, msg, &pending, -1)) { near_error("Sending %s failed", method); err = -EIO; goto error_done; } if (pending == NULL) { near_error("D-Bus connection not available"); err = -EIO; goto error_done; } /* Prepare for notification */ dbus_pending_call_set_notify(pending, bt_cb, oob, NULL); err = 0 ; error_done: dbus_message_unref(msg); return err; } static void bt_create_paired_device_cb(DBusPendingCall *pending, void *user_data) { DBusMessage *reply; DBusError error; struct near_oob_data *oob = user_data; DBG(""); reply = dbus_pending_call_steal_reply(pending); if (reply == NULL) return; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply)) { near_error("%s", error.message); dbus_error_free(&error); goto cb_done; } DBG("Successful pairing"); cb_done: /* task completed - clean memory*/ bt_eir_free(oob); dbus_message_unref(reply); dbus_pending_call_unref(pending); } static int bt_create_paired_device(DBusConnection *conn, struct near_oob_data *oob, const char *capabilities) { const char *agent_path = OOB_AGENT; return bt_generic_call(bt_conn, oob, BLUEZ_SERVICE, oob->def_adapter, ADAPTER_INTF, "CreatePairedDevice", bt_create_paired_device_cb, /* params */ DBUS_TYPE_STRING, &oob->bd_addr, DBUS_TYPE_OBJECT_PATH, &agent_path, DBUS_TYPE_STRING, &capabilities, DBUS_TYPE_INVALID); } static void bt_oob_add_remote_data_cb(DBusPendingCall *pending, void *user_data) { DBusMessage *reply; DBusError error; struct near_oob_data *oob = user_data; DBG(""); reply = dbus_pending_call_steal_reply(pending); if (reply == NULL) return; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply)) goto cb_fail; near_info("OOB data added"); dbus_message_unref(reply); dbus_pending_call_unref(pending); /* Jump to the next: Pairing !!!*/ DBG("Try to pair devices..."); bt_create_paired_device(bt_conn, oob, BT_DISPLAY_YESNO); return; cb_fail: near_error("%s", error.message); dbus_error_free(&error); bt_eir_free(oob); dbus_message_unref(reply); dbus_pending_call_unref(pending); } static int bt_oob_add_remote_data(DBusConnection *conn, struct near_oob_data *oob) { int16_t hash_len = 16; int16_t rdm_len = 16; return bt_generic_call(bt_conn, oob, BLUEZ_SERVICE, oob->def_adapter, OOB_INTF, "AddRemoteData", bt_oob_add_remote_data_cb, /* params */ DBUS_TYPE_STRING, &oob->bd_addr, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &oob->spair_hash, hash_len, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &oob->spair_randomizer, rdm_len, DBUS_TYPE_INVALID); } /* Pairing: JustWorks or OOB */ static int bt_do_pairing(struct near_oob_data *oob) { int err = 0; DBG("%s", oob->bd_addr); /* Is this a *real* oob pairing or a "JustWork" */ if ((oob->spair_hash) && (oob->spair_randomizer)) err = bt_oob_add_remote_data(bt_conn, oob); else err = bt_create_paired_device(bt_conn, oob, BT_NOINPUTOUTPUT); if (err < 0) near_error("Pairing failed. Err[%d]", err); return err; } /* */ static int extract_properties(DBusMessage *reply, struct near_oob_data *oob) { char *data = NULL; int idata; int i, j; DBusMessageIter array, dict; if (dbus_message_iter_init(reply, &array) == FALSE) return -1; if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) return -1; dbus_message_iter_recurse(&array, &dict); while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { DBusMessageIter entry, value; const char *key; dbus_message_iter_recurse(&dict, &entry); dbus_message_iter_get_basic(&entry, &key); dbus_message_iter_next(&entry); dbus_message_iter_recurse(&entry, &value); if (g_str_equal(key, "Address") == TRUE) { dbus_message_iter_get_basic(&value, &data); /* Now, fill the local struct */ oob->bd_addr = g_try_malloc0(BT_ADDRESS_SIZE); if (oob->bd_addr == NULL) return -ENOMEM; /* Address is like: "ff:ee:dd:cc:bb:aa" */ for (i = 5, j = 0 ; i >= 0; i--, j += 3) oob->bd_addr[i] = strtol(data + j, NULL, 16); DBG("local address: %s", data); } else if (g_str_equal(key, "Name") == TRUE) { dbus_message_iter_get_basic(&value, &data); oob->bt_name = g_strdup(data); if (oob->bt_name != NULL) { oob->bt_name_len = strlen(oob->bt_name); DBG("local name: %s", oob->bt_name); } } else if (g_str_equal(key, "Class") == TRUE) { dbus_message_iter_get_basic(&value, &idata); oob->class_of_device = idata; } else if (g_str_equal(key, "Powered") == TRUE) { dbus_message_iter_get_basic(&value, &idata); oob->powered = idata; } else if (g_str_equal(key, "Discoverable") == TRUE) { dbus_message_iter_get_basic(&value, &idata); oob->discoverable = idata; } else if (g_str_equal(key, "Pairable") == TRUE) { dbus_message_iter_get_basic(&value, &idata); oob->pairable = idata; } else if (g_str_equal(key, "UUIDs") == TRUE) { oob->uuids_len = sizeof(value); oob->uuids = g_try_malloc0(oob->uuids_len); if (oob->uuids == NULL) return -ENOMEM; memcpy(oob->uuids, &value, oob->uuids_len); } dbus_message_iter_next(&dict); } return 0; } static int bt_parse_properties(DBusMessage *reply, void *user_data) { struct near_oob_data *bt_props = user_data; DBG(""); /* Free datas */ g_free(bt_props->bd_addr); g_free(bt_props->bt_name); /* Grab properties from dbus */ if (extract_properties(reply, bt_props) < 0) goto fail; return 0; fail: g_free(bt_props->bd_addr); bt_props->bd_addr = NULL; g_free(bt_props->bt_name); bt_props->bt_name = NULL; return -ENOMEM; } static gboolean bt_adapter_property_changed(DBusConnection *conn, DBusMessage *message, void *user_data) { DBusMessageIter iter; DBusMessageIter var; const char *property; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &property); dbus_message_iter_next(&iter); if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return TRUE; dbus_message_iter_recurse(&iter, &var); if (g_str_equal(property, "Name") == TRUE) { const char *name; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING) return TRUE; dbus_message_iter_get_basic(&iter, &name); g_free(bt_def_oob_data.bt_name); bt_def_oob_data.bt_name = g_strdup(name); if (bt_def_oob_data.bt_name != NULL) bt_def_oob_data.bt_name_len = strlen(name); else bt_def_oob_data.bt_name_len = 0; DBG("%s: %s", property, name); } else if (g_str_equal(property, "Class") == TRUE) { int class; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_UINT32) return TRUE; dbus_message_iter_get_basic(&var, &class); bt_def_oob_data.class_of_device = class; DBG("%s: %x", property, bt_def_oob_data.class_of_device); } else if (g_str_equal(property, "Powered") == TRUE) { dbus_bool_t powered; if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN) return TRUE; dbus_message_iter_get_basic(&var, &powered); bt_def_oob_data.powered = powered; DBG("%s: %u", property, bt_def_oob_data.powered); } return TRUE; } /* Get default local adapter properties */ static void bt_get_properties_cb(DBusPendingCall *pending, void *user_data) { struct near_oob_data *bt_props = user_data; DBusMessage *reply; DBusError error; int err; DBG(""); reply = dbus_pending_call_steal_reply(pending); if (reply == NULL) return; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply)) goto cb_fail; err = bt_parse_properties(reply, bt_props); if (err < 0) near_error("Problem parsing local properties %d", err); else DBG("Get Properties complete: %s", bt_props->def_adapter); adapter_props_watch = g_dbus_add_signal_watch(bt_conn, NULL, NULL, ADAPTER_INTF, ADAPTER_PROPERTY_CHANGED, bt_adapter_property_changed, NULL, NULL); /* clean */ dbus_message_unref(reply); dbus_pending_call_unref(pending); return; cb_fail: near_error("%s", error.message); dbus_error_free(&error); dbus_message_unref(reply); dbus_pending_call_unref(pending); } static void bt_get_default_adapter_cb(DBusPendingCall *pending, void *user_data) { struct near_oob_data *bt_props = user_data; DBusMessage *reply; DBusError error; gchar *path; DBG(""); reply = dbus_pending_call_steal_reply(pending); if (reply == NULL) return; dbus_error_init(&error); if (dbus_set_error_from_message(&error, reply)) goto cb_fail; if (dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID) == FALSE) goto cb_fail; /* Save the default adapter */ bt_props->def_adapter = g_strdup(path); DBG("Using default adapter %s", bt_props->def_adapter); /* clean */ dbus_message_unref(reply); dbus_pending_call_unref(pending); /* Jump on getAdapterProperties */ bt_generic_call(bt_conn, bt_props, BLUEZ_SERVICE, bt_props->def_adapter, ADAPTER_INTF, "GetProperties", bt_get_properties_cb, DBUS_TYPE_INVALID); return; cb_fail: near_error("%s", error.message); dbus_error_free(&error); dbus_message_unref(reply); dbus_pending_call_unref(pending); } static int bt_refresh_adapter_props(DBusConnection *conn, void *user_data) { DBG("%p %p", conn, user_data); return bt_generic_call(conn, user_data, BLUEZ_SERVICE, MANAGER_PATH, MANAGER_INTF, DEFAULT_ADAPTER, bt_get_default_adapter_cb, DBUS_TYPE_INVALID); } /* Parse and fill the bluetooth oob information block */ static void bt_parse_eir(uint8_t *eir_data, uint16_t eir_data_len, struct near_oob_data *oob, uint16_t *props) { char *tmp; uint16_t len = 0; DBG("total len: %u", eir_data_len); while (len < eir_data_len - 1) { uint8_t eir_len = eir_data[0]; /* EIR field length */ uint8_t eir_code; /* EIR field type*/ uint8_t data_len; /* EIR data length */ uint8_t *data; /* check for early termination */ if (eir_len == 0) break; len += eir_len + 1; /* Do not continue EIR Data parsing if got incorrect length */ if (len > eir_data_len) break; data_len = eir_len - 1; eir_code = eir_data[1]; /* EIR code */ data = &eir_data[2]; DBG("type 0x%.2X data_len %u", eir_code, data_len); switch (eir_code) { case EIR_NAME_SHORT: case EIR_NAME_COMPLETE: oob->bt_name = g_try_malloc0(data_len + 1); /* eos */ if (oob->bt_name) { oob->bt_name_len = data_len; memcpy(oob->bt_name, data, oob->bt_name_len); oob->bt_name[data_len] = 0; /* end str*/ } break; case EIR_CLASS_OF_DEVICE: tmp = g_strdup_printf("%02X%02X%02X", *data, *(data + 1), *(data + 2)); if (tmp != NULL) { oob->class_of_device = strtol(tmp, NULL, 16); *props |= OOB_PROPS_COD; } g_free(tmp); break; case EIR_SP_HASH: oob->spair_hash = g_try_malloc0(OOB_SP_SIZE); if (oob->spair_hash) { memcpy(oob->spair_hash, data, OOB_SP_SIZE); *props |= OOB_PROPS_SP_HASH; } break; case EIR_SP_RANDOMIZER: oob->spair_randomizer = g_try_malloc0(OOB_SP_SIZE); if (oob->spair_randomizer) { memcpy(oob->spair_randomizer, data, OOB_SP_SIZE); *props |= OOB_PROPS_SP_RANDOM; } break; case EIR_SECURITY_MGR_FLAGS: oob->security_manager_oob_flags = *data; break; case EIR_UUID128_ALL: /* TODO: Process uuids128 * */ break; default: /* ignore and skip */ near_error("Unknown EIR x%02x (len: %d)", eir_code, eir_len); break; } /* Next eir */ eir_data += eir_len + 1; } } /* * Because of some "old" implementation, "version" will help * to determine the record data structure. * Some specifications are proprietary (eg. "short mode") * and are not fully documented. * mime_properties is a bitmask and should reflect the fields found in * the incoming oob. */ int __near_bluetooth_parse_oob_record(struct carrier_data *data, uint16_t *mime_properties, near_bool_t pair) { struct near_oob_data *oob; uint16_t bt_oob_data_size; uint8_t *ptr = data->data; uint8_t marker; char *tmp; DBG(""); oob = g_try_malloc0(sizeof(struct near_oob_data)); if (data->type == BT_MIME_V2_1) { /* * Total OOB data size (including size bytes) * Some implementations (e.g. Android 4.1) stores * the data_size in big endian but NDEF forum spec (BT Secure * Simple Pairing) requires a little endian. At the same time, * the NDEF forum NDEF spec define a payload length as single * byte (and the payload size IS the oob data size). */ bt_oob_data_size = near_get_le16(ptr); if (bt_oob_data_size > 0xFF) /* Big Endian */ bt_oob_data_size = GUINT16_FROM_BE(bt_oob_data_size); bt_oob_data_size -= 2 ; /* remove oob datas size len */ /* First item: BD_ADDR (mandatory) */ ptr = &data->data[2]; oob->bd_addr = g_strdup_printf("%02X:%02X:%02X:%02X:%02X:%02X", ptr[5], ptr[4], ptr[3], ptr[2], ptr[1], ptr[0]); /* Skip to the next element (optional) */ ptr += BT_ADDRESS_SIZE; bt_oob_data_size -= BT_ADDRESS_SIZE ; if (bt_oob_data_size) bt_parse_eir(ptr, bt_oob_data_size, oob, mime_properties); } else if (data->type == BT_MIME_V2_0) { marker = *ptr++; /* could be '$' */ oob->bd_addr = g_strdup_printf( "%02X:%02X:%02X:%02X:%02X:%02X", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4], ptr[5]); ptr = ptr + BT_ADDRESS_SIZE; /* Class of device */ tmp = g_strdup_printf("%02X%02X%02X", *ptr, *(ptr + 1), *(ptr + 2)); if (tmp != NULL) oob->class_of_device = strtol(tmp, NULL, 16); g_free(tmp); ptr = ptr + 3; /* "Short mode" seems to use a 4 bytes code * instead of 16 bytes... */ if (marker == '$') { /* Short NFC */ memcpy(oob->authentication, ptr, 4); ptr = ptr + 4; } else { memcpy(oob->authentication, ptr, 16); ptr = ptr + 16; } /* get the device name */ oob->bt_name_len = *ptr++; oob->bt_name = g_try_malloc0(oob->bt_name_len+1); if (oob->bt_name) { memcpy(oob->bt_name, ptr, oob->bt_name_len); oob->bt_name[oob->bt_name_len+1] = 0; } ptr = ptr + oob->bt_name_len; } else { return -EINVAL; } if (pair == FALSE) return 0; /* check and get the default adapter */ oob->def_adapter = g_strdup(bt_def_oob_data.def_adapter); if (oob->def_adapter == NULL) { near_error("bt_get_default_adapter failed"); bt_eir_free(oob); return -EIO; } return bt_do_pairing(oob); } int __near_bluetooth_pair(void *data) { struct near_oob_data *oob = data; /* check and get the default adapter */ oob->def_adapter = g_strdup(bt_def_oob_data.def_adapter); if (oob->bt_name == NULL) { near_error("bt_get_default_adapter failed: %d", -EIO); bt_eir_free(oob); return -EIO; } return bt_do_pairing(oob); } /* This function is synchronous as oob datas change on each session */ static int bt_sync_oob_readlocaldata(DBusConnection *conn, char *adapter_path, char *spair_hash, char *spair_randomizer) { DBusMessage *message, *reply; DBusError error; int hash_len, rndm_len; message = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_path, OOB_INTF, "ReadLocalData"); if (!message) return 0; dbus_error_init(&error); reply = dbus_connection_send_with_reply_and_block(conn, message, -1, &error); dbus_message_unref(message); if (!reply) { if (dbus_error_is_set(&error) == TRUE) { near_error("%s", error.message); dbus_error_free(&error); } else { near_error("Failed to set property"); } return 0; } if (dbus_message_get_args(reply, NULL, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, spair_hash, &hash_len, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, spair_randomizer, &rndm_len, DBUS_TYPE_INVALID) == FALSE) goto done; if ((hash_len != OOB_SP_SIZE) || (rndm_len != OOB_SP_SIZE)) { DBG("no OOB data found !"); goto done; } dbus_message_unref(reply); DBG("OOB data found"); return hash_len; done: dbus_message_unref(reply); return 0; } /* * External API to get bt properties * Prepare a "real" oob datas block * mime_props is a bitmask we use to add or not specific fields in the * oob frame (e.g.: OOB keys) * */ struct carrier_data *__near_bluetooth_local_get_properties(uint16_t mime_props) { struct carrier_data *data = NULL; uint8_t offset; char hash[OOB_SP_SIZE]; char random[OOB_SP_SIZE]; /* Check adapter datas */ if (bt_def_oob_data.def_adapter == NULL) { near_error("No bt adapter info"); goto fail; } data = g_try_malloc0(sizeof(*data)); if (data == NULL) goto fail; data->size = sizeof(uint16_t) /* stored oob size */ + BT_ADDRESS_SIZE; /* device address */ offset = sizeof(uint16_t); /* Skip size...will be filled later */ /* Now prepare data frame */ memcpy(data->data + offset, bt_def_oob_data.bd_addr, BT_ADDRESS_SIZE); offset += BT_ADDRESS_SIZE; /* CoD */ data->size += COD_SIZE + EIR_HEADER_LEN; data->data[offset++] = COD_SIZE + EIR_SIZE_LEN; data->data[offset++] = EIR_CLASS_OF_DEVICE; memcpy(data->data + offset, (uint8_t *)&bt_def_oob_data.class_of_device, COD_SIZE); offset += COD_SIZE; /* * The following data are generated dynamically so we have to read the * local oob data. Only add OOB pairing keys if needed. */ if ((mime_props & OOB_PROPS_SP) != 0 && bt_sync_oob_readlocaldata(bt_conn, bt_def_oob_data.def_adapter, hash, random) == OOB_SP_SIZE) { data->size += 2 * (OOB_SP_SIZE + EIR_HEADER_LEN); /* OOB datas */ if (hash != NULL) { data->data[offset++] = OOB_SP_SIZE + EIR_SIZE_LEN; data->data[offset++] = EIR_SP_HASH; memcpy(data->data + offset, hash, OOB_SP_SIZE); offset += OOB_SP_SIZE; } if (random != NULL) { data->data[offset++] = OOB_SP_SIZE + EIR_SIZE_LEN; data->data[offset++] = EIR_SP_RANDOMIZER; memcpy(data->data + offset, random, OOB_SP_SIZE); offset += OOB_SP_SIZE; } } /* bt name */ if (bt_def_oob_data.bt_name != NULL) { int name_len; data->size += EIR_HEADER_LEN; if (data->size + bt_def_oob_data.bt_name_len > EIR_SIZE_MAX) { name_len = EIR_SIZE_MAX - data->size; data->data[offset++] = name_len + EIR_SIZE_LEN; /* EIR data type */ data->data[offset++] = EIR_NAME_COMPLETE; } else { name_len = bt_def_oob_data.bt_name_len; data->data[offset++] = name_len + EIR_SIZE_LEN; /* EIR data type */ data->data[offset++] = EIR_NAME_SHORT; } data->size += name_len; memcpy(data->data + offset, bt_def_oob_data.bt_name, name_len); offset += name_len; } data->data[0] = data->size ; if (bt_def_oob_data.powered == TRUE) data->state = CPS_ACTIVE; else data->state = CPS_INACTIVE; return data; fail: g_free(data); return NULL; } /* BT adapter removed handler */ static gboolean bt_adapter_removed(DBusConnection *conn, DBusMessage *message, void *user_data) { DBusMessageIter iter; struct near_oob_data *bt_props = user_data; const char *adapter_path; DBG(""); if (bt_props->def_adapter == NULL) return TRUE; g_dbus_remove_watch(bt_conn, adapter_props_watch); adapter_props_watch = 0; if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; dbus_message_iter_get_basic(&iter, &adapter_path); if (g_strcmp0(adapter_path, bt_props->def_adapter) == 0) { near_info("Remove the default adapter [%s]", adapter_path); __bt_eir_free(bt_props); bt_props->def_adapter = NULL; } return TRUE; } /* BT default adapter changed handler */ static gboolean bt_default_adapter_changed(DBusConnection *conn, DBusMessage *message, void *user_data) { struct near_oob_data *bt_props = user_data; DBusMessageIter iter; const char *adapter_path; DBG(""); if (dbus_message_iter_init(message, &iter) == FALSE) return TRUE; g_dbus_remove_watch(bt_conn, adapter_props_watch); adapter_props_watch = 0; dbus_message_iter_get_basic(&iter, &adapter_path); DBG("New default adapter [%s]", adapter_path); /* Disable the old one */ __bt_eir_free(bt_props); bt_props->def_adapter = NULL; /* Refresh */ bt_refresh_adapter_props(conn, user_data); return TRUE; } static void bt_dbus_disconnect_cb(DBusConnection *conn, void *user_data) { near_error("D-Bus disconnect (BT)"); bt_conn = NULL; } static gboolean register_bluez(gpointer user_data) { DBG(""); register_bluez_timer = 0; removed_watch = g_dbus_add_signal_watch(bt_conn, NULL, NULL, MANAGER_INTF, ADAPTER_REMOVED, bt_adapter_removed, &bt_def_oob_data, NULL); adapter_watch = g_dbus_add_signal_watch(bt_conn, NULL, NULL, MANAGER_INTF, DEFAULT_ADAPTER_CHANGED, bt_default_adapter_changed, &bt_def_oob_data, NULL); if (removed_watch == 0 || adapter_watch == 0) { near_error("BlueZ event handlers failed to register."); g_dbus_remove_watch(bt_conn, removed_watch); g_dbus_remove_watch(bt_conn, adapter_watch); return FALSE; } if (bt_refresh_adapter_props(bt_conn, user_data) < 0) near_error("Failed to get BT adapter properties"); return FALSE; } static void bt_connect(DBusConnection *conn, void *data) { DBG("connection %p with %p", conn, data); if (__near_agent_handover_registered(HO_AGENT_BT) == TRUE) { DBG("Agent already registered"); return; } /* * BlueZ 5 will register itself as HandoverAgent, give it some time * to do it before going legacy way. */ register_bluez_timer = g_timeout_add_seconds(AGENT_REGISTER_TIMEOUT, register_bluez, data); } static void bt_disconnect(DBusConnection *conn, void *user_data) { DBG("%p", conn); /* If timer is running no BlueZ watchers were registered yet */ if (register_bluez_timer > 0) { g_source_remove(register_bluez_timer); register_bluez_timer = 0; return; } __bt_eir_free(user_data); g_dbus_remove_watch(bt_conn, removed_watch); removed_watch = 0; g_dbus_remove_watch(bt_conn, adapter_watch); adapter_watch = 0; g_dbus_remove_watch(bt_conn, adapter_props_watch); adapter_props_watch = 0; } static int bt_prepare_handlers(DBusConnection *conn) { if (__near_agent_handover_registered(HO_AGENT_BT) == TRUE) return 0; watch = g_dbus_add_service_watch(bt_conn, BLUEZ_SERVICE, bt_connect, bt_disconnect, &bt_def_oob_data, NULL); if (watch == 0) { near_error("BlueZ service watch handler failed to register."); g_dbus_remove_watch(bt_conn, watch); return -EIO; } return 0; } void __near_bluetooth_legacy_start(void) { DBG(""); bt_prepare_handlers(bt_conn); } void __near_bluetooth_legacy_stop(void) { DBG(""); g_dbus_remove_watch(bt_conn, watch); watch = 0; bt_disconnect(bt_conn, &bt_def_oob_data); } /* Bluetooth exiting function */ void __near_bluetooth_cleanup(void) { DBG(""); if (bt_conn == NULL) return; __near_bluetooth_legacy_stop(); dbus_connection_unref(bt_conn); } /* * Bluetooth initialization function. * Allocate bt local settings storage * and setup event handlers */ int __near_bluetooth_init(void) { DBusError err; DBG(""); dbus_error_init(&err); /* save the dbus connection */ bt_conn = near_dbus_get_connection(); if (bt_conn == NULL) { if (dbus_error_is_set(&err) == TRUE) { near_error("%s", err.message); dbus_error_free(&err); } else near_error("Can't register with system bus\n"); return -EIO; } /* dbus disconnect callback */ g_dbus_set_disconnect_function(bt_conn, bt_dbus_disconnect_cb, NULL, NULL); /* Set bluez event handlers */ return bt_prepare_handlers(bt_conn); }