<commit_before>/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2013  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 as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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
 *
 */

#include <errno.h>

#include <glib.h>

#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "lib/mgmt.h"
#include "src/shared/mgmt.h"
#include "src/glib-helper.h"
#include "src/eir.h"
#include "log.h"
#include "hal-msg.h"
#include "ipc.h"
#include "utils.h"
#include "adapter.h"

/* Default to DisplayYesNo */
#define DEFAULT_IO_CAPABILITY 0x01

static GIOChannel *notification_io = NULL;

struct bt_adapter {
	uint16_t index;
	struct mgmt *mgmt;

	bt_adapter_ready ready;

	bdaddr_t bdaddr;
	uint32_t dev_class;

	char *name;

	uint32_t supported_settings;
	uint32_t current_settings;

	bool discovering;
};

static struct bt_adapter *adapter;
static GSList *found_devices = NULL;

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;
	}

	if (!g_strcmp0(adapter->name, (const char *) rp->name))
		return;

	DBG("name: %s", rp->name);

	g_free(adapter->name);
	adapter->name = g_strdup((const char *) 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(notification_io, HAL_SERVICE_ID_BLUETOOTH,
			HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev, -1);
}

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)
{
	struct hal_ev_adapter_props_changed *ev;
	uint8_t *mode;
	int len;
	len = sizeof(*ev) + sizeof(struct hal_property) + 1;

	ev = g_malloc(len);

	ev->num_props = 1;
	ev->status = HAL_STATUS_SUCCESS;

	ev->props[0].type = HAL_PROP_ADAPTER_SCAN_MODE;
	ev->props[0].len = 1;

	mode = ev->props[0].val;
	*mode = settings2scan_mode();

	DBG("mode %u", *mode);

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
				HAL_EV_ADAPTER_PROPS_CHANGED, len, ev, -1);

	g_free(ev);
}

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;

	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 = bt_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;

	/* TODO: Inform prop change: Class */

	/* TODO: Gatt attrib set*/
}

static void store_link_key(const bdaddr_t *dst, const uint8_t *key,
					uint8_t type, uint8_t pin_length)
{
	/* TODO store link key */

}

static void send_bond_state_change(const bdaddr_t *addr, uint8_t status,
								uint8_t state)
{
	struct hal_ev_bond_state_changed ev;

	ev.status = status;
	ev.state = state;
	bdaddr2android(addr, ev.bdaddr);

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
			HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev, -1);
}

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;
	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;
	}

	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);
	}

	send_bond_state_change(&addr->bdaddr, HAL_STATUS_SUCCESS,
							HAL_BOND_STATE_BONDED);
}

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;
	char dst[18];

	if (length < sizeof(*ev)) {
		error("Too small PIN code request event");
		return;
	}

	ba2str(&ev->addr.bdaddr, dst);

	DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure);

	/* TODO name and CoD of remote devices should probably be cached */
	memset(&hal_ev, 0, sizeof(hal_ev));
	bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST,
						sizeof(hal_ev), &hal_ev, -1);
}

static void send_ssp_request(const bdaddr_t *addr, uint8_t variant,
							uint32_t passkey)
{
	struct hal_ev_ssp_request ev;

	/* TODO name and CoD of remote devices should probably be cached */
	memset(&ev, 0, sizeof(ev));
	bdaddr2android(addr, ev.bdaddr);
	ev.pairing_variant = variant;
	ev.passkey = passkey;

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
						sizeof(ev), &ev, -1);
}

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;
	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);

	if (ev->confirm_hint)
		send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONSENT, 0);
	else
		send_ssp_request(&ev->addr.bdaddr, 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;
	char dst[18];

	if (length < sizeof(*ev)) {
		error("Too small passkey request event");
		return;
	}

	ba2str(&ev->addr.bdaddr, dst);
	DBG("%s", dst);

	send_ssp_request(&ev->addr.bdaddr, 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;
	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)
		send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_NOTIF,
								ev->passkey);
}

