aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitlab/issue_templates/mytemplate.md3
-rw-r--r--.gitlab/merge_request_templates/mytemplate.md3
-rw-r--r--README.md12
-rwxr-xr-xautobuild/agl/autobuild131
-rwxr-xr-xautobuild/linux/autobuild131
-rw-r--r--binding/bluetooth-agent.c23
-rw-r--r--binding/bluetooth-api.c232
-rw-r--r--binding/bluetooth-api.h2
-rw-r--r--binding/bluetooth-bluez.c16
-rw-r--r--binding/bluetooth-common.h7
-rw-r--r--binding/bluetooth-conf.c64
-rw-r--r--binding/bluetooth-util.c5
-rw-r--r--conf.d/wgt/config.xml.in1
-rw-r--r--test/afb-test/tests/bluetooth.lua24
14 files changed, 512 insertions, 142 deletions
diff --git a/.gitlab/issue_templates/mytemplate.md b/.gitlab/issue_templates/mytemplate.md
new file mode 100644
index 0000000..25d91d8
--- /dev/null
+++ b/.gitlab/issue_templates/mytemplate.md
@@ -0,0 +1,3 @@
+**Please use https://gerrit.automotivelinux.org for code contributions.**
+See also: https://docs.automotivelinux.org/ chapter "How to contribute".
+
diff --git a/.gitlab/merge_request_templates/mytemplate.md b/.gitlab/merge_request_templates/mytemplate.md
new file mode 100644
index 0000000..25d91d8
--- /dev/null
+++ b/.gitlab/merge_request_templates/mytemplate.md
@@ -0,0 +1,3 @@
+**Please use https://gerrit.automotivelinux.org for code contributions.**
+See also: https://docs.automotivelinux.org/ chapter "How to contribute".
+
diff --git a/README.md b/README.md
index 85150c7..5abf125 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ Bluetooth service uses the respective BlueZ package to connect to bluetooth devi
| cancel_pairing | cancel an outgoing pair request | |
| confirm_pairing | confirm incoming/outgoing bluetooth pairing pincode | *Request:* {"pincode": 31415} |
| remove_device | remove already paired device | *Request:* {"device": "dev_88_0F_10_96_D3_20"} |
-
+| set_pincode | set pincode as string into database for outgoing pairing request | *Request:* {"pincode": "4321"} |
### managed_objects verb
@@ -149,11 +149,11 @@ This verb allows an client to get initial paired devices, and discovered unpaire
avrcp_controls verb allow controlling the playback of the defined device
-| Name | Description |
-|-----------------|----------------------------------------------------------------------------------------------|
-| adapter | Name of the adapter (i.e. hci0) |
-| device | Must be the name of the device (i.e. dev_88_0F_10_96_D3_20) |
-| action | Playback control action to take (e.g Play, Pause, Stop, Next, Previous, FastForward, Rewind) |
+| Name | Description |
+|-----------------|--------------------------------------------------------------------------------------------------------------|
+| adapter | Name of the adapter (optional, i.e. hci0) |
+| device | Must be the name of the device (optional, i.e. dev_88_0F_10_96_D3_20) |
+| action | Playback control action to take (e.g Play, Pause, Stop, Next, Previous, FastForward, Rewind) |
### connect/disconnect verbs
diff --git a/autobuild/agl/autobuild b/autobuild/agl/autobuild
index db00c1a..16181b8 100755
--- a/autobuild/agl/autobuild
+++ b/autobuild/agl/autobuild
@@ -1,5 +1,6 @@
#!/usr/bin/make -f
# Copyright (C) 2015 - 2018 "IoT.bzh"
+# Copyright (C) 2020 Konsulko Group
# Author "Romain Forlot" <romain.forlot@iot.bzh>
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,65 +16,113 @@
# limitations under the License.
THISFILE := $(lastword $(MAKEFILE_LIST))
-BUILD_DIR := $(abspath $(dir $(THISFILE))/../../build)
-DEST := ${BUILD_DIR}
+ROOT_DIR := $(abspath $(dir $(THISFILE))/../..)
-.PHONY: all clean distclean configure build package help update
+# Build directories
+# Note that the debug/test/coverage directories are defined in relation
+# to the release directory (BUILD_DIR), this needs to be kept in mind
+# if over-riding it and building those widget types, the specific widget
+# type variable (e.g. BUILD_DIR_DEBUG) may also need to be specified
+# to yield the desired output hierarchy.
+BUILD_DIR = $(ROOT_DIR)/build
+BUILD_DIR_DEBUG = $(abspath $(BUILD_DIR)/../build-debug)
+BUILD_DIR_TEST = $(abspath $(BUILD_DIR)/../build-test)
+BUILD_DIR_COVERAGE = $(abspath $(BUILD_DIR)/../build-coverage)
-all: help
+# Output directory variable for use in pattern rules.
+# This is intended for internal use only, hence the explicit override
+# definition.
+override OUTPUT_DIR = $(BUILD_DIR)
+
+# Final install directory for widgets
+DEST = $(OUTPUT_DIR)
+
+# Default build type for release/test builds
+BUILD_TYPE = RELEASE
+
+.PHONY: all help update install distclean
+.PHONY: clean clean-release clean-debug clean-test clean-coverage clean-all
+.PHONY: configure configure-release configure-debug configure-test configure-coverage
+.PHONY: build build-release build-debug build-test build-coverage build-all
+.PHONY: package package-release package-debug package-test package-coverage package-all
help:
@echo "List of targets available:"
@echo ""
@echo "- all"
+ @echo "- help"
@echo "- clean"
@echo "- distclean"
@echo "- configure"
@echo "- build: compilation, link and prepare files for package into a widget"
@echo "- package: output a widget file '*.wgt'"
- @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory"
+ @echo "- install: install in your $(CMAKE_INSTALL_DIR) directory"
@echo ""
@echo "Usage: ./autobuild/agl/autobuild package DEST=${HOME}/opt"
@echo "Don't use your build dir as DEST as wgt file is generated at this location"
-update: configure
- @cmake --build ${BUILD_DIR} --target autobuild
-
-clean:
- @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} ${CLEAN_ARGS} clean) || echo Nothing to clean
-
-distclean:
- @rm -rf ${BUILD_DIR}
-
-configure:
- @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
- @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
-
-build: configure
- @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all
-
-package: build
- @mkdir -p ${BUILD_DIR}/$@/bin
- @mkdir -p ${BUILD_DIR}/$@/etc
- @mkdir -p ${BUILD_DIR}/$@/lib
- @mkdir -p ${BUILD_DIR}/$@/htdocs
- @mkdir -p ${BUILD_DIR}/$@/var
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
- @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
- mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}; \
+all: package-all
+
+# Target specific variable over-rides so static pattern rules can be
+# used for the various type-specific targets.
+
+configure-test build-test package-test clean-test: OUTPUT_DIR = $(BUILD_DIR_TEST)
+
+configure-coverage build-coverage package-coverage clean-coverage: OUTPUT_DIR = $(BUILD_DIR_COVERAGE)
+configure-coverage build-coverage package-coverage: BUILD_TYPE = COVERAGE
+
+configure-debug build-debug package-debug clean-debug: OUTPUT_DIR = $(BUILD_DIR_DEBUG)
+configure-debug build-debug package-debug: BUILD_TYPE = DEBUG
+
+clean-release clean-test clean-debug clean-coverage:
+ @if [ -d $(OUTPUT_DIR) ]; then \
+ $(MAKE) -C $(OUTPUT_DIR) $(CLEAN_ARGS) clean; \
+ else \
+ echo Nothing to clean; \
fi
-package-test: build
- @mkdir -p ${BUILD_DIR}/$@/bin
- @mkdir -p ${BUILD_DIR}/$@/etc
- @mkdir -p ${BUILD_DIR}/$@/lib
- @mkdir -p ${BUILD_DIR}/$@/htdocs
- @mkdir -p ${BUILD_DIR}/$@/var
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target test_widget
- @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
- mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}; \
+clean: clean-release
+
+clean-all: clean-release clean-test clean-debug clean-coverage
+
+distclean: clean-all
+
+configure-release configure-test configure-debug configure-coverage:
+ @mkdir -p $(OUTPUT_DIR)
+ @if [ ! -f $(OUTPUT_DIR)/Makefile ]; then \
+ (cd $(OUTPUT_DIR) && cmake -S $(ROOT_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) $(CONFIGURE_ARGS)); \
fi
+configure: configure-release
+
+build-release build-debug build-coverage: build-%: configure-%
+ @cmake --build $(OUTPUT_DIR) $(BUILD_ARGS) --target all
+
+# Kept for consistency, empty to avoid building everything for test widget
+build-test: configure-test
+
+build: build-release
+
+build-all: build-release build-debug build-test build-coverage
+
+package-release package-debug package-coverage: package-%: build-%
+ @cmake --build $(OUTPUT_DIR) $(PACKAGE_ARGS) --target widget
+ @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \
+ mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \
+ fi
+
+package-test: build-test
+ @cmake --build $(OUTPUT_DIR) $(PACKAGE_ARGS) --target test_widget
+ @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \
+ mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \
+ fi
+
+package: package-release
+
+package-all: package-release package-test package-coverage package-debug
+
+update: configure
+ @cmake --build $(BUILD_DIR) --target autobuild
+
install: build
- @cmake --build ${BUILD_DIR} ${INSTALL_ARGS} --target install
+ @cmake --build $(BUILD_DIR) $(INSTALL_ARGS) --target install
diff --git a/autobuild/linux/autobuild b/autobuild/linux/autobuild
index db00c1a..16181b8 100755
--- a/autobuild/linux/autobuild
+++ b/autobuild/linux/autobuild
@@ -1,5 +1,6 @@
#!/usr/bin/make -f
# Copyright (C) 2015 - 2018 "IoT.bzh"
+# Copyright (C) 2020 Konsulko Group
# Author "Romain Forlot" <romain.forlot@iot.bzh>
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -15,65 +16,113 @@
# limitations under the License.
THISFILE := $(lastword $(MAKEFILE_LIST))
-BUILD_DIR := $(abspath $(dir $(THISFILE))/../../build)
-DEST := ${BUILD_DIR}
+ROOT_DIR := $(abspath $(dir $(THISFILE))/../..)
-.PHONY: all clean distclean configure build package help update
+# Build directories
+# Note that the debug/test/coverage directories are defined in relation
+# to the release directory (BUILD_DIR), this needs to be kept in mind
+# if over-riding it and building those widget types, the specific widget
+# type variable (e.g. BUILD_DIR_DEBUG) may also need to be specified
+# to yield the desired output hierarchy.
+BUILD_DIR = $(ROOT_DIR)/build
+BUILD_DIR_DEBUG = $(abspath $(BUILD_DIR)/../build-debug)
+BUILD_DIR_TEST = $(abspath $(BUILD_DIR)/../build-test)
+BUILD_DIR_COVERAGE = $(abspath $(BUILD_DIR)/../build-coverage)
-all: help
+# Output directory variable for use in pattern rules.
+# This is intended for internal use only, hence the explicit override
+# definition.
+override OUTPUT_DIR = $(BUILD_DIR)
+
+# Final install directory for widgets
+DEST = $(OUTPUT_DIR)
+
+# Default build type for release/test builds
+BUILD_TYPE = RELEASE
+
+.PHONY: all help update install distclean
+.PHONY: clean clean-release clean-debug clean-test clean-coverage clean-all
+.PHONY: configure configure-release configure-debug configure-test configure-coverage
+.PHONY: build build-release build-debug build-test build-coverage build-all
+.PHONY: package package-release package-debug package-test package-coverage package-all
help:
@echo "List of targets available:"
@echo ""
@echo "- all"
+ @echo "- help"
@echo "- clean"
@echo "- distclean"
@echo "- configure"
@echo "- build: compilation, link and prepare files for package into a widget"
@echo "- package: output a widget file '*.wgt'"
- @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory"
+ @echo "- install: install in your $(CMAKE_INSTALL_DIR) directory"
@echo ""
@echo "Usage: ./autobuild/agl/autobuild package DEST=${HOME}/opt"
@echo "Don't use your build dir as DEST as wgt file is generated at this location"
-update: configure
- @cmake --build ${BUILD_DIR} --target autobuild
-
-clean:
- @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} ${CLEAN_ARGS} clean) || echo Nothing to clean
-
-distclean:
- @rm -rf ${BUILD_DIR}
-
-configure:
- @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
- @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
-
-build: configure
- @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all
-
-package: build
- @mkdir -p ${BUILD_DIR}/$@/bin
- @mkdir -p ${BUILD_DIR}/$@/etc
- @mkdir -p ${BUILD_DIR}/$@/lib
- @mkdir -p ${BUILD_DIR}/$@/htdocs
- @mkdir -p ${BUILD_DIR}/$@/var
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
- @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
- mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}; \
+all: package-all
+
+# Target specific variable over-rides so static pattern rules can be
+# used for the various type-specific targets.
+
+configure-test build-test package-test clean-test: OUTPUT_DIR = $(BUILD_DIR_TEST)
+
+configure-coverage build-coverage package-coverage clean-coverage: OUTPUT_DIR = $(BUILD_DIR_COVERAGE)
+configure-coverage build-coverage package-coverage: BUILD_TYPE = COVERAGE
+
+configure-debug build-debug package-debug clean-debug: OUTPUT_DIR = $(BUILD_DIR_DEBUG)
+configure-debug build-debug package-debug: BUILD_TYPE = DEBUG
+
+clean-release clean-test clean-debug clean-coverage:
+ @if [ -d $(OUTPUT_DIR) ]; then \
+ $(MAKE) -C $(OUTPUT_DIR) $(CLEAN_ARGS) clean; \
+ else \
+ echo Nothing to clean; \
fi
-package-test: build
- @mkdir -p ${BUILD_DIR}/$@/bin
- @mkdir -p ${BUILD_DIR}/$@/etc
- @mkdir -p ${BUILD_DIR}/$@/lib
- @mkdir -p ${BUILD_DIR}/$@/htdocs
- @mkdir -p ${BUILD_DIR}/$@/var
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget
- @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target test_widget
- @if [ "${DEST}" != "${BUILD_DIR}" ]; then \
- mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST}; \
+clean: clean-release
+
+clean-all: clean-release clean-test clean-debug clean-coverage
+
+distclean: clean-all
+
+configure-release configure-test configure-debug configure-coverage:
+ @mkdir -p $(OUTPUT_DIR)
+ @if [ ! -f $(OUTPUT_DIR)/Makefile ]; then \
+ (cd $(OUTPUT_DIR) && cmake -S $(ROOT_DIR) -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) $(CONFIGURE_ARGS)); \
fi
+configure: configure-release
+
+build-release build-debug build-coverage: build-%: configure-%
+ @cmake --build $(OUTPUT_DIR) $(BUILD_ARGS) --target all
+
+# Kept for consistency, empty to avoid building everything for test widget
+build-test: configure-test
+
+build: build-release
+
+build-all: build-release build-debug build-test build-coverage
+
+package-release package-debug package-coverage: package-%: build-%
+ @cmake --build $(OUTPUT_DIR) $(PACKAGE_ARGS) --target widget
+ @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \
+ mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \
+ fi
+
+package-test: build-test
+ @cmake --build $(OUTPUT_DIR) $(PACKAGE_ARGS) --target test_widget
+ @if [ "$(abspath $(DEST))" != "$(abspath $(OUTPUT_DIR))" ]; then \
+ mkdir -p $(DEST) && cp $(OUTPUT_DIR)/*.wgt $(DEST); \
+ fi
+
+package: package-release
+
+package-all: package-release package-test package-coverage package-debug
+
+update: configure
+ @cmake --build $(BUILD_DIR) --target autobuild
+
install: build
- @cmake --build ${BUILD_DIR} ${INSTALL_ARGS} --target install
+ @cmake --build $(BUILD_DIR) $(INSTALL_ARGS) --target install
diff --git a/binding/bluetooth-agent.c b/binding/bluetooth-agent.c
index a942baa..3783b3f 100644
--- a/binding/bluetooth-agent.c
+++ b/binding/bluetooth-agent.c
@@ -41,6 +41,10 @@
static const gchar introspection_xml[] =
"<node>"
" <interface name='org.bluez.Agent1'>"
+" <method name='RequestPinCode'>"
+" <arg name='device' direction='in' type='o'/>"
+" <arg name='pincode' direction='out' type='s'/>"
+" </method>"
" <method name='RequestConfirmation'>"
" <arg name='device' direction='in' type='o'/>"
" <arg name='passkey' direction='in' type='u'/>"
@@ -69,7 +73,7 @@ static void handle_method_call(
GError *error = NULL;
json_object *jev = NULL;
const gchar *path = NULL;
-
+ char *device = NULL;
/* AFB_INFO("sender=%s", sender_name);
AFB_INFO("object_path=%s", object_path);
AFB_INFO("interface=%s", interface_name);
@@ -114,6 +118,23 @@ static void handle_method_call(
afb_event_push(ns->agent_event, jev);
return;
+
+ } else if (!g_strcmp0(method_name, "RequestPinCode")) {
+
+ g_variant_get(parameters, "(o)", &device);
+
+ call_work_lock(ns);
+ cw = call_work_lookup_unlocked(ns, BLUEZ_AT_DEVICE, device, "pair_device");
+
+ if (!cw)
+ g_dbus_method_invocation_return_dbus_error(invocation, "org.bluez.Error.Rejected", "No connection pending");
+ else
+ g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", cw->agent_data.fixed_pincode));
+
+ call_work_unlock(ns);
+
+ return;
+
} else if (!g_strcmp0(method_name, "AuthorizeService")) {
/* g_variant_get(parameters, "(os)", &path, &service);
diff --git a/binding/bluetooth-api.c b/binding/bluetooth-api.c
index faa9e02..5022ba9 100644
--- a/binding/bluetooth-api.c
+++ b/binding/bluetooth-api.c
@@ -231,11 +231,13 @@ void call_work_destroy_unlocked(struct call_work *cw)
/* agent struct data */
g_free(cw->agent_data.device_path);
-
+ cw->agent_data.device_path = NULL;
g_free(cw->access_type);
g_free(cw->type_arg);
g_free(cw->method);
g_free(cw->bluez_method);
+ g_free(cw->agent_data.fixed_pincode);
+ cw->agent_data.fixed_pincode = NULL;
}
void call_work_destroy(struct call_work *cw)
@@ -300,7 +302,7 @@ static void bluez_devices_signal_callback(
jresp = json_object_new_object();
- json_process_path(jresp, path);
+ json_process_path(jresp, path);
jobj = json_object_new_object();
@@ -519,6 +521,47 @@ static void bluez_devices_signal_callback(
json_object_put(jresp);
}
+static int bluetooth_select_init_adapter(struct init_data *id)
+{
+ struct bluetooth_state *ns =id->ns;
+ GError *error = NULL;
+ json_object *jresp, *jobj = NULL;
+ int ret, i;
+ size_t count;
+
+ jresp = object_properties(ns, &error);
+ if (error) {
+ AFB_INFO("object_properties for adapters error: %s",
+ error->message);
+ g_clear_error(&error);
+ ret = -EIO;
+ } else {
+ json_object_object_get_ex(jresp, "adapters", &jobj);
+ count = json_object_array_length(jobj);
+ ret = (int)count;
+
+ for (i = 1; (ret-i) >= 0; i++) {
+ json_object *idx = json_object_array_get_idx(jobj, count-i);
+ json_object *name = NULL;
+ const char *adapter;
+ json_object_object_get_ex(idx, "name", &name);
+ adapter = json_object_get_string(name);
+ if (!g_strcmp0(adapter, id->default_adapter)) {
+ id->ns->adapter = id->default_adapter;
+ break;
+ }
+ if (!(count-i)) {
+ /* fallback to 1st available adapter */
+ id->ns->adapter = g_strdup(adapter);
+ AFB_WARNING("default adapter %s not found, fell back to: %s",
+ id->default_adapter, adapter);
+ }
+ }
+ json_object_put(jresp);
+ }
+ return ret;
+}
+
static struct bluetooth_state *bluetooth_init(GMainLoop *loop)
{
struct bluetooth_state *ns;
@@ -608,6 +651,10 @@ static gpointer bluetooth_func(gpointer ptr)
struct bluetooth_state *ns;
GMainLoop *loop;
int rc = 0;
+ unsigned int delay;
+ unsigned int attempt;
+
+ g_atomic_rc_box_acquire(id);
loop = g_main_loop_new(NULL, FALSE);
if (!loop) {
@@ -615,7 +662,7 @@ static gpointer bluetooth_func(gpointer ptr)
goto err_no_loop;
}
- /* real bluetooth init */
+ // Do BlueZ D-Bus related init
ns = bluetooth_init(loop);
if (!ns) {
AFB_ERROR("bluetooth_init() failed");
@@ -629,17 +676,41 @@ static gpointer bluetooth_func(gpointer ptr)
goto err_no_agent;
}
- /* note that we wait for agent registration to signal done */
-
afb_api_set_userdata(id->api, ns);
- g_main_loop_run(loop);
+
+ // Let main process know initialization is done
+ signal_init_done(id, 0);
+
+ // Wait for an adapter to appear
+ rc = 0;
+ delay = 1;
+ attempt = 1;
+ while(rc <= 0) {
+ rc = bluetooth_select_init_adapter(id);
+ if (rc != 0)
+ break;
+
+ // Back off querying rate after the first 60 seconds
+ if (attempt++ == 60)
+ delay = 10;
+
+ sleep(delay);
+ }
+
+ if (rc > 0) {
+ g_main_loop_run(loop);
+ } else {
+ AFB_ERROR("bluetooth_select_init_adapter() failed");
+ }
g_main_loop_unref(ns->loop);
- bluetooth_unregister_agent(ns);
+ if (ns->agent_path)
+ bluetooth_unregister_agent(ns);
bluetooth_cleanup(ns);
afb_api_set_userdata(id->api, NULL);
+ g_atomic_rc_box_release(id);
return NULL;
@@ -651,18 +722,24 @@ err_no_ns:
err_no_loop:
signal_init_done(id, -1);
+ g_atomic_rc_box_release(id);
return NULL;
}
static int init(afb_api_t api)
{
- struct init_data init_data, *id = &init_data;
+ struct init_data *id = NULL;
json_object *args = NULL;
gint64 end_time;
int ret;
+ gboolean init_done;
+
+ id = g_atomic_rc_box_new0(struct init_data);
+ if (!id)
+ return -ENOMEM;
+ g_atomic_rc_box_acquire(id);
- memset(id, 0, sizeof(*id));
id->init_done = FALSE;
id->rc = 0;
id->api = api;
@@ -681,13 +758,21 @@ static int init(afb_api_t api)
return ret;
}
+ id->default_adapter = get_default_adapter(id->api);
+ if (!id->default_adapter) {
+ id->default_adapter = g_strdup(BLUEZ_DEFAULT_ADAPTER);
+ ret = set_default_adapter(api, BLUEZ_DEFAULT_ADAPTER);
+ if (ret)
+ AFB_WARNING("Request to save default adapter to persistent storage failed ");
+ }
+
args = json_object_new_object();
json_object_object_add(args , "technology", json_object_new_string("bluetooth"));
afb_api_call_sync(api, "network-manager", "enable_technology", args, NULL, NULL, NULL);
global_thread = g_thread_new("agl-service-bluetooth",
- bluetooth_func,
- id);
+ bluetooth_func,
+ id);
AFB_INFO("bluetooth-binding waiting for init done");
@@ -698,26 +783,31 @@ static int init(afb_api_t api)
if (!g_cond_wait_until(&id->cond, &id->mutex, end_time))
break;
}
+ ret = id->rc;
+ init_done = id->init_done;
g_mutex_unlock(&id->mutex);
+ g_atomic_rc_box_release(id);
- if (!id->init_done) {
+ if (!init_done) {
AFB_ERROR("bluetooth-binding init timeout");
return -1;
}
- if (id->rc)
- AFB_ERROR("bluetooth-binding init thread returned %d",
- id->rc);
+ if (ret)
+ AFB_ERROR("bluetooth-binding init thread returned %d", ret);
else
AFB_INFO("bluetooth-binding operational");
- id->ns->default_adapter = get_default_adapter(id->api);
-
- return id->rc;
+ return ret;
}
static void mediaplayer1_send_event(struct bluetooth_state *ns)
{
+ if (!ns->mediaplayer_path) {
+ AFB_ERROR("NULL mediaplayer_path");
+ return;
+ }
+
gchar *player = g_strdup(ns->mediaplayer_path);
json_object *jresp = mediaplayer_properties(ns, NULL, player);
@@ -738,11 +828,16 @@ static void bluetooth_subscribe_unsubscribe(afb_req_t request,
gboolean unsub)
{
struct bluetooth_state *ns = bluetooth_get_userdata(request);
- json_object *jresp = json_object_new_object();
+ json_object *jresp;
const char *value;
afb_event_t event;
int rc;
+ if (!ns) {
+ afb_req_fail(request, "failed", "Cannot process request");
+ return;
+ }
+
/* if value exists means to set offline mode */
value = afb_req_value(request, "value");
if (!value) {
@@ -773,6 +868,7 @@ static void bluetooth_subscribe_unsubscribe(afb_req_t request,
return;
}
+ jresp = json_object_new_object();
afb_req_success_f(request, jresp, "Bluetooth %s to event \"%s\"",
!unsub ? "subscribed" : "unsubscribed",
value);
@@ -794,9 +890,20 @@ static void bluetooth_list(afb_req_t request)
GError *error = NULL;
json_object *jresp;
+ if (!ns) {
+ afb_req_fail(request, "failed", "Cannot process request");
+ return;
+ }
+
jresp = object_properties(ns, &error);
+ if (error) {
+ AFB_INFO("object_properties error: %s",
+ error->message);
+ g_clear_error(&error);
+ afb_req_fail(request, "failed", "BlueZ managed objects error");
+ } else
+ afb_req_success(request, jresp, "BlueZ managed objects");
- afb_req_success(request, jresp, "Bluetooth - managed objects");
}
static void bluetooth_state(afb_req_t request)
@@ -806,7 +913,12 @@ static void bluetooth_state(afb_req_t request)
json_object *jresp;
const char *adapter = afb_req_value(request, "adapter");
- adapter = BLUEZ_ROOT_PATH(adapter ? adapter : ns->default_adapter);
+ if (!ns || (!adapter && !ns->adapter)) {
+ afb_req_fail(request, "failed", "No adapter");
+ return;
+ }
+
+ adapter = BLUEZ_ROOT_PATH(adapter ? adapter : ns->adapter);
jresp = adapter_properties(ns, &error, adapter);
if (!jresp) {
@@ -825,7 +937,12 @@ static void bluetooth_adapter(afb_req_t request)
const char *adapter = afb_req_value(request, "adapter");
const char *scan, *discoverable, *powered, *filter, *transport;
- adapter = BLUEZ_ROOT_PATH(adapter ? adapter : ns->default_adapter);
+ if (!ns || (!adapter && !ns->adapter)) {
+ afb_req_fail(request, "failed", "No adapter");
+ return;
+ }
+
+ adapter = BLUEZ_ROOT_PATH(adapter ? adapter : ns->adapter);
scan = afb_req_value(request, "discovery");
if (scan) {
@@ -891,7 +1008,7 @@ static void bluetooth_adapter(afb_req_t request)
uuid = json_array_to_strv(jobj);
g_variant_builder_add(&builder, "{sv}", "UUIDs",
- g_variant_new_strv((const gchar * const *) uuid, -1));
+ g_variant_new_strv((const gchar * const *) uuid, -1));
g_strfreev(uuid);
}
@@ -923,22 +1040,30 @@ static void bluetooth_adapter(afb_req_t request)
static void bluetooth_default_adapter(afb_req_t request)
{
- struct bluetooth_state *ns = bluetooth_get_userdata(request);
const char *adapter = afb_req_value(request, "adapter");
json_object *jresp = json_object_new_object();
+ afb_api_t api = afb_req_get_api(request);
+ const char *adapter_default = get_default_adapter(api);
+ int rc;
- call_work_lock(ns);
if (adapter) {
- set_default_adapter(afb_req_get_api(request), adapter);
-
- if (ns->default_adapter)
- g_free(ns->default_adapter);
- ns->default_adapter = g_strdup(adapter);
+ if (adapter_default && g_strcmp0(adapter_default, adapter)) {
+ rc = set_default_adapter(api, adapter);
+ if (rc) {
+ AFB_DEBUG("Request to save default adapter to persistent storage failed ");
+ afb_req_fail(request, "failed", "Update default adapter failed");
+ json_object_put(jresp);
+ return;
+ }
+ }
+ json_object_object_add(jresp, "adapter", json_object_new_string(adapter));
+ } else if (adapter_default) {
+ json_object_object_add(jresp, "adapter", json_object_new_string(adapter_default));
+ } else {
+ afb_req_fail(request, "failed", "No default adapter");
+ json_object_put(jresp);
+ return;
}
-
- json_object_object_add(jresp, "adapter", json_object_new_string(ns->default_adapter));
- call_work_unlock(ns);
-
afb_req_success(request, jresp, "Bluetooth - default adapter");
}
@@ -1111,6 +1236,8 @@ static void bluetooth_pair_device(afb_req_t request)
cw->request = request;
afb_req_addref(request);
+ cw->agent_data.fixed_pincode = get_pincode(afb_req_get_api(request));
+
cw->cpw = bluez_call_async(ns, BLUEZ_AT_DEVICE, device, "Pair", NULL, &error,
pair_service_callback, cw);
@@ -1171,10 +1298,10 @@ static void bluetooth_confirm_pairing(afb_req_t request)
const char *value = afb_req_value(request, "pincode");
if (value)
- pin = (int)strtol(value, NULL, 10);
+ pin = (int)strtol(value, NULL, 10);
if (!value || !pin) {
- afb_req_fail_f(request, "failed", "No pincode parameter");
+ afb_req_fail(request, "failed", "No pincode parameter");
return;
}
@@ -1259,9 +1386,10 @@ static void bluetooth_avrcp_controls(afb_req_t request)
{
struct bluetooth_state *ns = bluetooth_get_userdata(request);
const char *action = afb_req_value(request, "action");
- gchar *device, *player;
+ gchar *device, *player = NULL;
GVariant *reply;
GError *error = NULL;
+ json_object *jval = NULL;
if (!action) {
afb_req_fail(request, "failed", "No action given");
@@ -1271,7 +1399,11 @@ static void bluetooth_avrcp_controls(afb_req_t request)
device = return_bluez_path(request);
if (device) {
/* TODO: handle multiple players per device */
- player = g_strconcat(device, "/", BLUEZ_DEFAULT_PLAYER, NULL);
+ jval = bluez_get_property(ns, BLUEZ_AT_MEDIACONTROL, device, FALSE, "Player", NULL);
+ if (jval)
+ player = (gchar *)json_object_get_string(jval);
+ if (!player)
+ player = g_strconcat(device, "/", BLUEZ_DEFAULT_PLAYER, NULL);
g_free(device);
} else {
player = g_strdup(ns->mediaplayer_path);
@@ -1303,6 +1435,24 @@ out_success:
afb_req_success(request, NULL, "Bluetooth - AVRCP controls");
}
+static void bluetooth_pincode(afb_req_t request)
+{
+ char *error = NULL;
+ const char *pincode;
+ int rc;
+
+ pincode = afb_req_value(request, "pincode");
+ rc = set_pincode(afb_req_get_api(request), pincode, &error);
+
+ if (rc) {
+ afb_req_fail(request, "failed", error);
+ return;
+ }
+
+ afb_req_success(request, NULL, "Bluetooth - pincode");
+ return;
+}
+
static const afb_verb_t bluetooth_verbs[] = {
{
.verb = "subscribe",
@@ -1364,7 +1514,11 @@ static const afb_verb_t bluetooth_verbs[] = {
.session = AFB_SESSION_NONE,
.callback = bluetooth_avrcp_controls,
.info = "AVRCP controls"
- },
+ }, {
+ .verb = "set_pincode",
+ .session = AFB_SESSION_NONE,
+ .callback = bluetooth_pincode,
+ .info = "Set pincode for outgoing pairing"},
{ } /* marker for end of the array */
};
diff --git a/binding/bluetooth-api.h b/binding/bluetooth-api.h
index a0b807b..aecbad0 100644
--- a/binding/bluetooth-api.h
+++ b/binding/bluetooth-api.h
@@ -35,6 +35,7 @@
#define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1"
#define BLUEZ_MEDIAPLAYER_INTERFACE BLUEZ_SERVICE ".MediaPlayer1"
#define BLUEZ_MEDIATRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
+#define BLUEZ_MEDIACONTROL_INTERFACE BLUEZ_SERVICE ".MediaControl1"
#define BLUEZ_OBJECT_PATH "/"
#define BLUEZ_PATH "/org/bluez"
@@ -70,6 +71,7 @@
#define BLUEZ_AT_AGENTMANAGER "agent-manager"
#define BLUEZ_AT_MEDIAPLAYER "mediaplayer"
#define BLUEZ_AT_MEDIATRANSPORT "mediatransport"
+#define BLUEZ_AT_MEDIACONTROL "mediacontrol"
#define BLUEZ_DEFAULT_ADAPTER "hci0"
#define BLUEZ_DEFAULT_PLAYER "player0"
diff --git a/binding/bluetooth-bluez.c b/binding/bluetooth-bluez.c
index 18194d5..de4bc0f 100644
--- a/binding/bluetooth-bluez.c
+++ b/binding/bluetooth-bluez.c
@@ -99,6 +99,12 @@ static const struct property_info mediatransport_props[] = {
{ },
};
+static const struct property_info mediacontrol_props[] = {
+ { .name = "Connected", .fmt = "b", },
+ { .name = "Player", .fmt = "s", },
+ { },
+};
+
const struct property_info *bluez_get_property_info(
const char *access_type, GError **error)
{
@@ -112,6 +118,8 @@ const struct property_info *bluez_get_property_info(
pi = mediaplayer_props;
else if (!strcmp(access_type, BLUEZ_AT_MEDIATRANSPORT))
pi = mediatransport_props;
+ else if (!strcmp(access_type, BLUEZ_AT_MEDIACONTROL))
+ pi = mediacontrol_props;
else
g_set_error(error, NB_ERROR, NB_ERROR_ILLEGAL_ARGUMENT,
"illegal %s argument", access_type);
@@ -357,7 +365,8 @@ json_object *bluez_get_properties(struct bluetooth_state *ns,
if (!strcmp(access_type, BLUEZ_AT_DEVICE) ||
!strcmp(access_type, BLUEZ_AT_MEDIAPLAYER) ||
!strcmp(access_type, BLUEZ_AT_MEDIATRANSPORT) ||
- !strcmp(access_type, BLUEZ_AT_ADAPTER)) {
+ !strcmp(access_type, BLUEZ_AT_ADAPTER) ||
+ !strcmp(access_type, BLUEZ_AT_MEDIACONTROL)) {
pi = bluez_get_property_info(access_type, error);
if (!pi)
@@ -380,6 +389,8 @@ json_object *bluez_get_properties(struct bluetooth_state *ns,
interface2 = BLUEZ_MEDIATRANSPORT_INTERFACE;
else if (!strcmp(access_type, BLUEZ_AT_ADAPTER))
interface2 = BLUEZ_ADAPTER_INTERFACE;
+ else if (!strcmp(access_type, BLUEZ_AT_MEDIACONTROL))
+ interface2 = BLUEZ_MEDIACONTROL_INTERFACE;
else if (!strcmp(access_type, BLUEZ_AT_OBJECT))
interface2 = NULL;
else
@@ -404,7 +415,8 @@ json_object *bluez_get_properties(struct bluetooth_state *ns,
if (!strcmp(access_type, BLUEZ_AT_DEVICE) ||
!strcmp(access_type, BLUEZ_AT_MEDIAPLAYER) ||
!strcmp(access_type, BLUEZ_AT_MEDIATRANSPORT) ||
- !strcmp(access_type, BLUEZ_AT_ADAPTER)) {
+ !strcmp(access_type, BLUEZ_AT_ADAPTER) ||
+ !strcmp(access_type, BLUEZ_AT_MEDIACONTROL)) {
jprop = json_object_new_object();
g_variant_get(reply, "(a{sv})", &array);
while (g_variant_iter_loop(array, "{sv}", &key, &var)) {
diff --git a/binding/bluetooth-common.h b/binding/bluetooth-common.h
index 0e719eb..616e322 100644
--- a/binding/bluetooth-common.h
+++ b/binding/bluetooth-common.h
@@ -63,7 +63,7 @@ struct bluetooth_state {
gchar *mediaplayer_path;
/* adapter */
- gchar *default_adapter;
+ gchar *adapter;
};
struct init_data {
@@ -71,12 +71,14 @@ struct init_data {
GMutex mutex;
gboolean init_done;
afb_api_t api;
+ gchar *default_adapter;
struct bluetooth_state *ns; /* before setting afb_api_set_userdata() */
int rc;
};
struct agent_data {
int pin_code;
+ gchar *fixed_pincode;
gchar *device_path;
};
@@ -104,6 +106,9 @@ void bluetooth_unregister_agent(struct bluetooth_state *ns);
gchar *get_default_adapter(afb_api_t api);
int set_default_adapter(afb_api_t api, const char *adapter);
+int set_pincode(afb_api_t api, const char *pincode, char **error);
+gchar *get_pincode(afb_api_t api);
+
/* utility methods in bluetooth-util.c */
extern gboolean auto_lowercase_keys;
diff --git a/binding/bluetooth-conf.c b/binding/bluetooth-conf.c
index d9edf98..a5824bb 100644
--- a/binding/bluetooth-conf.c
+++ b/binding/bluetooth-conf.c
@@ -38,7 +38,7 @@
gchar *get_default_adapter(afb_api_t api)
{
json_object *response, *query, *val;
- gchar *adapter;
+ gchar *adapter = NULL;
int ret;
query = json_object_new_object();
@@ -46,13 +46,14 @@ gchar *get_default_adapter(afb_api_t api)
ret = afb_api_call_sync(api, "persistence", "read", query, &response, NULL, NULL);
if (ret < 0)
- return g_strdup(BLUEZ_DEFAULT_ADAPTER);
+ goto out;
- json_object_object_get_ex(response, "value", &val);
- adapter = g_strdup(json_object_get_string(val));
+ if (json_object_object_get_ex(response, "value", &val))
+ adapter = g_strdup(json_object_get_string(val));
json_object_put(response);
- return adapter ? adapter : g_strdup(BLUEZ_DEFAULT_ADAPTER);
+out:
+ return adapter;
}
int set_default_adapter(afb_api_t api, const char *adapter)
@@ -65,7 +66,60 @@ int set_default_adapter(afb_api_t api, const char *adapter)
json_object_object_add(query, "value", json_object_new_string(adapter));
ret = afb_api_call_sync(api, "persistence", "update", query, &response, NULL, NULL);
+ if (ret < 0) {
+ ret = afb_api_call_sync(api, "persistence", "insert", query, &response, NULL, NULL);
+ if (ret < 0)
+ goto out;
+ }
+
json_object_put(response);
+out:
return ret;
}
+
+gchar *get_pincode(afb_api_t api)
+{
+ json_object *response, *query, *val;
+ gchar *pincode = NULL;
+
+ query = json_object_new_object();
+ json_object_object_add(query, "key", json_object_new_string("pincode"));
+
+ afb_api_call_sync(api, "persistence", "read", query, &response, NULL, NULL);
+
+ if (json_object_object_get_ex(response, "value", &val))
+ pincode = g_strdup(json_object_get_string(val));
+ if (!pincode)
+ pincode = g_strdup("1234");
+ json_object_put(response);
+
+ return pincode;
+}
+
+int set_pincode(afb_api_t api, const char *pincode, char **error)
+{
+ json_object *response, *query;
+ int ret;
+ gchar *endptr = NULL;
+ query = json_object_new_object();
+ if (strlen(pincode) > 8 || strlen(pincode) < 4)
+ {
+ *error = "length of pincode must be between 4 and 8";
+ return -1;
+ }
+
+ g_ascii_strtoll(pincode, &endptr, 10);
+ if (*endptr)
+ {
+ *error = "pincode must be as digits";
+ return -1;
+ }
+ json_object_object_add(query, "key", json_object_new_string("pincode"));
+ json_object_object_add(query, "value", json_object_new_string(pincode));
+
+ ret = afb_api_call_sync(api, "persistence", "update", query, &response, NULL, NULL);
+ json_object_put(response);
+
+ return ret;
+} \ No newline at end of file
diff --git a/binding/bluetooth-util.c b/binding/bluetooth-util.c
index 8a50731..a1e15f0 100644
--- a/binding/bluetooth-util.c
+++ b/binding/bluetooth-util.c
@@ -1047,8 +1047,11 @@ gchar *return_bluez_path(afb_req_t request) {
const char *adapter = afb_req_value(request, "adapter");
const char *device, *tmp;
+ if (!ns || (!adapter && !ns->adapter))
+ return NULL;
+
call_work_lock(ns);
- adapter = adapter ? adapter : ns->default_adapter;
+ adapter = adapter ? adapter : ns->adapter;
call_work_unlock(ns);
device = afb_req_value(request, "device");
diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in
index aa1f563..eba7297 100644
--- a/conf.d/wgt/config.xml.in
+++ b/conf.d/wgt/config.xml.in
@@ -9,6 +9,7 @@
<feature name="urn:AGL:widget:required-permission">
<param name="urn:AGL:permission::public:hidden" value="required" />
+ <param name="urn:AGL:permission::public:bluetooth" value="required" />
<param name="urn:AGL:permission::public:no-htdocs" value="required" />
<param name="urn:AGL:permission::system:run-by-default" value="required" />
<param name="urn:AGL:permission::partner:scope-platform" value="required" />
diff --git a/test/afb-test/tests/bluetooth.lua b/test/afb-test/tests/bluetooth.lua
index 7130018..5b6a4a6 100644
--- a/test/afb-test/tests/bluetooth.lua
+++ b/test/afb-test/tests/bluetooth.lua
@@ -17,10 +17,6 @@
--]]
-
--- Version Verb test
-_AFT.testVerbStatusSuccess('testBtVersionSuccess','Bluetooth-Manager','version', {})
-
-- Default Adapter test
_AFT.testVerbStatusSuccess('testBtDefaultAdapterSuccess','Bluetooth-Manager','default_adapter', {})
@@ -48,5 +44,23 @@ _AFT.testVerbStatusSuccess('testBtAdpStateSuccess','Bluetooth-Manager','adapter_
-- Cancel pairing test - cancel an ongoing pairing
-- _AFT.testVerbStatusSuccess('testBtCancelPairSuccess', 'Bluetooth-Manager', 'cancel_pairing', {})
--- Connect to a paired device
+-- Confirm pairing test - confirm incoming/outgoing bluetooth pairing pincode
+-- _AFT.testVerbStatusSuccess('testBtConfirmPairSuccess', 'Bluetooth-Manager', 'confirm_pairing', {pincode="31415"})
+
+-- Remove device test - remove already paired device
+-- _AFT.testVerbStatusSuccess('testBtRemoveDeviceSuccess', 'Bluetooth-Manager', 'remove_device', {device="dev_01_23_45_67_89_0A"})
+
+-- Connect to a paired device test - connect to already paired device
-- _AFT.testVerbStatusSuccess('testBtConnectSuccess', 'Bluetooth-Manager', 'connect', {device="dev_01_23_45_67_89_0A"})
+
+-- avrcp_controls test -- requied connected to a smart phone
+-- _AFT.testVerbStatusSuccess('testBtConnectSuccess', 'Bluetooth-Manager', 'avrcp_controls', {action="Play"})
+-- _AFT.testVerbStatusSuccess('testBtConnectSuccess', 'Bluetooth-Manager', 'avrcp_controls', {action="Pause"})
+-- _AFT.testVerbStatusSuccess('testBtConnectSuccess', 'Bluetooth-Manager', 'avrcp_controls', {action="Stop"})
+-- _AFT.testVerbStatusSuccess('testBtConnectSuccess', 'Bluetooth-Manager', 'avrcp_controls', {action="Next"})
+-- _AFT.testVerbStatusSuccess('testBtConnectSuccess', 'Bluetooth-Manager', 'avrcp_controls', {action="Previous"})
+-- _AFT.testVerbStatusSuccess('testBtConnectSuccess', 'Bluetooth-Manager', 'avrcp_controls', {action="FastForward"})
+-- _AFT.testVerbStatusSuccess('testBtConnectSuccess', 'Bluetooth-Manager', 'avrcp_controls', {action="Rewind"})
+
+-- Disconnect to a connected device test
+-- _AFT.testVerbStatusSuccess('testBtDisConnectSuccess', 'Bluetooth-Manager', 'disconnect', {device="dev_01_23_45_67_89_0A"})