diff options
author | José Bollo <jose.bollo@iot.bzh> | 2017-10-31 10:50:24 +0100 |
---|---|---|
committer | José Bollo <jose.bollo@iot.bzh> | 2017-10-31 10:50:24 +0100 |
commit | 2ca7f3a4a03db3e7d7fa15504fa3d69e1c6bd217 (patch) | |
tree | 7dc6ff7e6efab4eeca79681efb672ed07061a545 /uds-ble-id-init-service | |
parent | 2ffcc61a750a2bf4598662b4612283fdc9d2a4e4 (diff) |
Split in two: agl-identity-service and uds-ble-id-init-service
The binding is splitted in two parts and packeged as widgets.
The two parts are:
- uds-ble-id-init-service
This service provides a UDS (User Data Service) over
BLE. It detects writes to the email of the user to
initiate the authentication process.
- agl-identity-service
This service queries the Forgerock's Edge Controller
to fill user data and make it available.
The actual code is not fully functionnal.
Signed-off-by: José Bollo <jose.bollo@iot.bzh>
Diffstat (limited to 'uds-ble-id-init-service')
-rw-r--r-- | uds-ble-id-init-service/CMakeLists.txt | 21 | ||||
-rw-r--r-- | uds-ble-id-init-service/btle-advise.service | 8 | ||||
-rw-r--r-- | uds-ble-id-init-service/btle-advise.timer | 6 | ||||
-rw-r--r-- | uds-ble-id-init-service/conf.d/cmake/config.cmake | 201 | ||||
-rw-r--r-- | uds-ble-id-init-service/conf.d/wgt/config.xml.in | 19 | ||||
-rw-r--r-- | uds-ble-id-init-service/src/CMakeLists.txt | 35 | ||||
-rw-r--r-- | uds-ble-id-init-service/src/aia-uds-bluez.c | 607 | ||||
-rw-r--r-- | uds-ble-id-init-service/src/aia-uds-bluez.h | 46 | ||||
-rw-r--r-- | uds-ble-id-init-service/src/uds-ble-id-init-service.c | 146 |
9 files changed, 1089 insertions, 0 deletions
diff --git a/uds-ble-id-init-service/CMakeLists.txt b/uds-ble-id-init-service/CMakeLists.txt new file mode 100644 index 0000000..14b6d55 --- /dev/null +++ b/uds-ble-id-init-service/CMakeLists.txt @@ -0,0 +1,21 @@ +########################################################################### +# Copyright 2017 IoT.bzh +# +# author: Jose Bollo <jose.bollo@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +cmake_minimum_required(VERSION 3.6) +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/) +include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) diff --git a/uds-ble-id-init-service/btle-advise.service b/uds-ble-id-init-service/btle-advise.service new file mode 100644 index 0000000..1a34237 --- /dev/null +++ b/uds-ble-id-init-service/btle-advise.service @@ -0,0 +1,8 @@ +[Unit] +Description=activates advertising of BlueTooth Low-Energy on hci0 + +[Service] +Type=oneshot +ExecStart=/bin/bash -c "rfkill list|sed '/[bB]luetooth/!d;s/:.*//'|xargs -n1 rfkill unblock" +ExecStart=/bin/bash -c "hcitool dev|tail -n+2|cut -f2|xargs -n1 -IX hciconfig X leadv" + diff --git a/uds-ble-id-init-service/btle-advise.timer b/uds-ble-id-init-service/btle-advise.timer new file mode 100644 index 0000000..b02594c --- /dev/null +++ b/uds-ble-id-init-service/btle-advise.timer @@ -0,0 +1,6 @@ +[Unit] +Description=Ensure BLE advise is up + +[Timer] +AccuracySec=1min +OnUnitActiveSec=1min diff --git a/uds-ble-id-init-service/conf.d/cmake/config.cmake b/uds-ble-id-init-service/conf.d/cmake/config.cmake new file mode 100644 index 0000000..4ea1d79 --- /dev/null +++ b/uds-ble-id-init-service/conf.d/cmake/config.cmake @@ -0,0 +1,201 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# Project Info +# ------------------ +set(PROJECT_NAME uds-ble-id-init) +set(PROJECT_VERSION "0.1") +set(PROJECT_PRETTY_NAME "AGL Identitity initiator above BLE's User Data Service") +set(PROJECT_DESCRIPTION "AGL Identitity initiator above BLE's User Data Service") +set(PROJECT_URL "https://github.com/iotbzh/aia-binding") +set(PROJECT_ICON "icon.png") +set(PROJECT_AUTHOR "José Bollo") +set(PROJECT_AUTHOR_MAIL "jose.bollo@iot.bzh") +set(PROJECT_LICENSE "APL2.0") +set(PROJECT_LANGUAGES,"C") + +# Where are stored default templates files from submodule or subtree app-templates in your project tree +# relative to the root project directory +set(PROJECT_APP_TEMPLATES_DIR "../conf.d/app-templates") + +# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain +# but used and must be built and linked. +# set(PROJECT_LIBDIR "libs") + +# Where are stored data for your application. Pictures, static resources must be placed in that folder. +# set(PROJECT_RESOURCES "data") + +# Which directories inspect to find CMakeLists.txt target files +# set(PROJECT_SRC_DIR_PATTERN "*") + +# Compilation Mode (DEBUG, RELEASE) +# ---------------------------------- + +# Kernel selection if needed. You can choose between a +# mandatory version to impose a minimal version. +# Or check Kernel minimal version and just print a Warning +# about missing features and define a preprocessor variable +# to be used as preprocessor condition in code to disable +# incompatibles features. Preprocessor define is named +# KERNEL_MINIMAL_VERSION_OK. +# +# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and +# Yocto SDK Kernel version. +# ----------------------------------------------- +#set (kernel_mandatory_version 4.8) +#set (kernel_minimal_version 4.8) + +# Compiler selection if needed. Impose a minimal version. +# ----------------------------------------------- +set (gcc_minimal_version 4.9) + +# PKG_CONFIG required packages +# ----------------------------- +set (PKG_REQUIRED_LIST + json-c + libsystemd>=222 + afb-daemon +) + +# Prefix path where will be installed the files +# Default: /usr/local (need root permission to write in) +# ------------------------------------------------------ +#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) + +# Customize link option +# ----------------------------- +#list(APPEND link_libraries -an-option) + +# Compilation options definition +# Use CMake generator expressions to specify only for a specific language +# Values are prefilled with default options that is currently used. +# Either separate options with ";", or each options must be quoted separately +# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED ! +# ---------------------------------------------------------------------------- +#set(COMPILE_OPTIONS +# -Wall +# -Wextra +# -Wconversion +# -Wno-unused-parameter +# -Wno-sign-compare +# -Wno-sign-conversion +# -Werror=maybe-uninitialized +# -Werror=implicit-function-declaration +# -ffunction-sections +# -fdata-sections +# -fPIC +# CACHE STRING "Compilation flags") +#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.") +#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.") +#set(PROFILING_COMPILE_OPTIONS +# -g +# -O0 +# -pg +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for PROFILING build type.") +#set(DEBUG_COMPILE_OPTIONS +# -g +# -ggdb +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for DEBUG build type.") +#set(CCOV_COMPILE_OPTIONS +# -g +# -O2 +# --coverage +# CACHE STRING "Compilation flags for CCOV build type.") +#set(RELEASE_COMPILE_OPTIONS +# -g +# -O2 +# CACHE STRING "Compilation flags for RELEASE build type.") + +# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] +# --------------------------------------------------------------------- +set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) +set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) + +# Optional location for config.xml.in +# ----------------------------------- +#set(WIDGET_ICON conf.d/wgt/${PROJECT_ICON} CACHE PATH "Path to the widget icon") +set(WIDGET_CONFIG_TEMPLATE ${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)") + +# Mandatory widget Mimetype specification of the main unit +# -------------------------------------------------------------------------- +# Choose between : +#- text/html : HTML application, +# content.src designates the home page of the application +# +#- application/vnd.agl.native : AGL compatible native, +# content.src designates the relative path of the binary. +# +# - application/vnd.agl.service: AGL service, content.src is not used. +# +#- ***application/x-executable***: Native application, +# content.src designates the relative path of the binary. +# For such application, only security setup is made. +# +set(WIDGET_TYPE application/vnd.agl.service) + +# Mandatory Widget entry point file of the main unit +# -------------------------------------------------------------- +# This is the file that will be executed, loaded, +# at launch time by the application framework. +# +set(WIDGET_ENTRY_POINT irrelevant) + +# Optional dependencies order +# --------------------------- +#set(EXTRA_DEPENDENCIES_ORDER) + +# Optional Extra global include path +# ----------------------------------- +#set(EXTRA_INCLUDE_DIRS) + +# Optional extra libraries +# ------------------------- +#set(EXTRA_LINK_LIBRARIES) + +# Optional force binding Linking flag +# ------------------------------------ +# set(BINDINGS_LINK_FLAG LinkOptions ) + +# Optional force package prefix generation, like widget +# ----------------------------------------------------- +# set(PKG_PREFIX DestinationPath) + +# Optional Application Framework security token +# and port use for remote debugging. +#------------------------------------------------------------ +set(AFB_TOKEN "" CACHE PATH "Default binder security token") +set(AFB_REMPORT "1234" CACHE PATH "Default binder listening port") + +# Print a helper message when every thing is finished +# ---------------------------------------------------- +set(CLOSING_MESSAGE "Typical binding launch: afb-daemon --port=${AFB_REMPORT} --workdir=${CMAKE_BINARY_DIR}/package --ldpaths=lib --roothttp=htdocs --token=\"${AFB_TOKEN}\" --tracereq=common --verbose") +set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") + +# Optional schema validator about now only XML, LUA and JSON +# are supported +#------------------------------------------------------------ +#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler") +#set(XML_CHECKER "xmllint" CACHE STRING "XML linter") +#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter") + +# This include is mandatory and MUST happens at the end +# of this file, else you expose you to unexpected behavior +# ----------------------------------------------------------- +include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake) diff --git a/uds-ble-id-init-service/conf.d/wgt/config.xml.in b/uds-ble-id-init-service/conf.d/wgt/config.xml.in new file mode 100644 index 0000000..0ccd9b5 --- /dev/null +++ b/uds-ble-id-init-service/conf.d/wgt/config.xml.in @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<widget xmlns="http://www.w3.org/ns/widgets" id="@PROJECT_NAME@" version="@PROJECT_VERSION@"> + <name>@PROJECT_NAME@</name> + <icon src="@PROJECT_ICON@"/> + <content src="@WIDGET_ENTRY_POINT@" type="@WIDGET_TYPE@"/> + <description>@PROJECT_DESCRIPTION@</description> + <author>@PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@></author> + <license>@PROJECT_LICENSE@</license> + <feature name="urn:AGL:widget:required-permission"> + <param name="urn:AGL:permission::public:hidden" value="required" /> + <param name="urn:AGL:permission::system:run-by-default" value="required" /> + </feature> + <feature name="urn:AGL:widget:provided-api"> + <param name="windowmanager" value="ws" /> + </feature> + <feature name="urn:AGL:widget:required-api"> + <param name="lib/afb-ll-database-binding.so" value="local" /> + </feature> +</widget> diff --git a/uds-ble-id-init-service/src/CMakeLists.txt b/uds-ble-id-init-service/src/CMakeLists.txt new file mode 100644 index 0000000..97e229a --- /dev/null +++ b/uds-ble-id-init-service/src/CMakeLists.txt @@ -0,0 +1,35 @@ +########################################################################### +# Copyright 2017 IoT.bzh +# +# author: Jose Bollo <jose.bollo@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +PROJECT_TARGET_ADD(afb-uds-ble-id-init-binding) + +add_library(afb-uds-ble-id-init-binding MODULE + uds-ble-id-init-service.c + aia-uds-bluez.c +) + +set_target_properties(afb-uds-ble-id-init-binding PROPERTIES + LABELS "BINDING" + PREFIX "" + COMPILE_FLAGS "${libsystemd_CFLAGS}" + INCLUDE_DIRECTORIES "${libsystemd_INCLUDE_DIRS}" + LINK_FLAGS "${BINDINGS_LINK_FLAG}" + LINK_LIBRARIES "${libsystemd_LIBRARIES}" + OUTPUT_NAME "${TARGET_NAME}" +) + diff --git a/uds-ble-id-init-service/src/aia-uds-bluez.c b/uds-ble-id-init-service/src/aia-uds-bluez.c new file mode 100644 index 0000000..35ba61d --- /dev/null +++ b/uds-ble-id-init-service/src/aia-uds-bluez.c @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Author: José Bollo <jose.bollo@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <systemd/sd-bus.h> + +#include "aia-uds-bluez.h" + +#define ROOT "/uds" + +#define ITF_SERVICE "org.bluez.GattService1" +#define ITF_CHARACTERISTIC "org.bluez.GattCharacteristic1" +#define ITF_DESCRIPTOR "org.bluez.GattDescriptor1" + +/****** internal types **********/ + +struct item +{ + const char *uuid; + const char *path; + struct item *parent; + struct aia_uds_value *value; +}; + +/****** internal data **********/ + +static sd_bus *busini; +static int activation; + +static aia_uds_on_change on_change_callback; + +static struct aia_uds uds; + +static struct aia_uds uds_descs = +{ + .first_name = { .data = "First name of the user" }, + .last_name = { .data = "Last name of the user" }, + .email = { .data = "Email of the user" }, + .language = { .data = "The Language definition is based on ISO639-1" } +}; + +static struct aia_uds_value woutf8 = { .data = "\031\000\047\000\001\000\000", .length = 7 }; + +static struct item services[1] = +{ + { + .uuid = "0000181C-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0" + } +}; + +static struct item characteristics[4] = +{ + { + .uuid = "00002A8A-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char0", + .parent = &services[0], + .value = &uds.first_name + }, { + .uuid = "00002A90-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char1", + .parent = &services[0], + .value = &uds.last_name + }, { + .uuid = "00002A87-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char2", + .parent = &services[0], + .value = &uds.email + }, { + .uuid = "00002AA2-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char3", + .parent = &services[0], + .value = &uds.language + } +}; + +static struct item descriptors[8] = +{ + { + .uuid = "00002901-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char0/desc0", + .parent = &characteristics[0], + .value = &uds_descs.first_name + }, { + .uuid = "00002904-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char0/desc1", + .parent = &characteristics[0], + .value = &woutf8 + }, { + .uuid = "00002901-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char1/desc0", + .parent = &characteristics[1], + .value = &uds_descs.last_name + }, { + .uuid = "00002904-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char1/desc1", + .parent = &characteristics[1], + .value = &woutf8 + }, { + .uuid = "00002901-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char2/desc0", + .parent = &characteristics[2], + .value = &uds_descs.email + }, { + .uuid = "00002904-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char2/desc1", + .parent = &characteristics[2], + .value = &woutf8 + }, { + .uuid = "00002901-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char3/desc0", + .parent = &characteristics[3], + .value = &uds_descs.language + }, { + .uuid = "00002904-0000-1000-8000-00805f9b34fb", + .path = ROOT"/service0/char3/desc1", + .parent = &characteristics[3], + .value = &woutf8 + } +}; + +/****** utility ***********/ + +static uint16_t get_offset(sd_bus_message *m) +{ + uint16_t offset; + const char *key; + + if (0 < sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sv")) { + while (sd_bus_message_read_basic(m, 's', &key) > 0) { + if (strcmp(key,"offset")) + sd_bus_message_skip(m, "v"); + else { + if (sd_bus_message_enter_container(m, SD_BUS_TYPE_VARIANT, "q")) { + sd_bus_message_read_basic(m, 'q', &offset); + sd_bus_message_exit_container(m); + return offset; + } + } + } + sd_bus_message_exit_container(m); + } + return 0; +} + +static int message_append_strings(sd_bus_message *m, const char **s) +{ + int rc = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, "s"); + while (rc >= 0 && *s) + rc = sd_bus_message_append_basic(m, 's', *s++); + if (rc >= 0) + rc = sd_bus_message_close_container(m); + return rc; +} + +/****** common callbacks ***********/ + +static int get_uuid( + struct sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) +{ + struct item *item = userdata; + return sd_bus_message_append_basic(reply, 's', item->uuid); +} + +static int get_parent( + struct sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) +{ + struct item *item = userdata; + return sd_bus_message_append_basic(reply, 'o', item->parent->path); +} + +static int get_children( + struct sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) +{ + size_t i, n; + int rc; + + rc = sd_bus_message_open_container(reply, SD_BUS_TYPE_ARRAY, "o"); + n = sizeof characteristics / sizeof *characteristics; + i = 0; + while (rc >= 0 && i < n) { + if (characteristics[i].parent == userdata) + rc = sd_bus_message_append_basic(reply, 'o', characteristics[i].path); + i++; + } + n = sizeof descriptors / sizeof *descriptors; + i = 0; + while (rc >= 0 && i < n) { + if (descriptors[i].parent == userdata) + rc = sd_bus_message_append_basic(reply, 'o', descriptors[i].path); + i++; + } + if (rc >= 0) + rc = sd_bus_message_close_container(reply); + return rc; +} + +static int read_value_offset( + struct item *item, + size_t offset, + sd_bus_message *reply) +{ + struct aia_uds_value *value = item->value; + const char *data = value ? value->data : NULL; + size_t size = !value ? 0 : value->length ? value->length : data ? strlen(data) : 0; + return sd_bus_message_append_array(reply, 'y', &data[offset], offset > size ? 0 : size - offset); +} + +static int read_value( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) +{ + int rc; + sd_bus_message *r; + + rc = sd_bus_message_new_method_return(m, &r); + rc = read_value_offset(userdata, get_offset(m), r); + rc = sd_bus_send(NULL, r, NULL); + return 1; +} + +static int write_value_offset( + struct item *item, + size_t offset, + const char *buffer, + size_t length) +{ + struct aia_uds_value *value = item->value; + const char *data = value ? value->data : NULL; + size_t size = !value ? 0 : value->length ? value->length : data ? strlen(data) : 0; + + + char *next = malloc(offset + length + 1); + if (!next) + return -ENOMEM; + if (offset) { + if (size >= offset) + memcpy(next, data, offset); + else { + memcpy(next, data, size); + memset(&next[size], 0, offset - size); + } + } + memcpy(&next[offset], buffer, length); + next[offset + length] = 0; + value->data = next; + value->length = offset + length; + free((char*)data); + + return 0; +} + +static int write_value( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) +{ + int rc; + struct item *item = userdata; + const void *data; + size_t size; + sd_bus_message *r; + + rc = sd_bus_message_read_array(m, 'y', &data, &size); + rc = write_value_offset(item, get_offset(m), data, size); + + rc = sd_bus_message_new_method_return(m, &r); + rc = sd_bus_send(NULL, r, NULL); + + sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), item->path, ITF_CHARACTERISTIC, "Value", NULL); + + if (on_change_callback) { + item->value->changed = 1; + on_change_callback(&uds); + item->value->changed = 0; + } + + return 1; +} + +static int get_value( + struct sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) +{ + return read_value_offset(userdata, 0, reply); +} + +static int get_true( + struct sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) +{ + int v = 1; + return sd_bus_message_append_basic(reply, 'b', &v); +} + +static int get_false( + struct sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) +{ + int v = 0; + return sd_bus_message_append_basic(reply, 'b', &v); +} + +static int not_supported( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) +{ + return sd_bus_reply_method_errorf(m, "org.bluez.Error.NotSupported", "not supported"); +} + +static int not_permitted( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) +{ + return sd_bus_reply_method_errorf(m, "org.bluez.Error.NotPermitted", "not permitted"); +} + +static int failed( + sd_bus_message *m, + void *userdata, + sd_bus_error *ret_error) +{ + return sd_bus_reply_method_errorf(m, "org.bluez.Error.Failed", "failed"); +} + +/****** service's callbacks ***********/ + +static int get_srv_includes( + struct sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) +{ + int rc = sd_bus_message_open_container(reply, SD_BUS_TYPE_ARRAY, "o"); + if (rc >= 0) + rc = sd_bus_message_close_container(reply); + return rc; +} + +/****** characteristic's callbacks ***********/ + +static int get_char_flags( + struct sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) +{ + static const char *flags[] = { "read", "write", NULL }; + return message_append_strings(reply, flags); +} + +/****** descriptor's callbacks ***********/ + +static int get_desc_flags( + struct sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *ret_error) +{ + static const char *flags[] = { "read", NULL }; + return message_append_strings(reply, flags); +} + +/****** description ***********/ + +static struct sd_bus_vtable vservice[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_PROPERTY("UUID", "s", get_uuid, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Primary", "b", get_true, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Includes", "ao", get_srv_includes, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Characteristics", "ao", get_children, 0, SD_BUS_VTABLE_PROPERTY_CONST), + + SD_BUS_VTABLE_END +}; + + +static struct sd_bus_vtable vcharacteristic[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_METHOD("ReadValue", "a{sv}", "ay", read_value, 0), + SD_BUS_METHOD("WriteValue", "aya{sv}", "", write_value, 0), + SD_BUS_METHOD("StartNotify", "", "", not_supported, 0), + SD_BUS_METHOD("StopNotify", "", "", failed, 0), + + SD_BUS_PROPERTY("UUID", "s", get_uuid, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Service", "o", get_parent, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Value", "ay", get_value, 0, 0), + SD_BUS_PROPERTY("WriteAcquired", "b", get_false, 0, 0), + SD_BUS_PROPERTY("NotifyAcquired", "b", get_false, 0, 0), + SD_BUS_PROPERTY("Notifying", "b", get_false, 0, 0), + SD_BUS_PROPERTY("Flags", "as", get_char_flags, 0, 0), + SD_BUS_PROPERTY("Descriptors", "ao", get_children, 0, 0), + + SD_BUS_VTABLE_END +}; + + +static struct sd_bus_vtable vdescriptor[] = { + SD_BUS_VTABLE_START(0), + + SD_BUS_METHOD("ReadValue", "a{sv}", "ay", read_value, 0), + SD_BUS_METHOD("WriteValue", "aya{sv}", "", not_permitted, 0), + + SD_BUS_PROPERTY("UUID", "s", get_uuid, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Characteristic", "o", get_parent, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Value", "ay", get_value, 0, 0), + SD_BUS_PROPERTY("Flags", "as", get_desc_flags, 0, 0), + + SD_BUS_VTABLE_END +}; + + +/******** Integration ***************/ + +aia_uds_on_change aia_uds_set_on_change(aia_uds_on_change callback) +{ + aia_uds_on_change prev = on_change_callback; + on_change_callback = callback; + return prev; +} + +int aia_uds_init(struct sd_bus *bus) +{ + int rc; + + if (busini) + return 0; + + rc = sd_bus_add_object_manager(bus, NULL, ROOT); + + rc = sd_bus_add_object_vtable(bus, NULL, services[0].path, + ITF_SERVICE, vservice, &services[0]); + + rc = sd_bus_add_object_vtable(bus, NULL, characteristics[0].path, + ITF_CHARACTERISTIC, vcharacteristic, &characteristics[0]); + rc = sd_bus_add_object_vtable(bus, NULL, characteristics[1].path, + ITF_CHARACTERISTIC, vcharacteristic, &characteristics[1]); + rc = sd_bus_add_object_vtable(bus, NULL, characteristics[2].path, + ITF_CHARACTERISTIC, vcharacteristic, &characteristics[2]); + rc = sd_bus_add_object_vtable(bus, NULL, characteristics[3].path, + ITF_CHARACTERISTIC, vcharacteristic, &characteristics[3]); + + rc = sd_bus_add_object_vtable(bus, NULL, descriptors[0].path, + ITF_DESCRIPTOR, vdescriptor, &descriptors[0]); + rc = sd_bus_add_object_vtable(bus, NULL, descriptors[1].path, + ITF_DESCRIPTOR, vdescriptor, &descriptors[1]); + rc = sd_bus_add_object_vtable(bus, NULL, descriptors[2].path, + ITF_DESCRIPTOR, vdescriptor, &descriptors[2]); + rc = sd_bus_add_object_vtable(bus, NULL, descriptors[3].path, + ITF_DESCRIPTOR, vdescriptor, &descriptors[3]); + rc = sd_bus_add_object_vtable(bus, NULL, descriptors[4].path, + ITF_DESCRIPTOR, vdescriptor, &descriptors[4]); + rc = sd_bus_add_object_vtable(bus, NULL, descriptors[5].path, + ITF_DESCRIPTOR, vdescriptor, &descriptors[5]); + rc = sd_bus_add_object_vtable(bus, NULL, descriptors[6].path, + ITF_DESCRIPTOR, vdescriptor, &descriptors[6]); + rc = sd_bus_add_object_vtable(bus, NULL, descriptors[7].path, + ITF_DESCRIPTOR, vdescriptor, &descriptors[7]); + + busini = sd_bus_ref(bus); + return 0; +} + +struct cb { + void *callback; + void *closure; + int onoff; +}; + +static struct cb *alloccb(void *callback, void *closure, int onoff) +{ + struct cb *cb = malloc(sizeof *cb); + if (cb) { + cb->callback = callback; + cb->closure = closure; + } + return cb; +} + +static int register_uds_cb(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) +{ + struct cb *cb = userdata; + void (*callback)(void *closure, int error, int state); + int iserr; + + iserr = sd_bus_message_is_method_error(m, NULL); + if (!iserr) + activation = cb->onoff; + callback = cb->callback; + if (callback) + callback(cb->closure, iserr, activation); + free(cb); + + return 1; +} + +static int register_uds(int onoff, void *data) +{ + int rc; + sd_bus_message *m; + + rc = sd_bus_message_new_method_call(busini, &m, "org.bluez", + "/org/bluez/hci0", "org.bluez.GattManager1", + onoff ? "RegisterApplication" : "UnregisterApplication" ); + rc = sd_bus_message_append_basic(m, 'o', ROOT); + if (onoff) { + rc = sd_bus_message_open_container(m, 'a', "{sv}"); + rc = sd_bus_message_close_container(m); + } + rc = sd_bus_call_async(busini, NULL, m, register_uds_cb, data, 5*1000*1000); + sd_bus_message_unref(m); + return rc; +} + +int aia_uds_activate(int onoff, void (*callback)(void *closure, int error, int state), void *closure) +{ + int rc; + struct cb *cb; + + if (!busini) + return -EINVAL; + + onoff = !!onoff; + if (activation == onoff) + return 0; + + cb = alloccb(callback, closure, onoff); + if (!cb) + return -ENOMEM; + + rc = register_uds(onoff, cb); + if (rc < 0) + free(cb); + + return rc < 0 ? rc : 1; +} + +int aia_uds_advise(int onoff, void (*callback)(void *closure, int error, int state), void *closure) +{ + return aia_uds_activate(onoff, callback, closure); +} + diff --git a/uds-ble-id-init-service/src/aia-uds-bluez.h b/uds-ble-id-init-service/src/aia-uds-bluez.h new file mode 100644 index 0000000..856da4e --- /dev/null +++ b/uds-ble-id-init-service/src/aia-uds-bluez.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Author: José Bollo <jose.bollo@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + + +#pragma once + +struct aia_uds_value +{ + const char *data; + size_t length; + int changed; +}; + +struct aia_uds +{ + struct aia_uds_value first_name; + struct aia_uds_value last_name; + struct aia_uds_value email; + struct aia_uds_value language; +}; + +typedef void (*aia_uds_on_change)(const struct aia_uds *); + +extern aia_uds_on_change aia_uds_set_on_change(aia_uds_on_change callback); + +extern int aia_uds_activate(int onoff, void (*callback)(void *closure, int error, int state), void *closure); + +extern int aia_uds_advise(int onoff, void (*callback)(void *closure, int error, int state), void *closure); + +extern int aia_uds_init(struct sd_bus *bus); + diff --git a/uds-ble-id-init-service/src/uds-ble-id-init-service.c b/uds-ble-id-init-service/src/uds-ble-id-init-service.c new file mode 100644 index 0000000..f887963 --- /dev/null +++ b/uds-ble-id-init-service/src/uds-ble-id-init-service.c @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2015, 2016, 2017 "IoT.bzh" + * Author: José Bollo <jose.bollo@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include <errno.h> +#include <stdint.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <json-c/json.h> +#include <systemd/sd-bus.h> + +#define AFB_BINDING_VERSION 2 +#include <afb/afb-binding.h> + +#include "aia-uds-bluez.h" + +#if !defined(AUTO_START_ADVISE) +#define AUTO_START_ADVISE 1 +#endif + +static int advising; + +static struct afb_event event; + +static int autoadvise = AUTO_START_ADVISE; + +/****************************************************************/ + +static void on_uds_change(const struct aia_uds *uds) +{ + struct json_object *object; + + AFB_INFO("UDS changed" + " first-name%s[%.*s]" + " last-name%s[%.*s]" + " email%s[%.*s]" + " language%s[%.*s]", + uds->first_name.changed ? "*" : "", (int)uds->first_name.length, uds->first_name.data ?:"", + uds->last_name.changed ? "*" : "", (int)uds->last_name.length, uds->last_name.data ?:"", + uds->email.changed ? "*" : "", (int)uds->email.length, uds->email.data ?:"", + uds->language.changed ? "*" : "", (int)uds->language.length, uds->language.data ?:""); + + if (uds->email.changed) { + object = json_object_new_object(); + json_object_object_add(object, "incoming", json_object_new_string(uds->email.data)); + afb_event_push(event, object); + } +} + +static void start (struct afb_req request) +{ + int rc; + + if (!advising) { + rc = aia_uds_advise(1, NULL, NULL); + if (rc < 0) { +/* +TODO: solve the issue + afb_req_fail(request, "failed", "start scan failed"); + return; +*/ + AFB_ERROR("Ignoring scan start failed, because probably already in progress"); + } + } + advising = advising + 1; + afb_req_subscribe(request, event); + afb_req_success(request, NULL, NULL); +} + + +static void stop (struct afb_req request) +{ + if (advising) { + advising = advising - 1; + if (!advising) + aia_uds_advise(0, NULL, NULL); + } + afb_req_success(request, NULL, NULL); +} + +static int init() +{ + sd_bus *bus; + int rc; + + bus = afb_daemon_get_system_bus(); + rc = bus ? aia_uds_init(bus) : -ENOTSUP; + if (rc < 0) { + errno = -rc; + return -1; + } + + aia_uds_set_on_change(on_uds_change); + + event = afb_daemon_make_event("event"); + if (!afb_event_is_valid(event)) + return -1; + + rc = aia_uds_advise(autoadvise, NULL, NULL); + advising = autoadvise && rc >= 0; + return rc < 0 ? rc : 0; +} + + +// NOTE: this sample does not use session to keep test a basic as possible +// in real application most APIs should be protected with AFB_SESSION_CHECK +static const struct afb_verb_v2 verbs[]= +{ + {"start" , start, NULL, "start User Data Service", AFB_SESSION_NONE }, + {"stop" , stop , NULL, "stop User Data Service" , AFB_SESSION_NONE }, + {NULL} +}; + +const struct afb_binding_v2 afbBindingV2 = +{ + .api = "uds-ble-init-id", + .specification = NULL, + .info = "AGL Identitity initiator above BLE's User Data Service", + .verbs = verbs, + .preinit = NULL, + .init = init, + .onevent = NULL, + .noconcurrency = 1 +}; + +/* vim: set colorcolumn=80: */ + |