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)
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})
#include "bluetooth-hf-agent.h"
#include "bluetooth-agent-profile.h"
+#include <audio_io.h>
+
+#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"
#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*/
" <method name='IsInbandRingtoneSupported'>"
" <arg type='b' name='status' direction='out'/>"
" </method>"
+" <method name='AudioMuteOn'>"
+" </method>"
+" <method name='AudioMuteOff'>"
+" </method>"
" </interface>"
"</node>";
"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("-");
"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;
}
}
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;
}
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) {
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*/
return result;
}
+/* for FHUB's HFP HF role */
+#include <sound_manager.h>
+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;
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");
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,
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);
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);
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;
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;
#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"
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;
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 {
--- /dev/null
+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/)
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <glib.h>
+#include <bluetooth.h>
+#include <bluetooth_extension.h>
+
+#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;
+}
+
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
%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} .
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"
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
%{_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
%{_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
[Service]
User=network_fw
Group=network_fw
+SupplementaryGroups=system_share
SmackProcessLabel=System
Type=dbus
BusName=org.bluez.hf_agent