static void mgmt_discovering_event(uint16_t index, uint16_t length,
					const void *param, void *user_data)
{
	const struct mgmt_ev_discovering *ev = param;
	struct hal_ev_discovery_state_changed cp;

	if (length < sizeof(*ev)) {
		error("Too small discovering event");
		return;
	}

	DBG("hci%u type %u discovering %u", adapter->index, ev->type,
							ev->discovering);

	if (adapter->discovering == !!ev->discovering)
		return;

	adapter->discovering = !!ev->discovering;

	DBG("new discovering state %u", ev->discovering);

	if (adapter->discovering) {
		cp.state = HAL_DISCOVERY_STATE_STARTED;
	} else {
		g_slist_free_full(found_devices, g_free);
		found_devices = NULL;

		cp.state = HAL_DISCOVERY_STATE_STOPPED;
	}

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
						HAL_EV_DISCOVERY_STATE_CHANGED,
						sizeof(cp), &cp, -1);
}

static int bdaddr_cmp(gconstpointer a, gconstpointer b)
{
	const bdaddr_t *bda = a;
	const bdaddr_t *bdb = b;

	return bacmp(bdb, bda);
}

static int fill_device_props(struct hal_property *prop, bdaddr_t *addr,
					uint32_t cod, int8_t rssi, char *name)
{
	uint8_t num_props = 0;

	/* fill cod */
	prop->type = HAL_PROP_DEVICE_CLASS;
	prop->len = sizeof(cod);
	memcpy(prop->val, &cod, prop->len);
	prop = ((void *) prop) + sizeof(*prop) + sizeof(cod);
	num_props++;

	/* fill rssi */
	prop->type = HAL_PROP_DEVICE_RSSI;
	prop->len = sizeof(rssi);
	memcpy(prop->val, &rssi, prop->len);
	prop = ((void *) prop) + sizeof(*prop) + sizeof(rssi);
	num_props++;

	/* fill name */
	if (name) {
		prop->type = HAL_PROP_DEVICE_NAME;
		prop->len = HAL_MAX_NAME_LENGTH;
		strncpy((char *) prop->val, name, HAL_MAX_NAME_LENGTH - 1);
		prop = ((void *) prop) + sizeof(*prop) + HAL_MAX_NAME_LENGTH;
		num_props++;
	}

	return num_props;
}

static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
					int8_t rssi, bool confirm,
					const uint8_t *data, uint8_t data_len)
{
	bool is_new_dev = false;
	size_t props_size = 0;
	size_t buff_size = 0;
	void *buf;
	struct eir_data eir;
	GSList *l;
	bdaddr_t *remote = NULL;
	int err;

	memset(&eir, 0, sizeof(eir));

	err = eir_parse(&eir, data, data_len);
	if (err < 0) {
		error("Error parsing EIR data: %s (%d)", strerror(-err), -err);
		return;
	}

	l = g_slist_find_custom(found_devices, bdaddr, bdaddr_cmp);
	if (l)
		remote = l->data;

	if (!remote) {
		char addr[18];

		remote = g_new0(bdaddr_t, 1);
		bacpy(remote, bdaddr);

		found_devices = g_slist_prepend(found_devices, remote);
		is_new_dev = true;

		ba2str(remote, addr);
		DBG("New device found: %s", addr);
	}

	props_size += sizeof(struct hal_property) + sizeof(eir.class);
	props_size += sizeof(struct hal_property) + sizeof(rssi);

	if (eir.name)
		props_size += sizeof(struct hal_property) + HAL_MAX_NAME_LENGTH;

	if (is_new_dev) {
		struct hal_ev_device_found *ev = NULL;
		struct hal_property *prop = NULL;

		/* with new device we also send bdaddr prop */
		props_size += sizeof(struct hal_property) + sizeof(eir.addr);

		buff_size = sizeof(struct hal_ev_device_found) + props_size;
		buf = g_new0(char, buff_size);
		ev = buf;
		prop = ev->props;

		/* fill first prop with bdaddr */
		prop->type = HAL_PROP_DEVICE_ADDR;
		prop->len = sizeof(bdaddr_t);
		bdaddr2android(bdaddr, prop->val);
		prop = ((void *) prop) + sizeof(*prop) + sizeof(bdaddr_t);
		ev->num_props += 1;

		/* fill eir, name, and cod props */
		ev->num_props += fill_device_props(prop, remote, eir.class,
								rssi, eir.name);

		ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
							HAL_EV_DEVICE_FOUND,
							buff_size, ev, -1);
		g_free(buf);
	} else {
		struct hal_ev_remote_device_props *ev = NULL;

		buff_size = sizeof(*ev) + props_size;
		buf = g_new0(char, buff_size);
		ev = buf;

		ev->num_props = fill_device_props(ev->props, remote, eir.class,
								rssi, eir.name);

		ev->status = HAL_STATUS_SUCCESS;
		bdaddr2android(bdaddr, ev->bdaddr);

		ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
						HAL_EV_REMOTE_DEVICE_PROPS,
						buff_size, ev, -1);
		g_free(buf);
	}

	/* TODO: name confirmation */

	eir_data_free(&eir);
}

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;
	char addr[18];<commit_msg>android: Add support for remote device name confirmation<commit_after>/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2013  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 as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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
 *
 */

