#define GATT_CHAR_INTERFACE "org.bluez.GattCharacteristic1"
#define GATT_DEFAULT_TIMEOUT (6 * 1000) /* Dependent on supervision timeout 6 sec */
+#define BT_INDICATION_TIMEOUT_MAX 30000 /* Timeout for Indication from OTP Server in msec */
/* OTP Notification Request structure */
typedef struct {
char *handle;
char *sender;
+ unsigned int notification_timeout_id;
int req_id;
} bt_otp_notification_info;
g_clear_error(&error);
status = BLUETOOTH_ERROR_INTERNAL;
}
- }
-
- if (result) {
+ } else {
g_variant_get(result, "(i)", &status);
+ g_variant_unref(result);
}
BT_DBG("Status [%d]", status);
param);
out_param = g_variant_new_from_data((const GVariantType*)"i",
- result, sizeof(int), TRUE, NULL, NULL);
+ &status, sizeof(int), TRUE, NULL, NULL);
if (req_info) {
g_dbus_method_invocation_return_value(req_info->context,
_bt_delete_request_list(req_info->req_id);
}
-
- g_variant_unref(result);
}
int bt_otp_server_init(int request_id, const char *directory)
g_clear_error(&error);
status = BLUETOOTH_ERROR_INTERNAL;
}
- }
-
- if (result) {
+ } else {
g_variant_get(result, "(i)", &status);
+ g_variant_unref(result);
}
BT_DBG("Status [%d]", status);
if (req_info) {
out_param = g_variant_new_from_data((const GVariantType*)"i",
- result, sizeof(int), TRUE, NULL, NULL);
+ &status, sizeof(int), TRUE, NULL, NULL);
g_dbus_method_invocation_return_value(req_info->context,
g_variant_new("(iv)", status, out_param));
_bt_delete_request_list(req_info->req_id);
}
- g_variant_unref(result);
-
if (otp_gproxy) {
g_object_unref(otp_gproxy);
otp_gproxy = NULL;
g_free(info);
}
+static int __bluetooth_get_att_error_code(GError *error)
+{
+ int att_ecode = 0;
+ int len;
+ char *str = NULL;
+
+ BT_ERR("Error : %s", error->message);
+ str = g_strrstr(error->message, "ATT error: 0x");
+ if (str) {
+ len = strlen(str);
+ att_ecode = g_ascii_xdigit_value(str[len-2]) << 4;
+ att_ecode += g_ascii_xdigit_value(str[len-1]);
+ } else
+ return BLUETOOTH_ATT_ERROR_INTERNAL;
+
+ return att_ecode;
+}
+
static void __bt_otp_read_char_cb(GObject *source_object,
GAsyncResult *res, gpointer user_data)
{
bt_gatt_char_descriptor_property_t att_value = { 0, };
GDBusConnection *system_gconn = NULL;
GVariant *var_data, *param = NULL;
- int result = BLUETOOTH_ERROR_NONE;
+ int result = BLUETOOTH_ATT_ERROR_NONE;
bt_otp_read_req_info *info = NULL;
GByteArray *gp_byte_array = NULL;
request_info_t *req_info = NULL;
value = g_dbus_connection_call_finish(system_gconn, res, &error);
if (error) {
- BT_ERR("Error : %s \n", error->message);
- g_free(handle);
- if (info) {
- req_info = _bt_get_request_info(info->req_id);
- __bt_otp_remove_read_info(info);
- }
- result = BLUETOOTH_ERROR_INTERNAL;
+ result = __bluetooth_get_att_error_code(error);
+ att_value.val_len = 0;
goto dbus_return;
}
- gp_byte_array = g_byte_array_new();
g_variant_get(value, "(ay)", &iter);
+ gp_byte_array = g_byte_array_new();
+
while (g_variant_iter_loop(iter, "y", &g_byte))
g_byte_array_append(gp_byte_array, &g_byte, 1);
otp_data = (char *)g_memdup(att_value.val, att_value.val_len);
+dbus_return:
var_data = g_variant_new_from_data((const GVariantType*)"ay",
otp_data, att_value.val_len, TRUE, NULL, NULL);
__bt_otp_remove_read_info(info);
}
-dbus_return:
if (req_info == NULL) {
BT_ERR("OTP data read Request not found!!");
goto done;
{
GDBusConnection *conn;
bt_otp_read_req_info *info = NULL;
+ char *charc_handle = NULL;
GVariantBuilder *builder = NULL;
- char *charc_handle = g_strdup(handle);
guint16 offset = 0;
BT_CHECK_PARAMETER(handle, return);
/* If OTP data read already pending on same Server, then return In progress */
if (__bt_otp_get_read_info(handle) != NULL) {
- BT_ERR("Read Req is ongoing in remote server [%s]", charc_handle);
- g_free(charc_handle);
+ BT_ERR("Read Req is ongoing in remote server [%s]", handle);
return BLUETOOTH_ERROR_IN_PROGRESS;
}
g_variant_builder_add(builder, "{sv}", "offset",
g_variant_new("q", offset));
+ charc_handle = g_strdup(handle);
+
g_dbus_connection_call(conn,
BT_BLUEZ_NAME,
handle,
g_free(info->handle);
if (info->sender)
g_free(info->sender);
+ if (info->notification_timeout_id > 0) {
+ g_source_remove(info->notification_timeout_id);
+ info->notification_timeout_id = 0;
+ }
g_free(info);
}
if (info)
req_info = _bt_get_request_info(info->req_id);
- /* CCCD Enable request failed */
+ /* If CCCD Enable request failed for any reason, reset timer */
if (result != BLUETOOTH_ERROR_NONE && info != NULL) {
BT_ERR("Activation Request failed");
+ /* Reset Timer */
+ if (info->notification_timeout_id > 0) {
+ g_source_remove(info->notification_timeout_id);
+ info->notification_timeout_id = 0;
+ }
+
/* Remove Indication Info */
__bt_otp_remove_notification_info(info);
} else {
int _bt_otp_enable_notification(int request_id, char *sender, char *handle)
{
bt_otp_notification_info *info = NULL;
- char *charc_handle = g_strdup(handle);
+ char *charc_handle = NULL;
GDBusConnection *conn;
BT_CHECK_PARAMETER(handle, return);
if (__bt_otp_get_notification_info(handle) != NULL) {
BT_ERR("Activation is already ongoing for same remote server");
- g_free(charc_handle);
return BLUETOOTH_ERROR_IN_PROGRESS;
}
+ charc_handle = g_strdup(handle);
+
BT_INFO("Start Notify to Bluez");
g_dbus_connection_call(conn,
BT_BLUEZ_NAME,
BT_DBG("-");
return BLUETOOTH_ERROR_NONE;
}
+
+static void __bt_otp_write_request_cb(GObject *source_object,
+ GAsyncResult *res, gpointer user_data)
+{
+ GError *error = NULL;
+ GDBusConnection *system_gconn = NULL;
+ GVariant *value = NULL;
+ GVariant *param = NULL;
+ GVariant *out_param1 = NULL;
+ int result = BLUETOOTH_ATT_ERROR_NONE;
+ char *handle = NULL;
+ bt_otp_notification_info *info = NULL;
+ request_info_t *req_info = NULL;
+ BT_DBG("+");
+
+ system_gconn = _bt_gdbus_get_system_gconn();
+ value = g_dbus_connection_call_finish(system_gconn, res, &error);
+
+ if (error)
+ result = __bluetooth_get_att_error_code(error);
+
+ handle = (char *)user_data;
+ info = __bt_otp_get_notification_info(handle);
+ if (info)
+ req_info = _bt_get_request_info(info->req_id);
+
+ /* Is Activation request failed for any reason, reset timer */
+ if (result != BLUETOOTH_ATT_ERROR_NONE && info != NULL) {
+ BT_ERR("Activation Request failed");
+ /* Remove Indication Info */
+ __bt_otp_remove_notification_info(info);
+ }
+
+ /* Activation Request successful */
+ if (info) {
+ param = g_variant_new("(is)", result, handle);
+ _bt_send_event_to_dest(info->sender, BT_OTP_EVENT,
+ BLUETOOTH_EVENT_OTP_WRITE_CHAR_VAL,
+ param);
+ }
+
+ if (req_info == NULL) {
+ BT_ERR("OTP Write Request is not found!!");
+ goto done;
+ }
+
+ if (req_info->context == NULL)
+ goto done;
+
+ out_param1 = g_variant_new_from_data((const GVariantType*)"ay",
+ handle, __get_handle_length(handle), TRUE, NULL, NULL);
+ g_dbus_method_invocation_return_value(req_info->context,
+ g_variant_new("(iv)", result, out_param1));
+
+ _bt_delete_request_list(req_info->req_id);
+
+done:
+ if (value)
+ g_variant_unref(value);
+ if (error)
+ g_clear_error(&error);
+ if (handle)
+ g_free(handle);
+
+ BT_DBG("-");
+ return;
+}
+
+static void __bt_otp_send_indication_event(bt_otp_notification_info *info,
+ unsigned char *buffer, int len, int result)
+{
+ GVariant *otp_data;
+ GVariant *param;
+
+ otp_data = g_variant_new_from_data((const GVariantType*)"ay",
+ buffer, len, TRUE, NULL, NULL);
+
+ BT_DBG("Send Indication event to sender");
+ param = g_variant_new("(is@ay)", result, info->handle, otp_data);
+ _bt_send_event_to_dest(info->sender, BT_OTP_EVENT,
+ BLUETOOTH_EVENT_OTP_INDICATION,
+ param);
+
+ /* Remove info from list */
+ __bt_otp_remove_notification_info(info);
+}
+
+static bool __bt_otp_indication_timeout_cb(gpointer user_data)
+{
+ char *handle = NULL;
+ handle = (char *) user_data;
+ bt_otp_notification_info *info = NULL;
+ /* Indication:Fail*/
+ info = __bt_otp_get_notification_info(handle);
+ if (info) {
+ BT_DBG("Activation timer Expired [Server] [%s]", info->handle);
+ __bt_otp_send_indication_event(info, NULL, 0, BLUETOOTH_ERROR_INTERNAL);
+ }
+
+ return FALSE;
+}
+
+int _bt_otp_write_characteristic_value(int request_id, char *sender, char *handle,
+ unsigned char *param, int length)
+{
+ GVariantBuilder *builder1;
+ GVariant *val;
+ GVariant *options;
+ GVariantBuilder *builder2;
+ guint16 offset = 0;
+ bt_otp_notification_info *info = NULL;
+ GDBusConnection *conn;
+ char *charc_handle = NULL;
+ int i;
+
+ BT_DBG("+");
+
+ BT_CHECK_PARAMETER(handle, return);
+ BT_CHECK_PARAMETER(sender, return);
+ BT_CHECK_PARAMETER(param, return);
+
+ conn = _bt_gdbus_get_system_gconn();
+ retv_if(conn == NULL, BLUETOOTH_ERROR_INTERNAL);
+
+ BT_DBG("OTP Write Characteristic value handle [%s] data length [%d]", handle, length);
+ /* Check if activation is ongoing for the same Remote Server */
+
+ info = __bt_otp_get_notification_info(handle);
+ if (info && info->notification_timeout_id > 0) {
+ BT_ERR("Write Request is already ongoing in remote server");
+ return BLUETOOTH_ERROR_IN_PROGRESS;
+ }
+
+ builder1 = g_variant_builder_new(G_VARIANT_TYPE("ay"));
+
+ for (i = 0; i < length; i++) {
+ g_variant_builder_add(builder1, "y", param[i]);
+ }
+
+ val = g_variant_new("ay", builder1);
+ builder2 = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+ /*offset*/
+ g_variant_builder_add(builder2, "{sv}", "offset",
+ g_variant_new_uint16(offset));
+
+ options = g_variant_new("a{sv}", builder2);
+
+ charc_handle = g_strdup(handle);
+
+ /* Activate Control Point */
+ g_dbus_connection_call(conn,
+ BT_BLUEZ_NAME,
+ handle,
+ GATT_CHAR_INTERFACE,
+ "WriteValue",
+ g_variant_new("(@ay@a{sv})",
+ val, options),
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL,
+ (GAsyncReadyCallback)__bt_otp_write_request_cb,
+ (gpointer)charc_handle);
+
+ g_variant_builder_unref(builder1);
+ g_variant_builder_unref(builder2);
+
+ if (info == NULL) {
+ info = g_malloc0(sizeof(bt_otp_notification_info));
+ info->handle = g_strdup(handle);
+ info->sender = g_strdup(sender);
+ otp_notification_info_list = g_slist_append(otp_notification_info_list, info);
+ }
+ info->req_id = request_id;
+ /* Set timeout only for cp charc */
+ info->notification_timeout_id = g_timeout_add(BT_INDICATION_TIMEOUT_MAX,
+ (GSourceFunc)__bt_otp_indication_timeout_cb, (gpointer)info->handle);
+
+ BT_DBG("-");
+ return BLUETOOTH_ERROR_NONE;
+}
+
+void _bt_otp_check_indication(const char *path, GVariant *msg)
+{
+ bt_otp_notification_info *info = NULL;
+ unsigned char *buffer = NULL;
+ int len = 0;
+ int i;
+ GVariant *value = NULL;
+ BT_DBG("+");
+
+ info = __bt_otp_get_notification_info((char *)path);
+
+ if (info) {
+ g_variant_get(msg, "(is@ay)", NULL, NULL, &value);
+ len = g_variant_get_size(value);
+ BT_DBG("Indication data from Server len[%d]", len);
+ if (len > 0) {
+ buffer = (unsigned char *)g_variant_get_data(value);
+ /* DEBUG */
+ for (i = 0; i < len; i++)
+ BT_DBG("%.2x", buffer[i]);
+ }
+
+ /* Reset Timer */
+ if (info->notification_timeout_id > 0)
+ g_source_remove(info->notification_timeout_id);
+
+ /* Send Indication & info removed internally */
+ __bt_otp_send_indication_event(info, buffer, len, BLUETOOTH_ERROR_NONE);
+
+ if (value)
+ g_variant_unref(value);
+ }
+ BT_DBG("-");
+}
+
+int _bt_otp_connect_otc(int req_id, const bluetooth_device_address_t *bd_addr)
+{
+ char device_address[BT_ADDRESS_STRING_SIZE] = { 0 };
+ gchar *device_path = NULL;
+ GDBusProxy *device_proxy = NULL;
+ GDBusConnection *conn;
+ int ret = BLUETOOTH_ERROR_NONE;
+ GVariant *result = NULL;
+ GError *err = NULL;
+
+ BT_CHECK_PARAMETER(bd_addr, return);
+
+ _bt_convert_addr_type_to_string(device_address,
+ (unsigned char *)bd_addr->addr);
+
+ conn = _bt_gdbus_get_system_gconn();
+ retv_if(conn == NULL, BLUETOOTH_ERROR_INTERNAL);
+
+ device_path = _bt_get_device_object_path(device_address);
+ if (device_path == NULL) {
+ BT_DBG("device_path NULL");
+ ret = BLUETOOTH_ERROR_INTERNAL;
+ goto fail;
+ }
+
+ retv_if(device_path == NULL, BLUETOOTH_ERROR_INTERNAL);
+
+ device_proxy = g_dbus_proxy_new_sync(conn,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL, BT_BLUEZ_NAME,
+ device_path, BT_DEVICE_INTERFACE,
+ NULL, NULL);
+ g_free(device_path);
+ retv_if(device_proxy == NULL, BLUETOOTH_ERROR_INTERNAL);
+
+ result = g_dbus_proxy_call_sync(device_proxy, "ConnectOtc",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &err);
+
+ if (result == NULL) {
+ if (err != NULL) {
+ g_dbus_error_strip_remote_error(err);
+ BT_ERR("OTC Connect Error: %s\n", err->message);
+ if (g_strcmp0(err->message, "Already Exists") == 0)
+ ret = BLUETOOTH_ERROR_ALREADY_INITIALIZED;
+ else
+ ret = BLUETOOTH_ERROR_INTERNAL;
+ g_error_free(err);
+ }
+ }
+ g_variant_unref(result);
+
+fail:
+ g_object_unref(device_proxy);
+ return ret;
+}
+
+int _bt_otp_disconnect_otc(const bluetooth_device_address_t *bd_addr)
+{
+ char device_address[BT_ADDRESS_STRING_SIZE] = { 0 };
+ gchar *device_path = NULL;
+ GError *error = NULL;
+ GDBusProxy *device_proxy = NULL;
+ GDBusProxy *adapter_proxy;
+ GDBusConnection *conn;
+ int ret = BLUETOOTH_ERROR_NONE;
+
+ BT_CHECK_PARAMETER(bd_addr, return);
+
+ _bt_convert_addr_type_to_string(device_address,
+ (unsigned char *)bd_addr->addr);
+
+ conn = _bt_gdbus_get_system_gconn();
+ retv_if(conn == NULL, BLUETOOTH_ERROR_INTERNAL);
+
+ adapter_proxy = _bt_get_adapter_proxy();
+ retv_if(adapter_proxy == NULL, BLUETOOTH_ERROR_INTERNAL);
+
+ device_path = _bt_get_device_object_path(device_address);
+ if (device_path == NULL) {
+ BT_DBG("device_path NULL");
+ return BLUETOOTH_ERROR_INTERNAL;
+ }
+
+ retv_if(device_path == NULL, BLUETOOTH_ERROR_INTERNAL);
+
+ device_proxy = g_dbus_proxy_new_sync(conn,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL, BT_BLUEZ_NAME,
+ device_path,
+ BT_DEVICE_INTERFACE,
+ NULL, NULL);
+ g_free(device_path);
+ retv_if(device_proxy == NULL, BLUETOOTH_ERROR_INTERNAL);
+
+ g_dbus_proxy_call_sync(device_proxy, "DisconnectOtc",
+ NULL,
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ NULL,
+ &error);
+
+ if (error) {
+ BT_ERR("DisconnectOtc Call Error %s[%s]",
+ error->message, device_address);
+ g_error_free(error);
+ g_object_unref(device_proxy);
+ return BLUETOOTH_ERROR_INTERNAL;
+ }
+
+ if (device_proxy)
+ g_object_unref(device_proxy);
+
+ return ret;
+}