From 2d1f0002e2e0c4cdf1d847da98fb295ec4e7677f Mon Sep 17 00:00:00 2001 From: Ayush Garg Date: Tue, 4 Oct 2022 14:00:50 +0530 Subject: [PATCH] fhub: Apply lastest FHUB4.0 bluetooth-agent Applied patches: [DA] enable bluetooth-hf-agent & add service file to multi-user.target.wants for systemd [DA] move the audio function to bluetooth-hf-agnet from application layer [DA] add AudioMute method [DA] add hf-test (latest version + some modification) [DA] audio patches (DA-audio) (latest version) - add audio-input mute function and seperate audio-in and audio-out init - move audio init into __bt_hf_agent_connection in order to reduce latency This patchset should be merged with the following bluetooth-capi's patchset in order to build successfully: bluetooth-capi change-id: I3144f6091206891a67e19e81badf80737d8c5df6 Change-Id: Id2cbdcd0dbeb8238aa19aeea1f1f87804ff06a04 Signed-off-by: shss-choi Signed-off-by: Ayush Garg --- CMakeLists.txt | 3 +- hf-agent/CMakeLists.txt | 5 +- hf-agent/bluetooth-hf-agent.c | 315 +++++++++++++++++-- hf-agent/bluetooth-hf-agent.h | 20 ++ hf-test/CMakeLists.txt | 25 ++ hf-test/hf-test.c | 566 +++++++++++++++++++++++++++++++++++ packaging/bluetooth-agent.spec | 18 ++ packaging/bluetooth-hf-agent.service | 1 + 8 files changed, 926 insertions(+), 27 deletions(-) create mode 100644 hf-test/CMakeLists.txt create mode 100644 hf-test/hf-test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f8fc2f..492d084 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,4 +11,5 @@ ADD_SUBDIRECTORY(ipsp-agent) ADD_SUBDIRECTORY(hid-agent) IF (BUILD_GCOV) ADD_SUBDIRECTORY(unittest) -ENDIF (BUILD_GCOV) \ No newline at end of file +ENDIF (BUILD_GCOV) +ADD_SUBDIRECTORY(hf-test) diff --git a/hf-agent/CMakeLists.txt b/hf-agent/CMakeLists.txt index 7b5e4a7..b62cdfc 100644 --- a/hf-agent/CMakeLists.txt +++ b/hf-agent/CMakeLists.txt @@ -11,13 +11,14 @@ pkg_check_modules(pkgs_hf_agent REQUIRED dlog aul bluetooth-api alarm-service capi-appfw-app-manager glib-2.0 gio-2.0 gio-unix-2.0 capi-system-device vconf - capi-system-info) + capi-system-info capi-media-audio-io) FOREACH(flag ${pkgs_hf_agent_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") ENDFOREACH(flag) -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIC -Wall -Werror") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -Wall -Werror") +SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie") ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_hf_agent_LDFLAGS}) diff --git a/hf-agent/bluetooth-hf-agent.c b/hf-agent/bluetooth-hf-agent.c index 87d2611..edc623a 100644 --- a/hf-agent/bluetooth-hf-agent.c +++ b/hf-agent/bluetooth-hf-agent.c @@ -42,6 +42,11 @@ #include "bluetooth-hf-agent.h" #include "bluetooth-agent-profile.h" +#include + +#define HFP_SCO_DATA_BUFFER_SIZE 48 +#define HFP_FHUB_SAMPLE_RATE 8000 + #define BT_AGENT_SYSPOPUP_MAX_ATTEMPT 3 #define CALL_ALIAS_APP_ID "org.tizen.call-ui" @@ -79,6 +84,7 @@ int clcc_async_retry_count = 0; #define CLCC_RETRY_TIMER 100 /* 100 msec */ #define HFP_HF_UUID "0000111e-0000-1000-8000-00805f9b34fb" +#define PBAP_PCE_UUID "0000112e-0000-1000-8000-00805f9b34fb" #define DEFAULT_ADAPTER_OBJECT_PATH "/org/bluez/hci0" /*Below Inrospection data is exposed to bluez from agent*/ @@ -149,6 +155,10 @@ static const gchar hf_agent_introspection_xml[] = " " " " " " +" " +" " +" " +" " " " ""; @@ -702,6 +712,14 @@ static void __hf_agent_method(GDBusConnection *connection, "Supported" : "NotSupported"); g_dbus_method_invocation_return_value(context, g_variant_new("(b)", is_supported)); + } else if (g_strcmp0(method_name, "AudioMuteOn") == 0) { + DBG("Going to MUTE audio"); + bt_hf_info.is_audio_input_mute = TRUE; + g_dbus_method_invocation_return_value(context, NULL); + } else if (g_strcmp0(method_name, "AudioMuteOff") == 0) { + DBG("Going to UNMUTE audio"); + bt_hf_info.is_audio_input_mute = FALSE; + g_dbus_method_invocation_return_value(context, NULL); } INFO("-"); @@ -1194,18 +1212,24 @@ static void __bt_hf_agent_handle_ind_change(bt_hf_agent_info_t *bt_hf_info, "CallStarted", NULL); bt_hf_info->is_dialing = FALSE; bt_hf_info->call_active = TRUE; + if (TIZEN_MODEL_NAME_FHUB) + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_STARTED; } else if (bt_hf_info->call_active) { __bt_hf_agent_emit_signal(conn, BT_HF_AGENT_OBJECT_PATH, BT_HF_SERVICE_INTERFACE, "CallEnded", NULL); bt_hf_info->call_active = FALSE; + if (TIZEN_MODEL_NAME_FHUB) + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_ENDED; if (bt_hf_info->ciev_call_setup_status == 0) { __bt_hf_agent_emit_signal(gdbus_conn, BT_HF_AGENT_OBJECT_PATH, BT_HF_SERVICE_INTERFACE, "CallIdle", NULL); + if (TIZEN_MODEL_NAME_FHUB) + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_IDLE; } } @@ -1218,6 +1242,8 @@ static void __bt_hf_agent_handle_ind_change(bt_hf_agent_info_t *bt_hf_info, BT_HF_SERVICE_INTERFACE, "CallTerminated", NULL); + if (TIZEN_MODEL_NAME_FHUB) + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_TERMINATED; } else if (!bt_hf_info->is_dialing && value > 0) { bt_hf_info->is_dialing = TRUE; } @@ -1227,6 +1253,8 @@ static void __bt_hf_agent_handle_ind_change(bt_hf_agent_info_t *bt_hf_info, BT_HF_AGENT_OBJECT_PATH, BT_HF_SERVICE_INTERFACE, "CallSetupIncoming", NULL); + if (TIZEN_MODEL_NAME_FHUB) + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_SETUP_INCOMING; if (__bt_hf_agent_launch_call_app() == FALSE) DBG("call app launching failed"); } else if (bt_hf_info->ciev_call_setup_status == 2) { @@ -1234,17 +1262,23 @@ static void __bt_hf_agent_handle_ind_change(bt_hf_agent_info_t *bt_hf_info, BT_HF_AGENT_OBJECT_PATH, BT_HF_SERVICE_INTERFACE, "CallSetupDialing", NULL); + if (TIZEN_MODEL_NAME_FHUB) + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_SETUP_DIALING; } else if (bt_hf_info->ciev_call_setup_status == 3) { __bt_hf_agent_emit_signal(gdbus_conn, BT_HF_AGENT_OBJECT_PATH, BT_HF_SERVICE_INTERFACE, "CallSetupAlerting", NULL); + if (TIZEN_MODEL_NAME_FHUB) + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_SETUP_ALERTING; } else if (bt_hf_info->ciev_call_status == 0 && bt_hf_info->ciev_call_setup_status == 0) { __bt_hf_agent_emit_signal(gdbus_conn, BT_HF_AGENT_OBJECT_PATH, BT_HF_SERVICE_INTERFACE, "CallIdle", NULL); + if (TIZEN_MODEL_NAME_FHUB) + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_IDLE; } } else if (!strcmp(name, "\"callheld\"")) { if (value == 0) { /* No calls held*/ @@ -2515,6 +2549,196 @@ static guint __bt_hf_get_hold_mpty_features(gchar *features) return result; } +/* for FHUB's HFP HF role */ +#include +sound_stream_info_h g_stream_info_write_h = NULL; + +static void __release_audio_in(bt_hf_agent_info_t *bt_hf_info) +{ + INFO("+"); + + if (bt_hf_info->audio_input != NULL) { + audio_in_unprepare((audio_in_h)bt_hf_info->audio_input); + audio_in_destroy((audio_in_h)bt_hf_info->audio_input); + bt_hf_info->audio_input = NULL; + } + + INFO("-"); +} + +static void __release_audio_out(bt_hf_agent_info_t *bt_hf_info) +{ + INFO("+"); + + if (bt_hf_info->audio_output != NULL) { + audio_out_drain((audio_out_h)bt_hf_info->audio_output); + audio_out_unprepare((audio_out_h)bt_hf_info->audio_output); + audio_out_destroy((audio_out_h)bt_hf_info->audio_output); + bt_hf_info->audio_output = NULL; + } + + INFO("-"); +} + +static int __initialize_audio_in(bt_hf_agent_info_t *bt_hf_info) +{ + int ret = 0; + + INFO("+"); + if (bt_hf_info->audio_input != NULL) { + __release_audio_in(bt_hf_info); + } + + //init capturing + ret = audio_in_create(HFP_FHUB_SAMPLE_RATE, AUDIO_CHANNEL_MONO, + AUDIO_SAMPLE_TYPE_S16_LE, (audio_in_h *)&bt_hf_info->audio_input); + if (ret == AUDIO_IO_ERROR_NONE) { + if (!(audio_in_prepare((audio_in_h)bt_hf_info->audio_input))) { + DBG("voice capturing is ready!\n"); + bt_hf_info->is_audio_input_mute = FALSE; + } else { + ERR("audio_in_prepare failed, err(0x%x)\n", ret); + audio_in_destroy((audio_in_h)bt_hf_info->audio_input); + ret = -18; + } + } else { + ERR("audio_in_create failed.\n"); + ret = -17; + } + + INFO("-"); + return ret; +} + +static int __initialize_audio_out(bt_hf_agent_info_t *bt_hf_info) +{ + int ret = 0; + INFO("+"); + + if (bt_hf_info->audio_output != NULL) { + __release_audio_out(bt_hf_info); + } + + //init playback + ret = audio_out_create_new(HFP_FHUB_SAMPLE_RATE, AUDIO_CHANNEL_MONO, + AUDIO_SAMPLE_TYPE_S16_LE, (audio_out_h *)&bt_hf_info->audio_output); + if (ret == AUDIO_IO_ERROR_NONE) { + INFO("after audio_out_create!!!"); + + /* set sound stream info */ + if (g_stream_info_write_h) { + if (!(ret = audio_out_set_sound_stream_info(bt_hf_info->audio_output, g_stream_info_write_h))) { + INFO("after audio_out_set_sound_stream_info"); + } else { + ERR("fail to audio_out_set_sound_stream_info(), ret(0x%x)\n", ret); + } + } else { + ERR("no stream info!!!!"); + } + + /* prepare */ + if (!(ret = audio_out_prepare((audio_out_h)bt_hf_info->audio_output))) { + DBG("voice playback is ready!\n"); + } else { + ERR("audio_out_prepare failed, err(0x%x)\n", ret); + audio_out_destroy((audio_out_h)bt_hf_info->audio_output); + bt_hf_info->audio_output = NULL; + } + } else { + ERR("audio_out_create failed. \n"); + } + + INFO("-"); + return ret; +} + +static gboolean __bt_hf_agent_sco_event_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) +{ + bt_hf_agent_info_t *bt_hf_info = user_data; + GDBusConnection *conn; + int sco_skt = g_io_channel_unix_get_fd(chan); + + if (cond & G_IO_NVAL) + return FALSE; + + if (cond & (G_IO_HUP | G_IO_ERR)) { + g_io_channel_shutdown(chan, TRUE, NULL); + close(bt_hf_info->cli_sco_fd); + bt_hf_info->cli_sco_fd = -1; + g_io_channel_unref(chan); + DBG("Emit AudioDisconnected Signal"); + + sco_audio_connected = BT_HF_AUDIO_DISCONNECTED; + + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_IDLE; + bt_hf_info->is_first_audio_out = TRUE; + + __release_audio_out(bt_hf_info); + __release_audio_in(bt_hf_info); + + conn = __bt_hf_get_gdbus_connection(); + if (!conn) { + ERR("Unable to get connection"); + return FALSE; + } + __bt_hf_agent_emit_signal(conn, + BT_HF_AGENT_OBJECT_PATH, + BT_HF_SERVICE_INTERFACE, + "AudioDisconnected", NULL); + + return FALSE; + } + + if (cond & G_IO_IN) { + gchar buf_rx[HFP_SCO_DATA_BUFFER_SIZE] = { 0, }; + int read; + + read = recv(sco_skt, buf_rx, HFP_SCO_DATA_BUFFER_SIZE, MSG_DONTWAIT); + if (read > 0) { + if (bt_hf_info->call_state == BT_HF_DA_CALL_STAT_CALL_SETUP_DIALING || + bt_hf_info->call_state == BT_HF_DA_CALL_STAT_CALL_SETUP_ALERTING || + bt_hf_info->call_active) { + + if (bt_hf_info->is_first_audio_out) { + __initialize_audio_out(bt_hf_info); + bt_hf_info->is_first_audio_out = FALSE; + } + if (bt_hf_info->audio_output) { + audio_out_write(bt_hf_info->audio_output, buf_rx, read); + } else { + ERR("can't write audio"); + } + } + } + } + if (cond & G_IO_OUT) { + gchar buf_tx[HFP_SCO_DATA_BUFFER_SIZE] = { 0, }; + int size; + + if (bt_hf_info->is_first_audio_in) { + __initialize_audio_in(bt_hf_info); + bt_hf_info->is_first_audio_in = FALSE; + return TRUE; + } + + size = audio_in_read(bt_hf_info->audio_input, (void *)buf_tx, HFP_SCO_DATA_BUFFER_SIZE); + if (size > 0) { + if (bt_hf_info->call_active) { + + if (bt_hf_info->is_audio_input_mute) { + memset(buf_tx, 0x0, size); + } + + if (send(sco_skt, buf_tx, size, MSG_DONTWAIT) < 0) { + ERR("Write failed, error = %d[%s]", errno, strerror(errno)); + } + } + } + } + return TRUE; +} + + static gboolean __bt_hf_agent_sco_disconnect_cb(GIOChannel *chan, GIOCondition cond, gpointer user_data) { bt_hf_agent_info_t *bt_hf_info = user_data; @@ -2581,8 +2805,17 @@ static gboolean __bt_hf_agent_sco_accept_cb(GIOChannel *chan, GIOCondition cond, g_io_channel_set_flags(sco_io, G_IO_FLAG_NONBLOCK, NULL); g_io_channel_set_buffered(sco_io, FALSE); - g_io_add_watch(sco_io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, - __bt_hf_agent_sco_disconnect_cb, bt_hf_info); + if (TIZEN_MODEL_NAME_FHUB) { + bt_hf_info->call_state = BT_HF_DA_CALL_STAT_CALL_IDLE; + bt_hf_info->is_first_audio_out = TRUE; + bt_hf_info->is_first_audio_in = TRUE; + //__initialize_audio_in(bt_hf_info); + + g_io_add_watch(sco_io, G_IO_IN | G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL, + __bt_hf_agent_sco_event_cb, bt_hf_info); + } else + g_io_add_watch(sco_io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, + __bt_hf_agent_sco_disconnect_cb, bt_hf_info); if (bt_hf_info->state != BT_HF_STATE_CONNECTED) ERR("HFP is not yet connected"); @@ -2789,14 +3022,22 @@ static gboolean __bt_establish_service_level_conn(bt_hf_agent_info_t *bt_hf_info gchar cmd_buf[BT_HF_CMD_BUF_SIZE] = {0}; gboolean ret; char *buf_ptr; - guint feature = BT_HF_FEATURE_EC_ANDOR_NR | - BT_HF_FEATURE_CALL_WAITING_AND_3WAY | - BT_HF_FEATURE_CLI_PRESENTATION | - BT_HF_FEATURE_REMOTE_VOLUME_CONTROL | - BT_HF_FEATURE_ENHANCED_CALL_STATUS; - - if (TIZEN_PROFILE_WEARABLE) - feature = feature | BT_HF_FEATURE_CODEC_NEGOTIATION; + guint feature; + + if (TIZEN_MODEL_NAME_FHUB) + feature = BT_HF_FEATURE_CLI_PRESENTATION | + BT_HF_FEATURE_REMOTE_VOLUME_CONTROL | + BT_HF_FEATURE_ENHANCED_CALL_STATUS; + else { + feature = BT_HF_FEATURE_EC_ANDOR_NR | + BT_HF_FEATURE_CALL_WAITING_AND_3WAY | + BT_HF_FEATURE_CLI_PRESENTATION | + BT_HF_FEATURE_REMOTE_VOLUME_CONTROL | + BT_HF_FEATURE_ENHANCED_CALL_STATUS; + + if (TIZEN_PROFILE_WEARABLE) + feature = feature | BT_HF_FEATURE_CODEC_NEGOTIATION; + } snprintf(cmd_buf, sizeof(cmd_buf), BT_HF_FEATURES, feature); ret = __bt_hf_send_and_read(bt_hf_info, cmd_buf, buf, @@ -3205,11 +3446,18 @@ static int __bt_hf_register_profile(const char *uuid, uint16_t version, g_variant_builder_add(builder, "{sv}", "features", g_variant_new("q", features)); - ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile", - g_variant_new("(osa{sv})", path, - HFP_HF_UUID, builder), - G_DBUS_CALL_FLAGS_NONE, -1, - NULL, &error); + if (TIZEN_MODEL_NAME_FHUB) + ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile", + g_variant_new("(osa{sv})", object, + uuid, builder), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, &error); + else + ret = g_dbus_proxy_call_sync(proxy, "RegisterProfile", + g_variant_new("(osa{sv})", path, + HFP_HF_UUID, builder), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, &error); g_variant_builder_unref(builder); @@ -3246,7 +3494,15 @@ static void __bt_hf_agent_register(void) ret = __bt_hf_register_profile(HFP_HF_UUID, version, name, path, features); if (ret) - ERR("Error in register"); + ERR("Error in hf register"); + + if (TIZEN_MODEL_NAME_FHUB) { + ret = __bt_hf_register_profile(PBAP_PCE_UUID, 0x0101, + "Phone Book Access Client", + BT_PBAP_CLIENT_OBJECT_PATH, 0); + if (ret) + ERR("Error in pce register"); + } g_free(path); g_free(name); @@ -3777,15 +4033,23 @@ static int bt_hf_agent_send_at_cmd(GDBusMethodInvocation *context, char *atcmd) static uint32_t __bt_hf_agent_get_hf_features(void) { - uint32_t hf_features = BT_HF_FEATURE_EC_ANDOR_NR | - BT_HF_FEATURE_CALL_WAITING_AND_3WAY | - BT_HF_FEATURE_CLI_PRESENTATION | - BT_HF_FEATURE_REMOTE_VOLUME_CONTROL | - BT_HF_FEATURE_ENHANCED_CALL_STATUS | - BT_HF_FEATURE_CODEC_NEGOTIATION; + uint32_t hf_features; - if (TIZEN_PROFILE_WEARABLE) - hf_features = hf_features | BT_HF_FEATURE_CODEC_NEGOTIATION; + if (TIZEN_MODEL_NAME_FHUB) + hf_features = BT_HF_FEATURE_CLI_PRESENTATION | + BT_HF_FEATURE_REMOTE_VOLUME_CONTROL | + BT_HF_FEATURE_ENHANCED_CALL_STATUS; + else { + hf_features = BT_HF_FEATURE_EC_ANDOR_NR | + BT_HF_FEATURE_CALL_WAITING_AND_3WAY | + BT_HF_FEATURE_CLI_PRESENTATION | + BT_HF_FEATURE_REMOTE_VOLUME_CONTROL | + BT_HF_FEATURE_ENHANCED_CALL_STATUS | + BT_HF_FEATURE_CODEC_NEGOTIATION; + + if (TIZEN_PROFILE_WEARABLE) + hf_features = hf_features | BT_HF_FEATURE_CODEC_NEGOTIATION; + } hf_ver = HFP_VERSION_1_6; @@ -3806,6 +4070,9 @@ int main(void) hf_features = __bt_hf_agent_get_hf_features(); bt_hf_info.feature = (uint16_t) hf_features & 0x3F; + bt_hf_info.audio_input = NULL; + bt_hf_info.audio_output = NULL; + memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDSTOP; sa.sa_handler = __bt_hf_agent_sigterm_handler; diff --git a/hf-agent/bluetooth-hf-agent.h b/hf-agent/bluetooth-hf-agent.h index d544bae..bbac08a 100644 --- a/hf-agent/bluetooth-hf-agent.h +++ b/hf-agent/bluetooth-hf-agent.h @@ -106,6 +106,8 @@ extern "C" { #define BT_HF_SERVICE_INTERFACE "org.tizen.HfApp" #define BT_HF_BLUEZ_OBJECT_PATH "/org/tizen/handsfree" #define BT_HF_BLUEZ_INTERFACE "org.bluez.HandsfreeAgent" +#define BT_PBAP_CLIENT_OBJECT_PATH "/org/tizen/pbap_client" + #define BLUEZ_SERVICE_NAME "org.bluez" #define BLUEZ_HF_INTERFACE_NAME "org.bluez.HandsfreeGateway" #define BLUEZ_PROFILE_MGMT_INTERFACE "org.bluez.ProfileManager1" @@ -259,6 +261,17 @@ typedef enum { BT_HF_CALL_STAT_WAITING, } bt_hf_call_status_t; +typedef enum { + BT_HF_DA_CALL_STAT_CALL_STARTED, + BT_HF_DA_CALL_STAT_CALL_ENDED, + BT_HF_DA_CALL_STAT_CALL_IDLE, + BT_HF_DA_CALL_STAT_CALL_TERMINATED, + BT_HF_DA_CALL_STAT_CALL_WAITING, + BT_HF_DA_CALL_STAT_CALL_SETUP_INCOMING, + BT_HF_DA_CALL_STAT_CALL_SETUP_DIALING, + BT_HF_DA_CALL_STAT_CALL_SETUP_ALERTING, +} bt_hf_da_call_status_t; + typedef struct { guint32 fd; gint sco_fd; @@ -293,6 +306,13 @@ typedef struct { GDBusMethodInvocation *context; char *path; + + void *audio_output; + void *audio_input; + bt_hf_da_call_status_t call_state; + gboolean is_first_audio_out; + gboolean is_first_audio_in; + gboolean is_audio_input_mute; } bt_hf_agent_info_t; typedef struct { diff --git a/hf-test/CMakeLists.txt b/hf-test/CMakeLists.txt new file mode 100644 index 0000000..21754f7 --- /dev/null +++ b/hf-test/CMakeLists.txt @@ -0,0 +1,25 @@ +PROJECT(hf_role_test C) + +SET(fw_test "hf-test") + +SET(dependents "capi-base-common glib-2.0 capi-network-bluetooth") +SET(pc_dependents "capi-base-common") + +INCLUDE(FindPkgConfig) +pkg_check_modules(${fw_test} REQUIRED ${dependents}) +FOREACH(flag ${${fw_test}_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -fPIE") +SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -pie") + +aux_source_directory(. sources) +FOREACH(src ${sources}) + GET_FILENAME_COMPONENT(src_name ${src} NAME_WE) + MESSAGE("${src_name}") + ADD_EXECUTABLE(${src_name} ${src}) + TARGET_LINK_LIBRARIES(${src_name} ${fw_name} ${${fw_test}_LDFLAGS}) +ENDFOREACH() + +INSTALL(TARGETS hf-test RUNTIME DESTINATION bin/) diff --git a/hf-test/hf-test.c b/hf-test/hf-test.c new file mode 100644 index 0000000..edaa0ae --- /dev/null +++ b/hf-test/hf-test.c @@ -0,0 +1,566 @@ +#include +#include +#include +#include +#include +#include +#include + +#define LOG_RED "\033[0;31m" +#define LOG_GREEN "\033[0;32m" +#define LOG_BROWN "\033[0;33m" +#define LOG_BLUE "\033[0;34m" +#define LOG_END "\033[0;m" + +#define BT_PROFILE_SERVICE_UUID_HFP_HF "111e" +#define BT_PROFILE_SERVICE_UUID_HFP_AG "111f" +#define BT_PROFILE_SERVICE_UUID_A2DP_SOURCE "110a" +#define BT_PROFILE_SERVICE_UUID_A2DP_SINK "110b" +#define PHONE_NUMBER_LENGTH 15 + + +static const char *__bt_get_error(bt_error_e err) +{ + const char *err_str = NULL; + + switch (err) { + case BT_ERROR_NONE: + err_str = "BT_ERROR_NONE"; + break; + case BT_ERROR_CANCELLED: + err_str = "BT_ERROR_CANCELLED"; + break; + case BT_ERROR_INVALID_PARAMETER: + err_str = "BT_ERROR_INVALID_PARAMETER"; + break; + case BT_ERROR_OUT_OF_MEMORY: + err_str = "BT_ERROR_OUT_OF_MEMORY"; + break; + case BT_ERROR_RESOURCE_BUSY: + err_str = "BT_ERROR_RESOURCE_BUSY"; + break; + case BT_ERROR_TIMED_OUT: + err_str = "BT_ERROR_TIMED_OUT"; + break; + case BT_ERROR_NOW_IN_PROGRESS: + err_str = "BT_ERROR_NOW_IN_PROGRESS"; + break; + case BT_ERROR_NOT_INITIALIZED: + err_str = "BT_ERROR_NOT_INITIALIZED"; + break; + case BT_ERROR_NOT_ENABLED: + err_str = "BT_ERROR_NOT_ENABLED"; + break; + case BT_ERROR_ALREADY_DONE: + err_str = "BT_ERROR_ALREADY_DONE"; + break; + case BT_ERROR_OPERATION_FAILED: + err_str = "BT_ERROR_OPERATION_FAILED"; + break; + case BT_ERROR_NOT_IN_PROGRESS: + err_str = "BT_ERROR_NOT_IN_PROGRESS"; + break; + case BT_ERROR_REMOTE_DEVICE_NOT_BONDED: + err_str = "BT_ERROR_REMOTE_DEVICE_NOT_BONDED"; + break; + case BT_ERROR_AUTH_REJECTED: + err_str = "BT_ERROR_AUTH_REJECTED"; + break; + case BT_ERROR_AUTH_FAILED: + err_str = "BT_ERROR_AUTH_FAILED"; + break; + case BT_ERROR_REMOTE_DEVICE_NOT_FOUND: + err_str = "BT_ERROR_REMOTE_DEVICE_NOT_FOUND"; + break; + case BT_ERROR_SERVICE_SEARCH_FAILED: + err_str = "BT_ERROR_SERVICE_SEARCH_FAILED"; + break; + case BT_ERROR_REMOTE_DEVICE_NOT_CONNECTED: + err_str = "BT_ERROR_REMOTE_DEVICE_NOT_CONNECTED"; + break; + case BT_ERROR_PERMISSION_DENIED: + err_str = "BT_ERROR_PERMISSION_DENIED"; + break; + case BT_ERROR_SERVICE_NOT_FOUND: + err_str = "BT_ERROR_SERVICE_NOT_FOUND"; + break; + case BT_ERROR_NO_DATA: + err_str = "BT_ERROR_NO_DATA"; + break; + case BT_ERROR_NOT_SUPPORTED: + err_str = "BT_ERROR_NOT_SUPPORTED"; + break; + case BT_ERROR_DEVICE_POLICY_RESTRICTION: + err_str = "DEVICE_POLICY_RESTRICTION"; + break; + default: + err_str = "NOT Defined"; + break; + } + + return err_str; +} + + +gboolean test_thread(GIOChannel *source, GIOCondition condition, gpointer data); + +static char remote_phone_number[PHONE_NUMBER_LENGTH] = "+8200000000000"; +static char remote_phone_bt_mac[18] = "00:00:00:00:00:00"; +static int remote_phone_pb_size = 0; +static bool ringing_status = false; + +void __bt_hf_set_remote_call_event_cb(bt_hf_remote_call_event_e event, + char *phone_number, void *user_data) +{ + switch (event) { + case BT_HF_REMOTE_CALL_EVENT_IDLE: { + printf("[remote_call_event_cb] event [IDLE]\n"); + if (ringing_status) { + printf("[APP] STOP ALARM\n"); + ringing_status = false; + } + break; + } + case BT_HF_REMOTE_CALL_EVENT_INCOMING: + printf("[remote_call_event_cb] event [INCOMING]\n"); + break; + case BT_HF_REMOTE_CALL_EVENT_DIALING: { + int ret = 0; + GSList *list; + + printf("[remote_call_event_cb] event [DIALING]\n"); + ret = bt_hf_get_call_status_info_list(&list); + + if (ret < BT_ERROR_NONE) + printf("failed with [0x%04x]\n", ret); + else { + for (; list; list = g_slist_next(list)) { + bt_hf_call_status_info_s *call_info = list->data; + printf("[CALL INFO] number : %s\n", call_info->number); + printf("[CALL INFO] direction (0: outgoing, 1: incoming) : %d\n", call_info->direction); + printf("[CALL INFO] status (0: active, 1: held, 2: dialing, 3: alerting) : %d\n", call_info->status); + strncpy(remote_phone_number, call_info->number, PHONE_NUMBER_LENGTH - 1); + } + bt_hf_free_call_status_info_list(list); + } + break; + } + case BT_HF_REMOTE_CALL_EVENT_ALERTING: + printf("[remote_call_event_cb] event [ALERTING]\n"); + printf("[remote_call_event_cb] number : %s\n", remote_phone_number); + break; + case BT_HF_REMOTE_CALL_EVENT_CALL_TERMINATED: + printf("[remote_call_event_cb] event [TERMINATED]\n"); + break; + case BT_HF_REMOTE_CALL_EVENT_CALL_STARTED: { + printf("[remote_call_event_cb] event [STARTED]\n"); + if (ringing_status) { + printf("[APP] STOP ALARM\n"); + ringing_status = false; + } + break; + } + case BT_HF_REMOTE_CALL_EVENT_CALL_ENDED: + printf("[remote_call_event_cb] event [ENDED]\n"); + break; + case BT_HF_REMOTE_CALL_EVENT_UNHELD: + printf("[remote_call_event_cb] event [UNHELD]\n"); + break; + case BT_HF_REMOTE_CALL_EVENT_HELD: + printf("[remote_call_event_cb] event [HELD]\n"); + break; + case BT_HF_REMOTE_CALL_EVENT_RINGING: { + printf("[remote_call_event_cb] event [RINGING]\n"); + printf("[remote_call_event_cb] phone_number [%s]\n", phone_number); + if (!ringing_status) { + printf("[APP] START ALARM\n"); + ringing_status = true; + } + break; + } + case BT_HF_REMOTE_CALL_EVENT_WAITING: + printf("[remote_call_event_cb] event [WAITING]\n"); + break; + case BT_HF_REMOTE_CALL_EVENT_FAILED_TO_DIALING: + printf("[remote_call_event_cb] event [FAILED_TO_DIALING]\n"); + break; + default: + printf("[remote_call_event_cb] event [UNKNOWN]\n"); + } + +} + + +void __bt_pbap_phonebook_pull_cb(int result, const char *remote_address, + const char *vcf_file, void *user_data) +{ + printf("[__bt_pbap_phonebook_pull_cb] Result: %d\n", result); + printf("[__bt_pbap_phonebook_pull_cb] Remote Device: %s\n", remote_address); + printf("[__bt_pbap_phonebook_pull_cb] Phonebook Download File: %s\n", vcf_file); + printf("[__bt_pbap_phonebook_pull_cb] Phonebook Download Status: %s\n", + (result == BT_ERROR_NONE) ? "Successful" : "Unsuccessful"); + + if (result == BT_ERROR_NONE) { + printf("[__bt_pbap_phonebook_pull_cb] PHONEBOOK DOWNLOAD DONE.. DISCONNECT PBAP CLIENT\n"); + } else + printf("[__bt_pbap_phonebook_pull_cb] PHONEBOOK DOWNLOAD FAILED.. (ERROR: %s)\n", __bt_get_error(result)); +} + +void __bt_pbap_phonebook_size_cb(int result, const char *remote_address, int size, void *user_data) +{ + int ret = 0; + + printf("[__bt_pbap_phonebook_size_cb] Result: %d\n", result); + printf("[__bt_pbap_phonebook_size_cb] Remote Device: %s\n", remote_address); + printf("[__bt_pbap_phonebook_size_cb] Phonebook Size: %d\n", size); + + remote_phone_pb_size = size; + + if (result == BT_ERROR_NONE) { + ret = bt_pbap_client_get_phone_book(remote_address, BT_PBAP_SOURCE_DEVICE, BT_PBAP_FOLDER_PHONE_BOOK, + BT_PBAP_VCARD_FORMAT_VCARD30, BT_PBAP_ORDER_INDEXED, 0, size, BT_PBAP_FIELD_N | BT_PBAP_FIELD_TEL, + __bt_pbap_phonebook_pull_cb, NULL); + if (ret != BT_ERROR_NONE) { + printf("bt_pbap_client_get_phone_book failed (%d)\n", ret); + } + } +} +void __bt_pbap_connection_state_changed_cb(int result, bool connected, const char *remote_address, void *user_data) +{ + int ret = 0; + + printf("[__bt_pbap_connection_state_changed_cb] Result: %d\n", result); + printf("[__bt_pbap_connection_state_changed_cb] Remote Device: %s\n", remote_address); + printf("[__bt_pbap_connection_state_changed_cb] Connected Status: %d\n", connected); + + // if phone is PBAP connected, get connected mobile's phonebook size + if ((result == 0) && connected) { + ret = bt_pbap_client_get_phone_book_size(remote_address, BT_PBAP_SOURCE_DEVICE, + BT_PBAP_FOLDER_PHONE_BOOK, __bt_pbap_phonebook_size_cb, NULL); + if (ret != BT_ERROR_NONE) { + printf("bt_pbap_client_get_phone_book_size failed (%s)\n", __bt_get_error(result)); + } + } +} + +void __bt_audio_connection_state_changed_cb(int result, bool connected, + const char *remote_address, bt_audio_profile_type_e type, void *user_data) +{ + + if (type == BT_AUDIO_PROFILE_TYPE_AG) { + printf("[__bt_audio_connection_state_changed_cb] result : %d\n", result); + printf("[__bt_audio_connection_state_changed_cb] connected : %d\n", connected); + printf("[__bt_audio_connection_state_changed_cb] address : %s\n", remote_address); + printf("[__bt_audio_connection_state_changed_cb] type : %d\n", type); + + if (connected) { + printf("[__bt_audio_connection_state_changed_cb] mobile connected!!!!\n"); + + strncpy(remote_phone_bt_mac, remote_address, strlen(remote_phone_bt_mac)); + } + if (!connected) { + printf("[__bt_audio_connection_state_changed_cb] mobile disconnected!!!!\n"); + if (ringing_status) { + //mobile is disconnected during RINING event.. Stop all alarm + printf("[APP] STOP ALARM\n"); + ringing_status = false; + } + } + } +} + +static int initialize_bluetooth(void) +{ + int ret; + + ret = bt_initialize(); + if (ret != BT_ERROR_NONE) { + printf("bt_initialize failed (%s)\n", __bt_get_error(ret)); + return -1; + } + + ret = bt_audio_initialize(); + if (ret != BT_ERROR_NONE) { + printf("bt_audio_initialize failed (%s)\n", __bt_get_error(ret)); + return -1; + } + + ret = bt_hf_initialize(); + if (ret != BT_ERROR_NONE) { + printf("bt_hf_initialize failed (%s)\n", __bt_get_error(ret)); + } + + ret = bt_hf_set_remote_call_event_cb(__bt_hf_set_remote_call_event_cb, NULL); + if (ret != BT_ERROR_NONE) { + printf("bt_hf_set_remote_call_event_cb failed (%s)\n", __bt_get_error(ret)); + return -1; + } + + ret = bt_audio_set_connection_state_changed_cb(__bt_audio_connection_state_changed_cb, NULL); + if (ret != BT_ERROR_NONE) { + printf("bt_audio_set_connection_state_changed_cb failed (%s)\n", __bt_get_error(ret)); + } + + // PBAP initialize + ret = bt_pbap_client_initialize(); + if (ret != BT_ERROR_NONE && ret != BT_ERROR_ALREADY_DONE) { + printf("bt_pbap_client_initialize failed (%s)\n", __bt_get_error(ret)); + } + + ret = bt_pbap_client_set_connection_state_changed_cb(__bt_pbap_connection_state_changed_cb, NULL); + if (ret != BT_ERROR_NONE) { + printf("bt_pbap_client_set_connection_state_changed_cb failed (%s)\n", __bt_get_error(ret)); + } + + return 1; +} + +void test_deinitialize(void) +{ + int ret = 0; + + ret = bt_hf_unset_remote_call_event_cb(); + if (ret != BT_ERROR_NONE && ret != BT_ERROR_NOT_INITIALIZED) { + printf("bt_hf_unset_remote_call_event_cb failed (%s)\n", __bt_get_error(ret)); + } + + ret = bt_pbap_client_unset_connection_state_changed_cb(); + if (ret != BT_ERROR_NONE && ret != BT_ERROR_NOT_INITIALIZED) { + printf("bt_pbap_client_unset_connection_state_changed_cb failed (%s)\n", __bt_get_error(ret)); + } + + ret = bt_audio_unset_connection_state_changed_cb(); + if (ret != BT_ERROR_NONE && ret != BT_ERROR_NOT_INITIALIZED) { + printf("bt_audio_set_connection_state_changed_cb failed (%s)\n", __bt_get_error(ret)); + } + + ret = bt_pbap_client_deinitialize(); + if (ret != BT_ERROR_NONE && ret != BT_ERROR_NOT_INITIALIZED) { + printf("bt_pbap_client_deinitialize failed (%s)\n", __bt_get_error(ret)); + } + + ret = bt_hf_deinitialize(); + if (ret != BT_ERROR_NONE && ret != BT_ERROR_NOT_INITIALIZED) { + printf("bt_hf_deinitialize failed (%s)\n", __bt_get_error(ret)); + } + + ret = bt_audio_deinitialize(); + if (ret != BT_ERROR_NONE && ret != BT_ERROR_NOT_INITIALIZED) { + printf("bt_audio_deinitialize failed (%s)\n", __bt_get_error(ret)); + } + + ret = bt_deinitialize(); + if (ret != BT_ERROR_NONE && ret != BT_ERROR_NOT_INITIALIZED) { + printf("bt_deinitialize failed (%s)\n", __bt_get_error(ret)); + } + +} +int test_accept_incoming_call(void) +{ + int ret = 0; + ret = bt_hf_notify_call_event(BT_HF_CALL_EVENT_ANSWER, NULL); + + if (ret != BT_ERROR_NONE) { + printf("bt_hf_notify_call_event(BT_HF_CALL_EVENT_ANSWER) failed (%s)\n", __bt_get_error(ret)); + return -1; + } else + printf("[SUCCESS] accept incoming call\n"); + + return 1; +} + +int test_initiate_call(void) +{ + int ret = 0; + + printf("Input full phone number to dial : "); + ret = scanf("%14s", remote_phone_number); + + ret = bt_hf_notify_call_event(BT_HF_CALL_EVENT_DIAL, remote_phone_number); + if (ret != BT_ERROR_NONE) { + printf("bt_hf_notify_call_event(BT_HF_CALL_EVENT_DIAL) failed (%s)\n", __bt_get_error(ret)); + printf("phone number : %s", remote_phone_number); + return -1; + } else + printf("[SUCCESS] initiate call.. [%s]\n", remote_phone_number); + + return 1; +} + +int test_terminate_call(void) +{ + int ret = 0; + ret = bt_hf_notify_call_event(BT_HF_CALL_EVENT_IDLE, NULL); + + if (ret != BT_ERROR_NONE) { + printf("bt_hf_notify_call_event(BT_HF_CALL_EVENT_IDLE) failed (%s)\n", __bt_get_error(ret)); + return -1; + } else + printf("[SUCCESS] terminate call\n"); + + return 1; +} + +int test_audio_mute_on(void) +{ + int ret = 0; + ret = bt_hf_notify_call_event(BT_HF_CALL_EVENT_AUDIO_MUTE_ON, NULL); + + if (ret != BT_ERROR_NONE) { + printf("bt_hf_notify_call_event(BT_HF_CALL_EVENT_AUDIO_MUTE_ON) failed (%s)\n", __bt_get_error(ret)); + return -1; + } else + printf("[SUCCESS] audio mute\n"); + + return 1; +} + +int test_audio_mute_off(void) +{ + int ret = 0; + ret = bt_hf_notify_call_event(BT_HF_CALL_EVENT_AUDIO_MUTE_OFF, NULL); + + if (ret != BT_ERROR_NONE) { + printf("bt_hf_notify_call_event(BT_HF_CALL_EVENT_AUDIO_MUTE_OFF) failed (%s)\n", __bt_get_error(ret)); + return -1; + } else + printf("[SUCCESS] audio unmute\n"); + + return 1; +} + +int test_pbap_connect(void) +{ + int ret = 0; + + // try to PBAP connect to connected mobile phone device + ret = bt_pbap_client_connect(remote_phone_bt_mac); + if (ret != BT_ERROR_NONE) { + if (ret == BT_ERROR_ALREADY_DONE) { + printf("PBAP already connected [%s]\n", remote_phone_bt_mac); + + /// if phone is PBAP connected, get connected mobile's phonebook size + ret = bt_pbap_client_get_phone_book_size(remote_phone_bt_mac, BT_PBAP_SOURCE_DEVICE, + BT_PBAP_FOLDER_PHONE_BOOK, __bt_pbap_phonebook_size_cb, NULL); + if (ret != BT_ERROR_NONE) { + printf("bt_pbap_client_get_phone_book_size failed (%s)\n", __bt_get_error(ret)); + } + } else { + printf("bt_pbap_client_connect failed (%s)\n", __bt_get_error(ret)); + return -1; + } + } else + printf("[SUCCESS] pbap connect.. [%s]\n", remote_phone_bt_mac); + + return 1; +} + +int test_pbap_disconnect(void) +{ + int ret = 0; + + // try to PBAP connect to connected mobile phone device + ret = bt_pbap_client_disconnect(remote_phone_bt_mac); + if (ret != BT_ERROR_NONE) { + printf("bt_pbap_client_disconnect failed (%s)\n", __bt_get_error(ret)); + return -1; + } else + printf("[SUCCESS] pbap disconnect.. [%s]\n", remote_phone_bt_mac); + + return 1; +} + +int main(void) +{ + GMainLoop *gmain_loop; + + gmain_loop = g_main_loop_new(NULL, FALSE); + + if (gmain_loop == NULL) { + printf("GMainLoop create failed\n"); + return EXIT_FAILURE; + } + + GIOChannel *channel = g_io_channel_unix_new(0); + g_io_add_watch(channel, (G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL), test_thread, NULL); + + printf("Test Thread created...\n"); + + g_main_loop_run(gmain_loop); + + if (gmain_loop) + g_main_loop_unref(gmain_loop); + + return 0; +} + +gboolean test_thread(GIOChannel *source, GIOCondition condition, gpointer data) +{ + int rv; + char a[10]; + + printf("Event received from stdin\n"); + + rv = read(0, a, 10); + + if (rv <= 0 || a[0] == '0') { + test_deinitialize(); + exit(1); + } + + if (a[0] == '\n' || a[0] == '\r') { +/* Public API */ + printf("\n\n Bluetooth HFP Test App\n\n"); + printf("Options..\n"); + printf(LOG_GREEN "1 - Bluetooth init and set callbacks\n" LOG_END); + printf("2 - accept incoming call\n"); + printf("3 - initiate call\n"); + printf("4 - terminatate call\n"); + printf("5 - audio mute\n"); + printf("6 - audio unmute\n"); + printf("7 - connect PBAP\n"); + printf("8 - disconnect PBAP\n"); + printf(LOG_RED "0 - Exit \n" LOG_END); + + printf("ENTER - Show options menu.......\n"); + } + + switch (a[0]) { +/* Public API */ + case '1': + rv = initialize_bluetooth(); + break; + case '2': + rv = test_accept_incoming_call(); + break; + case '3': + rv = test_initiate_call(); + break; + case '4': + rv = test_terminate_call(); + break; + case '5': + rv = test_audio_mute_on(); + break; + case '6': + rv = test_audio_mute_off(); + break; + case '7': + rv = test_pbap_connect(); + break; + case '8': + rv = test_pbap_disconnect(); + break; + + default: + break; + } + + if (rv == 1) + printf("Operation succeeded!\n"); + else + printf("Operation failed!\n"); + + return TRUE; +} + diff --git a/packaging/bluetooth-agent.spec b/packaging/bluetooth-agent.spec index f6c36e0..81da085 100644 --- a/packaging/bluetooth-agent.spec +++ b/packaging/bluetooth-agent.spec @@ -11,6 +11,7 @@ Source1001: bluetooth-agent.manifest BuildRequires: pkgconfig(aul) BuildRequires: pkgconfig(bluetooth-api) +BuildRequires: pkgconfig(capi-media-audio-io) # The profile macro usage with the following comments may be removed after # Tizen OBS build projects are merged. However, each vendor may keep using @@ -98,6 +99,12 @@ Group: Network & Connectivity/Bluetooth %description ipsp Bluetooth agent binary compiled for IPSP(Internet Protocol Support Profile) +%package tool +Summary: Test Application for HF + +%description tool +Test Application for HF + %prep %setup -q cp %{SOURCE1001} . @@ -148,6 +155,9 @@ find . -name '*.gcno' -exec cp '{}' gcov-obj ';' rm -rf %{buildroot} %make_install +mkdir -p %{buildroot}%{_libdir}/systemd/system/ +mkdir -p %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/ + # This usage of profile macro does NOT conflict 4.0 configurability. #%if "%{?profile}" != "mobile" && "%{?profile}" != "tv" # Original: wearable, ivi. Added: common, "undefined" @@ -176,6 +186,9 @@ pushd unittest popd %endif +install -D -m 0644 packaging/bluetooth-hf-agent.service %{buildroot}%{_libdir}/systemd/system/bluetooth-hf-agent.service +ln -s ../bluetooth-hf-agent.service %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/bluetooth-hf-agent.service + %files %manifest %{name}.manifest %license LICENSE @@ -204,6 +217,8 @@ popd %{_unitdir}/bluetooth-hf-agent.service #%exclude %{_unitdir}/bluetooth-map-agent.service #%exclude %{_unitdir}/bluetooth-pbap-agent.service +%{_libdir}/systemd/system/bluetooth-hf-agent.service +%{_libdir}/systemd/system/multi-user.target.wants/bluetooth-hf-agent.service %files map %manifest %{name}.manifest @@ -235,3 +250,6 @@ popd %{_bindir}/bluetooth-ipsp-agent %{_datadir}/dbus-1/system-services/org.bluez.ipsp_agent.service %{_sysconfdir}/dbus-1/system.d/bluetooth-ipsp-agent.conf + +%files tool +%{_bindir}/hf-test diff --git a/packaging/bluetooth-hf-agent.service b/packaging/bluetooth-hf-agent.service index db368b8..2407817 100644 --- a/packaging/bluetooth-hf-agent.service +++ b/packaging/bluetooth-hf-agent.service @@ -4,6 +4,7 @@ Description=Bluetooth HF Agent [Service] User=network_fw Group=network_fw +SupplementaryGroups=system_share SmackProcessLabel=System Type=dbus BusName=org.bluez.hf_agent -- 2.7.4