blob: 51a31fec797865b6280c21c0630f63c6bafd1b81 [file] [log] [blame]
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*
* Copyright (C) 2013-2014 Intel Corporation. All rights reserved.
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; 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 <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <glib.h>
#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "lib/mgmt.h"
#include "lib/uuid.h"
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
#include "src/shared/queue.h"
#include "src/eir.h"
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
#include "src/sdp-client.h"
#include "src/sdpd.h"
#include "src/log.h"
#include "hal-msg.h"
#include "ipc-common.h"
#include "ipc.h"
#include "utils.h"
#include "bluetooth.h"
#define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode"
#define SETTINGS_FILE ANDROID_STORAGEDIR"/settings"
#define DEVICES_FILE ANDROID_STORAGEDIR"/devices"
#define CACHE_FILE ANDROID_STORAGEDIR"/cache"
#define ADAPTER_MAJOR_CLASS 0x02 /* Phone */
#define ADAPTER_MINOR_CLASS 0x03 /* Smartphone */
/* Default to DisplayYesNo */
#define DEFAULT_IO_CAPABILITY 0x01
/* Default discoverable timeout 120sec as in Android */
#define DEFAULT_DISCOVERABLE_TIMEOUT 120
#define DEVICES_CACHE_MAX 300
#define BASELEN_PROP_CHANGED (sizeof(struct hal_ev_adapter_props_changed) \
+ sizeof(struct hal_property))
#define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \
+ sizeof(struct hal_property))
#define SCAN_TYPE_NONE 0
#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
#define BDADDR_LE (BDADDR_LE_RANDOM | BDADDR_LE_PUBLIC)
struct device {
bdaddr_t bdaddr;
uint8_t bdaddr_type;
bdaddr_t rpa;
uint8_t rpa_type;
bool le;
bool bredr;
bool pairing;
bool bredr_paired;
bool bredr_bonded;
bool le_paired;
bool le_bonded;
bool in_white_list;
bool connected;
char *name;
char *friendly_name;
uint32_t class;
int32_t rssi;
time_t bredr_seen;
time_t le_seen;
GSList *uuids;
bool found; /* if device is found in current discovery session */
unsigned int confirm_id; /* mgtm command id if command pending */
bool valid_remote_csrk;
bool remote_csrk_auth;
uint8_t remote_csrk[16];
uint32_t remote_sign_cnt;
bool valid_local_csrk;
bool local_csrk_auth;
uint8_t local_csrk[16];
uint32_t local_sign_cnt;
uint16_t gatt_ccc;
};
struct browse_req {
bdaddr_t bdaddr;
GSList *uuids;
int search_uuid;
int reconnect_attempt;
};
static struct {
uint16_t index;
bdaddr_t bdaddr;
uint32_t dev_class;
char *name;
uint8_t max_advert_instance;
uint8_t rpa_offload_supported;
uint8_t max_irk_list_size;
uint8_t max_scan_filters_supported;
uint16_t scan_result_storage_size;
uint8_t activity_energy_info_supported;
uint32_t current_settings;
uint32_t supported_settings;
bool le_scanning;
uint8_t cur_discovery_type;
uint8_t exp_discovery_type;
uint32_t discoverable_timeout;
GSList *uuids;
} adapter = {
.index = MGMT_INDEX_NONE,
.dev_class = 0,
.name = NULL,
.max_advert_instance = 0,
.rpa_offload_supported = 0,
.max_irk_list_size = 0,
.max_scan_filters_supported = 0,
.scan_result_storage_size = 0,
.activity_energy_info_supported = 0,
.current_settings = 0,
.supported_settings = 0,
.cur_discovery_type = SCAN_TYPE_NONE,
.exp_discovery_type = SCAN_TYPE_NONE,
.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT,
.uuids = NULL,
};
static const uint16_t uuid_list[] = {
L2CAP_UUID,
PNP_INFO_SVCLASS_ID,
PUBLIC_BROWSE_GROUP,
0
};
static uint16_t option_index = MGMT_INDEX_NONE;
static struct mgmt *mgmt_if = NULL;
static GSList *bonded_devices = NULL;
static GSList *cached_devices = NULL;
static bt_le_device_found gatt_device_found_cb = NULL;
static bt_le_discovery_stopped gatt_discovery_stopped_cb = NULL;
/* This list contains addresses which are asked for records */
static GSList *browse_reqs;
static struct ipc *hal_ipc = NULL;
static bool kernel_conn_control = false;
static struct queue *unpaired_cb_list = NULL;
static struct queue *paired_cb_list = NULL;
static void get_device_android_addr(struct device *dev, uint8_t *addr)
{
/*
* If RPA is set it means that IRK was received and ID address is being
* used. Android Framework is still using old RPA and it needs to be
* used in notifications.
*/
if (bacmp(&dev->rpa, BDADDR_ANY))
bdaddr2android(&dev->rpa, addr);
else
bdaddr2android(&dev->bdaddr, addr);
}
static void mgmt_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
info("%s%s", prefix, str);
}
static void store_adapter_config(void)
{
GKeyFile *key_file;
gsize length = 0;
char addr[18];
char *data;
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
ba2str(&adapter.bdaddr, addr);
g_key_file_set_string(key_file, "General", "Address", addr);
if (adapter.name)
g_key_file_set_string(key_file, "General", "Name",
adapter.name);
g_key_file_set_integer(key_file, "General", "DiscoverableTimeout",
adapter.discoverable_timeout);
data = g_key_file_to_data(key_file, &length, NULL);
g_file_set_contents(SETTINGS_FILE, data, length, NULL);
g_free(data);
g_key_file_free(key_file);
}
static void load_adapter_config(void)
{
GError *gerr = NULL;
GKeyFile *key_file;
char *str;
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, SETTINGS_FILE, 0, NULL);
str = g_key_file_get_string(key_file, "General", "Address", NULL);
if (!str) {
g_key_file_free(key_file);
return;
}
str2ba(str, &adapter.bdaddr);
g_free(str);
adapter.name = g_key_file_get_string(key_file, "General", "Name", NULL);
adapter.discoverable_timeout = g_key_file_get_integer(key_file,
"General", "DiscoverableTimeout", &gerr);
if (gerr) {
adapter.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT;
g_clear_error(&gerr);
}
g_key_file_free(key_file);
}
static void store_device_info(struct device *dev, const char *path)
{
GKeyFile *key_file;
char addr[18];
gsize length = 0;
char **uuids = NULL;
char *str;
ba2str(&dev->bdaddr, addr);
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, path, 0, NULL);
g_key_file_set_boolean(key_file, addr, "BREDR", dev->bredr);
if (dev->le)
g_key_file_set_integer(key_file, addr, "AddressType",
dev->bdaddr_type);
g_key_file_set_string(key_file, addr, "Name", dev->name);
if (dev->friendly_name)
g_key_file_set_string(key_file, addr, "FriendlyName",
dev->friendly_name);
else
g_key_file_remove_key(key_file, addr, "FriendlyName", NULL);
if (dev->class)
g_key_file_set_integer(key_file, addr, "Class", dev->class);
else
g_key_file_remove_key(key_file, addr, "Class", NULL);
if (dev->bredr_seen > dev->le_seen)
g_key_file_set_integer(key_file, addr, "Timestamp",
dev->bredr_seen);
else
g_key_file_set_integer(key_file, addr, "Timestamp",
dev->le_seen);
if (dev->uuids) {
GSList *l;
int i;
uuids = g_new0(char *, g_slist_length(dev->uuids) + 1);
for (i = 0, l = dev->uuids; l; l = g_slist_next(l), i++) {
int j;
uint8_t *u = l->data;
char *uuid_str = g_malloc0(33);
for (j = 0; j < 16; j++)
sprintf(uuid_str + (j * 2), "%2.2X", u[j]);
uuids[i] = uuid_str;
}
g_key_file_set_string_list(key_file, addr, "Services",
(const char **)uuids, i);
} else {
g_key_file_remove_key(key_file, addr, "Services", NULL);
}
str = g_key_file_to_data(key_file, &length, NULL);
g_file_set_contents(path, str, length, NULL);
g_free(str);
g_key_file_free(key_file);
g_strfreev(uuids);
}
static void remove_device_info(struct device *dev, const char *path)
{
GKeyFile *key_file;
gsize length = 0;
char addr[18];
char *str;
ba2str(&dev->bdaddr, addr);
key_file = g_key_file_new();
g_key_file_load_from_file(key_file, path, 0, NULL);
g_key_file_remove_group(key_file, addr, NULL);
str = g_key_file_to_data(key_file, &length, NULL);
g_file_set_contents(path, str, length, NULL);
g_free(str);
g_key_file_free(key_file);
}
static int device_match(gconstpointer a, gconstpointer b)
{
const struct device *dev = a;
const bdaddr_t *bdaddr = b;
/* Android is using RPA even if IRK was received and ID addr resolved */
if (!bacmp(&dev->rpa, bdaddr))
return 0;
return bacmp(&dev->bdaddr, bdaddr);
}
static struct device *find_device(const bdaddr_t *bdaddr)
{
GSList *l;
l = g_slist_find_custom(bonded_devices, bdaddr, device_match);
if (l)
return l->data;
l = g_slist_find_custom(cached_devices, bdaddr, device_match);
if (l)
return l->data;
return NULL;
}
static void free_device(struct device *dev)
{
if (dev->confirm_id)
mgmt_cancel(mgmt_if, dev->confirm_id);
g_free(dev->name);
g_free(dev->friendly_name);
g_slist_free_full(dev->uuids, g_free);
g_free(dev);
}
static void cache_device(struct device *new_dev)
{
struct device *dev;
GSList *l;
l = g_slist_find(cached_devices, new_dev);
if (l) {
cached_devices = g_slist_remove(cached_devices, new_dev);
goto cache;
}
if (g_slist_length(cached_devices) < DEVICES_CACHE_MAX)
goto cache;
l = g_slist_last(cached_devices);
dev = l->data;
cached_devices = g_slist_remove(cached_devices, dev);
remove_device_info(dev, CACHE_FILE);
free_device(dev);
cache:
cached_devices = g_slist_prepend(cached_devices, new_dev);
store_device_info(new_dev, CACHE_FILE);
}
static struct device *create_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type)
{
struct device *dev;
char addr[18];
ba2str(bdaddr, addr);
DBG("%s", addr);
dev = g_new0(struct device, 1);
bacpy(&dev->bdaddr, bdaddr);
if (bdaddr_type == BDADDR_BREDR) {
dev->bredr = true;
dev->bredr_seen = time(NULL);
} else {
dev->le = true;
dev->bdaddr_type = bdaddr_type;
dev->le_seen = time(NULL);
}
/*
* Use address for name, will be change if one is present
* eg. in EIR or set by set_property.
*/
dev->name = g_strdup(addr);
return dev;
}
static struct device *get_device(const bdaddr_t *bdaddr, uint8_t type)
{
struct device *dev;
dev = find_device(bdaddr);
if (dev)
return dev;
dev = create_device(bdaddr, type);
cache_device(dev);
return dev;
}
static struct device *find_device_android(const uint8_t *addr)
{
bdaddr_t bdaddr;
android2bdaddr(addr, &bdaddr);
return find_device(&bdaddr);
}
static struct device *get_device_android(const uint8_t *addr)
{
bdaddr_t bdaddr;
android2bdaddr(addr, &bdaddr);
return get_device(&bdaddr, BDADDR_BREDR);
}
static void send_adapter_property(uint8_t type, uint16_t len, const void *val)
{
uint8_t buf[BASELEN_PROP_CHANGED + len];
struct hal_ev_adapter_props_changed *ev = (void *) buf;
ev->status = HAL_STATUS_SUCCESS;
ev->num_props = 1;
ev->props[0].type = type;
ev->props[0].len = len;
memcpy(ev->props[0].val, val, len);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), buf);
}
static void adapter_name_changed(const uint8_t *name)
{
/* Android expects string value without NULL terminator */
send_adapter_property(HAL_PROP_ADAPTER_NAME,
strlen((const char *) name), name);
}
static void adapter_set_name(const uint8_t *name)
{
if (!g_strcmp0(adapter.name, (const char *) name))
return;
DBG("%s", name);
g_free(adapter.name);
adapter.name = g_strdup((const char *) name);
store_adapter_config();
adapter_name_changed(name);
}
static void mgmt_local_name_changed_event(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_cp_set_local_name *rp = param;
if (length < sizeof(*rp)) {
error("Wrong size of local name changed parameters");
return;
}
adapter_set_name(rp->name);
/* TODO Update services if needed */
}
static void powered_changed(void)
{
struct hal_ev_adapter_state_changed ev;
ev.state = (adapter.current_settings & MGMT_SETTING_POWERED) ?
HAL_POWER_ON : HAL_POWER_OFF;
DBG("%u", ev.state);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev);
}
static uint8_t settings2scan_mode(void)
{
bool connectable, discoverable;
connectable = adapter.current_settings & MGMT_SETTING_CONNECTABLE;
discoverable = adapter.current_settings & MGMT_SETTING_DISCOVERABLE;
if (connectable && discoverable)
return HAL_ADAPTER_SCAN_MODE_CONN_DISC;
if (connectable)
return HAL_ADAPTER_SCAN_MODE_CONN;
return HAL_ADAPTER_SCAN_MODE_NONE;
}
static void scan_mode_changed(void)
{
uint8_t mode;
mode = settings2scan_mode();
DBG("mode %u", mode);
send_adapter_property(HAL_PROP_ADAPTER_SCAN_MODE, sizeof(mode), &mode);
}
static void adapter_class_changed(void)
{
send_adapter_property(HAL_PROP_ADAPTER_CLASS, sizeof(adapter.dev_class),
&adapter.dev_class);
}
static void settings_changed(uint32_t settings)
{
uint32_t changed_mask;
uint32_t scan_mode_mask;
changed_mask = adapter.current_settings ^ settings;
adapter.current_settings = settings;
DBG("0x%08x", changed_mask);
if (changed_mask & MGMT_SETTING_POWERED)
powered_changed();
scan_mode_mask = MGMT_SETTING_CONNECTABLE |
MGMT_SETTING_DISCOVERABLE;
/*
* Only when powered, the connectable and discoverable
* state changes should be communicated.
*/
if (adapter.current_settings & MGMT_SETTING_POWERED)
if (changed_mask & scan_mode_mask)
scan_mode_changed();
}
static void new_settings_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
uint32_t settings;
if (length < sizeof(settings)) {
error("Wrong size of new settings parameters");
return;
}
settings = get_le32(param);
DBG("settings: 0x%8.8x -> 0x%8.8x", adapter.current_settings,
settings);
if (settings == adapter.current_settings)
return;
settings_changed(settings);
}
static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_cod *rp = param;
uint32_t dev_class;
if (length < sizeof(*rp)) {
error("Wrong size of class of device changed parameters");
return;
}
dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16);
if (dev_class == adapter.dev_class)
return;
DBG("Class: 0x%06x", dev_class);
adapter.dev_class = dev_class;
adapter_class_changed();
/* TODO: Gatt attrib set*/
}
void bt_store_gatt_ccc(const bdaddr_t *dst, uint16_t value)
{
struct device *dev;
GKeyFile *key_file;
gsize length = 0;
char addr[18];
char *data;
dev = find_device(dst);
if (!dev)
return;
key_file = g_key_file_new();
if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
g_key_file_free(key_file);
return;
}
ba2str(&dev->bdaddr, addr);
DBG("%s Gatt CCC %d", addr, value);
g_key_file_set_integer(key_file, addr, "GattCCC", value);
data = g_key_file_to_data(key_file, &length, NULL);
g_file_set_contents(DEVICES_FILE, data, length, NULL);
g_free(data);
g_key_file_free(key_file);
dev->gatt_ccc = value;
}
uint16_t bt_get_gatt_ccc(const bdaddr_t *addr)
{
struct device *dev;
dev = find_device(addr);
if (!dev)
return 0;
return dev->gatt_ccc;
}
static void store_link_key(const bdaddr_t *dst, const uint8_t *key,
uint8_t type, uint8_t pin_length)
{
GKeyFile *key_file;
char key_str[33];
gsize length = 0;
char addr[18];
char *data;
int i;
key_file = g_key_file_new();
if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
g_key_file_free(key_file);
return;
}
ba2str(dst, addr);
DBG("%s type %u pin_len %u", addr, type, pin_length);
for (i = 0; i < 16; i++)
sprintf(key_str + (i * 2), "%2.2X", key[i]);
g_key_file_set_string(key_file, addr, "LinkKey", key_str);
g_key_file_set_integer(key_file, addr, "LinkKeyType", type);
g_key_file_set_integer(key_file, addr, "LinkKeyPinLength", pin_length);
data = g_key_file_to_data(key_file, &length, NULL);
g_file_set_contents(DEVICES_FILE, data, length, NULL);
g_free(data);
g_key_file_free(key_file);
}
static void send_bond_state_change(struct device *dev, uint8_t status,
uint8_t state)
{
struct hal_ev_bond_state_changed ev;
ev.status = status;
ev.state = state;
get_device_android_addr(dev, ev.bdaddr);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev);
}
static void update_bredr_state(struct device *dev, bool pairing, bool paired,
bool bonded)
{
if (pairing == dev->pairing && paired == dev->bredr_paired &&
bonded == dev->bredr_bonded)
return;
/* avoid unpairing device on incoming pairing request */
if (pairing && dev->bredr_paired)
goto done;
/* avoid unpairing device if pairing failed */
if (!pairing && !paired && dev->pairing && dev->bredr_paired)
goto done;
if (paired && !dev->le_paired && !dev->bredr_paired) {
cached_devices = g_slist_remove(cached_devices, dev);
bonded_devices = g_slist_prepend(bonded_devices, dev);
remove_device_info(dev, CACHE_FILE);
store_device_info(dev, DEVICES_FILE);
} else if (!paired && !dev->le_paired) {
bonded_devices = g_slist_remove(bonded_devices, dev);
remove_device_info(dev, DEVICES_FILE);
cache_device(dev);
}
dev->bredr_paired = paired;
if (dev->bredr_paired)
dev->bredr_bonded = dev->bredr_bonded || bonded;
else
dev->bredr_bonded = false;
done:
dev->pairing = pairing;
}
static void update_le_state(struct device *dev, bool pairing, bool paired,
bool bonded)
{
if (pairing == dev->pairing && paired == dev->le_paired &&
bonded == dev->le_bonded)
return;
/* avoid unpairing device on incoming pairing request */
if (pairing && dev->le_paired)
goto done;
/* avoid unpairing device if pairing failed */
if (!pairing && !paired && dev->pairing && dev->le_paired)
goto done;
if (paired && !dev->bredr_paired && !dev->le_paired) {
cached_devices = g_slist_remove(cached_devices, dev);
bonded_devices = g_slist_prepend(bonded_devices, dev);
remove_device_info(dev, CACHE_FILE);
store_device_info(dev, DEVICES_FILE);
} else if (!paired && !dev->bredr_paired) {
bonded_devices = g_slist_remove(bonded_devices, dev);
remove_device_info(dev, DEVICES_FILE);
dev->valid_local_csrk = false;
dev->valid_remote_csrk = false;
dev->local_sign_cnt = 0;
dev->remote_sign_cnt = 0;
memset(dev->local_csrk, 0, sizeof(dev->local_csrk));
memset(dev->remote_csrk, 0, sizeof(dev->remote_csrk));
cache_device(dev);
}
dev->le_paired = paired;
if (dev->le_paired)
dev->le_bonded = dev->le_bonded || bonded;
else
dev->le_bonded = false;
done:
dev->pairing = pairing;
}
static uint8_t device_bond_state(struct device *dev)
{
if (dev->pairing)
return HAL_BOND_STATE_BONDING;
/*
* We are checking for paired here instead of bonded as HAL API is
* using BOND state also if there was no bonding pairing.
*/
if (dev->bredr_paired || dev->le_paired)
return HAL_BOND_STATE_BONDED;
return HAL_BOND_STATE_NONE;
}
static void update_bond_state(struct device *dev, uint8_t status,
uint8_t old_bond, uint8_t new_bond)
{
if (old_bond == new_bond)
return;
/*
* When internal bond state changes from bond to non-bond or other way,
* BfA needs to send bonding state to Android in the middle. Otherwise
* Android will not handle it correctly
*/
if ((old_bond == HAL_BOND_STATE_NONE &&
new_bond == HAL_BOND_STATE_BONDED) ||
(old_bond == HAL_BOND_STATE_BONDED &&
new_bond == HAL_BOND_STATE_NONE))
send_bond_state_change(dev, HAL_STATUS_SUCCESS,
HAL_BOND_STATE_BONDING);
send_bond_state_change(dev, status, new_bond);
}
static void send_paired_notification(void *data, void *user_data)
{
bt_paired_device_cb cb = data;
struct device *dev = user_data;
cb(&dev->bdaddr);
}
static void update_device_state(struct device *dev, uint8_t addr_type,
uint8_t status, bool pairing, bool paired,
bool bonded)
{
uint8_t old_bond, new_bond;
old_bond = device_bond_state(dev);
if (addr_type == BDADDR_BREDR)
update_bredr_state(dev, pairing, paired, bonded);
else
update_le_state(dev, pairing, paired, bonded);
new_bond = device_bond_state(dev);
update_bond_state(dev, status, old_bond, new_bond);
}
static void send_device_property(struct device *dev, uint8_t type,
uint16_t len, const void *val)
{
uint8_t buf[BASELEN_REMOTE_DEV_PROP + len];
struct hal_ev_remote_device_props *ev = (void *) buf;
ev->status = HAL_STATUS_SUCCESS;
get_device_android_addr(dev, ev->bdaddr);
ev->num_props = 1;
ev->props[0].type = type;
ev->props[0].len = len;
memcpy(ev->props[0].val, val, len);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_REMOTE_DEVICE_PROPS, sizeof(buf), buf);
}
static void send_device_uuids_notif(struct device *dev)
{
uint8_t buf[sizeof(uint128_t) * g_slist_length(dev->uuids)];
uint8_t *ptr = buf;
GSList *l;
for (l = dev->uuids; l; l = g_slist_next(l)) {
memcpy(ptr, l->data, sizeof(uint128_t));
ptr += sizeof(uint128_t);
}
send_device_property(dev, HAL_PROP_DEVICE_UUIDS, sizeof(buf), buf);
}
static void set_device_uuids(struct device *dev, GSList *uuids)
{
g_slist_free_full(dev->uuids, g_free);
dev->uuids = uuids;
if (dev->le_paired || dev->bredr_paired)
store_device_info(dev, DEVICES_FILE);
else
store_device_info(dev, CACHE_FILE);
send_device_uuids_notif(dev);
}
static void browse_req_free(struct browse_req *req)
{
g_slist_free_full(req->uuids, g_free);
g_free(req);
}
static int uuid_128_cmp(gconstpointer a, gconstpointer b)
{
return memcmp(a, b, sizeof(uint128_t));
}
static void update_records(struct browse_req *req, sdp_list_t *recs)
{
for (; recs; recs = recs->next) {
sdp_record_t *rec = (sdp_record_t *) recs->data;
sdp_list_t *svcclass = NULL;
uuid_t uuid128;
uuid_t *tmp;
uint8_t *new_uuid;
if (!rec)
break;
if (sdp_get_service_classes(rec, &svcclass) < 0)
continue;
if (!svcclass)
continue;
tmp = svcclass->data;
switch (tmp->type) {
case SDP_UUID16:
sdp_uuid16_to_uuid128(&uuid128, tmp);
break;
case SDP_UUID32:
sdp_uuid32_to_uuid128(&uuid128, tmp);
break;
case SDP_UUID128:
memcpy(&uuid128, tmp, sizeof(uuid_t));
break;
default:
sdp_list_free(svcclass, free);
continue;
}
new_uuid = g_malloc(16);/* size of 128 bit uuid */
memcpy(new_uuid, &uuid128.value.uuid128,
sizeof(uuid128.value.uuid128));
/* Check if uuid is already added */
if (g_slist_find_custom(req->uuids, new_uuid, uuid_128_cmp))
g_free(new_uuid);
else
req->uuids = g_slist_append(req->uuids, new_uuid);
sdp_list_free(svcclass, free);
}
}
static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
{
struct browse_req *req = user_data;
struct device *dev;
uuid_t uuid;
/*
* If we have a valid response and req->search_uuid == 2, then L2CAP
* UUID & PNP searching was successful -- we are done
*/
if (err < 0 || req->search_uuid == 2) {
if (err == -ECONNRESET && req->reconnect_attempt < 1) {
req->search_uuid--;
req->reconnect_attempt++;
} else {
goto done;
}
}
update_records(req, recs);
/* Search for mandatory uuids */
if (uuid_list[req->search_uuid]) {
sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
bt_search_service(&adapter.bdaddr, &req->bdaddr, &uuid,
browse_cb, user_data, NULL, 0);
return;
}
done:
dev = find_device(&req->bdaddr);
if (dev) {
set_device_uuids(dev, req->uuids);
req->uuids = NULL;
}
browse_reqs = g_slist_remove(browse_reqs, req);
browse_req_free(req);
}
static int req_cmp(gconstpointer a, gconstpointer b)
{
const struct browse_req *req = a;
const bdaddr_t *bdaddr = b;
return bacmp(&req->bdaddr, bdaddr);
}
static uint8_t browse_remote_sdp(const bdaddr_t *addr)
{
struct browse_req *req;
uuid_t uuid;
if (g_slist_find_custom(browse_reqs, addr, req_cmp))
return HAL_STATUS_SUCCESS;
req = g_new0(struct browse_req, 1);
bacpy(&req->bdaddr, addr);
sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
if (bt_search_service(&adapter.bdaddr,
&req->bdaddr, &uuid, browse_cb, req, NULL , 0) < 0) {
browse_req_free(req);
return HAL_STATUS_FAILED;
}
browse_reqs = g_slist_append(browse_reqs, req);
return HAL_STATUS_SUCCESS;
}
static void send_remote_sdp_rec_notify(bt_uuid_t *uuid, int channel,
char *name, uint8_t name_len,
uint8_t status, bdaddr_t *bdaddr)
{
struct hal_prop_device_service_rec *prop;
uint8_t buf[BASELEN_REMOTE_DEV_PROP + name_len + sizeof(*prop)];
struct hal_ev_remote_device_props *ev = (void *) buf;
size_t prop_len = sizeof(*prop) + name_len;
memset(buf, 0, sizeof(buf));
if (uuid && status == HAL_STATUS_SUCCESS) {
prop = (void *) &ev->props[0].val;
prop->name_len = name_len;
prop->channel = (uint16_t)channel;
memcpy(prop->name, name, name_len);
memcpy(prop->uuid, &uuid->value.u128, sizeof(prop->uuid));
}
ev->num_props = 1;
ev->status = status;
ev->props[0].len = prop_len;
bdaddr2android(bdaddr, ev->bdaddr);
ev->props[0].type = HAL_PROP_DEVICE_SERVICE_REC;
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_REMOTE_DEVICE_PROPS,
sizeof(buf), buf);
}
static void find_remote_sdp_rec_cb(sdp_list_t *recs, int err,
gpointer user_data)
{
bdaddr_t *addr = user_data;
uint8_t name_len;
uint8_t status;
char name_buf[256];
int channel;
bt_uuid_t uuid;
uuid_t uuid128_sdp;
sdp_list_t *protos;
sdp_record_t *sdp_rec;
if (err < 0) {
error("error while search remote sdp records");
status = HAL_STATUS_FAILED;
send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr);
goto done;
}
if (!recs) {
info("No service records found on remote");
status = HAL_STATUS_SUCCESS;
send_remote_sdp_rec_notify(NULL, 0, NULL, 0, status, addr);
goto done;
}
for ( ; recs; recs = recs->next) {
sdp_rec = recs->data;
switch (sdp_rec->svclass.type) {
case SDP_UUID16:
sdp_uuid16_to_uuid128(&uuid128_sdp,
&sdp_rec->svclass);
break;
case SDP_UUID32:
sdp_uuid32_to_uuid128(&uuid128_sdp,
&sdp_rec->svclass);
break;
case SDP_UUID128:
break;
default:
error("wrong sdp uuid type");
goto done;
}
if (!sdp_get_access_protos(sdp_rec, &protos)) {
channel = sdp_get_proto_port(protos, RFCOMM_UUID);
sdp_list_foreach(protos,
(sdp_list_func_t) sdp_list_free,
NULL);
sdp_list_free(protos, NULL);
} else
channel = -1;
if (channel < 0) {
error("can't get channel for sdp record");
channel = 0;
}
if (!sdp_get_service_name(sdp_rec, name_buf, sizeof(name_buf)))
name_len = strlen(name_buf);
else
name_len = 0;
uuid.type = BT_UUID128;
memcpy(&uuid.value.u128, uuid128_sdp.value.uuid128.data,
sizeof(uuid.value.u128));
status = HAL_STATUS_SUCCESS;
send_remote_sdp_rec_notify(&uuid, channel, name_buf, name_len,
status, addr);
}
done:
g_free(addr);
}
static uint8_t find_remote_sdp_rec(const bdaddr_t *addr,
const uint8_t *find_uuid)
{
bdaddr_t *bdaddr;
uuid_t uuid;
/* from android we always get full 128bit length uuid */
sdp_uuid128_create(&uuid, find_uuid);
bdaddr = g_new(bdaddr_t, 1);
if (!bdaddr)
return HAL_STATUS_NOMEM;
memcpy(bdaddr, addr, sizeof(*bdaddr));
if (bt_search_service(&adapter.bdaddr, addr, &uuid,
find_remote_sdp_rec_cb, bdaddr, NULL, 0) < 0) {
g_free(bdaddr);
return HAL_STATUS_FAILED;
}
return HAL_STATUS_SUCCESS;
}
static void new_link_key_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_new_link_key *ev = param;
const struct mgmt_addr_info *addr = &ev->key.addr;
struct device *dev;
char dst[18];
if (length < sizeof(*ev)) {
error("Too small new link key event");
return;
}
ba2str(&addr->bdaddr, dst);
DBG("new key for %s type %u pin_len %u",
dst, ev->key.type, ev->key.pin_len);
if (ev->key.pin_len > 16) {
error("Invalid PIN length (%u) in new_key event",
ev->key.pin_len);
return;
}
dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type);
if (!dev)
return;
update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
true, !!ev->store_hint);
if (ev->store_hint) {
const struct mgmt_link_key_info *key = &ev->key;
store_link_key(&addr->bdaddr, key->val, key->type,
key->pin_len);
}
browse_remote_sdp(&addr->bdaddr);
}
static uint8_t get_device_name(struct device *dev)
{
send_device_property(dev, HAL_PROP_DEVICE_NAME,
strlen(dev->name), dev->name);
return HAL_STATUS_SUCCESS;
}
static void pin_code_request_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_pin_code_request *ev = param;
struct hal_ev_pin_request hal_ev;
struct device *dev;
char dst[18];
if (length < sizeof(*ev)) {
error("Too small PIN code request event");
return;
}
ba2str(&ev->addr.bdaddr, dst);
dev = get_device(&ev->addr.bdaddr, BDADDR_BREDR);
/*
* Workaround for Android Bluetooth.apk issue: send remote
* device property
*/
get_device_name(dev);
update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
false, false);
DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure);
/* Name already sent in remote device prop */
memset(&hal_ev, 0, sizeof(hal_ev));
get_device_android_addr(dev, hal_ev.bdaddr);
hal_ev.class_of_dev = dev->class;
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST,
sizeof(hal_ev), &hal_ev);
}
static void send_ssp_request(struct device *dev, uint8_t variant,
uint32_t passkey)
{
struct hal_ev_ssp_request ev;
memset(&ev, 0, sizeof(ev));
get_device_android_addr(dev, ev.bdaddr);
memcpy(ev.name, dev->name, strlen(dev->name));
ev.class_of_dev = dev->class;
ev.pairing_variant = variant;
ev.passkey = passkey;
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
sizeof(ev), &ev);
}
static void user_confirm_request_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_user_confirm_request *ev = param;
struct device *dev;
char dst[18];
if (length < sizeof(*ev)) {
error("Too small user confirm request event");
return;
}
ba2str(&ev->addr.bdaddr, dst);
DBG("%s confirm_hint %u", dst, ev->confirm_hint);
dev = get_device(&ev->addr.bdaddr, ev->addr.type);
if (!dev)
return;
update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
false, false);
if (ev->confirm_hint)
send_ssp_request(dev, HAL_SSP_VARIANT_CONSENT, 0);
else
send_ssp_request(dev, HAL_SSP_VARIANT_CONFIRM, ev->value);
}
static void user_passkey_request_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_user_passkey_request *ev = param;
struct device *dev;
char dst[18];
if (length < sizeof(*ev)) {
error("Too small passkey request event");
return;
}
ba2str(&ev->addr.bdaddr, dst);
DBG("%s", dst);
dev = get_device(&ev->addr.bdaddr, ev->addr.type);
if (!dev)
return;
update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
false, false);
send_ssp_request(dev, HAL_SSP_VARIANT_ENTRY, 0);
}
static void user_passkey_notify_callback(uint16_t index, uint16_t length,
const void *param,
void *user_data)
{
const struct mgmt_ev_passkey_notify *ev = param;
struct device *dev;
char dst[18];
if (length < sizeof(*ev)) {
error("Too small passkey notify event");
return;
}
ba2str(&ev->addr.bdaddr, dst);
DBG("%s entered %u", dst, ev->entered);
/* HAL seems to not support entered characters */
if (ev->entered)
return;
dev = find_device(&ev->addr.bdaddr);
if (!dev)
return;
update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, true,
false, false);
send_ssp_request(dev, HAL_SSP_VARIANT_NOTIF, ev->passkey);
}
static void clear_device_found(gpointer data, gpointer user_data)
{
struct device *dev = data;
dev->found = false;
}
static uint8_t get_supported_discovery_type(void)
{
uint8_t type = SCAN_TYPE_NONE;
if (adapter.current_settings & MGMT_SETTING_BREDR)
type |= SCAN_TYPE_BREDR;
if (adapter.current_settings & MGMT_SETTING_LE)
type |= SCAN_TYPE_LE;
return type;
}
static bool start_discovery(uint8_t type)
{
struct mgmt_cp_start_discovery cp;
cp.type = get_supported_discovery_type() & type;
DBG("type=0x%x", cp.type);
if (cp.type == SCAN_TYPE_NONE)
return false;
if (mgmt_send(mgmt_if, MGMT_OP_START_DISCOVERY, adapter.index,
sizeof(cp), &cp, NULL, NULL, NULL) > 0)
return true;
error("Failed to start discovery");
return false;
}
/*
* Send discovery state change event only if it is related to dual type
* discovery session (triggered by start/cancel discovery commands)
*/
static void check_discovery_state(uint8_t new_type, uint8_t old_type)
{
struct hal_ev_discovery_state_changed ev;
DBG("%u %u", new_type, old_type);
if (new_type == get_supported_discovery_type()) {
g_slist_foreach(bonded_devices, clear_device_found, NULL);
g_slist_foreach(cached_devices, clear_device_found, NULL);
ev.state = HAL_DISCOVERY_STATE_STARTED;
goto done;
}
if (old_type != get_supported_discovery_type())
return;
ev.state = HAL_DISCOVERY_STATE_STOPPED;
done:
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(ev), &ev);
}
static void mgmt_discovering_event(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_discovering *ev = param;
uint8_t type;
if (length < sizeof(*ev)) {
error("Too small discovering event");
return;
}
DBG("type %u discovering %u", ev->type, ev->discovering);
if (!!adapter.cur_discovery_type == !!ev->discovering)
return;
type = ev->discovering ? ev->type : SCAN_TYPE_NONE;
check_discovery_state(type, adapter.cur_discovery_type);
adapter.cur_discovery_type = type;
if (ev->discovering) {
adapter.exp_discovery_type = adapter.le_scanning ?
SCAN_TYPE_LE : SCAN_TYPE_NONE;
return;
}
/* One shot notification about discovery stopped */
if (gatt_discovery_stopped_cb) {
gatt_discovery_stopped_cb();
gatt_discovery_stopped_cb = NULL;
}
type = adapter.exp_discovery_type;
adapter.exp_discovery_type = adapter.le_scanning ? SCAN_TYPE_LE :
SCAN_TYPE_NONE;
if (type != SCAN_TYPE_NONE)
start_discovery(type);
}
static void confirm_device_name_cb(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_rp_confirm_name *rp = param;
struct device *dev;
DBG("Confirm name status: %s (0x%02x)", mgmt_errstr(status), status);
if (length < sizeof(*rp)) {
error("Wrong size of confirm name response");
return;
}
dev = find_device(&rp->addr.bdaddr);
if (!dev)
return;
dev->confirm_id = 0;
}
static unsigned int confirm_device_name(const bdaddr_t *addr, uint8_t addr_type,
bool resolve_name)
{
struct mgmt_cp_confirm_name cp;
unsigned int res;
memset(&cp, 0, sizeof(cp));
bacpy(&cp.addr.bdaddr, addr);
cp.addr.type = addr_type;
if (!resolve_name)
cp.name_known = 1;
res = mgmt_send(mgmt_if, MGMT_OP_CONFIRM_NAME, adapter.index,
sizeof(cp), &cp, confirm_device_name_cb,
NULL, NULL);
if (!res)
error("Failed to send confirm name request");
return res;
}
static int fill_hal_prop(void *buf, uint8_t type, uint16_t len,
const void *val)
{
struct hal_property *prop = buf;
prop->type = type;
prop->len = len;
if (len)
memcpy(prop->val, val, len);
return sizeof(*prop) + len;
}
static uint8_t get_device_android_type(struct device *dev)
{
if (dev->bredr && dev->le)
return HAL_TYPE_DUAL;
if (dev->le)
return HAL_TYPE_LE;
return HAL_TYPE_BREDR;
}
uint8_t bt_get_device_android_type(const bdaddr_t *addr)
{
struct device *dev;
dev = get_device(addr, BDADDR_BREDR);
return get_device_android_type(dev);
}
bool bt_is_device_le(const bdaddr_t *addr)
{
struct device *dev;
dev = find_device(addr);
if (!dev)
return false;
return dev->le;
}
const bdaddr_t *bt_get_id_addr(const bdaddr_t *addr, uint8_t *type)
{
struct device *dev;
dev = find_device(addr);
if (!dev)
return NULL;
if (type)
*type = dev->bdaddr_type;
return &dev->bdaddr;
}
const char *bt_get_adapter_name(void)
{
return adapter.name;
}
bool bt_device_is_bonded(const bdaddr_t *bdaddr)
{
if (g_slist_find_custom(bonded_devices, bdaddr, device_match))
return true;
return false;
}
bool bt_device_set_uuids(const bdaddr_t *addr, GSList *uuids)
{
struct device *dev;
dev = find_device(addr);
if (!dev)
return false;
set_device_uuids(dev, uuids);
return true;
}
bool bt_kernel_conn_control(void)
{
return kernel_conn_control;
}
bool bt_auto_connect_add(const bdaddr_t *addr)
{
struct mgmt_cp_add_device cp;
struct device *dev;
if (!kernel_conn_control)
return false;
dev = find_device(addr);
if (!dev)
return false;
if (dev->bdaddr_type == BDADDR_BREDR) {
DBG("auto-connection feature is not available for BR/EDR");
return false;
}
if (dev->in_white_list) {
DBG("Device already in white list");
return true;
}
memset(&cp, 0, sizeof(cp));
bacpy(&cp.addr.bdaddr, addr);
cp.addr.type = dev->bdaddr_type;
cp.action = 0x02;
if (mgmt_send(mgmt_if, MGMT_OP_ADD_DEVICE, adapter.index, sizeof(cp),
&cp, NULL, NULL, NULL) > 0) {
dev->in_white_list = true;
return true;
}
error("Failed to add device");
return false;
}
void bt_auto_connect_remove(const bdaddr_t *addr)
{
struct mgmt_cp_remove_device cp;
struct device *dev;
if (!kernel_conn_control)
return;
dev = find_device(addr);
if (!dev)
return;
if (dev->bdaddr_type == BDADDR_BREDR) {
DBG("auto-connection feature is not available for BR/EDR");
return;
}
if (!dev->in_white_list) {
DBG("Device already removed from white list");
return;
}
memset(&cp, 0, sizeof(cp));
bacpy(&cp.addr.bdaddr, addr);
cp.addr.type = dev->bdaddr_type;
if (mgmt_send(mgmt_if, MGMT_OP_REMOVE_DEVICE, adapter.index,
sizeof(cp), &cp, NULL, NULL, NULL) > 0) {
dev->in_white_list = false;
return;
}
error("Failed to remove device");
}
static bool match_by_value(const void *data, const void *user_data)
{
return data == user_data;
}
bool bt_unpaired_register(bt_unpaired_device_cb cb)
{
if (queue_find(unpaired_cb_list, match_by_value, cb))
return false;
return queue_push_head(unpaired_cb_list, cb);
}
void bt_unpaired_unregister(bt_unpaired_device_cb cb)
{
queue_remove(unpaired_cb_list, cb);
}
bool bt_paired_register(bt_paired_device_cb cb)
{
if (queue_find(paired_cb_list, match_by_value, cb))
return false;
return queue_push_head(paired_cb_list, cb);
}
void bt_paired_unregister(bt_paired_device_cb cb)
{
queue_remove(paired_cb_list, cb);
}
bool bt_is_pairing(const bdaddr_t *addr)
{
struct device *dev;
dev = find_device(addr);
if (!dev)
return false;
return dev->pairing;
}
static bool rssi_above_threshold(int old, int new)
{
/* only 8 dBm or more */
return abs(old - new) >= 8;
}
static void update_new_device(struct device *dev, int8_t rssi,
const struct eir_data *eir)
{
uint8_t buf[IPC_MTU];
struct hal_ev_device_found *ev = (void *) buf;
uint8_t android_bdaddr[6];
uint8_t android_type;
size_t size;
memset(buf, 0, sizeof(buf));
if (adapter.cur_discovery_type)
dev->found = true;
size = sizeof(*ev);
get_device_android_addr(dev, android_bdaddr);
size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_ADDR,
sizeof(android_bdaddr), android_bdaddr);
ev->num_props++;
android_type = get_device_android_type(dev);
size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
sizeof(android_type), &android_type);
ev->num_props++;
if (eir->class)
dev->class = eir->class;
if (dev->class) {
size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
sizeof(dev->class), &dev->class);
ev->num_props++;
}
if (rssi && rssi_above_threshold(dev->rssi, rssi))
dev->rssi = rssi;
if (dev->rssi) {
size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
sizeof(dev->rssi), &dev->rssi);
ev->num_props++;
}
if (eir->name && strlen(eir->name)) {
g_free(dev->name);
dev->name = g_strdup(eir->name);
}
if (dev->name) {
size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
strlen(dev->name), dev->name);
ev->num_props++;
/* when updating name also send stored friendly name */
if (dev->friendly_name) {
size += fill_hal_prop(buf + size,
HAL_PROP_DEVICE_FRIENDLY_NAME,
strlen(dev->friendly_name),
dev->friendly_name);
ev->num_props++;
}
}
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_DEVICE_FOUND,
size, buf);
}
static void update_device(struct device *dev, int8_t rssi,
const struct eir_data *eir)
{
uint8_t buf[IPC_MTU];
struct hal_ev_remote_device_props *ev = (void *) buf;
uint8_t old_type, new_type;
size_t size;
memset(buf, 0, sizeof(buf));
size = sizeof(*ev);
ev->status = HAL_STATUS_SUCCESS;
get_device_android_addr(dev, ev->bdaddr);
old_type = get_device_android_type(dev);
new_type = get_device_android_type(dev);
if (old_type != new_type) {
size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_TYPE,
sizeof(new_type), &new_type);
ev->num_props++;
}
if (eir->class && dev->class != eir->class) {
dev->class = eir->class;
size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_CLASS,
sizeof(dev->class), &dev->class);
ev->num_props++;
}
if (rssi && rssi_above_threshold(dev->rssi, rssi)) {
dev->rssi = rssi;
size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_RSSI,
sizeof(dev->rssi), &dev->rssi);
ev->num_props++;
}
if (eir->name && strlen(eir->name) && strcmp(dev->name, eir->name)) {
g_free(dev->name);
dev->name = g_strdup(eir->name);
size += fill_hal_prop(buf + size, HAL_PROP_DEVICE_NAME,
strlen(dev->name), dev->name);
ev->num_props++;
/* when updating name also send stored friendly name */
if (dev->friendly_name) {
size += fill_hal_prop(buf + size,
HAL_PROP_DEVICE_FRIENDLY_NAME,
strlen(dev->friendly_name),
dev->friendly_name);
ev->num_props++;
}
}
if (ev->num_props)
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_REMOTE_DEVICE_PROPS, size, buf);
}
static bool is_new_device(const struct device *dev, unsigned int flags,
uint8_t bdaddr_type)
{
if (dev->found)
return false;
if (dev->bredr_paired || dev->le_paired)
return false;
if (bdaddr_type != BDADDR_BREDR &&
!(flags & (EIR_LIM_DISC | EIR_GEN_DISC)))
return false;
return true;
}
static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
int8_t rssi, bool confirm,
bool connectable,
const uint8_t *data, uint8_t data_len)
{
struct eir_data eir;
struct device *dev;
memset(&eir, 0, sizeof(eir));
eir_parse(&eir, data, data_len);
dev = get_device(bdaddr, bdaddr_type);
if (bdaddr_type == BDADDR_BREDR) {
dev->bredr = true;
dev->bredr_seen = time(NULL);
} else {
dev->le = true;
dev->bdaddr_type = bdaddr_type;
dev->le_seen = time(NULL);
}
/*
* Device found event needs to be send also for known device if this is
* new discovery session. Otherwise framework will ignore it.
*/
if (is_new_device(dev, eir.flags, bdaddr_type))
update_new_device(dev, rssi, &eir);
else
update_device(dev, rssi, &eir);
eir_data_free(&eir);
/* Notify Gatt if its registered for LE events */
if (bdaddr_type != BDADDR_BREDR && gatt_device_found_cb) {
const bdaddr_t *addr;
/*
* If RPA is set it means that IRK was received and ID address
* is being used. Android Framework is still using old RPA and
* it needs to be used also in GATT notifications. Also GATT
* HAL implementation is using RPA for devices matching.
*/
if (bacmp(&dev->rpa, BDADDR_ANY))
addr = &dev->rpa;
else
addr = &dev->bdaddr;
gatt_device_found_cb(addr, rssi, data_len, data, connectable,
dev->le_bonded);
}
if (!dev->bredr_paired && !dev->le_paired)
cache_device(dev);
if (confirm) {
char addr[18];
bool resolve_name = true;
ba2str(bdaddr, addr);
/*
* Don't need to confirm name if we have it already in cache
* Just check if device name is different than bdaddr
*/
if (g_strcmp0(dev->name, addr)) {
get_device_name(dev);
resolve_name = false;
}
info("Device %s needs name confirmation (resolve_name=%d)",
addr, resolve_name);
dev->confirm_id = confirm_device_name(bdaddr, bdaddr_type,
resolve_name);
}
}
static void mgmt_device_found_event(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_device_found *ev = param;
const uint8_t *eir;
uint16_t eir_len;
uint32_t flags;
bool confirm_name;
bool connectable;
char addr[18];
if (length < sizeof(*ev)) {
error("Too short device found event (%u bytes)", length);
return;
}
eir_len = le16_to_cpu(ev->eir_len);
if (length != sizeof(*ev) + eir_len) {
error("Device found event size mismatch (%u != %zu)",
length, sizeof(*ev) + eir_len);
return;
}
if (eir_len == 0)
eir = NULL;
else
eir = ev->eir;
flags = le32_to_cpu(ev->flags);
ba2str(&ev->addr.bdaddr, addr);
DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
index, addr, ev->rssi, flags, eir_len);
confirm_name = flags & MGMT_DEV_FOUND_CONFIRM_NAME;
connectable = !(flags & MGMT_DEV_FOUND_NOT_CONNECTABLE);
update_found_device(&ev->addr.bdaddr, ev->addr.type, ev->rssi,
confirm_name, connectable, eir, eir_len);
}
static void mgmt_device_connected_event(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_device_connected *ev = param;
struct hal_ev_acl_state_changed hal_ev;
struct device *dev;
char addr[18];
if (length < sizeof(*ev)) {
error("Too short device connected event (%u bytes)", length);
return;
}
ba2str(&ev->addr.bdaddr, addr);
DBG("%s type %u", addr, ev->addr.type);
update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false, false,
&ev->eir[0], le16_to_cpu(ev->eir_len));
hal_ev.status = HAL_STATUS_SUCCESS;
hal_ev.state = HAL_ACL_STATE_CONNECTED;
dev = find_device(&ev->addr.bdaddr);
if (!dev)
return;
dev->connected = true;
get_device_android_addr(dev, hal_ev.bdaddr);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
}
static bool device_is_paired(struct device *dev, uint8_t addr_type)
{
if (addr_type == BDADDR_BREDR)
return dev->bredr_paired;
return dev->le_paired;
}
static bool device_is_bonded(struct device *dev)
{
return dev->bredr_bonded || dev->le_bonded;
}
static void mgmt_device_disconnected_event(uint16_t index, uint16_t length,
const void *param,
void *user_data)
{
const struct mgmt_ev_device_disconnected *ev = param;
struct hal_ev_acl_state_changed hal_ev;
struct device *dev;
uint8_t type = ev->addr.type;
if (length < sizeof(*ev)) {
error("Too short device disconnected event (%u bytes)", length);
return;
}
dev = find_device(&ev->addr.bdaddr);
if (!dev)
return;
hal_ev.status = HAL_STATUS_SUCCESS;
hal_ev.state = HAL_ACL_STATE_DISCONNECTED;
get_device_android_addr(dev, hal_ev.bdaddr);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_ACL_STATE_CHANGED, sizeof(hal_ev), &hal_ev);
if (device_is_paired(dev, type) && !device_is_bonded(dev))
update_device_state(dev, type, HAL_STATUS_SUCCESS, false,
false, false);
dev->connected = false;
}
static uint8_t status_mgmt2hal(uint8_t mgmt)
{
switch (mgmt) {
case MGMT_STATUS_SUCCESS:
return HAL_STATUS_SUCCESS;
case MGMT_STATUS_NO_RESOURCES:
return HAL_STATUS_NOMEM;
case MGMT_STATUS_BUSY:
return HAL_STATUS_BUSY;
case MGMT_STATUS_NOT_SUPPORTED:
return HAL_STATUS_UNSUPPORTED;
case MGMT_STATUS_INVALID_PARAMS:
return HAL_STATUS_INVALID;
case MGMT_STATUS_AUTH_FAILED:
return HAL_STATUS_AUTH_FAILURE;
case MGMT_STATUS_NOT_CONNECTED:
return HAL_STATUS_REMOTE_DEVICE_DOWN;
default:
return HAL_STATUS_FAILED;
}
}
static void mgmt_connect_failed_event(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_connect_failed *ev = param;
struct device *dev;
if (length < sizeof(*ev)) {
error("Too short connect failed event (%u bytes)", length);
return;
}
DBG("");
dev = find_device(&ev->addr.bdaddr);
if (!dev)
return;
/*
* In case security mode 3 pairing we will get connect failed event
* in case e.g wrong PIN code entered. Let's check if device is
* bonding, if so update bond state
*/
if (!dev->pairing)
return;
update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
false, false, false);
}
static void mgmt_auth_failed_event(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_auth_failed *ev = param;
struct device *dev;
if (length < sizeof(*ev)) {
error("Too small auth failed mgmt event (%u bytes)", length);
return;
}
DBG("");
dev = find_device(&ev->addr.bdaddr);
if (!dev)
return;
if (!dev->pairing)
return;
update_device_state(dev, ev->addr.type, status_mgmt2hal(ev->status),
false, false, false);
}
static void mgmt_device_unpaired_event(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_device_unpaired *ev = param;
struct device *dev;
if (length < sizeof(*ev)) {
error("Too small device unpaired event (%u bytes)", length);
return;
}
DBG("");
/* TODO should device be disconnected ? */
dev = find_device(&ev->addr.bdaddr);
if (!dev)
return;
update_device_state(dev, ev->addr.type, HAL_STATUS_SUCCESS, false,
false, false);
/* Unpaired device is removed from the white list */
dev->in_white_list = false;
}
static void store_ltk(const bdaddr_t *dst, uint8_t bdaddr_type, bool master,
const uint8_t *key, uint8_t key_type, uint8_t enc_size,
uint16_t ediv, uint64_t rand)
{
const char *key_s, *keytype_s, *encsize_s, *ediv_s, *rand_s;
GKeyFile *key_file;
char key_str[33];
gsize length = 0;
char addr[18];
char *data;
int i;
key_file = g_key_file_new();
if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
g_key_file_free(key_file);
return;
}
ba2str(dst, addr);
key_s = master ? "LongTermKey" : "SlaveLongTermKey";
keytype_s = master ? "LongTermKeyType" : "SlaveLongTermKeyType";
encsize_s = master ? "LongTermKeyEncSize" : "SlaveLongTermKeyEncSize";
ediv_s = master ? "LongTermKeyEDiv" : "SlaveLongTermKeyEDiv";
rand_s = master ? "LongTermKeyRand" : "SlaveLongTermKeyRand";
for (i = 0; i < 16; i++)
sprintf(key_str + (i * 2), "%2.2X", key[i]);
g_key_file_set_string(key_file, addr, key_s, key_str);
g_key_file_set_integer(key_file, addr, keytype_s, key_type);
g_key_file_set_integer(key_file, addr, encsize_s, enc_size);
g_key_file_set_integer(key_file, addr, ediv_s, ediv);
g_key_file_set_uint64(key_file, addr, rand_s, rand);
data = g_key_file_to_data(key_file, &length, NULL);
g_file_set_contents(DEVICES_FILE, data, length, NULL);
g_free(data);
g_key_file_free(key_file);
}
static void new_long_term_key_event(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_new_long_term_key *ev = param;
struct device *dev;
char dst[18];
if (length < sizeof(*ev)) {
error("Too small long term key event (%u bytes)", length);
return;
}
ba2str(&ev->key.addr.bdaddr, dst);
DBG("new LTK for %s type %u enc_size %u store_hint %u",
dst, ev->key.type, ev->key.enc_size, ev->store_hint);
dev = find_device(&ev->key.addr.bdaddr);
if (!dev)
return;
update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
true, !!ev->store_hint);
if (ev->store_hint) {
const struct mgmt_ltk_info *key = &ev->key;
uint16_t ediv;
uint64_t rand;
ediv = le16_to_cpu(key->ediv);
rand = le64_to_cpu(key->rand);
store_ltk(&key->addr.bdaddr, key->addr.type, key->master,
key->val, key->type, key->enc_size, ediv, rand);
}
/* TODO browse services here? */
}
static void store_csrk(struct device *dev)
{
GKeyFile *key_file;
char key_str[33];
char addr[18];
int i;
gsize length = 0;
char *data;
ba2str(&dev->bdaddr, addr);
key_file = g_key_file_new();
if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
g_key_file_free(key_file);
return;
}
if (dev->valid_local_csrk) {
for (i = 0; i < 16; i++)
sprintf(key_str + (i * 2), "%2.2X",
dev->local_csrk[i]);
g_key_file_set_string(key_file, addr, "LocalCSRK", key_str);
g_key_file_set_boolean(key_file, addr, "LocalCSRKAuthenticated",
dev->local_csrk_auth);
}
if (dev->valid_remote_csrk) {
for (i = 0; i < 16; i++)
sprintf(key_str + (i * 2), "%2.2X",
dev->remote_csrk[i]);
g_key_file_set_string(key_file, addr, "RemoteCSRK", key_str);
g_key_file_set_boolean(key_file, addr,
"RemoteCSRKAuthenticated",
dev->remote_csrk_auth);
}
data = g_key_file_to_data(key_file, &length, NULL);
g_file_set_contents(DEVICES_FILE, data, length, NULL);
g_free(data);
g_key_file_free(key_file);
}
static void new_csrk_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_new_csrk *ev = param;
struct device *dev;
char dst[18];
if (length < sizeof(*ev)) {
error("Too small csrk event (%u bytes)", length);
return;
}
ba2str(&ev->key.addr.bdaddr, dst);
dev = get_device(&ev->key.addr.bdaddr, ev->key.addr.type);
if (!dev)
return;
switch (ev->key.type) {
case 0x00:
case 0x02:
memcpy(dev->local_csrk, ev->key.val, 16);
dev->local_sign_cnt = 0;
dev->valid_local_csrk = true;
dev->local_csrk_auth = ev->key.type == 0x02;
break;
case 0x01:
case 0x03:
memcpy(dev->remote_csrk, ev->key.val, 16);
dev->remote_sign_cnt = 0;
dev->valid_remote_csrk = true;
dev->remote_csrk_auth = ev->key.type == 0x03;
break;
default:
error("Unknown CSRK key type 02%02x", ev->key.type);
return;
}
update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
true, !!ev->store_hint);
if (ev->store_hint)
store_csrk(dev);
}
static void store_irk(struct device *dev, const uint8_t *val)
{
GKeyFile *key_file;
char key_str[33];
char addr[18];
int i;
gsize length = 0;
char *data;
ba2str(&dev->bdaddr, addr);
key_file = g_key_file_new();
if (!g_key_file_load_from_file(key_file, DEVICES_FILE, 0, NULL)) {
g_key_file_free(key_file);
return;
}
for (i = 0; i < 16; i++)
sprintf(key_str + (i * 2), "%2.2X", val[i]);
g_key_file_set_string(key_file, addr, "IdentityResolvingKey", key_str);
data = g_key_file_to_data(key_file, &length, NULL);
g_file_set_contents(DEVICES_FILE, data, length, NULL);
g_free(data);
g_key_file_free(key_file);
}
static void new_irk_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_new_irk *ev = param;
const struct mgmt_addr_info *addr = &ev->key.addr;
struct device *dev;
char dst[18], rpa[18];
if (length < sizeof(*ev)) {
error("To small New Irk Event (%u bytes)", length);
return;
}
ba2str(&ev->key.addr.bdaddr, dst);
ba2str(&ev->rpa, rpa);
DBG("new IRK for %s, RPA %s", dst, rpa);
if (!bacmp(&ev->rpa, BDADDR_ANY)) {
dev = get_device(&addr->bdaddr, addr->type);
if (!dev)
return;
} else {
dev = find_device(&addr->bdaddr);
if (dev && dev->bredr_paired) {
bacpy(&dev->rpa, &addr->bdaddr);
dev->rpa_type = addr->type;
/* TODO merge properties ie. UUIDs */
} else {
dev = find_device(&ev->rpa);
if (!dev)
return;
/* don't leave garbage in cache file */
remove_device_info(dev, CACHE_FILE);
/*
* RPA resolution is transparent for Android Framework
* ie. device is still access by RPA so it need to be
* keep. After bluetoothd restart device is advertised
* to Android with IDA and RPA is not set.
*/
bacpy(&dev->rpa, &dev->bdaddr);
dev->rpa_type = dev->bdaddr_type;
bacpy(&dev->bdaddr, &addr->bdaddr);
dev->bdaddr_type = addr->type;
}
}
update_device_state(dev, ev->key.addr.type, HAL_STATUS_SUCCESS, false,
true, !!ev->store_hint);
if (ev->store_hint)
store_irk(dev, ev->key.val);
}
static void register_mgmt_handlers(void)
{
mgmt_register(mgmt_if, MGMT_EV_NEW_SETTINGS, adapter.index,
new_settings_callback, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_CLASS_OF_DEV_CHANGED, adapter.index,
mgmt_dev_class_changed_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_LOCAL_NAME_CHANGED, adapter.index,
mgmt_local_name_changed_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_NEW_LINK_KEY, adapter.index,
new_link_key_callback, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_PIN_CODE_REQUEST, adapter.index,
pin_code_request_callback, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_USER_CONFIRM_REQUEST, adapter.index,
user_confirm_request_callback, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_USER_PASSKEY_REQUEST, adapter.index,
user_passkey_request_callback, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_PASSKEY_NOTIFY, adapter.index,
user_passkey_notify_callback, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_DISCOVERING, adapter.index,
mgmt_discovering_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_DEVICE_FOUND, adapter.index,
mgmt_device_found_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_DEVICE_CONNECTED, adapter.index,
mgmt_device_connected_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_DEVICE_DISCONNECTED, adapter.index,
mgmt_device_disconnected_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_CONNECT_FAILED, adapter.index,
mgmt_connect_failed_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_AUTH_FAILED, adapter.index,
mgmt_auth_failed_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_DEVICE_UNPAIRED, adapter.index,
mgmt_device_unpaired_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_NEW_LONG_TERM_KEY, adapter.index,
new_long_term_key_event, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_NEW_CSRK, adapter.index,
new_csrk_callback, NULL, NULL);
mgmt_register(mgmt_if, MGMT_EV_NEW_IRK, adapter.index, new_irk_callback,
NULL, NULL);
}
static void load_link_keys_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
bt_bluetooth_ready cb = user_data;
int err;
if (status) {
error("Failed to load link keys for index %u: %s (0x%02x)",
adapter.index, mgmt_errstr(status), status);
err = -EIO;
goto failed;
}
DBG("status %u", status);
cb(0, &adapter.bdaddr);
return;
failed:
cb(err, NULL);
}
static void load_link_keys(GSList *keys, bt_bluetooth_ready cb)
{
struct mgmt_cp_load_link_keys *cp;
struct mgmt_link_key_info *key;
size_t key_count, cp_size;
unsigned int id;
key_count = g_slist_length(keys);
DBG("keys %zu ", key_count);
cp_size = sizeof(*cp) + (key_count * sizeof(*key));
cp = g_malloc0(cp_size);
/*
* Even if the list of stored keys is empty, it is important to
* load an empty list into the kernel. That way it is ensured
* that no old keys from a previous daemon are present.
*/
cp->key_count = cpu_to_le16(key_count);
for (key = cp->keys; keys != NULL; keys = g_slist_next(keys), key++)
memcpy(key, keys->data, sizeof(*key));
id = mgmt_send(mgmt_if, MGMT_OP_LOAD_LINK_KEYS, adapter.index,
cp_size, cp, load_link_keys_complete, cb, NULL);
g_free(cp);
if (id == 0) {
error("Failed to load link keys");
cb(-EIO, NULL);
}
}
static void load_ltk_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
if (status == MGMT_STATUS_SUCCESS)
return;
info("Failed to load LTKs: %s (0x%02x)", mgmt_errstr(status), status);
}
static void load_ltks(GSList *ltks)
{
struct mgmt_cp_load_long_term_keys *cp;
struct mgmt_ltk_info *ltk;
size_t ltk_count, cp_size;
GSList *l;
ltk_count = g_slist_length(ltks);
DBG("ltks %zu", ltk_count);
cp_size = sizeof(*cp) + (ltk_count * sizeof(*ltk));
cp = g_malloc0(cp_size);
/*
* Even if the list of stored keys is empty, it is important to load
* an empty list into the kernel. That way it is ensured that no old
* keys from a previous daemon are present.
*/
cp->key_count = cpu_to_le16(ltk_count);
for (l = ltks, ltk = cp->keys; l != NULL; l = g_slist_next(l), ltk++)
memcpy(ltk, l->data, sizeof(*ltk));
if (mgmt_send(mgmt_if, MGMT_OP_LOAD_LONG_TERM_KEYS, adapter.index,
cp_size, cp, load_ltk_complete, NULL, NULL) == 0)
error("Failed to load LTKs");
g_free(cp);
}
static void load_irk_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
if (status == MGMT_STATUS_SUCCESS)
return;
info("Failed to load IRKs: %s (0x%02x)", mgmt_errstr(status), status);
}
static void load_irks(GSList *irks)
{
struct mgmt_cp_load_irks *cp;
struct mgmt_irk_info *irk;
size_t irk_count, cp_size;
GSList *l;
irk_count = g_slist_length(irks);
DBG("irks %zu", irk_count);
cp_size = sizeof(*cp) + (irk_count * sizeof(*irk));
cp = g_malloc0(cp_size);
cp->irk_count = cpu_to_le16(irk_count);
for (l = irks, irk = cp->irks; l != NULL; l = g_slist_next(l), irk++)
memcpy(irk, irks->data, sizeof(*irk));
if (mgmt_send(mgmt_if, MGMT_OP_LOAD_IRKS, adapter.index, cp_size, cp,
load_irk_complete, NULL, NULL) == 0)
error("Failed to load IRKs");
g_free(cp);
}
static uint8_t get_adapter_uuids(void)
{
struct hal_ev_adapter_props_changed *ev;
GSList *list = adapter.uuids;
size_t uuid_count = g_slist_length(list);
size_t len = uuid_count * sizeof(uint128_t);
uint8_t buf[BASELEN_PROP_CHANGED + len];
uint8_t *p;
memset(buf, 0, sizeof(buf));
ev = (void *) buf;
ev->num_props = 1;
ev->status = HAL_STATUS_SUCCESS;
ev->props[0].type = HAL_PROP_ADAPTER_UUIDS;
ev->props[0].len = len;
p = ev->props->val;
for (; list; list = g_slist_next(list)) {
uuid_t *uuid = list->data;
memcpy(p, &uuid->value.uuid128, sizeof(uint128_t));
p += sizeof(uint128_t);
}
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_ADAPTER_PROPS_CHANGED, sizeof(buf), ev);
return HAL_STATUS_SUCCESS;
}
static void remove_uuid_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
if (status != MGMT_STATUS_SUCCESS) {
error("Failed to remove UUID: %s (0x%02x)", mgmt_errstr(status),
status);
return;
}
mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
get_adapter_uuids();
}
static void remove_uuid(uuid_t *uuid)
{
uint128_t uint128;
struct mgmt_cp_remove_uuid cp;
ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
htob128(&uint128, (uint128_t *) cp.uuid);
mgmt_send(mgmt_if, MGMT_OP_REMOVE_UUID, adapter.index, sizeof(cp), &cp,
remove_uuid_complete, NULL, NULL);
}
static void add_uuid_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
if (status != MGMT_STATUS_SUCCESS) {
error("Failed to add UUID: %s (0x%02x)", mgmt_errstr(status),
status);
return;
}
mgmt_dev_class_changed_event(adapter.index, length, param, NULL);
get_adapter_uuids();
}
static void add_uuid(uint8_t svc_hint, uuid_t *uuid)
{
uint128_t uint128;
struct mgmt_cp_add_uuid cp;
ntoh128((uint128_t *) uuid->value.uuid128.data, &uint128);
htob128(&uint128, (uint128_t *) cp.uuid);
cp.svc_hint = svc_hint;
mgmt_send(mgmt_if, MGMT_OP_ADD_UUID, adapter.index, sizeof(cp), &cp,
add_uuid_complete, NULL, NULL);
}
int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint)
{
uuid_t *uuid;
uuid = sdp_uuid_to_uuid128(&rec->svclass);
if (g_slist_find_custom(adapter.uuids, uuid, sdp_uuid_cmp)) {
char uuid_str[32];
sdp_uuid2strn(uuid, uuid_str, sizeof(uuid_str));
DBG("UUID %s already added", uuid_str);
bt_free(uuid);
return -EALREADY;
}
adapter.uuids = g_slist_prepend(adapter.uuids, uuid);
add_uuid(svc_hint, uuid);
return add_record_to_server(&adapter.bdaddr, rec);
}
void bt_adapter_remove_record(uint32_t handle)
{
sdp_record_t *rec;
GSList *uuid_found;
rec = sdp_record_find(handle);
if (!rec)