#include <errno.h>

#include <glib.h>

#include "lib/bluetooth.h"
#include "lib/sdp.h"
#include "lib/mgmt.h"
#include "src/shared/mgmt.h"
#include "src/glib-helper.h"
#include "src/eir.h"
#include "log.h"
#include "hal-msg.h"
#include "ipc.h"
#include "utils.h"
#include "adapter.h"

/* Default to DisplayYesNo */
#define DEFAULT_IO_CAPABILITY 0x01

static GIOChannel *notification_io = NULL;

struct bt_adapter {
	uint16_t index;
	struct mgmt *mgmt;

	bt_adapter_ready ready;

	bdaddr_t bdaddr;
	uint32_t dev_class;

	char *name;

	uint32_t supported_settings;
	uint32_t current_settings;

	bool discovering;
};

static struct bt_adapter *adapter;
static GSList *found_devices = NULL;

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;
	}

	if (!g_strcmp0(adapter->name, (const char *) rp->name))
		return;

	DBG("name: %s", rp->name);

	g_free(adapter->name);
	adapter->name = g_strdup((const char *) 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(notification_io, HAL_SERVICE_ID_BLUETOOTH,
			HAL_EV_ADAPTER_STATE_CHANGED, sizeof(ev), &ev, -1);
}

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)
{
	struct hal_ev_adapter_props_changed *ev;
	uint8_t *mode;
	int len;
	len = sizeof(*ev) + sizeof(struct hal_property) + 1;

	ev = g_malloc(len);

	ev->num_props = 1;
	ev->status = HAL_STATUS_SUCCESS;

	ev->props[0].type = HAL_PROP_ADAPTER_SCAN_MODE;
	ev->props[0].len = 1;

	mode = ev->props[0].val;
	*mode = settings2scan_mode();

	DBG("mode %u", *mode);

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
				HAL_EV_ADAPTER_PROPS_CHANGED, len, ev, -1);

	g_free(ev);
}

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;

	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 = bt_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;

	/* TODO: Inform prop change: Class */

	/* TODO: Gatt attrib set*/
}

static void store_link_key(const bdaddr_t *dst, const uint8_t *key,
					uint8_t type, uint8_t pin_length)
{
	/* TODO store link key */

}

static void send_bond_state_change(const bdaddr_t *addr, uint8_t status,
								uint8_t state)
{
	struct hal_ev_bond_state_changed ev;

	ev.status = status;
	ev.state = state;
	bdaddr2android(addr, ev.bdaddr);

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
			HAL_EV_BOND_STATE_CHANGED, sizeof(ev), &ev, -1);
}

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;
	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;
	}

	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);
	}

	send_bond_state_change(&addr->bdaddr, HAL_STATUS_SUCCESS,
							HAL_BOND_STATE_BONDED);
}

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;
	char dst[18];

	if (length < sizeof(*ev)) {
		error("Too small PIN code request event");
		return;
	}

	ba2str(&ev->addr.bdaddr, dst);

	DBG("%s type %u secure %u", dst, ev->addr.type, ev->secure);

	/* TODO name and CoD of remote devices should probably be cached */
	memset(&hal_ev, 0, sizeof(hal_ev));
	bdaddr2android(&ev->addr.bdaddr, hal_ev.bdaddr);

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_PIN_REQUEST,
						sizeof(hal_ev), &hal_ev, -1);
}

