Fix the crash in the fail case
[platform/core/connectivity/bluetooth-frwk.git] / bt-service / bt-service-otp.c
index 3e49707..17a2669 100644 (file)
 #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;
 
@@ -118,10 +120,9 @@ void server_init_cb(GObject *object, GAsyncResult *res,
                        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);
@@ -139,7 +140,7 @@ void server_init_cb(GObject *object, GAsyncResult *res,
                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,
@@ -147,8 +148,6 @@ void server_init_cb(GObject *object, GAsyncResult *res,
 
                _bt_delete_request_list(req_info->req_id);
        }
-
-       g_variant_unref(result);
 }
 
 int bt_otp_server_init(int request_id, const char *directory)
@@ -199,10 +198,9 @@ void server_deinit_cb(GObject *object, GAsyncResult *res,
                        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);
@@ -218,7 +216,7 @@ void server_deinit_cb(GObject *object, GAsyncResult *res,
 
        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));
@@ -226,8 +224,6 @@ void server_deinit_cb(GObject *object, GAsyncResult *res,
                _bt_delete_request_list(req_info->req_id);
        }
 
-       g_variant_unref(result);
-
        if (otp_gproxy) {
                g_object_unref(otp_gproxy);
                otp_gproxy = NULL;
@@ -295,13 +291,31 @@ static void __bt_otp_remove_read_info(bt_otp_read_req_info *info)
        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;
@@ -322,19 +336,15 @@ static void __bt_otp_read_char_cb(GObject *source_object,
        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);
 
@@ -345,6 +355,7 @@ static void __bt_otp_read_char_cb(GObject *source_object,
 
        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);
 
@@ -357,7 +368,6 @@ static void __bt_otp_read_char_cb(GObject *source_object,
                __bt_otp_remove_read_info(info);
        }
 
-dbus_return:
        if (req_info == NULL) {
                BT_ERR("OTP data read Request not found!!");
                goto done;
@@ -394,8 +404,8 @@ int _bt_otp_read_characteristic_value(int request_id, char *sender, char *handle
 {
        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);
@@ -408,8 +418,7 @@ int _bt_otp_read_characteristic_value(int request_id, char *sender, char *handle
 
        /* 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;
        }
 
@@ -418,6 +427,8 @@ int _bt_otp_read_characteristic_value(int request_id, char *sender, char *handle
        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,
@@ -471,6 +482,10 @@ static void __bt_otp_remove_notification_info(bt_otp_notification_info *info)
                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);
 }
 
@@ -516,9 +531,15 @@ static void __bt_otp_notification_enable_request_cb(GObject *source_object,
        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 {
@@ -561,7 +582,7 @@ done:
 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);
@@ -574,10 +595,11 @@ int _bt_otp_enable_notification(int request_id, char *sender, char *handle)
 
        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,
@@ -600,3 +622,337 @@ int _bt_otp_enable_notification(int request_id, char *sender, char *handle)
        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;
+}