blob: 3e67f1c88b8411059150ab05cad7dbbdae1696b0 [file] [log] [blame]
/*
*
* 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 <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <gdbus.h>
#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 */
bool powered;
bool pairable;
bool 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) {
g_free(oob->def_adapter);
oob->def_adapter = NULL;
}
if (oob->bd_addr) {
g_free(oob->bd_addr);
oob->bd_addr = NULL;
}
if (oob->bt_name) {
g_free(oob->bt_name);
oob->bt_name = NULL;
}
if (oob->spair_hash) {
g_free(oob->spair_hash);
oob->spair_hash = NULL;
}
if (oob->spair_randomizer) {
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) {
near_error("Unable to allocate new D-Bus %s message", method);
return -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) {
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)
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)
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))
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")) {
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)
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")) {
dbus_message_iter_get_basic(&value, &data);
oob->bt_name = g_strdup(data);
if (oob->bt_name) {
oob->bt_name_len = strlen(oob->bt_name);
DBG("local name: %s", oob->bt_name);
}
} else if (g_str_equal(key, "Class")) {
dbus_message_iter_get_basic(&value, &idata);
oob->class_of_device = idata;
} else if (g_str_equal(key, "Powered")) {
dbus_message_iter_get_basic(&value, &idata);
oob->powered = idata;
} else if (g_str_equal(key, "Discoverable")) {
dbus_message_iter_get_basic(&value, &idata);
oob->discoverable = idata;
} else if (g_str_equal(key, "Pairable")) {
dbus_message_iter_get_basic(&value, &idata);
oob->pairable = idata;
} else if (g_str_equal(key, "UUIDs")) {
oob->uuids_len = sizeof(value);
oob->uuids = g_try_malloc0(oob->uuids_len);
if (!oob->uuids)
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))
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")) {
const char *name;
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
return TRUE;
dbus_message_iter_get_basic(&var, &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)
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")) {
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")) {
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)
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)
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))
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("Could not get Bluetooth default adapter %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) {
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,
bool 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)
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 {
bt_eir_free(oob);
return -EINVAL;
}
if (!pair) {
bt_eir_free(oob);
return 0;
}
/* check and get the default adapter */
oob->def_adapter = g_strdup(bt_def_oob_data.def_adapter);
if (!oob->def_adapter) {
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) {
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)) {
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))
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) {
near_error("No bt adapter info");
goto fail;
}
data = g_try_malloc0(sizeof(*data));
if (!data)
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 */
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;
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) {
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)
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)
return TRUE;
g_dbus_remove_watch(bt_conn, adapter_props_watch);
adapter_props_watch = 0;
if (!dbus_message_iter_init(message, &iter))
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))
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)) {
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))
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)
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) {
if (dbus_error_is_set(&err)) {
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);
}