static void send_ssp_request(const bdaddr_t *addr, uint8_t variant,
							uint32_t passkey)
{
	struct hal_ev_ssp_request ev;

	/* TODO name and CoD of remote devices should probably be cached */
	memset(&ev, 0, sizeof(ev));
	bdaddr2android(addr, ev.bdaddr);
	ev.pairing_variant = variant;
	ev.passkey = passkey;

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH, HAL_EV_SSP_REQUEST,
						sizeof(ev), &ev, -1);
}

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;
	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);

	if (ev->confirm_hint)
		send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_CONSENT, 0);
	else
		send_ssp_request(&ev->addr.bdaddr, 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;
	char dst[18];

	if (length < sizeof(*ev)) {
		error("Too small passkey request event");
		return;
	}

	ba2str(&ev->addr.bdaddr, dst);
	DBG("%s", dst);

	send_ssp_request(&ev->addr.bdaddr, 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;
	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)
		send_ssp_request(&ev->addr.bdaddr, HAL_SSP_VARIANT_NOTIF,
								ev->passkey);
}

static void mgmt_discovering_event(uint16_t index, uint16_t length,
					const void *param, void *user_data)
{
	const struct mgmt_ev_discovering *ev = param;
	struct hal_ev_discovery_state_changed cp;

	if (length < sizeof(*ev)) {
		error("Too small discovering event");
		return;
	}

	DBG("hci%u type %u discovering %u", adapter->index, ev->type,
							ev->discovering);

	if (adapter->discovering == !!ev->discovering)
		return;

	adapter->discovering = !!ev->discovering;

	DBG("new discovering state %u", ev->discovering);

	if (adapter->discovering) {
		cp.state = HAL_DISCOVERY_STATE_STARTED;
	} else {
		g_slist_free_full(found_devices, g_free);
		found_devices = NULL;

		cp.state = HAL_DISCOVERY_STATE_STOPPED;
	}

	ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
						HAL_EV_DISCOVERY_STATE_CHANGED,
						sizeof(cp), &cp, -1);
}

static void confirm_name_complete(uint8_t status, uint16_t length,
					const void *param, void *user_data)
{
	if (status != MGMT_STATUS_SUCCESS)
		error("Failed to confirm name: %s (0x%02x)",
						mgmt_errstr(status), status);
}

static void confirm_device_name(const bdaddr_t *addr, uint8_t addr_type)
{
	struct mgmt_cp_confirm_name cp;

	memset(&cp, 0, sizeof(cp));
	bacpy(&cp.addr.bdaddr, addr);
	cp.addr.type = addr_type;

	if (mgmt_reply(adapter->mgmt, MGMT_OP_CONFIRM_NAME, adapter->index,
					sizeof(cp), &cp, confirm_name_complete,
					NULL, NULL) == 0)
		error("Failed to send confirm name request");
}

static int bdaddr_cmp(gconstpointer a, gconstpointer b)
{
	const bdaddr_t *bda = a;
	const bdaddr_t *bdb = b;

	return bacmp(bdb, bda);
}

static int fill_device_props(struct hal_property *prop, bdaddr_t *addr,
					uint32_t cod, int8_t rssi, char *name)
{
	uint8_t num_props = 0;

	/* fill cod */
	prop->type = HAL_PROP_DEVICE_CLASS;
	prop->len = sizeof(cod);
	memcpy(prop->val, &cod, prop->len);
	prop = ((void *) prop) + sizeof(*prop) + sizeof(cod);
	num_props++;

	/* fill rssi */
	prop->type = HAL_PROP_DEVICE_RSSI;
	prop->len = sizeof(rssi);
	memcpy(prop->val, &rssi, prop->len);
	prop = ((void *) prop) + sizeof(*prop) + sizeof(rssi);
	num_props++;

	/* fill name */
	if (name) {
		prop->type = HAL_PROP_DEVICE_NAME;
		prop->len = HAL_MAX_NAME_LENGTH;
		strncpy((char *) prop->val, name, HAL_MAX_NAME_LENGTH - 1);
		prop = ((void *) prop) + sizeof(*prop) + HAL_MAX_NAME_LENGTH;
		num_props++;
	}

	return num_props;
}

