Skip to content
This repository has been archived by the owner on Jun 27, 2019. It is now read-only.

Add the Bluetooth agent API (for BlueZ) v3 #2107

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 68 additions & 1 deletion src/lib/common/sol-bus.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,10 @@ _match_properties_changed(sd_bus_message *m, void *userdata,
SOL_INT_CHECK(r, < 0, r);

t = find_property_table(client, iface, path);
SOL_NULL_CHECK(t, -ENOENT);
if (!t) {
SOL_DBG("Property table not found for iface %s path %s", iface, path);
return 0;
}

/* Ignore PropertiesChanged signals until the GetAll() method returns */
if (t->getall_slot)
Expand Down Expand Up @@ -947,3 +950,67 @@ sol_bus_log_callback(sd_bus_message *reply, void *userdata,

return -1;
}

SOL_API int
sol_bus_parse_dict(sd_bus_message *m, const struct sol_bus_dict_entry *dict)
{
int r;

r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sv}");
SOL_INT_CHECK(r, < 0, r);

do {
const struct sol_bus_dict_entry *entry;
const char *member, *contents;
char type;

r = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv");
if (r <= 0) {
r = 0;
break;
}

r = sd_bus_message_read_basic(m, SD_BUS_TYPE_STRING, &member);
SOL_INT_CHECK_GOTO(r, < 0, end);

r = sd_bus_message_peek_type(m, &type, &contents);
SOL_INT_CHECK_GOTO(r, < 0, end);

for (entry = dict; entry && entry->name; entry++) {
if (streq(entry->name, member))
break;
}

if (entry->name) {
size_t len;

r = sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, contents);
SOL_INT_CHECK_GOTO(r, < 0, end);

len = strlen(contents);

if (entry->type == 'v' && entry->parse_variant)
r = entry->parse_variant(m, entry->value);
else if (len == 1 && entry->type == contents[0])
r = sd_bus_message_read_basic(m, entry->type, entry->value);
else {
SOL_WRN("Invalid type in message '%s', expecting '%c'",
contents, entry->type);
r = -EINVAL;
}
SOL_INT_CHECK_GOTO(r, < 0, end);

r = sd_bus_message_exit_container(m);
SOL_INT_CHECK_GOTO(r, < 0, end);
} else {
r = sd_bus_message_skip(m, "v");
SOL_INT_CHECK_GOTO(r, < 0, end);
}

r = sd_bus_message_exit_container(m);
SOL_INT_CHECK_GOTO(r, < 0, end);
} while (1);

