--- /dev/null
+/*
+ * Bluetooth-frwk
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Atul Kumar Rai <a.rai@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+#include <gio/gunixfdlist.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <dlog.h>
+#include <vconf.h>
+
+#include "bt-hal-hdp-dbus-handler.h"
+#include "bt-hal-dbus-common-utils.h"
+#include "bt-hal-internal.h"
+#include "bt-hal-msg.h"
+
+#define BT_HAL_HDP_MANAGER_INTERFACE "org.bluez.HealthManager1"
+#define BT_HAL_HDP_DEVICE_INTERFACE "org.bluez.HealthDevice1"
+#define BT_HAL_HDP_CHANNEL_INTERFACE "org.bluez.HealthChannel1"
+#define BT_HAL_HDP_CHANNEL_ID_MAX 255
+
+static GSList *app_list;
+typedef struct {
+ int app_id;
+ char address[BT_HAL_ADDRESS_STRING_SIZE];
+ int channel_id;
+ char *obj_path;
+ int fd;
+}hdp_conn_info_t;
+
+typedef struct {
+ int id;
+ char *path;
+ int channel_type;
+ GSList *conn_list;
+}hdp_app_data_t;
+
+static handle_stack_msg event_cb = NULL;
+static int latest_id = -1;
+static gboolean id_used[BT_HAL_HDP_CHANNEL_ID_MAX];
+
+static void __hdp_send_conn_event(hdp_conn_info_t *conn_info, int state)
+{
+ struct hal_ev_hdp_conn_state ev;
+
+ DBG("+");
+
+ /* Prepare to send HDP app registration state event */
+ memset(&ev, 0, sizeof(ev));
+ ev.app_id = conn_info->app_id;
+ _bt_convert_addr_string_to_type(ev.bdaddr, conn_info->address);
+ ev.mdep_index = 0;
+ ev.channel_id = conn_info->channel_id;
+ ev.data_fd = conn_info->fd;
+ ev.channel_state = state;
+ if (!event_cb)
+ ERR("HDP dbus handler callback not registered");
+ else
+ event_cb(HAL_EV_HDP_CONN_STATE, (void *)&ev, sizeof(ev));
+
+ DBG("-");
+}
+
+int __hdp_assign_channel_id(void)
+{
+ int index;
+
+ DBG("latest_id: %d", latest_id);
+
+ index = latest_id + 1;
+ if (index >= BT_HAL_HDP_CHANNEL_ID_MAX)
+ index = 0;
+
+ DBG("index: %d", index);
+
+ while (id_used[index] == TRUE) {
+ if (index == latest_id) {
+ /* No available ID */
+ ERR("All request ID is used");
+ return -1;
+ }
+
+ index++;
+ if (index >= BT_HAL_HDP_CHANNEL_ID_MAX)
+ index = 0;
+ }
+
+ latest_id = index;
+ id_used[index] = TRUE;
+ DBG("Assigned Id: %d", latest_id);
+
+ return latest_id;
+}
+
+void __hdp_delete_channnel_id(int id)
+{
+ if (id >= BT_HAL_HDP_CHANNEL_ID_MAX || id < 0) {
+ ERR("Invalid id %d", id);
+ return;
+ }
+
+ id_used[id] = FALSE;
+ latest_id = id - 1;
+ DBG("id: %d, latest_id: %d", id, latest_id);
+}
+
+static hdp_app_data_t *__get_hdp_app_by_id(int id)
+{
+ GSList *l;
+
+ for (l = app_list; l != NULL; l = l->next) {
+ hdp_app_data_t *app = l->data;
+ if (app && app->id == id)
+ return app;
+ }
+
+ return NULL;
+}
+
+static hdp_app_data_t *__get_hdp_app_by_path(char *app_path)
+{
+ GSList *l;
+
+ if (!app_path)
+ return NULL;
+
+ for (l = app_list; l != NULL; l = l->next) {
+ hdp_app_data_t *app = l->data;
+ if (app && 0 == g_strcmp0(app->path, app_path))
+ return app;
+ }
+
+ return NULL;
+}
+
+static hdp_conn_info_t *__hdp_find_conn_info_by_ch_path(const char *channel_path)
+{
+ GSList *l;
+ GSList *l1;
+
+ if (!channel_path)
+ return NULL;
+
+ for (l = app_list; l != NULL; l = l->next) {
+ hdp_app_data_t *app = l->data;
+ if (!app)
+ continue;
+
+ for (l1 = app->conn_list; l1 != NULL; l1 = l1->next) {
+ hdp_conn_info_t *info = l1->data;
+ if (info && 0 == g_strcmp0(info->obj_path, channel_path))
+ return info;
+ }
+ }
+
+ return NULL;
+}
+
+static hdp_conn_info_t *__hdp_find_conn_info_by_ch_id(int channel_id)
+{
+ GSList *l;
+ GSList *l1;
+
+ for (l = app_list; l != NULL; l = l->next) {
+ hdp_app_data_t *app = l->data;
+ if (!app)
+ continue;
+
+ for (l1 = app->conn_list; l1 != NULL; l1 = l1->next) {
+ hdp_conn_info_t *info = l1->data;
+ if (info && info->channel_id == channel_id)
+ return info;
+ }
+ }
+
+ return NULL;
+}
+
+static hdp_conn_info_t *__hdp_app_create_incomming_conn_info(const char *path)
+{
+ hdp_conn_info_t *conn_info = NULL;
+ hdp_app_data_t *app;
+ GDBusConnection *conn;
+ GDBusProxy *proxy;
+ GVariantIter *property_iter;
+ const char *property;
+ GVariant *value = NULL;
+ GVariant *reply;
+ GError *err = NULL;
+ char *device = NULL;
+ char *dev_path;
+ char *app_path = NULL;
+ char *type_qos = NULL;
+ gsize len;
+
+ DBG("+");
+
+ conn = _bt_get_system_gconn();
+ if(!conn) {
+ ERR("_bt_get_system_gconn returned NULL, return");
+ return NULL;
+ }
+
+ /* Retrive device info */
+ proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ BT_HAL_BLUEZ_NAME,
+ path,
+ BT_HAL_PROPERTIES_INTERFACE,
+ NULL, &err);
+ if (!proxy) {
+ ERR("Unable to create proxy: %s", err->message);
+ g_clear_error(&err);
+ return NULL;
+ }
+
+ dev_path = g_strdup(BT_HAL_HDP_CHANNEL_INTERFACE);
+ reply = g_dbus_proxy_call_sync(proxy, "GetAll",
+ g_variant_new("(s)", dev_path),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &err);
+ g_free(dev_path);
+ g_object_unref(proxy);
+ if (!reply) {
+ ERR(" HDP:dbus Can't get the reply");
+ if (err) {
+ ERR("%s", err->message);;
+ g_clear_error(&err);
+ }
+ return NULL;
+ }
+
+ g_variant_get(reply, "(a{sv})", &property_iter);
+ while (g_variant_iter_loop(property_iter, "{sv}", &property, &value)) {
+ DBG("String received = %s\n", property);
+ if (g_strcmp0("Type", property) == 0)
+ type_qos = g_variant_dup_string(value, &len);
+ else if (g_strcmp0("Device", property) == 0)
+ device = g_variant_dup_string(value, &len);
+ else if (g_strcmp0("Application", property) == 0)
+ app_path = g_variant_dup_string(value, &len);
+ }
+
+ DBG("QOS = %s, Device = %s, Apphandler = %s", type_qos, device, app_path);
+ g_variant_iter_free(property_iter);
+ g_variant_unref(reply);
+ g_free(device);
+
+ if (!type_qos || !app_path) {
+ ERR("Pasing failed");
+ goto free;
+ }
+
+ app = __get_hdp_app_by_path(app_path);
+ if (!app) {
+ ERR("Could not find the app with path: %s", app_path);
+ goto free;
+ }
+
+ conn_info = g_new0(hdp_conn_info_t, 1);
+ conn_info->channel_id = __hdp_assign_channel_id();
+ _bt_convert_device_path_to_address(path, conn_info->address);
+ conn_info->app_id = app->id;
+ conn_info->fd = -1;
+
+ DBG("App Id: %d, remote_address: %s", app->id, conn_info->address);
+ app->conn_list = g_slist_append(app->conn_list, conn_info);
+
+free:
+ g_free(type_qos);
+ g_free(app_path);
+
+ DBG("-");
+ return conn_info;
+}
+
+static void __hdp_free_conn_info(hdp_conn_info_t *conn_info)
+{
+ DBG("+");
+
+ if (!conn_info) {
+ ERR("conn_info is NULL");
+ return;
+ }
+
+ __hdp_delete_channnel_id(conn_info->channel_id);
+ if (0 < conn_info->fd)
+ close(conn_info->fd);
+ g_free(conn_info->obj_path);
+ g_free(conn_info);
+
+ DBG("-");
+}
+
+static void __hdp_free_app_data(hdp_app_data_t *app)
+{
+ DBG("+");
+
+ g_slist_foreach(app->conn_list, (GFunc)__hdp_free_conn_info, NULL);
+ g_free(app->conn_list);
+ g_free(app->path);
+ g_free(app);
+
+ DBG("-");
+}
+
+static int __hdp_acquire_fd(const char *path)
+{
+ GDBusConnection *conn;
+ GDBusProxy *proxy;
+ GVariant *reply;
+ GUnixFDList *out_fd_list = NULL;
+ GError *err = NULL;
+ int index;
+ int fd;
+
+ DBG("+");
+
+ conn = _bt_get_system_gconn();
+ if(!conn) {
+ ERR("_bt_get_system_gconn returned NULL, return");
+ return -1;
+ }
+
+ proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ BT_HAL_BLUEZ_NAME,
+ path,
+ BT_HAL_HDP_CHANNEL_INTERFACE,
+ NULL, &err);
+ if (!proxy) {
+ ERR("Unable to create proxy: %s", err->message);
+ g_clear_error(&err);
+ return -1;
+ }
+
+ reply = g_dbus_proxy_call_with_unix_fd_list_sync(proxy,
+ "Acquire",
+ NULL, G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &out_fd_list,
+ NULL, &err);
+ g_object_unref(proxy);
+ if (!reply) {
+ ERR(" HDP:****** dbus Can't create application ****");
+ if (err) {
+ ERR("%s", err->message);;
+ g_clear_error(&err);
+ }
+
+ return -1;
+ }
+
+ g_variant_get(reply, "(h)", &index);
+ fd = g_unix_fd_list_get(out_fd_list, index, NULL);
+
+ INFO("File Descriptor = %d, Dev_path = %s \n", fd, path);
+ g_variant_unref(reply);
+ g_object_unref(out_fd_list);
+
+ DBG("-");
+ return fd;
+}
+
+static void __hdp_handle_connect(GVariant *parameters)
+{
+ hdp_conn_info_t *conn_info = NULL;
+ const char *obj_channel_path;
+ int fd;
+
+ DBG("+");
+
+ g_variant_get(parameters, "(&o)", &obj_channel_path);
+ INFO("Channel connected, Path = %s", obj_channel_path);
+
+ conn_info = __hdp_find_conn_info_by_ch_path(obj_channel_path);
+
+ fd = __hdp_acquire_fd(obj_channel_path);
+ if (0 > fd) {
+ ERR("__hdp_acquire_fd failed");
+ goto err;
+ }
+
+ if (!conn_info) {
+ INFO("conn_info not found, incomming connection request");
+ /* Create hdp app connection info */
+ conn_info = __hdp_app_create_incomming_conn_info(obj_channel_path);
+ if (!conn_info) {
+ ERR("Could not create app conn info");
+ close(fd);
+ return;
+ }
+ }
+
+ conn_info->fd = fd;
+ /* Send channel_connect callback with status: success */
+ __hdp_send_conn_event(conn_info, BTHL_CONN_STATE_CONNECTED);
+
+ DBG("-");
+ return;
+err:
+ if (conn_info) {
+ hdp_app_data_t *app = __get_hdp_app_by_id(conn_info->app_id);
+
+ ERR("Outgoing connection failed for app_id:%d, send event", conn_info->app_id);
+ /* Send channel_connect callback with status: disconnected and fd = -1 */
+ __hdp_send_conn_event(conn_info, BTHL_CONN_STATE_DISCONNECTED);
+ app->conn_list = g_slist_remove(app->conn_list, conn_info);
+ __hdp_free_conn_info(conn_info);
+ }
+}
+
+static void __hdp_handle_disconnect(GVariant *parameters,
+ const gchar *object_path)
+{
+ const char *obj_channel_path;
+ hdp_app_data_t *app;
+ hdp_conn_info_t *conn_info;
+
+ DBG("+");
+
+ g_variant_get(parameters, "(&o)", &obj_channel_path);
+ INFO("Channel Deleted, Path = %s, channel_path: %s",
+ object_path, obj_channel_path);
+
+ conn_info = __hdp_find_conn_info_by_ch_path(obj_channel_path);
+ if (!conn_info) {
+ INFO("No conn_info found for channel path:%s", obj_channel_path);
+ return;
+ }
+
+ /* Send channel_connect callback with status: success */
+ __hdp_send_conn_event(conn_info, BTHL_CONN_STATE_DESTROYED);
+
+ app = __get_hdp_app_by_id(conn_info->app_id);
+ app->conn_list = g_slist_remove(app->conn_list, conn_info);
+ __hdp_free_conn_info(conn_info);
+
+ DBG("-");
+}
+
+static void __hdp_handle_property_changed(GVariant *parameters)
+{
+ char *property = NULL;
+ GVariant *value = NULL;
+ char *obj_main_channel_path = NULL;
+ GVariantIter *property_iter = NULL;
+ gsize len;
+
+ DBG("+");
+
+ g_variant_get(parameters, "(a{sv})", &property_iter);
+ while (g_variant_iter_loop(property_iter, "{sv}", &property, &value)) {
+ if (g_strcmp0("MainChannel", property) == 0) {
+ INFO("Property MainChannel received");
+ obj_main_channel_path = g_variant_dup_string(value, &len);
+ DBG("Main Channel Path = %s", obj_main_channel_path);
+ break;
+ }
+ }
+ g_variant_iter_free(property_iter);
+ g_free(property);
+ g_variant_unref(value);
+ g_free(obj_main_channel_path);
+
+ DBG("-");
+}
+
+static void __hdp_event_filter(GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ DBG("Path = %s", object_path);
+ if (object_path == NULL || g_strcmp0(object_path, "/") == 0)
+ return;
+
+ if (signal_name == NULL)
+ return;
+
+ if (strcasecmp(signal_name, "ChannelConnected") == 0)
+ __hdp_handle_connect(parameters);
+
+ else if (strcasecmp(signal_name, "ChannelDeleted") == 0)
+ __hdp_handle_disconnect(parameters, object_path);
+
+ else if (strcasecmp(signal_name, "PropertyChanged") == 0)
+ __hdp_handle_property_changed(parameters);
+
+ return;
+}
+
+static int __hdp_filter_subscribe_signal(GDBusConnection *conn,
+ gboolean subscribe)
+{
+ static guint subs_add_filter_id = 0;
+
+ if (conn == NULL)
+ return BT_HAL_ERROR_INVALID_PARAM;
+
+ if (subscribe && subs_add_filter_id) {
+ }
+
+ if (subscribe) {
+ if (subs_add_filter_id == 0) {
+ subs_add_filter_id = g_dbus_connection_signal_subscribe(
+ conn, NULL, BT_HAL_HDP_DEVICE_INTERFACE,
+ NULL, NULL, NULL, 0,
+ __hdp_event_filter, NULL, NULL);
+ } else {
+ INFO("Signal already subscribed");
+ }
+ } else {
+ if (subs_add_filter_id > 0) {
+ g_dbus_connection_signal_unsubscribe(conn,
+ subs_add_filter_id);
+ subs_add_filter_id = 0;
+ }
+ }
+
+ return BT_HAL_ERROR_NONE;
+}
+
+static int __hdp_add_filter(void)
+{
+ static GDBusConnection *hdp_conn;
+
+ DBG("+");
+
+ hdp_conn = _bt_get_system_gconn();
+ if(!hdp_conn) {
+ ERR("_bt_get_system_gconn returned NULL, return");
+ return BT_HAL_ERROR_INTERNAL;
+ }
+
+ return __hdp_filter_subscribe_signal(hdp_conn, TRUE);
+
+ DBG("-");
+}
+
+static int __hdp_remove_filter(void)
+{
+ static GDBusConnection *hdp_conn;
+
+ DBG("+");
+
+ hdp_conn = _bt_get_system_gconn();
+ if(!hdp_conn) {
+ ERR("_bt_get_system_gconn returned NULL, return");
+ return BT_HAL_ERROR_INTERNAL;
+ }
+
+ return __hdp_filter_subscribe_signal(hdp_conn, FALSE);
+
+ DBG("-");
+}
+
+static void __hdp_send_app_reg_event(int app_id, unsigned int state)
+{
+ struct hal_ev_hdp_app_reg_state ev;
+
+ DBG("+");
+
+ /* Prepare to send HDP app registration state event */
+ memset(&ev, 0, sizeof(ev));
+ ev.app_id = app_id;
+ ev.state = state;
+ if (!event_cb)
+ ERR("HDP dbus handler callback not registered");
+ else
+ event_cb(HAL_EV_HDP_APP_REG_STATE, (void *)&ev, sizeof(ev));
+
+ DBG("-");
+}
+
+static gboolean __hdp_application_created_cb(gpointer data)
+{
+ hdp_app_data_t *app = data;
+
+ DBG("+");
+ if (!app) {
+ ERR("__hdp_application_created_cb called with null app data");
+ return FALSE;
+ }
+
+ app_list = g_slist_append(app_list, app);
+ if (BT_HAL_ERROR_NONE != __hdp_add_filter()) {
+ ERR("Funtion failed");
+ __hdp_send_app_reg_event(app->id, BTHL_APP_REG_STATE_REG_FAILED);
+ return FALSE;
+ }
+
+ __hdp_send_app_reg_event(app->id, BTHL_APP_REG_STATE_REG_SUCCESS);
+
+ DBG("-");
+ return FALSE;
+}
+
+static gboolean __hdp_application_destroyed_cb(gpointer data)
+{
+ hdp_app_data_t *app = data;
+ int len;
+
+ DBG("+");
+ if (!app) {
+ ERR("__hdp_application_destroyed_cb called with null app data");
+ return FALSE;
+ }
+
+ app_list = g_slist_remove(app_list, app);
+
+ len = g_slist_length(app_list);
+ DBG("List length = %d\n", len);
+ if (0 == len)
+ __hdp_remove_filter();
+
+ __hdp_send_app_reg_event(app->id, BTHL_APP_REG_STATE_DEREG_SUCCESS);
+ __hdp_free_app_data(app);
+
+ DBG("-");
+ return FALSE;
+}
+
+static GDBusProxy *__get_health_device_proxy(char *address)
+{
+ GDBusConnection *conn;
+ GDBusProxy *hdp_proxy;
+ GError *err = NULL;
+ char *adapter_path;
+ char *dev_path;
+
+ DBG("+");
+
+ adapter_path = _bt_get_adapter_path();
+ if (!adapter_path) {
+ ERR("Could not get adapter path");
+ return NULL;
+ }
+
+ dev_path = g_strdup_printf("%s/dev_%s", adapter_path, address);
+ if (!dev_path) {
+ ERR("Failed to create dev_path");
+ return NULL;
+ }
+
+ g_strdelimit(dev_path, ":", '_');
+ DBG("dev_path: %s", dev_path);
+
+ conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
+ if(!conn) {
+ ERR("ERROR: Can't get on system bus [%s]", err->message);
+ g_clear_error(&err);
+ g_free(dev_path);
+ return NULL;
+ }
+
+ hdp_proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
+ NULL, BT_HAL_BLUEZ_NAME,
+ dev_path, BT_HAL_HDP_DEVICE_INTERFACE,
+ NULL, NULL);
+ if (!hdp_proxy)
+ ERR("Failed to get the HDP server proxy");
+ g_object_unref(conn);
+ g_free(dev_path);
+
+ DBG("-");
+ return hdp_proxy;
+}
+
+static void __hdp_connect_request_cb(GDBusProxy *hdp_proxy,
+ GAsyncResult *res, gpointer user_data)
+{
+ GVariant *reply;
+ GError *err = NULL;
+ hdp_conn_info_t *conn_info = user_data;
+ hdp_app_data_t *app;
+ char *obj_path = NULL;
+
+ DBG("+");
+
+ reply = g_dbus_proxy_call_finish(hdp_proxy, res, &err);
+ g_object_unref(hdp_proxy);
+ if (!reply) {
+ if (err) {
+ ERR("HDP connection Dbus Call Error: %s", err->message);
+ g_clear_error(&err);
+ }
+ /* Send channel_connect callback with status: disconnected and fd = -1 */
+ __hdp_send_conn_event(conn_info, BTHL_CONN_STATE_DISCONNECTED);
+ __hdp_free_conn_info(conn_info);
+ return;
+ }
+
+ g_variant_get(reply, "(&o)", &obj_path);
+ DBG("Obj Path returned = %s\n", obj_path);
+ conn_info->obj_path = g_strdup(obj_path);
+
+ app = __get_hdp_app_by_id(conn_info->app_id);
+ if (!app) {
+ ERR("No app with app_id: %d exists");
+ __hdp_free_conn_info(conn_info);
+ return;
+ }
+
+ app->conn_list = g_slist_append(app->conn_list, conn_info);
+ DBG("-");
+}
+
+static void __hdp_disconnect_request_cb(GDBusProxy *hdp_proxy,
+ GAsyncResult *res, gpointer user_data)
+{
+ hdp_conn_info_t *conn_info = user_data;
+ GVariant *reply;
+ GError *err = NULL;
+
+ DBG("+");
+
+ reply = g_dbus_proxy_call_finish(hdp_proxy, res, &err);
+ g_object_unref(hdp_proxy);
+ if (!reply) {
+ if (err) {
+ ERR("HDP disconnection Dbus Call Error: %s", err->message);
+ g_clear_error(&err);
+ }
+
+ /* Send channel_connect callback with status: connected */
+ __hdp_send_conn_event(conn_info, BTHL_CONN_STATE_CONNECTED);
+ return;
+ }
+
+ DBG("-");
+}
+
+bt_status_t _bt_hal_dbus_handler_hdp_register_application(int role,
+ int data_type, int channel_type, const char *description, int *app_id)
+{
+ GDBusConnection *conn;
+ GDBusProxy *proxy = NULL;
+ GVariantBuilder *builder;
+ const char *key_type;
+ const char *svalue;
+ guint16 value;
+ GVariant *reply = NULL;
+ char *app_path;
+ GError *err = NULL;
+ hdp_app_data_t *app;
+
+ DBG("+");
+
+ conn = _bt_get_system_gconn();
+ if(!conn) {
+ ERR("_bt_get_system_gconn returned NULL, return");
+ return BT_STATUS_FAIL;
+ }
+
+ proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ BT_HAL_BLUEZ_NAME,
+ "/org/bluez",
+ BT_HAL_HDP_MANAGER_INTERFACE,
+ NULL, &err);
+ if (!proxy) {
+ ERR("Unable to create proxy: %s", err->message);
+ g_clear_error(&err);
+ return BT_STATUS_FAIL;
+ }
+
+ builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+
+ key_type = "DataType";
+ value = (guint16)data_type;
+ g_variant_builder_add(builder, "{sv}", key_type, g_variant_new("q", value));
+
+ key_type = "Role";
+ svalue = (role == BTHL_MDEP_ROLE_SINK) ? "Sink" : "Source";
+ g_variant_builder_add(builder, "{sv}", key_type, g_variant_new("s", svalue));
+
+ key_type = "Description";
+ svalue = (description) ? description : "Health Device";
+ g_variant_builder_add(builder, "{sv}", key_type, g_variant_new("s", svalue));
+
+ if (role == BTHL_MDEP_ROLE_SOURCE) {
+ key_type = "ChannelType";
+ if (channel_type == BTHL_CHANNEL_TYPE_RELIABLE) {
+ svalue = "reliable";
+ DBG("%s : %s", key_type, svalue);
+ } else if (channel_type == BTHL_CHANNEL_TYPE_STREAMING) {
+ svalue = "streaming";
+ DBG("%s : %s", key_type, svalue);
+ } else {
+ g_variant_builder_unref(builder);
+ g_object_unref(proxy);
+ return BT_STATUS_PARM_INVALID;
+ }
+
+ g_variant_builder_add(builder, "{sv}", key_type, g_variant_new("s", svalue));
+ }
+
+ reply = g_dbus_proxy_call_sync(proxy, "CreateApplication",
+ g_variant_new("(a{sv})", builder),
+ G_DBUS_CALL_FLAGS_NONE, -1,
+ NULL, &err);
+ g_variant_builder_unref(builder);
+ g_object_unref(proxy);
+ if (!reply) {
+ ERR(" HDP:dbus Can't create application");
+ if (err) {
+ ERR("%s", err->message);
+ g_clear_error(&err);
+ }
+ return BT_STATUS_FAIL;
+ }
+
+ g_variant_get(reply, "(&o)", &app_path);
+ DBG("Created health application: %s", (char *)app_path);
+
+ app = g_new0(hdp_app_data_t, 1);
+ app->path = g_strdup(app_path);
+ sscanf(app_path, "/org/bluez/health_app_%d", &(app->id));
+ app->channel_type = channel_type;
+ g_variant_unref(reply);
+
+ *app_id = app->id;
+ DBG("App Id: %d", *app_id);
+
+ g_idle_add(__hdp_application_created_cb, (gpointer)app);
+
+ return BT_STATUS_SUCCESS;
+}
+
+bt_status_t _bt_hal_dbus_handler_hdp_unregister_application(int app_id)
+{
+ GDBusConnection *conn;
+ GDBusProxy *proxy = NULL;
+ GVariant *reply = NULL;
+ GError *err = NULL;
+ hdp_app_data_t *app;
+
+ DBG("+");
+
+ DBG("app_id: %d", app_id);
+ app = __get_hdp_app_by_id(app_id);
+ if (!app) {
+ ERR("No app with app_id: %d exists");
+ return BT_STATUS_PARM_INVALID;
+ }
+
+ conn = _bt_get_system_gconn();
+ if(!conn) {
+ ERR("_bt_get_system_gconn returned NULL, return");
+ return BT_STATUS_FAIL;
+ }
+
+ proxy = g_dbus_proxy_new_sync(conn, G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ BT_HAL_BLUEZ_NAME,
+ "/org/bluez",
+ BT_HAL_HDP_MANAGER_INTERFACE,
+ NULL, &err);
+ if (!proxy) {
+ if (err) {
+ ERR("Unable to create proxy: %s", err->message);
+ g_clear_error(&err);
+ } else {
+ ERR("Unable to create proxy");
+ }
+ return BT_STATUS_FAIL;
+ }
+
+ DBG("app->path: %s", app->path);
+ reply = g_dbus_proxy_call_sync(proxy, "DestroyApplication",
+ g_variant_new("(o)", app->path),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1, NULL, &err);
+ g_object_unref(proxy);
+ if (!reply) {
+ ERR(" HDP:dbus Can't Destroy application");
+ if (err) {
+ ERR("%s", err->message);
+ g_clear_error(&err);
+ }
+ return BT_STATUS_FAIL;
+ }
+
+ g_variant_unref(reply);
+ DBG("Destroyed health application: %s", (char *)app->path);
+ g_idle_add(__hdp_application_destroyed_cb, (gpointer)app);
+ DBG("-");
+
+ return BT_STATUS_SUCCESS;
+}
+
+bt_status_t _bt_hal_dbus_handler_hdp_connect_channel(int app_id, bt_bdaddr_t *bd_addr, int *channel_id)
+{
+ GDBusProxy *hdp_proxy;
+ hdp_app_data_t *app;
+ hdp_conn_info_t *conn_info;
+ char *ch_type;
+ char address[BT_HAL_ADDRESS_STRING_SIZE];
+
+ DBG("+");
+
+ if (!bd_addr) {
+ ERR("Address is NULL");
+ return BT_STATUS_PARM_INVALID;
+ }
+
+ _bt_convert_addr_type_to_string(address, bd_addr->address);
+
+ if (!channel_id) {
+ ERR("channel_id is NULL");
+ return BT_STATUS_PARM_INVALID;
+ }
+
+ app = __get_hdp_app_by_id(app_id);
+ if (!app) {
+ ERR("No app with app_id: %d exists", app_id);
+ return BT_STATUS_PARM_INVALID;
+ }
+
+ if (BTHL_CHANNEL_TYPE_RELIABLE == app->channel_type)
+ ch_type = "Reliable";
+ else if (BTHL_CHANNEL_TYPE_STREAMING == app->channel_type)
+ ch_type = "Streaming";
+ else
+ ch_type = "Any";
+
+ DBG("create conection to %s, channel_type: %s", address, ch_type);
+
+ hdp_proxy = __get_health_device_proxy(address);
+ if (!hdp_proxy) {
+ ERR("Failed to get the health device proxy");
+ return BT_STATUS_FAIL;
+ }
+
+ DBG("app path %s", app->path);
+
+ *channel_id = __hdp_assign_channel_id();
+ conn_info = g_new0(hdp_conn_info_t, 1);
+ conn_info->app_id = app->id;
+ g_strlcpy(conn_info->address, address, BT_HAL_ADDRESS_STRING_SIZE);
+ conn_info->channel_id = *channel_id;
+ conn_info->fd = -1;
+
+ g_dbus_proxy_call(hdp_proxy, "CreateChannel",
+ g_variant_new("(os)", app->path, ch_type),
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL,
+ (GAsyncReadyCallback)__hdp_connect_request_cb,
+ conn_info);
+
+ /* Send channel_connect callback with status: connecting and fd = -1 */
+ __hdp_send_conn_event(conn_info, BTHL_CONN_STATE_CONNECTING);
+ DBG("-");
+
+ return BT_STATUS_SUCCESS;
+}
+
+bt_status_t _bt_hal_dbus_handler_hdp_destroy_channel(int channel_id)
+{
+ GDBusProxy *hdp_proxy;
+ hdp_conn_info_t *conn_info;
+
+ DBG("+");
+
+ conn_info = __hdp_find_conn_info_by_ch_id(channel_id);
+ if (!conn_info) {
+ ERR("No conn found with channel_id: %d", channel_id);
+ return BT_STATUS_PARM_INVALID;
+ }
+
+ DBG("Destroy channel with channel_id: %d, remote_addr: %s", channel_id, conn_info->address);
+
+ hdp_proxy = __get_health_device_proxy(conn_info->address);
+ if (!hdp_proxy) {
+ ERR("Failed to get the health device proxy");
+ return BT_STATUS_FAIL;
+ }
+
+ g_dbus_proxy_call(hdp_proxy, "DestroyChannel",
+ g_variant_new("(o)", conn_info->obj_path),
+ G_DBUS_CALL_FLAGS_NONE, -1, NULL,
+ (GAsyncReadyCallback)__hdp_disconnect_request_cb,
+ conn_info);
+
+ /* Send channel_connect callback with status: connecting and fd = -1 */
+ __hdp_send_conn_event(conn_info, BTHL_CONN_STATE_DISCONNECTING);
+
+ DBG("-");
+ return BT_STATUS_SUCCESS;
+}
+
+/* To send stack event to hal-hidhost handler */
+void _bt_hal_register_hdp_dbus_handler_cb(handle_stack_msg cb)
+{
+ event_cb = cb;
+}
+
+/* To send stack event to hal-hidhost handler */
+void _bt_hal_unregister_hdp_dbus_handler_cb()
+{
+ event_cb = NULL;
+}
--- /dev/null
+/*
+ * Bluetooth-frwk
+ *
+ * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Atul Kumar Rai <a.rai@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dlog.h>
+
+#include "bt-hal.h"
+#include "bt-hal-log.h"
+#include "bt-hal-msg.h"
+#include "bt-hal-utils.h"
+
+#include "bt-hal-event-receiver.h"
+#include "bt-hal-hdp-dbus-handler.h"
+
+static const bthl_callbacks_t *bt_hal_hdp_cbacks;
+
+static bool interface_ready(void)
+{
+ return bt_hal_hdp_cbacks != NULL;
+}
+
+static void __bt_hal_handle_app_reg_state(void *buf, uint16_t len)
+{
+ struct hal_ev_hdp_app_reg_state *ev = buf;
+
+ DBG("+");
+
+ if (!ev) {
+ ERR("Event data is NULL, return");
+ return;
+ }
+
+ if (bt_hal_hdp_cbacks->app_reg_state_cb)
+ bt_hal_hdp_cbacks->app_reg_state_cb(ev->app_id, ev->state);
+
+ DBG("-");
+}
+
+static void __bt_hal_handle_hdp_conn_state(void *buf, uint16_t len)
+{
+ struct hal_ev_hdp_conn_state *ev = buf;
+
+ DBG("+");
+
+ if (!ev) {
+ ERR("Event data is NULL, return");
+ return;
+ }
+
+ if (bt_hal_hdp_cbacks->channel_state_cb)
+ bt_hal_hdp_cbacks->channel_state_cb(ev->app_id, (bt_bdaddr_t *)(ev->bdaddr),
+ ev->mdep_index, ev->channel_id, ev->channel_state, ev->data_fd);
+
+ DBG("-");
+}
+
+static void __bt_hal_handle_hdp_events(int message, void *buf, uint16_t len)
+{
+ DBG("+");
+
+ if (!interface_ready())
+ return;
+
+ switch(message) {
+ case HAL_EV_HDP_APP_REG_STATE:
+ DBG("Event: HAL_EV_HDP_APP_REG_STATE");
+ __bt_hal_handle_app_reg_state(buf, len);
+ break;
+ case HAL_EV_HDP_CONN_STATE:
+ DBG("Event: HAL_EV_HDP_CONN_STATE");
+ __bt_hal_handle_hdp_conn_state(buf, len);
+ break;
+ default:
+ DBG("Event Currently not handled!!");
+ break;
+ }
+
+ DBG("-");
+}
+
+/** Register HL application */
+static bt_status_t hdp_register_application(bthl_reg_param_t *p_reg_param, int *app_id)
+{
+ int ret = BT_STATUS_SUCCESS;
+ int role;
+ int data_type;
+ int channel_type;
+ const char *desc;
+
+ if (!p_reg_param)
+ return BT_STATUS_PARM_INVALID;
+
+ if (!p_reg_param->application_name)
+ return BT_STATUS_PARM_INVALID;
+
+ if (!app_id)
+ return BT_STATUS_PARM_INVALID;
+
+ /* Currently only 1 dep config is supported per app id */
+ if (0 > p_reg_param->number_of_mdeps || 1 < p_reg_param->number_of_mdeps) {
+ ERR("Currently only 1 dep config is supported per app");
+ return BT_STATUS_PARM_INVALID;
+ }
+
+ role = p_reg_param->mdep_cfg[0].mdep_role;
+ data_type = p_reg_param->mdep_cfg[0].data_type;
+ channel_type = p_reg_param->mdep_cfg[0].channel_type;
+ desc = p_reg_param->mdep_cfg[0].mdep_description;
+
+ ret = _bt_hal_dbus_handler_hdp_register_application(
+ role, data_type, channel_type, desc, app_id);
+ if (BT_STATUS_SUCCESS != ret) {
+ ERR("_bt_hal_dbus_handler_hdp_register_application failed");
+ *app_id = -1;
+ } else {
+ DBG("app_id: %d", *app_id);
+ }
+
+ return ret;
+}
+
+/** Unregister HL application */
+static bt_status_t hdp_unregister_application(int app_id)
+{
+ int ret;
+
+ DBG("+");
+
+ ret = _bt_hal_dbus_handler_hdp_unregister_application(app_id);
+ if (BT_STATUS_SUCCESS != ret)
+ ERR("_bt_hal_dbus_handler_hdp_unregister_application failed");
+ DBG("-");
+
+ return ret;
+}
+
+/** connect channel */
+static bt_status_t hdp_connect_channel(int app_id, bt_bdaddr_t *bd_addr, int mdep_cfg_index, int *channel_id)
+{
+ int ret;
+
+ DBG("+");
+
+ *channel_id = -1;
+ ret = _bt_hal_dbus_handler_hdp_connect_channel(app_id, bd_addr, channel_id);
+ if (BT_STATUS_SUCCESS != ret)
+ ERR("_bt_hal_dbus_handler_hdp_unregister_application failed");
+
+ DBG("-");
+ return ret;
+}
+
+/** destroy channel */
+static bt_status_t hdp_destroy_channel(int channel_id)
+{
+ int ret;
+
+ DBG("+");
+
+ ret = _bt_hal_dbus_handler_hdp_destroy_channel(channel_id);
+ if (BT_STATUS_SUCCESS != ret)
+ ERR("_bt_hal_dbus_handler_hdp_unregister_application failed");
+
+ DBG("-");
+ return ret;
+}
+
+static bt_status_t init(bthl_callbacks_t *callbacks)
+{
+ DBG("");
+
+ if (interface_ready())
+ return BT_STATUS_DONE;
+
+ bt_hal_hdp_cbacks = callbacks;
+ DBG("Register HDP events callback function");
+ _bt_hal_register_hdp_dbus_handler_cb(__bt_hal_handle_hdp_events);
+
+ return BT_STATUS_SUCCESS;
+}
+
+static void cleanup(void)
+{
+ DBG("");
+
+ if (!interface_ready())
+ return;
+
+ DBG("Un-register HDP events callback function");
+ _bt_hal_unregister_hdp_dbus_handler_cb();
+
+ bt_hal_hdp_cbacks = NULL;
+}
+
+static bthl_interface_t hl_if = {
+ .size = sizeof(hl_if),
+ .init = init,
+ .register_application = hdp_register_application,
+ .unregister_application = hdp_unregister_application,
+ .connect_channel = hdp_connect_channel,
+ .destroy_channel = hdp_destroy_channel,
+ .cleanup = cleanup
+};
+
+bthl_interface_t *bt_get_hl_interface(void)
+{
+ return &hl_if;
+}