static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
					int8_t rssi, bool confirm,
					const uint8_t *data, uint8_t data_len)
{
	bool is_new_dev = false;
	size_t props_size = 0;
	size_t buff_size = 0;
	void *buf;
	struct eir_data eir;
	GSList *l;
	bdaddr_t *remote = NULL;
	int err;

	memset(&eir, 0, sizeof(eir));

	err = eir_parse(&eir, data, data_len);
	if (err < 0) {
		error("Error parsing EIR data: %s (%d)", strerror(-err), -err);
		return;
	}

	l = g_slist_find_custom(found_devices, bdaddr, bdaddr_cmp);
	if (l)
		remote = l->data;

	if (!remote) {
		char addr[18];

		remote = g_new0(bdaddr_t, 1);
		bacpy(remote, bdaddr);

		found_devices = g_slist_prepend(found_devices, remote);
		is_new_dev = true;

		ba2str(remote, addr);
		DBG("New device found: %s", addr);
	}

	props_size += sizeof(struct hal_property) + sizeof(eir.class);
	props_size += sizeof(struct hal_property) + sizeof(rssi);

	if (eir.name)
		props_size += sizeof(struct hal_property) + HAL_MAX_NAME_LENGTH;

	if (is_new_dev) {
		struct hal_ev_device_found *ev = NULL;
		struct hal_property *prop = NULL;

		/* with new device we also send bdaddr prop */
		props_size += sizeof(struct hal_property) + sizeof(eir.addr);

		buff_size = sizeof(struct hal_ev_device_found) + props_size;
		buf = g_new0(char, buff_size);
		ev = buf;
		prop = ev->props;

		/* fill first prop with bdaddr */
		prop->type = HAL_PROP_DEVICE_ADDR;
		prop->len = sizeof(bdaddr_t);
		bdaddr2android(bdaddr, prop->val);
		prop = ((void *) prop) + sizeof(*prop) + sizeof(bdaddr_t);
		ev->num_props += 1;

		/* fill eir, name, and cod props */
		ev->num_props += fill_device_props(prop, remote, eir.class,
								rssi, eir.name);

		ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
							HAL_EV_DEVICE_FOUND,
							buff_size, ev, -1);
		g_free(buf);
	} else {
		struct hal_ev_remote_device_props *ev = NULL;

		buff_size = sizeof(*ev) + props_size;
		buf = g_new0(char, buff_size);
		ev = buf;

		ev->num_props = fill_device_props(ev->props, remote, eir.class,
								rssi, eir.name);

		ev->status = HAL_STATUS_SUCCESS;
		bdaddr2android(bdaddr, ev->bdaddr);

		ipc_send(notification_io, HAL_SERVICE_ID_BLUETOOTH,
						HAL_EV_REMOTE_DEVICE_PROPS,
						buff_size, ev, -1);
		g_free(buf);
	}

	if (confirm) {
		char addr[18];

		ba2str(bdaddr, addr);
		info("Device %s needs name confirmation.", addr);
		confirm_device_name(bdaddr, bdaddr_type);
	}

	eir_data_free(&eir);
}

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;
	char addr[18];
{'fraction_non_alphanumeric': 0.12383443256765977, 'fraction_numerical': 0.014024713820028808, 'mean_word_length': 4.467979274611399, 'pattern_counts': {'":': 0, '<': 35, '<?xml version=': 0, '>': 181, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2436812', 'n_tokens_mistral': 10879, 'n_tokens_neox': 9946, 'n_words': 2370}