end:
return r;
}
41 changes: 41 additions & 0 deletions src/lib/common/sol-bus.h
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,44 @@ int sol_bus_remove_interfaces_watch(struct sol_bus_client *client,
*/
int sol_bus_log_callback(sd_bus_message *reply, void *userdata,
sd_bus_error *ret_error);

/**
* @brief Represents an entry in a D-Bus dictionary
*
* A common idiom in D-Bus is having an array of type 'a{sv}', representing
* a dictionary, this struct and sol_bus_parse_dict() help parsing those
* data structures.
*/
struct sol_bus_dict_entry {
/**
* @brief The name of the dictionary entry
*/
const char *name;
/**
* @brief The type of value of the property in the dictionary
*
* It can be any of the basic types, or 'v' (SD_BUS_TYPE_VARIANT), in
* case the type is not basic, the function parse_variant() will be called.
*/
char type;
/**
* @brief In which place to store the parsed value
*/
void *value;
/**
* @brief Function to be called in case the value being parsed is not basic.
*/
int (*parse_variant)(sd_bus_message *m, void *value);
};

/**
* @brief Helper to parse D-Bus dictionaries
*
* @see #sol_bus_dict_entry, the @a dict array must be terminated by an empty entry.
*
* @param m D-Bus message containing the D-Bus dictionary array
* @param dict Dictionary representation, informing how to parse the dictionary.
*
* @return 0 on success, -errno otherwise.
*/
int sol_bus_parse_dict(sd_bus_message *m, const struct sol_bus_dict_entry *dict);
208 changes: 208 additions & 0 deletions src/lib/comms/include/sol-bluetooth.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,15 @@ void sol_bt_conn_unref(struct sol_bt_conn *conn);
const struct sol_network_link_addr *sol_bt_conn_get_addr(
const struct sol_bt_conn *conn);

/**
* @brief Returns the device info associated with a connection.
*
* @param conn The reference to a connection.
*
* @return Information about the device connected
*/
const struct sol_bt_device_info *sol_bt_conn_get_device_info(const struct sol_bt_conn *conn);

/**
* @brief Attempts to establish a connection with a remote device.
*
Expand Down Expand Up @@ -335,6 +344,205 @@ struct sol_bt_scan_pending *sol_bt_start_scan(
*/
int sol_bt_stop_scan(struct sol_bt_scan_pending *handle);

/**
* @brief Initiates a pairing procedure with an device
*
* The callback will not be called if sol_bt_conn_pair_cancel() is called
* and returns successfully.
*
* @param conn Connection with the device to pair
* @param cb Callback to be called when the pairing finishes.
*
* @return 0 on success, -errno otherwise.
*/
int sol_bt_conn_pair(struct sol_bt_conn *conn,
void (*on_pair)(void *user_data, bool success, struct sol_bt_conn *conn),
void *user_data);

/**
* @brief Cancels a pairing attempt
*
* @param conn Connection in which the pairing was initiated
*
* @return 0 on success, -errno otherwise.
*/
int sol_bt_conn_pair_cancel(struct sol_bt_conn *conn);

/**
* @brief Forgets a device, removing any stored security key
*
* Removes any security key saved in permanent stoorage associated with
* a device.
*
* @param addr Address of the device to be removed
*
* @return 0 on success, -errno otherwise.
*/
int sol_bt_forget_device(const struct sol_network_link_addr *addr);

/**
* @brief Represents an Bluetooth agent
*
* The agent is used when user input is necessary, when pairing, for example, we
* may request a passkey to be displayed to the user.
*
* When pairing with devices, the input and output capabilities are taken into account,
* the callbacks not set to NULL are used to determine the input/output capabilities
* of the system.
*/
struct sol_bt_agent {
/**
* @brief Called when a pairing procedure needs to display a passkey.
*
* Indicates that the @a passkey should be displayed to the user.
*
* @param data User data
* @param conn Connection being authenticated
* @param passkey The passkey that needs to be displayed
*/
void (*passkey_display)(void *data, struct sol_bt_conn *conn, uint32_t passkey);

/**
* @brief Called when a pairing procedure needs a passkey to be input.
*
* Indicates that the user needs to input a passkey. sol_bt_agent_reply_passkey_entry()
* should be called with the input passkey.
*
* @param data User data
* @param conn Connection being authenticated
*/
void (*passkey_entry)(void *data, struct sol_bt_conn *conn);

/**
* @brief Called when a pairing procedure needs a passkey to be confirmed.
*
* Indicates that the user needs confirm a passkey. sol_bt_agent_reply_passkey_confirm()
* should be called with the input passkey, sol_bt_agent_reply_cancel() should be called
* otherwise.
*
* @param data User data
* @param conn Connection being authenticated
* @param passkey The passkey that needs to be confirmed
*/
void (*passkey_confirm)(void *data, struct sol_bt_conn *conn, uint32_t passkey);

/**
* @brief Called when the pairing procedure is cancelled by the other party.
*
* @param data User data
* @param conn Connection being authenticated
*/
void (*cancel)(void *data, struct sol_bt_conn *conn);

/**
* @brief Called when a pairing attempt needs to be confirmed.
*
* Indicates that the user needs to confirm a pairing attempt.
* sol_bt_agent_reply_pairing_confirm() should be called if the pairing is
* confirmed, sol_bt_agent_reply_cancel() otherwise.
*
* @param data User data
* @param conn Connection being authenticated
*/
void (*pairing_confirm)(void *data, struct sol_bt_conn *conn);

/**
* @brief Called when a pairing procedure needs a pincode to be entered.
*
* Indicates that the user needs to input a pincode. sol_bt_agent_reply_pincode_entry()
* should be called with the input pincode, sol_bt_agent_reply_cancel() otherwise.
*
* This is only used when pairing with legacy Bluetooth devices.
*
* @param data User data
* @param conn Connection being authenticated
* @param highsec Informs that the pincode needs to be 16 digits long.
*
*/
void (*pincode_entry)(void *data, struct sol_bt_conn *conn, bool highsec);
};

/**
* @brief Registers an agent for the system
*
* It is only possible to have one agent at a time. Pass NULL to unregister the
* current agent.
*
* @param agent The agent to be registered
* @param data The user data to be passed to each agent callback
* @return 0 on sucess, -errno otherwise
*/
int sol_bt_register_agent(const struct sol_bt_agent *agent, const void *data);

/**
* @brief Replies to a request to the user to enter a passkey
*
* This should be called after passkey_entry() is called with the passkey
* entered by the user.
*
* @param conn The connection to be authenticated
* @param passkey The passkey entered by the user
* @return 0 on sucess, -errno otherwise
*/
int sol_bt_agent_finish_passkey_entry(struct sol_bt_conn *conn, uint32_t passkey);

/**
* @brief Informs that the passkey was displayed to the user
*
* This should be called after passkey_display() is called to inform that
* it is no longer displayed.
*
* @param conn The connection to be authenticated
* @return 0 on sucess, -errno otherwise
*/
int sol_bt_agent_finish_passkey_display(struct sol_bt_conn *conn);

/**
* @brief Cancels an attempt to authenticate a connection
*
* Rejects the pairing the attempt.
*
* @param conn The connection to be authenticated
* @param passkey The passkey entered by the user
* @return 0 on sucess, -errno otherwise
*/
int sol_bt_agent_finish_cancel(struct sol_bt_conn *conn);

/**
* @brief Confirms that the same passkey is display in both devices
*
* This should be called after passkey_confirm() is called with the passkey
* to be displayed and confirmed
*
* @param conn The connection to be authenticated
* @param passkey The passkey entered by the user
* @return 0 on sucess, -errno otherwise
*/
int sol_bt_agent_finish_passkey_confirm(struct sol_bt_conn *conn);

/**
* @brief Confirms the pairing attempt
*
* This should be called after pairing_confirm() indicates a pairing attempt.
*
* @param conn The connection to be authenticated
* @param passkey The passkey entered by the user
* @return 0 on sucess, -errno otherwise
*/
int sol_bt_agent_finish_pairing_confirm(struct sol_bt_conn *conn);

/**
* @brief Replies to a request to the user to enter a pincode
*
* This should be called after pincode_entry() is called with the pincode
* entered by the user.
*
* @param conn The connection to be authenticated
* @param passkey The passkey entered by the user
* @return 0 on sucess, -errno otherwise
*/
int sol_bt_agent_finish_pincode_entry(struct sol_bt_conn *conn, const char *pin);

/**
* @}
*/
Expand Down
10 changes: 10 additions & 0 deletions src/lib/comms/include/sol-gatt.h
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@ struct sol_gatt_pending;
const struct sol_gatt_attr *sol_gatt_pending_get_attr(
const struct sol_gatt_pending *op);

/**
* @brief Returns the connection referenced by a pending operation
*
* @param op The pending operation
*
* @return reference to a connection
*/
struct sol_bt_conn *sol_gatt_pending_get_conn(
const struct sol_gatt_pending *op);

/**
* @brief Representation of a GATT Attribute
*/
Expand Down
Loading