--- /dev/null
+/*
+ * Bluetooth-frwk
+ *
+ * Copyright (c) 2015 - 2016 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Nilesh Trimbake <t.shripati@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 <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <vconf.h>
+
+#include "oal-hardware.h"
+#include "oal-device-mgr.h"
+#include <oal-manager.h>
+#include <oal-avrcp-ct.h>
+
+#include <bt-service-avrcp-ctrl.h>
+#include <bluetooth-media-control.h>
+#include <bt-service-event.h>
+
+int _bt_avrcp_connect_remote_target(bluetooth_device_address_t *device_address)
+{
+ oal_status_t status = OAL_STATUS_SUCCESS;
+ int result = BLUETOOTH_ERROR_NONE;
+ BT_INFO("+");
+
+ status = avrcp_ct_connect((bt_address_t*)device_address);
+ if (status != OAL_STATUS_SUCCESS) {
+ BT_ERR("Connection could not be established, err: [%d]", status);
+ result = BLUETOOTH_ERROR_INTERNAL;
+ }
+ return result;
+}
+
+int _bt_avrcp_disconnect_remote_target(bluetooth_device_address_t *device_address)
+{
+ oal_status_t status = OAL_STATUS_SUCCESS;
+ int result = BLUETOOTH_ERROR_NONE;
+ BT_INFO("+");
+
+ status = avrcp_ct_disconnect((bt_address_t*)device_address);
+ if (status != OAL_STATUS_SUCCESS) {
+ BT_ERR("DisConnection err: [%d]", status);
+ result = BLUETOOTH_ERROR_INTERNAL;
+ }
+ return result;
+}
+
+int _bt_avrcp_control_cmd(int type)
+{
+ char connected_address[BT_ADDRESS_STRING_SIZE + 1];
+ gboolean connected;
+ bluetooth_device_address_t device_address;
+ oal_status_t status = OAL_STATUS_SUCCESS;
+ int result = BLUETOOTH_ERROR_NONE;
+ BT_INFO("+");
+
+ connected = _bt_is_headset_type_connected(BT_AVRCP, connected_address);
+
+ if (connected) {
+ _bt_convert_addr_string_to_type(device_address.addr,
+ connected_address);
+ switch(type) {
+ case RC_PASS_CMD_PLAY:
+ status = avrcp_ct_play((bt_address_t*)&device_address);
+ break;
+ case RC_PASS_CMD_PAUSE:
+ status = avrcp_ct_pause((bt_address_t*)&device_address);
+ break;
+ case RC_PASS_CMD_STOP:
+ status = avrcp_ct_stop((bt_address_t*)&device_address);
+ break;
+ case RC_PASS_CMD_NEXT:
+ status = avrcp_ct_next_track((bt_address_t*)&device_address);
+ break;
+ case RC_PASS_CMD_PREVIOUS:
+ status = avrcp_ct_prev_track((bt_address_t*)&device_address);
+ break;
+ case RC_PASS_CMD_PRESS_FAST_FORWARD:
+ status = avrcp_ct_fforward((bt_address_t*)&device_address, PRESS_STATE);
+ break;
+ case RC_PASS_CMD_RELEASE_FAST_FORWARD:
+ status = avrcp_ct_fforward((bt_address_t*)&device_address, RELEASE_STATE);
+ break;
+ case RC_PASS_CMD_PRESS_REWIND:
+ status = avrcp_ct_rewind((bt_address_t*)&device_address, PRESS_STATE);
+ break;
+ case RC_PASS_CMD_RELEASE_REWIND:
+ status = avrcp_ct_rewind((bt_address_t*)&device_address, RELEASE_STATE);
+ break;
+ default:
+ break;
+ }
+
+ if (status != OAL_STATUS_SUCCESS) {
+ BT_ERR("Send pass through command err: [%d]", status);
+ result = BLUETOOTH_ERROR_INTERNAL;
+ }
+ } else {
+ BT_ERR("Device is not connected:");
+ return BLUETOOTH_ERROR_NOT_CONNECTED;
+ }
+
+ return result;
+}
+
+int _bt_avrcp_control_set_property(int type, unsigned int value)
+{
+ char connected_address[BT_ADDRESS_STRING_SIZE + 1];
+ gboolean connected;
+ bluetooth_device_address_t device_address;
+ oal_status_t status = OAL_STATUS_SUCCESS;
+ int result = BLUETOOTH_ERROR_NONE;
+ BT_INFO("+");
+ connected = _bt_is_headset_type_connected(BT_AVRCP, connected_address);
+
+ if (connected) {
+ _bt_convert_addr_string_to_type(device_address.addr,
+ connected_address);
+ status = avrcp_ct_set_property((bt_address_t*)&device_address,
+ type, value);
+ if (status != OAL_STATUS_SUCCESS) {
+ BT_ERR("Set peoperty err: [%d]", status);
+ result = BLUETOOTH_ERROR_INTERNAL;
+ }
+ } else {
+ BT_ERR("Device is not connected:");
+ return BLUETOOTH_ERROR_NOT_CONNECTED;
+ }
+ return result;
+}
+
+int _bt_avrcp_control_get_property(int type, unsigned int *value)
+{
+ char connected_address[BT_ADDRESS_STRING_SIZE + 1];
+ gboolean connected;
+ bluetooth_device_address_t device_address;
+ oal_status_t status = OAL_STATUS_SUCCESS;
+ int result = BLUETOOTH_ERROR_NONE;
+ BT_INFO("+");
+ connected = _bt_is_headset_type_connected(BT_AVRCP, connected_address);
+
+ if (connected) {
+ avrcp_ct_player_property_type_t oal_type;
+
+ _bt_convert_addr_string_to_type(
+ device_address.addr,
+ connected_address);
+
+ switch (type) {
+ case EQUALIZER:
+ oal_type = OAL_EQUALIZER;
+ break;
+ case SHUFFLE:
+ oal_type = OAL_SHUFFLE;
+ break;
+ case REPEAT:
+ oal_type = OAL_REPEAT;
+ break;
+ case SCAN:
+ oal_type = OAL_SCAN;
+ break;
+ case POSITION:
+ oal_type = OAL_PLAY_POSITION;
+ break;
+ case STATUS:
+ oal_type = OAL_PLAY_STATUS;
+ break;
+ default:
+ return BLUETOOTH_ERROR_INTERNAL;
+ }
+
+ status = avrcp_ct_get_property((bt_address_t*)&device_address,
+ oal_type, value);
+ if (status != OAL_STATUS_SUCCESS) {
+ BT_ERR("Get peoperty err: [%d]", status);
+ result = BLUETOOTH_ERROR_INTERNAL;
+ }
+ } else {
+ BT_ERR("Device is not connected:");
+ return BLUETOOTH_ERROR_NOT_CONNECTED;
+ }
+ return result;
+}
+
+int _bt_avrcp_control_get_track_info(void)
+{
+ char connected_address[BT_ADDRESS_STRING_SIZE + 1];
+ gboolean connected;
+ bluetooth_device_address_t device_address;
+ oal_status_t status = OAL_STATUS_SUCCESS;
+ int result = BLUETOOTH_ERROR_NONE;
+ BT_INFO("+");
+ connected = _bt_is_headset_type_connected(BT_AVRCP, connected_address);
+
+ if (connected) {
+ _bt_convert_addr_string_to_type(
+ device_address.addr,
+ connected_address);
+
+ status = avrcp_ct_get_media_attribute((bt_address_t*)&device_address);
+ if (status != OAL_STATUS_SUCCESS) {
+ BT_ERR("Get track info err: [%d]", status);
+ result = BLUETOOTH_ERROR_INTERNAL;
+ }
+ } else {
+ BT_ERR("Device is not connected:");
+ return BLUETOOTH_ERROR_NOT_CONNECTED;
+ }
+ return result;
+}
+
+static void __bt_reply_avrcp_ct_connection_pending_request(bluetooth_device_address_t *address)
+{
+ BT_DBG("+");
+ char addr[BT_ADDRESS_STRING_SIZE] = { 0 };
+ int result = BLUETOOTH_ERROR_NONE;
+ bluetooth_device_address_t device_address;
+ GArray *out_param;
+ invocation_info_t *req_info;
+ memcpy(device_address.addr, address->addr, BLUETOOTH_ADDRESS_LENGTH);
+ _bt_convert_addr_type_to_string(addr, address->addr);
+
+ req_info = _bt_get_request_info_data(BT_AVRCP_CONTROL_CONNECT, addr);
+ if (NULL == req_info) {
+ BT_INFO("AVRCP CT Connect request not found or possibly already replied");
+ return;
+ } else {
+ BT_INFO("AVRCP CT Connect request found for [%s]", addr);
+ }
+
+ out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
+ g_array_append_vals(out_param, addr, BT_ADDRESS_STRING_SIZE);
+ _bt_service_method_return(req_info->context,
+ out_param, result);
+ g_array_free(out_param, TRUE);
+ g_free(req_info->user_data);
+ _bt_free_info_from_invocation_list(req_info);
+}
+
+static void __bt_handle_avrcp_target_connected_state(bluetooth_device_address_t *address)
+{
+ char addr[BT_ADDRESS_STRING_SIZE] = { 0 };
+ GVariant *param;
+ int result = BLUETOOTH_ERROR_NONE;
+ ret_if(NULL == address);
+ BT_INFO("+");
+
+ _bt_convert_addr_type_to_string(addr, address->addr);
+ BT_INFO("Address of connected device [%s]", addr);
+
+ /* Add data from the connected list */
+ _bt_add_headset_to_list(BT_AVRCP, BT_STATE_CONNECTED, addr);
+
+ /* Replay to avrcp cotroller connect */
+ __bt_reply_avrcp_ct_connection_pending_request(address);
+
+ /* Send AVRCP(TARGET Role) connected event to Application */
+ param = g_variant_new("(is)", result, addr);
+ _bt_send_event(BT_AVRCP_CONTROL_EVENT, BLUETOOTH_EVENT_AVRCP_CONNECTED, param);
+
+ BT_INFO("-");
+}
+
+static void __bt_reply_avrcp_ct_disconnection_pending_request(bluetooth_device_address_t *address)
+{
+ char addr[BT_ADDRESS_STRING_SIZE] = { 0 };
+ int result = BLUETOOTH_ERROR_NONE;
+ GArray *out_param;
+ invocation_info_t *req_info;
+
+ BT_DBG("+");
+ _bt_convert_addr_type_to_string(addr, address->addr);
+
+ req_info = _bt_get_request_info_data(BT_AVRCP_CONTROL_DISCONNECT, addr);
+ if (NULL == req_info) {
+ BT_INFO("AVRCP CT Disconnect request not found or possibly already replied");
+ return;
+ } else {
+ BT_INFO("AVRCP CT Disconnect request found for [%s]", addr);
+ }
+
+ out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
+ g_array_append_vals(out_param, addr, BT_ADDRESS_STRING_SIZE);
+ _bt_service_method_return(req_info->context,
+ out_param, result);
+ g_array_free(out_param, TRUE);
+ g_free(req_info->user_data);
+ _bt_free_info_from_invocation_list(req_info);
+}
+
+static void __bt_handle_avrcp_target_disconnected_state(bluetooth_device_address_t *address)
+{
+ char addr[BT_ADDRESS_STRING_SIZE] = { 0 };
+ GVariant *param;
+ int result = BLUETOOTH_ERROR_NONE;
+ ret_if(NULL == address);
+ BT_INFO("+");
+
+ _bt_convert_addr_type_to_string(addr, address->addr);
+ BT_INFO("Address of disconnected device [%s]", addr);
+
+ /* Remove data from the connected list */
+ _bt_remove_headset_from_list(BT_AVRCP, addr);
+
+ /* Replay to avrcp cotroller connect */
+ __bt_reply_avrcp_ct_disconnection_pending_request(address);
+
+ /* Send AVRCP(TARGET Role) disconnected event to Application */
+ param = g_variant_new("(is)", result, addr);
+ _bt_send_event(BT_AVRCP_CONTROL_EVENT, BLUETOOTH_EVENT_AVRCP_DISCONNECTED, param);
+
+ BT_INFO("-");
+}
+
+static int __bt_oal_to_bt_event(int oal_event)
+{
+ int ret =0;
+
+ switch(oal_event) {
+ case OAL_EVENT_AVRCP_CTRL_EQUALIZER_STATUS:
+ ret = BLUETOOTH_EVENT_AVRCP_CONTROL_EQUALIZER_STATUS;
+ break;
+ case OAL_EVENT_AVRCP_CTRL_REPEAT_STATUS:
+ ret = BLUETOOTH_EVENT_AVRCP_CONTROL_REPEAT_STATUS;
+ break;
+ case OAL_EVENT_AVRCP_CTRL_SHUFFLE_STATUS:
+ ret = BLUETOOTH_EVENT_AVRCP_CONTROL_SHUFFLE_STATUS;
+ break;
+ case OAL_EVENT_AVRCP_CTRL_SCAN_STATUS:
+ ret = BLUETOOTH_EVENT_AVRCP_CONTROL_SCAN_STATUS;
+ break;
+ case OAL_EVENT_AVRCP_CTRL_PLAY_POSITION_STATUS:
+ ret = BLUETOOTH_EVENT_AVRCP_SONG_POSITION_STATUS;
+ break;
+ case OAL_EVENT_AVRCP_CTRL_PLAY_STATUS_CHANGED:
+ ret = BLUETOOTH_EVENT_AVRCP_PLAY_STATUS_CHANGED;
+ break;
+ case OAL_EVENT_AVRCP_CTRL_TRACK_INFO_CHANGED:
+ ret = BLUETOOTH_EVENT_AVRCP_TRACK_CHANGED;
+ break;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static void __bt_handle_avrcp_target_player_property(unsigned int property_value, int oal_event)
+{
+ GVariant *param;
+ BT_INFO("+");
+ /* Send AVRCP Target player property event to Application */
+ param = g_variant_new("(u)", property_value);
+ _bt_send_event(BT_AVRCP_CONTROL_EVENT,
+ __bt_oal_to_bt_event(oal_event), param);
+}
+
+static void __bt_handle_avrcp_track_info_changed(avrcp_ct_media_metadata_attr_t* metadata)
+{
+ GVariant *param;
+
+ BT_INFO("+");
+ /* Send AVRCP Target player track info changed event to application*/
+ param = g_variant_new("(ssssuuu)",
+ metadata->title,
+ metadata->artist,
+ metadata->album,
+ metadata->genre,
+ metadata->total_tracks,
+ metadata->number,
+ metadata->duration);
+ _bt_send_event(BT_AVRCP_CONTROL_EVENT,
+ BLUETOOTH_EVENT_AVRCP_TRACK_CHANGED, param);
+}
+
+static invocation_info_t* __bt_get_request_info(int service_function)
+{
+ GSList *l;
+ invocation_info_t *req_info = NULL;
+
+ BT_DBG("+");
+ /* Get method invocation context */
+ for (l = _bt_get_invocation_list(); l != NULL; l = g_slist_next(l)) {
+ req_info = l->data;
+ if (req_info == NULL || req_info->service_function != service_function)
+ continue;
+ return req_info;
+ }
+ return NULL;
+}
+
+static void __bt_handle_avrcp_track_info(avrcp_ct_media_metadata_attr_t* metadata)
+{
+ media_metadata_t meta_data;
+ invocation_info_t *req_info = NULL;
+ GArray *out_param = NULL;
+ int result = BLUETOOTH_ERROR_NONE;
+
+ memset(&meta_data, 0x00, sizeof(media_metadata_t));
+
+ if (_bt_copy_utf8_string(meta_data.title, metadata->title,
+ BT_META_DATA_MAX_LEN))
+ BT_ERR("Error in copying Title\n");
+ if (_bt_copy_utf8_string(meta_data.artist, metadata->artist,
+ BT_META_DATA_MAX_LEN))
+ BT_ERR("Error in copying Artist\n");
+ if (_bt_copy_utf8_string(meta_data.album, metadata->album,
+ BT_META_DATA_MAX_LEN))
+ BT_ERR("Error in copying Album\n");
+ if (_bt_copy_utf8_string(meta_data.genre, metadata->genre,
+ BT_META_DATA_MAX_LEN))
+ BT_ERR("Error in copying Genre\n");
+
+ if (_bt_utf8_validate(meta_data.title) == FALSE)
+ meta_data.title[0] = '\0';
+
+ if (_bt_utf8_validate(meta_data.artist) == FALSE)
+ meta_data.artist[0] = '\0';
+
+ if (_bt_utf8_validate(meta_data.album) == FALSE)
+ meta_data.album[0] = '\0';
+
+ if (_bt_utf8_validate(meta_data.genre) == FALSE)
+ meta_data.genre[0] = '\0';
+ meta_data.total_tracks = metadata->total_tracks;
+ meta_data.number = metadata->number;
+ meta_data.duration = metadata->duration;
+
+ g_free((gpointer)metadata->title);
+ g_free((gpointer)metadata->artist);
+ g_free((gpointer)metadata->album);
+ g_free((gpointer)metadata->genre);
+
+ req_info = __bt_get_request_info(BT_AVRCP_GET_TRACK_INFO);
+
+ out_param = g_array_new(FALSE, FALSE, sizeof(gchar));
+ g_array_append_vals(out_param, &meta_data,
+ sizeof(media_metadata_t));
+
+ _bt_service_method_return(req_info->context, out_param, result);
+ g_array_free(out_param, TRUE);
+ _bt_free_info_from_invocation_list(req_info);
+}
+
+static void __bt_handle_avrcp_pass_cmd_res(avrcp_ct_pass_cmd_t *pass_cmd)
+{
+ BT_INFO(" Send Command Response [%d]", pass_cmd->key_code);
+}
+
+static void __bt_handle_avrcp_player_setting_res(avrcp_ct_playersetting_t *player_setting_res)
+{
+ BT_INFO("Set Property Response [%d]", player_setting_res->accepted);
+}
+
+void _bt_avrcp_ctrl_event_handler(int oal_event, gpointer event_data)
+{
+ BT_INFO("+");
+ bluetooth_device_address_t* bd_addr;
+
+ switch(oal_event) {
+ case OAL_EVENT_AVRCP_CTRL_CONNECTING:
+ case OAL_EVENT_AVRCP_CTRL_CONNECTED: {
+ BT_INFO("AVRCP Controller Profile connected..");
+ bd_addr= (bluetooth_device_address_t*)event_data;
+ __bt_handle_avrcp_target_connected_state(bd_addr);
+ break;
+ }
+ case OAL_EVENT_AVRCP_CTRL_DISCONNECTING:
+ case OAL_EVENT_AVRCP_CTRL_DISCONNECTED: {
+ BT_INFO("AVRCP Controller Profile dissconnected..");
+ bd_addr= (bluetooth_device_address_t*)event_data;
+ __bt_handle_avrcp_target_disconnected_state(bd_addr);
+ break;
+ }
+ case OAL_EVENT_AVRCP_CTRL_EQUALIZER_STATUS:
+ case OAL_EVENT_AVRCP_CTRL_REPEAT_STATUS:
+ case OAL_EVENT_AVRCP_CTRL_SHUFFLE_STATUS:
+ case OAL_EVENT_AVRCP_CTRL_SCAN_STATUS:
+ case OAL_EVENT_AVRCP_CTRL_PLAY_STATUS_CHANGED: {
+ avrcp_ct_property_value_t* property_val;
+ property_val = (avrcp_ct_property_value_t*)event_data;
+ __bt_handle_avrcp_target_player_property(property_val->value, oal_event);
+ break;
+ }
+ case OAL_EVENT_AVRCP_CTRL_PLAY_POSITION_STATUS: {
+ avrcp_ct_play_position_t* play_position;
+ play_position = (avrcp_ct_play_position_t*)event_data;
+ __bt_handle_avrcp_target_player_property(play_position->song_pos, oal_event);
+ break;
+ }
+ case OAL_EVENT_AVRCP_CTRL_TRACK_INFO_CHANGED: {
+ BT_INFO("AVRCP Controller Track Info Changed event..");
+ avrcp_ct_media_metadata_attr_t* metadata;
+
+ metadata = (avrcp_ct_media_metadata_attr_t* )event_data;
+ __bt_handle_avrcp_track_info_changed(metadata);
+ break;
+ }
+ case OAL_EVENT_AVRCP_CTRL_TRACK_INFO: {
+ BT_INFO("AVRCP Controller Track Info event..");
+ avrcp_ct_media_metadata_attr_t* metadata;
+
+ metadata = (avrcp_ct_media_metadata_attr_t* )event_data;
+ __bt_handle_avrcp_track_info(metadata);
+ break;
+ }
+ case OAL_EVENT_AVRCP_CTRL_PASS_CMD_RES: {
+ BT_INFO("AVRCP Controller Pass Command Res");
+ avrcp_ct_pass_cmd_t *pass_cmd;
+
+ pass_cmd = (avrcp_ct_pass_cmd_t *)event_data;
+ __bt_handle_avrcp_pass_cmd_res(pass_cmd);
+ break;
+ }
+ case OAL_EVENT_AVRCP_CTRL_PLAYER_SETTING_RES: {
+ BT_INFO("AVRCP Player setting response");
+ avrcp_ct_playersetting_t *player_setting_res;
+
+ player_setting_res = (avrcp_ct_playersetting_t *)event_data;
+ __bt_handle_avrcp_player_setting_res(player_setting_res);
+ break;
+ }
+ default:
+ BT_INFO("Invalid Event = %d", oal_event);
+ break;
+ }
+
+ BT_INFO("-");
+}