diff options
author | Loïc Collignon <loic.collignon@iot.bzh> | 2018-06-05 10:29:47 +0200 |
---|---|---|
committer | Loïc Collignon <loic.collignon@iot.bzh> | 2018-06-12 15:26:21 +0200 |
commit | 322f8932476eda944c7d3ac65eafde12c69b2ae9 (patch) | |
tree | 3146f053d8f3f8f8324d7e41493b929d348a3f9c /ahl-binding | |
parent | 545c14e62971b23c704bc3d7f696e934e330656d (diff) |
Rewrite of the High Level API using the new HAL model
The new HAL model need the High Level API to be rewritten.
This is the first version of this rewrite, still in progress
but should work.
Change-Id: I5c94cf39d84cefae6b7a179c09d95e645673e8d4
Signed-off-by: Loïc Collignon <loic.collignon@iot.bzh>
Diffstat (limited to 'ahl-binding')
-rw-r--r-- | ahl-binding/CMakeLists.txt | 25 | ||||
-rw-r--r-- | ahl-binding/ahl-4a.cpp | 49 | ||||
-rw-r--r-- | ahl-binding/ahl-4a.hpp | 47 | ||||
-rw-r--r-- | ahl-binding/ahl-apidef.h | 237 | ||||
-rw-r--r-- | ahl-binding/ahl-apidef.json | 448 | ||||
-rw-r--r-- | ahl-binding/ahl-binding.c | 1276 | ||||
-rw-r--r-- | ahl-binding/ahl-binding.cpp | 344 | ||||
-rw-r--r-- | ahl-binding/ahl-binding.h | 119 | ||||
-rw-r--r-- | ahl-binding/ahl-binding.hpp | 74 | ||||
-rw-r--r-- | ahl-binding/ahl-config.c | 251 | ||||
-rw-r--r-- | ahl-binding/ahl-deviceenum.c | 336 | ||||
-rw-r--r-- | ahl-binding/ahl-json.c | 296 | ||||
-rw-r--r-- | ahl-binding/ahl-json.h | 26 | ||||
-rw-r--r-- | ahl-binding/config_entry.cpp | 62 | ||||
-rw-r--r-- | ahl-binding/config_entry.hpp | 49 | ||||
-rw-r--r-- | ahl-binding/jsonc_utils.hpp | 90 | ||||
-rw-r--r-- | ahl-binding/role.cpp | 107 | ||||
-rw-r--r-- | ahl-binding/role.hpp | 60 |
18 files changed, 898 insertions, 2998 deletions
diff --git a/ahl-binding/CMakeLists.txt b/ahl-binding/CMakeLists.txt index 0dee304..b36cb1e 100644 --- a/ahl-binding/CMakeLists.txt +++ b/ahl-binding/CMakeLists.txt @@ -20,30 +20,37 @@ PROJECT_TARGET_ADD(audiohighlevel) # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c ahl-config.c ahl-json.c) + ADD_LIBRARY(${TARGET_NAME} MODULE + config_entry.cpp + role.cpp + ahl-4a.cpp + ahl-binding.cpp + ) # Binder exposes a unique public entry point SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES - PREFIX "afb-" - LABELS "BINDINGV2" - LINK_FLAGS ${BINDINGS_LINK_FLAG} + PREFIX "afb-" + LABELS "BINDING" + LINK_FLAGS ${BINDINGS_LINK_FLAG} OUTPUT_NAME ${TARGET_NAME} ) - # Change default OPenAPI file - SET_OPENAPI_FILENAME("ahl-apidef") - # Define target includes TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} PUBLIC ${GLIB_PKG_INCLUDE_DIRS} + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../controller/ctl-lib + ${CMAKE_CURRENT_SOURCE_DIR}/../afb-utilities ) # Library dependencies (include updates automatically) # Find package for GLIB does not seem to export TARGET_LINK_LIBRARIES(${TARGET_NAME} - ahl-policy - ahl-utilities + #ahl-policy + #ahl-utilities afb-utilities + #afb-helpers + ctl-utilities ${GLIB_PKG_LIBRARIES} ${link_libraries} ) diff --git a/ahl-binding/ahl-4a.cpp b/ahl-binding/ahl-4a.cpp new file mode 100644 index 0000000..b5fdc16 --- /dev/null +++ b/ahl-binding/ahl-4a.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@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. + */ + +#include "ahl-4a.hpp" + +ahl_4a_t::ahl_4a_t(json_object* j) +{ + jcast_array(hals_, j, "hals"); + jcast_array(roles_, j, "roles"); +} + +ahl_4a_t& ahl_4a_t::operator<<(json_object* j) +{ + jcast_array(hals_, j, "hals"); + jcast_array(roles_, j, "roles"); + return *this; +} + +const std::vector<std::string>& ahl_4a_t::hals() const +{ + return hals_; +} + +const std::vector<role_t>& ahl_4a_t::roles() const +{ + return roles_; +} + +void ahl_4a_t::set_device_uri(std::string stream, std::string device_uri) +{ + for(auto& r : roles_) + { + if (r.stream() == stream) r.device_uri(device_uri); + } +} diff --git a/ahl-binding/ahl-4a.hpp b/ahl-binding/ahl-4a.hpp new file mode 100644 index 0000000..acab71e --- /dev/null +++ b/ahl-binding/ahl-4a.hpp @@ -0,0 +1,47 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@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. + */ + +#include <vector> +#include "jsonc_utils.hpp" +#include "role.hpp" + +class ahl_4a_t +{ +private: + std::vector<std::string> hals_; + std::vector<role_t> roles_; + +public: + explicit ahl_4a_t() = default; + explicit ahl_4a_t(const ahl_4a_t&) = default; + explicit ahl_4a_t(ahl_4a_t&&) = default; + + ~ahl_4a_t() = default; + + ahl_4a_t& operator=(const ahl_4a_t&) = default; + ahl_4a_t& operator=(ahl_4a_t&&) = default; + + explicit ahl_4a_t(json_object* j); + ahl_4a_t& operator<<(json_object* j); + + const std::vector<std::string>& hals() const; + const std::vector<role_t>& roles() const; + void set_device_uri(std::string stream, std::string device_uri); +}; + diff --git a/ahl-binding/ahl-apidef.h b/ahl-binding/ahl-apidef.h deleted file mode 100644 index a9cb6dd..0000000 --- a/ahl-binding/ahl-apidef.h +++ /dev/null @@ -1,237 +0,0 @@ - -static const char _afb_description_v2_ahl_4a[] = - "{\"openapi\":\"3.0.0\",\"info\":{\"description\":\"Audio high level API " - "for AGL applications\",\"title\":\"audiohighlevel\",\"version\":\"1.0\"," - "\"x-binding-c-generator\":{\"api\":\"ahl-4a\",\"version\":2,\"prefix\":\"" - "audiohlapi_\",\"postfix\":\"\",\"start\":null,\"onevent\":\"AhlOnEvent\"" - ",\"init\":\"AhlBindingInit\",\"scope\":\"\",\"private\":false,\"noconcur" - "rency\":false}},\"servers\":[{\"url\":\"ws://{host}:{port}/api/audiohl\"" - ",\"description\":\"Audio high level API for AGL applications.\",\"variab" - "les\":{\"host\":{\"default\":\"localhost\"},\"port\":{\"default\":\"1234" - "\"}},\"x-afb-events\":[{\"$ref\":\"#/components/schemas/afb-event\"}]}]," - "\"components\":{\"schemas\":{\"afb-reply\":{\"$ref\":\"#/components/sche" - "mas/afb-reply-v2\"},\"afb-event\":{\"$ref\":\"#/components/schemas/afb-e" - "vent-v2\"},\"afb-reply-v2\":{\"title\":\"Generic response.\",\"type\":\"" - "object\",\"required\":[\"jtype\",\"request\"],\"properties\":{\"jtype\":" - "{\"type\":\"string\",\"const\":\"afb-reply\"},\"request\":{\"type\":\"ob" - "ject\",\"required\":[\"status\"],\"properties\":{\"status\":{\"type\":\"" - "string\"},\"info\":{\"type\":\"string\"},\"token\":{\"type\":\"string\"}" - ",\"uuid\":{\"type\":\"string\"},\"reqid\":{\"type\":\"string\"}}},\"resp" - "onse\":{\"type\":\"object\"}}},\"afb-event-v2\":{\"type\":\"object\",\"r" - "equired\":[\"jtype\",\"event\"],\"properties\":{\"jtype\":{\"type\":\"st" - "ring\",\"const\":\"afb-event\"},\"event\":{\"type\":\"string\"},\"data\"" - ":{\"type\":\"object\"}}},\"endpoint_info\":{\"type\":\"object\",\"requir" - "ed\":[\"endpoint_id\",\"type\",\"device_name\",\"device_uri\"],\"propert" - "ies\":{\"endpoint_id\":{\"type\":\"int\"},\"type\":{\"type\":\"enum\"},\"" - "device_name\":{\"type\":\"string\"},\"device_uri_type\":{\"type\":\"stri" - "ng\"}}},\"stream_info\":{\"type\":\"object\",\"required\":[\"stream_id\"" - ",\"state\",\"mute\",\"endpoint_info\"],\"properties\":{\"stream_id\":{\"" - "type\":\"int\"},\"state\":{\"type\":\"int\"},\"mute\":{\"type\":\"int\"}" - ",\"device_uri\":{\"type\":\"string\"},\"$ref\":\"#/components/schemas/en" - "dpoint_info\"}}},\"x-permissions\":{\"streamcontrol\":{\"permission\":\"" - "urn:AGL:permission:audio:public:streamcontrol\"},\"endpointcontrol\":{\"" - "permission\":\"urn:AGL:permission:audio:public:endpointcontrol\"},\"audi" - "ostream\":{\"permission\":\"urn:AGL:permission:audio:public:audiostream\"" - "},\"soundevent\":{\"permission\":\"urn:AGL:permission:audio:public:sound" - "event\"}},\"responses\":{\"200\":{\"description\":\"A complex object arr" - "ay response\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"" - "#/components/schemas/afb-reply\"}}}},\"400\":{\"description\":\"Invalid " - "arguments\"}}},\"paths\":{\"/get_endpoints\":{\"description\":\"Retrieve" - " array of available audio endpoints\",\"get\":{\"parameters\":[{\"in\":\"" - "query\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"" - "string\"}},{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":tru" - "e,\"schema\":{\"type\":\"enum\"}}],\"responses\":{\"200\":{\"$ref\":\"#/" - "components/responses/200\",\"response\":{\"description\":\"Array of endp" - "oint info structures\",\"type\":\"array\",\"items\":{\"$ref\":\"#/compon" - "ents/schemas/endpoint_info\"}}},\"400\":{\"$ref\":\"#/components/respons" - "es/400\"}}}},\"/stream_open\":{\"description\":\"Request opening a strea" - "m\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/au" - "diostream\"},\"parameters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"" - "required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"na" - "me\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}}" - ",{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":false,\"schema\"" - ":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/re" - "sponses/200\",\"response\":{\"description\":\"Stream information structu" - "re\",\"$ref\":\"#/components/schemas/stream_info\"}},\"400\":{\"$ref\":\"" - "#/components/responses/400\"}}}},\"/stream_close\":{\"description\":\"Re" - "quest closing a stream\",\"get\":{\"x-permissions\":{\"$ref\":\"#/compon" - "ents/x-permissions/audiostream\"},\"parameters\":[{\"in\":\"query\",\"na" - "me\":\"stream_id\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"" - "responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{" - "\"$ref\":\"#/components/responses/400\"}}}},\"/set_stream_state\":{\"des" - "cription\":\"Change stream active and/or mute state\",\"get\":{\"x-permi" - "ssions\":{\"$ref\":\"#/components/x-permissions/streamcontrol\"},\"param" - "eters\":[{\"in\":\"query\",\"name\":\"stream_id\",\"required\":false,\"s" - "chema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state\",\"requi" - "red\":false,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"" - "mute\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":" - "{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#" - "/components/responses/400\"}}}},\"/get_stream_info\":{\"description\":\"" - "Retrieve stream information\",\"get\":{\"parameters\":[{\"in\":\"query\"" - ",\"name\":\"stream_id\",\"required\":true,\"schema\":{\"type\":\"int\"}}" - "],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"resp" - "onse\":{\"description\":\"Stream information structure\",\"$ref\":\"#/co" - "mponents/schemas/stream_info\"}},\"400\":{\"$ref\":\"#/components/respon" - "ses/400\"}}}},\"/volume\":{\"description\":\"Set or get volume on endpoi" - "nt\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/e" - "ndpointcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_" - "type\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query" - "\",\"name\":\"endpoint_id\",\"required\":true,\"schema\":{\"type\":\"int" - "\"}},{\"in\":\"query\",\"name\":\"volume\",\"required\":false,\"schema\"" - ":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components" - "/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"" - "/get_endpoint_info\":{\"description\":\"Retrieve endpoint information in" - "cluding its properties\",\"get\":{\"parameters\":[{\"in\":\"query\",\"na" - "me\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}}" - ",{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"schema\"" - ":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/re" - "sponses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/p" - "roperty\":{\"description\":\"Set/get endpoint property value\",\"get\":{" - "\"x-permissions\":{\"$ref\":\"#/components/x-permissions/endpointcontrol" - "\"},\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"requi" - "red\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"" - "endpoint_id\",\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"" - "query\",\"name\":\"property_name\",\"required\":true,\"schema\":{\"type\"" - ":\"string\"}},{\"in\":\"query\",\"name\":\"value\",\"required\":false,\"" - "schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/co" - "mponents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400" - "\"}}}},\"/get_list_actions\":{\"description\":\"Retrieve a list of suppo" - "rted actions for a particular audio role\",\"get\":{\"parameters\":[{\"i" - "n\":\"query\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"ty" - "pe\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/respo" - "nses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/post" - "_action\":{\"description\":\"Post sound or audio device related action e" - "vent (extendable mechanism)\",\"get\":{\"x-permissions\":{\"$ref\":\"#/c" - "omponents/x-permissions/soundevent\"},\"parameters\":[{\"in\":\"query\"," - "\"name\":\"action_name\",\"required\":true,\"schema\":{\"type\":\"string" - "\"}},{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"schem" - "a\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"media_name\",\"r" - "equired\":false,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"na" - "me\":\"action_context\",\"required\":false,\"schema\":{\"type\":\"object" - "\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"" - "400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/event_subscription" - "\":{\"description\":\"Subscribe to audio high level events\",\"get\":{\"" - "parameters\":[{\"in\":\"query\",\"name\":\"events\",\"required\":true,\"" - "schema\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},{\"in\":\"" - "query\",\"name\":\"subscribe\",\"required\":true,\"schema\":{\"type\":\"" - "int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"" - "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}}}}" -; - -static const struct afb_auth _afb_auths_v2_ahl_4a[] = { - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:audiostream" }, - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:streamcontrol" }, - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:endpointcontrol" }, - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:soundevent" } -}; - - void audiohlapi_get_endpoints(struct afb_req req); - void audiohlapi_stream_open(struct afb_req req); - void audiohlapi_stream_close(struct afb_req req); - void audiohlapi_set_stream_state(struct afb_req req); - void audiohlapi_get_stream_info(struct afb_req req); - void audiohlapi_volume(struct afb_req req); - void audiohlapi_get_endpoint_info(struct afb_req req); - void audiohlapi_property(struct afb_req req); - void audiohlapi_get_list_actions(struct afb_req req); - void audiohlapi_post_action(struct afb_req req); - void audiohlapi_event_subscription(struct afb_req req); - -static const struct afb_verb_v2 _afb_verbs_v2_ahl_4a[] = { - { - .verb = "get_endpoints", - .callback = audiohlapi_get_endpoints, - .auth = NULL, - .info = "Retrieve array of available audio endpoints", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "stream_open", - .callback = audiohlapi_stream_open, - .auth = &_afb_auths_v2_ahl_4a[0], - .info = "Request opening a stream", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "stream_close", - .callback = audiohlapi_stream_close, - .auth = &_afb_auths_v2_ahl_4a[0], - .info = "Request closing a stream", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "set_stream_state", - .callback = audiohlapi_set_stream_state, - .auth = &_afb_auths_v2_ahl_4a[1], - .info = "Change stream active and/or mute state", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_stream_info", - .callback = audiohlapi_get_stream_info, - .auth = NULL, - .info = "Retrieve stream information", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "volume", - .callback = audiohlapi_volume, - .auth = &_afb_auths_v2_ahl_4a[2], - .info = "Set or get volume on endpoint", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_endpoint_info", - .callback = audiohlapi_get_endpoint_info, - .auth = NULL, - .info = "Retrieve endpoint information including its properties", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "property", - .callback = audiohlapi_property, - .auth = &_afb_auths_v2_ahl_4a[2], - .info = "Set/get endpoint property value", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_list_actions", - .callback = audiohlapi_get_list_actions, - .auth = NULL, - .info = "Retrieve a list of supported actions for a particular audio role", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "post_action", - .callback = audiohlapi_post_action, - .auth = &_afb_auths_v2_ahl_4a[3], - .info = "Post sound or audio device related action event (extendable mechanism)", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "event_subscription", - .callback = audiohlapi_event_subscription, - .auth = NULL, - .info = "Subscribe to audio high level events", - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = NULL, - .callback = NULL, - .auth = NULL, - .info = NULL, - .session = 0 - } -}; - -const struct afb_binding_v2 afbBindingV2 = { - .api = "ahl-4a", - .specification = _afb_description_v2_ahl_4a, - .info = "Audio high level API for AGL applications", - .verbs = _afb_verbs_v2_ahl_4a, - .preinit = NULL, - .init = AhlBindingInit, - .onevent = AhlOnEvent, - .noconcurrency = 0 -}; - diff --git a/ahl-binding/ahl-apidef.json b/ahl-binding/ahl-apidef.json deleted file mode 100644 index 35ffae6..0000000 --- a/ahl-binding/ahl-apidef.json +++ /dev/null @@ -1,448 +0,0 @@ -{ - "openapi": "3.0.0", - "info": { - "description": "Audio high level API for AGL applications", - "title": "audiohighlevel", - "version": "1.0", - "x-binding-c-generator": { - "api": "ahl-4a", - "version": 2, - "prefix": "audiohlapi_", - "postfix": "", - "start": null, - "onevent": "AhlOnEvent", - "init": "AhlBindingInit", - "scope": "", - "private": false, - "noconcurrency": false - } - }, - "servers": [ - { - "url": "ws://{host}:{port}/api/audiohl", - "description": "Audio high level API for AGL applications.", - "variables": { - "host": { - "default": "localhost" - }, - "port": { - "default": "1234" - } - }, - "x-afb-events": [ - { - "$ref": "#/components/schemas/afb-event" - } - ] - } - ], - "components": { - "schemas": { - "afb-reply": { - "$ref": "#/components/schemas/afb-reply-v2" - }, - "afb-event": { - "$ref": "#/components/schemas/afb-event-v2" - }, - "afb-reply-v2": { - "title": "Generic response.", - "type": "object", - "required": ["jtype", "request"], - "properties": { - "jtype": { - "type": "string", - "const": "afb-reply" - }, - "request": { - "type": "object", - "required": ["status"], - "properties": { - "status": { - "type": "string" - }, - "info": { - "type": "string" - }, - "token": { - "type": "string" - }, - "uuid": { - "type": "string" - }, - "reqid": { - "type": "string" - } - } - }, - "response": { - "type": "object" - } - } - }, - "afb-event-v2": { - "type": "object", - "required": ["jtype", "event"], - "properties": { - "jtype": { - "type": "string", - "const": "afb-event" - }, - "event": { - "type": "string" - }, - "data": { - "type": "object" - } - } - }, - "endpoint_info": { - "type": "object", - "required": [ "endpoint_id", "type", "device_name", "device_uri" ], - "properties": { - "endpoint_id": { "type": "int" }, - "type": { "type": "enum" }, - "device_name": { "type": "string" }, - "device_uri_type": { "type": "string" } - } - }, - "stream_info": { - "type": "object", - "required": [ "stream_id", "state", "mute", "endpoint_info" ], - "properties": { - "stream_id": { "type": "int" }, - "state": { "type": "int" }, - "mute": { "type": "int" }, - "device_uri": { "type": "string" }, - "$ref": "#/components/schemas/endpoint_info" - } - } - }, - "x-permissions": { - "streamcontrol": { "permission": "urn:AGL:permission:audio:public:streamcontrol"}, - "endpointcontrol": { "permission": "urn:AGL:permission:audio:public:endpointcontrol"}, - "audiostream": { "permission": "urn:AGL:permission:audio:public:audiostream"}, - "soundevent": {"permission": "urn:AGL:permission:audio:public:soundevent"} - }, - "responses": { - "200": { - "description": "A complex object array response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/afb-reply" - } - } - } - }, - "400": { "description": "Invalid arguments" } - } - }, - "paths": { - "/get_endpoints": { - "description": "Retrieve array of available audio endpoints", - "get": { - "parameters": [ - { - "in": "query", - "name": "audio_role", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Array of endpoint info structures", - "type": "array", - "items": { "$ref": "#/components/schemas/endpoint_info"} - } - }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/stream_open": { - "description": "Request opening a stream", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/audiostream" }, - "parameters": [ - { - "in": "query", - "name": "audio_role", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", - "required": false, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Stream information structure", - "$ref": "#/components/schemas/stream_info" - } - }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/stream_close": { - "description": "Request closing a stream", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/audiostream" }, - "parameters": [ - { - "in": "query", - "name": "stream_id", - "required": false, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/set_stream_state": { - "description": "Change stream active and/or mute state", - "get": { - "x-permissions": { - "$ref": "#/components/x-permissions/streamcontrol" - }, - "parameters": [ - { - "in": "query", - "name": "stream_id", - "required": false, - "schema": {"type": "int"} - }, - { - "in": "query", - "name": "state", - "required": false, - "schema": {"type": "int"} - }, - { - "in": "query", - "name": "mute", - "required": false, - "schema": {"type": "int"} - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/get_stream_info": { - "description": "Retrieve stream information", - "get": { - "parameters": [ - { - "in": "query", - "name": "stream_id", - "required": true, - "schema": {"type": "int"} - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Stream information structure", - "$ref": "#/components/schemas/stream_info" - } - }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/volume": { - "description": "Set or get volume on endpoint", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/endpointcontrol" }, - "parameters": [ - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", - "required": true, - "schema": { "type": "int" } - }, - { - "in": "query", - "name": "volume", - "required": false, - "schema": { "type": "string" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/get_endpoint_info": { - "description": "Retrieve endpoint information including its properties", - "get": { - "parameters": [ - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", - "required": true, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/property": { - "description": "Set/get endpoint property value", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/endpointcontrol" }, - "parameters": [ - { - "in": "query", - "name": "endpoint_type", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "endpoint_id", - "required": true, - "schema": { "type": "int" } - }, - { - "in": "query", - "name": "property_name", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "value", - "required": false, - "schema": { "type": "string" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/get_list_actions": { - "description": "Retrieve a list of supported actions for a particular audio role", - "get": { - "parameters": [ - { - "in": "query", - "name": "audio_role", - "required": true, - "schema": { "type": "string" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/post_action": { - "description": "Post sound or audio device related action event (extendable mechanism)", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/soundevent" }, - "parameters": [ - { - "in": "query", - "name": "action_name", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "audio_role", - "required": true, - "schema": { "type": "string" } - }, - { - "in": "query", - "name": "media_name", - "required": false, - "schema": { "type": "string"} - }, - { - "in": "query", - "name": "action_context", - "required": false, - "schema": { "type": "object" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/event_subscription": { - "description": "Subscribe to audio high level events", - "get": { - "parameters": [ - { - "in": "query", - "name": "events", - "required": true, - "schema": { "type": "array", - "items": { "type": "string" } - } - }, - { - "in": "query", - "name": "subscribe", - "required": true, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - } - } -} diff --git a/ahl-binding/ahl-binding.c b/ahl-binding/ahl-binding.c deleted file mode 100644 index a8b7702..0000000 --- a/ahl-binding/ahl-binding.c +++ /dev/null @@ -1,1276 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <string.h> - -#include "ahl-binding.h" -#include "ahl-apidef.h" // Generated from JSON OpenAPI -#include "wrap-json.h" -#include "ahl-policy.h" -#include "ahl-json.h" - -// Global high-level binding context -AHLCtxT g_AHLCtx; - -static EndpointTypeT EndpointTypeToEnum(char * in_pEndpointTypeStr) -{ - if (in_pEndpointTypeStr == NULL) { - return ENDPOINTTYPE_MAXVALUE; - } - else if (strcasecmp(in_pEndpointTypeStr,AHL_ENDPOINTTYPE_SOURCE)==0) { - return ENDPOINTTYPE_SOURCE; - } - else if (strcasecmp(in_pEndpointTypeStr,AHL_ENDPOINTTYPE_SINK)==0) { - return ENDPOINTTYPE_SINK; - } - else - return ENDPOINTTYPE_MAXVALUE; -} - -static StreamStateT StreamStateToEnum(char * in_pStreamStateStr) -{ - if (in_pStreamStateStr == NULL) { - return STREAM_STATE_MAXVALUE; - } - else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_IDLE)==0) { - return STREAM_STATE_IDLE; - } - else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_RUNNING)==0) { - return STREAM_STATE_RUNNING; - } - else if (strcasecmp(in_pStreamStateStr,AHL_STREAM_STATE_PAUSED)==0) { - return STREAM_STATE_PAUSED; - } - else - return STREAM_STATE_MAXVALUE; -} - -static streamID_t CreateNewStreamID() -{ - streamID_t newID = g_AHLCtx.nextStreamID; - g_AHLCtx.nextStreamID++; - return newID; -} - -static EndpointInfoT * GetEndpointInfoWithRole(endpointID_t in_endpointID, EndpointTypeT in_endpointType, RoleInfoT * in_pRole) -{ - EndpointInfoT * pEndpointInfo = NULL; - GPtrArray * pDeviceArray = NULL; - if (in_endpointType == ENDPOINTTYPE_SOURCE){ - pDeviceArray = in_pRole->pSourceEndpoints; - } - else { - pDeviceArray = in_pRole->pSinkEndpoints; - } - g_assert_nonnull(pDeviceArray); - - for (int j = 0; j < pDeviceArray->len; j++) { - EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pDeviceArray,j); - g_assert_nonnull(pCurEndpointInfo); - if (pCurEndpointInfo->endpointID == in_endpointID) { - pEndpointInfo = pCurEndpointInfo; - break; - } - } - - return pEndpointInfo; -} - -static EndpointInfoT * GetEndpointInfo(endpointID_t in_endpointID, EndpointTypeT in_endpointType) -{ - EndpointInfoT * pEndpointInfo = NULL; - - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pRoleInfo); - while (pEndpointInfo == NULL && g_hash_table_iter_next (&iter, &key, &value)) - { - RoleInfoT * pRoleInfo = (RoleInfoT*)value; - pEndpointInfo = GetEndpointInfoWithRole(in_endpointID,in_endpointType,pRoleInfo); - } - - return pEndpointInfo; -} - -static StreamInfoT * GetStream(streamID_t in_streamID) -{ - if (g_AHLCtx.policyCtx.pStreams == NULL) - return NULL; - - return g_hash_table_lookup(g_AHLCtx.policyCtx.pStreams,GINT_TO_POINTER(&in_streamID)); -} - -static RoleInfoT * GetRole(char * in_pAudioRoleName) -{ - if (g_AHLCtx.policyCtx.pRoleInfo == NULL) - return NULL; - - return g_hash_table_lookup(g_AHLCtx.policyCtx.pRoleInfo,in_pAudioRoleName); -} - -static int CloseStream(AHLClientCtxT * in_pClientCtx, streamID_t streamID,struct afb_req * pReq) { - StreamInfoT * pStreamInfo = GetStream(streamID); - if (pStreamInfo == NULL) { - AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID); - return AHL_FAIL; - } - -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyStreamJ = NULL; - int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - AFB_ERROR("Audio policy violation, Unable to get JSON object for Policy_CloseStream"); - return AHL_FAIL; - } - int policyAllowed = Policy_CloseStream(pPolicyStreamJ); - if (policyAllowed == AHL_POLICY_REJECT) - { - AFB_ERROR("Close stream not allowed in current context"); - return AHL_FAIL; - } -#endif - // Unsubscribe client from stream events - if (pReq != NULL) { - char streamEventName[128]; - snprintf(streamEventName,128,"ahl_streamstate_%d",streamID); - int iValid = afb_event_is_valid(pStreamInfo->streamStateEvent); - if (iValid) { - err = afb_req_unsubscribe(*pReq,pStreamInfo->streamStateEvent); - if (err) { - AFB_ERROR("Could not unsubscribe state change event for streamID:%i with AudioRole: %s", streamID, pStreamInfo->pRoleName); - return AHL_FAIL; - } - } - } - - // Remove from stream list (if present) - if (g_AHLCtx.policyCtx.pStreams) - g_hash_table_remove(g_AHLCtx.policyCtx.pStreams,GINT_TO_POINTER(&pStreamInfo->streamID)); - free(pStreamInfo); - pStreamInfo = NULL; - - // Find index for cases where there are multiple streams per client - // Remove from client context stream ID and endpoint ID access rights - if (in_pClientCtx->pStreamAccessList) { - for (int i = 0; i < in_pClientCtx->pStreamAccessList->len ; i++) { - streamID_t iID = g_array_index(in_pClientCtx->pStreamAccessList,streamID_t,i); - if (iID == streamID) { - g_array_remove_index(in_pClientCtx->pStreamAccessList, i); - } - } - } - - return AHL_SUCCESS; -} - -static int CloseAllClientStreams(AHLClientCtxT * in_pClientCtx, struct afb_req * pReq) -{ - g_assert_nonnull(in_pClientCtx); - if (in_pClientCtx->pStreamAccessList != NULL) { - while( in_pClientCtx->pStreamAccessList->len ) - { - streamID_t streamID = g_array_index(in_pClientCtx->pStreamAccessList,streamID_t,0); - int err = CloseStream(in_pClientCtx,streamID,pReq); - if (err) { - return err; - } - } - } - - return AHL_SUCCESS; -} - -static AHLClientCtxT * AllocateClientContext() -{ - AHLClientCtxT * pClientCtx = malloc(sizeof(AHLClientCtxT)); - pClientCtx->pStreamAccessList = g_array_new(FALSE, TRUE, sizeof(streamID_t)); - return pClientCtx; -} - -static void TerminateClientContext(void * ptr) -{ - AHLClientCtxT * pClientCtx = (AHLClientCtxT *) ptr; - if (pClientCtx != NULL) { - CloseAllClientStreams(pClientCtx,NULL); - - if (pClientCtx->pStreamAccessList) { - g_array_free( pClientCtx->pStreamAccessList, TRUE); - pClientCtx->pStreamAccessList = NULL; - } - - free(pClientCtx); - } -} - -static int CheckStreamAccessControl(AHLClientCtxT * pClientCtx, streamID_t streamID) -{ - int iAccessControl = AHL_ACCESS_CONTROL_DENIED; - if (pClientCtx && pClientCtx->pStreamAccessList) { - for (int i = 0; i < pClientCtx->pStreamAccessList->len ; i++) { - streamID_t iID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); - if (iID == streamID) { - iAccessControl = AHL_ACCESS_CONTROL_GRANTED; - } - } - } - return iAccessControl; -} - -static int CreateEvents() -{ - g_AHLCtx.policyCtx.propertyEvent = afb_daemon_make_event(AHL_ENDPOINT_PROPERTY_EVENT); - int err = !afb_event_is_valid(g_AHLCtx.policyCtx.propertyEvent); - if (err) { - AFB_ERROR("Could not create endpoint property change event"); - return AHL_FAIL; - } - - g_AHLCtx.policyCtx.volumeEvent = afb_daemon_make_event(AHL_ENDPOINT_VOLUME_EVENT); - err = !afb_event_is_valid(g_AHLCtx.policyCtx.volumeEvent); - if (err) { - AFB_ERROR("Could not create endpoint volume change event"); - return AHL_FAIL; - } - - g_AHLCtx.policyCtx.postActionEvent = afb_daemon_make_event(AHL_POST_ACTION_EVENT); - err = !afb_event_is_valid(g_AHLCtx.policyCtx.postActionEvent); - if (err) { - AFB_ERROR("Could not create post action event call event"); - return AHL_FAIL; - } - - return AHL_SUCCESS; -} - -static void AhlBindingTerm() -{ -#ifndef AHL_DISCONNECT_POLICY - // Policy termination - Policy_Term(); -#endif - - // Roles - if (g_AHLCtx.policyCtx.pRoleInfo != NULL) { - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init(&iter, g_AHLCtx.policyCtx.pRoleInfo); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - RoleInfoT * pRole = (RoleInfoT *)value; - if (pRole) - { - if(pRole->pRoleName) { - g_free(pRole->pRoleName); - pRole->pRoleName = NULL; - } - // Actions - if (pRole->pActionList) { - for (int i = 0; i < pRole->pActionList->len; i++) - { - g_ptr_array_remove_index( pRole->pActionList, i ); // Free char * is called by GLib - } - } - // Source endpoints - if (pRole->pSourceEndpoints) { - g_ptr_array_unref(pRole->pSourceEndpoints); - } - // Sink endpoints - if (pRole->pSinkEndpoints) { - g_ptr_array_unref(pRole->pSinkEndpoints); - } - free(pRole); - } - } - g_hash_table_remove_all(g_AHLCtx.policyCtx.pRoleInfo); - g_hash_table_destroy(g_AHLCtx.policyCtx.pRoleInfo); - g_AHLCtx.policyCtx.pRoleInfo = NULL; - } - - if (g_AHLCtx.policyCtx.pStreams) { - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pStreams); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - if (value) - free(value); - } - g_hash_table_remove_all(g_AHLCtx.policyCtx.pStreams); - g_hash_table_destroy(g_AHLCtx.policyCtx.pStreams); - g_AHLCtx.policyCtx.pStreams = NULL; - } - - if (g_AHLCtx.policyCtx.pHALList) { - g_ptr_array_free(g_AHLCtx.policyCtx.pHALList,TRUE); - g_AHLCtx.policyCtx.pHALList = NULL; - } - - AFB_INFO("Audio high-level binding termination success"); -} - -// Binding initialization -PUBLIC int AhlBindingInit() -{ - memset(&g_AHLCtx,0,sizeof(g_AHLCtx)); - - // Register exit function - atexit(AhlBindingTerm); - - // Create AGL Events - int err = CreateEvents(); - if(err) { - // Error messages already reported inside CreateEvents - return AHL_FAIL; - } - - // Parse high-level binding JSON configuration file (will build device lists) - err = ParseHLBConfig(); - if(err) { - // Error messages already reported inside ParseHLBConfig - return AHL_FAIL; - } - -#ifndef AHL_DISCONNECT_POLICY - // Policy initialization - AFB_DEBUG("Audio high-level policy initialization"); - err = Policy_Init(); - if(err == AHL_POLICY_REJECT) { - //Error messages already reported inside PolicyInit - return AHL_FAIL; - } - - // Call policy for initalization of all source + sink endpoints for all audio Roles - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, g_AHLCtx.policyCtx.pRoleInfo); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - RoleInfoT * pRoleInfo = (RoleInfoT*)value; - if (pRoleInfo->pSourceEndpoints){ - // for all source endpoints - for (int j = 0; j < pRoleInfo->pSourceEndpoints->len; j++) { - EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSourceEndpoints,j); - g_assert_nonnull(pCurEndpointInfo); - json_object *pInPolicyEndpointJ = NULL; - err = EndpointInfoToJSON(pCurEndpointInfo, &pInPolicyEndpointJ); - if (err) { - AFB_ERROR("Unable to create source endpoint JSON object from endpoint info for endpoint_id:%d error:%s ",pCurEndpointInfo->endpointID, wrap_json_get_error_string(err)); - return AHL_FAIL; - } - else - { - json_object * pOutPolicyEndpointJ = NULL; - err = Policy_Endpoint_Init(pInPolicyEndpointJ,&pOutPolicyEndpointJ); - if (err == AHL_POLICY_REJECT) { - AFB_WARNING("Policy source endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); - // continue - } - json_object_put(pInPolicyEndpointJ); - err = UpdateEndpointInfo(pCurEndpointInfo,pOutPolicyEndpointJ); - if (err) { - AFB_ERROR("Policy source endpoint properties update failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); - return AHL_FAIL; - } - // json_object_put(pOutPolicyEndpointJ); - } - } - } - - if (pRoleInfo->pSinkEndpoints){ - // for all sink endpoints - for (int j = 0; j < pRoleInfo->pSinkEndpoints->len; j++) { - EndpointInfoT * pCurEndpointInfo = g_ptr_array_index(pRoleInfo->pSinkEndpoints,j); - g_assert_nonnull(pCurEndpointInfo); - json_object *pInPolicyEndpointJ = NULL; - err = EndpointInfoToJSON(pCurEndpointInfo, &pInPolicyEndpointJ); - if (err) { - AFB_ERROR("Unable to create sink endpoint JSON object from endpoint info for endpoint_id:%d error:%s ",pCurEndpointInfo->endpointID, wrap_json_get_error_string(err)); - return AHL_FAIL; - } - else - { - json_object *pOutPolicyEndpointJ = NULL; - err = Policy_Endpoint_Init(pInPolicyEndpointJ,&pOutPolicyEndpointJ); - if (err == AHL_POLICY_REJECT) { - AFB_WARNING("Policy sink endpoint properties initalization failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); - // continue - } - json_object_put(pInPolicyEndpointJ); - err = UpdateEndpointInfo(pCurEndpointInfo,pOutPolicyEndpointJ); - if (err) { - AFB_ERROR("Policy sink endpoint properties update failed for endpoint_id :%d type:%d",pCurEndpointInfo->endpointID, pCurEndpointInfo->type); - return AHL_FAIL; - } - //json_object_put(pOutPolicyEndpointJ); - } - } - } - } -#endif // AHL_DISCONNECT_POLICY - - // Initialize list of active streams - g_AHLCtx.policyCtx.pStreams = g_hash_table_new(g_int_hash, g_int_equal); - if(g_AHLCtx.policyCtx.pStreams == NULL) - { - AFB_ERROR("Unable to create Active Stream List"); - return AHL_FAIL; - } - AFB_DEBUG("Audio high-level binding initialization success"); - return AHL_SUCCESS; -} - -PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ) -{ - AFB_DEBUG("AHL received event %s", evtname); - - // TODO: Handle event from the policy to update internal information (currently not possible since within the same binding) - -#ifndef AHL_DISCONNECT_POLICY - // Temp: currently forward to policy to handle events (they will be received directly when disconnected into separate binding) - Policy_OnEvent(evtname, eventJ); -#endif -} - -PUBLIC void audiohlapi_get_endpoints(struct afb_req req) -{ - json_object *devicesJ = NULL; - json_object *deviceJ = NULL; - json_object *queryJ = NULL; - char * audioRole = NULL; - char * pEndpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:s}", "audio_role", &audioRole,"endpoint_type",&pEndpointTypeStr); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - - RoleInfoT * pRole = GetRole(audioRole); - if ( pRole == NULL ) - { - afb_req_fail_f(req, "Invalid arguments", "Requested audio role:%s does not exist in current configuration -> %s", audioRole, json_object_get_string(queryJ)); - return; - } - else - { - devicesJ = json_object_new_array(); - GPtrArray * pDeviceArray = NULL; - if (endpointType == ENDPOINTTYPE_SOURCE) - pDeviceArray = pRole->pSourceEndpoints; - else - pDeviceArray = pRole->pSinkEndpoints; - if (pDeviceArray) { - int iNumberDevices = pDeviceArray->len; - for ( int j = 0 ; j < iNumberDevices; j++) - { - EndpointInfoT * pEndpointInfo = g_ptr_array_index(pDeviceArray,j); - if (pEndpointInfo) { - JSONPublicPackageEndpoint(pEndpointInfo,&deviceJ); - json_object_array_add(devicesJ, deviceJ); - } - } - } - } - - afb_req_success(req, devicesJ, "List of endpoints"); -} - -PUBLIC void audiohlapi_stream_open(struct afb_req req) -{ - json_object *streamInfoJ = NULL; - StreamInfoT * pStreamInfo = NULL; - json_object *queryJ = NULL; - char * audioRole = NULL; - char * endpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - endpointID_t endpointID = AHL_UNDEFINED; - EndpointInfoT * pEndpointInfo = NULL; - EndpointSelectionModeT endpointSelMode = ENDPOINTSELMODEMAXVALUE; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointTypeStr, "endpoint_id", &endpointID); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(endpointTypeStr); - - // Check if there is already an existing context for this client - AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure - if (pClientCtx == NULL) - { - pClientCtx = AllocateClientContext(); - afb_req_context_set(req, pClientCtx, TerminateClientContext); - } - - RoleInfoT * pRole = GetRole(audioRole); - if ( pRole == NULL ) - { - afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); - return; - } - - GPtrArray * pDeviceArray = NULL; - if (endpointType == ENDPOINTTYPE_SOURCE){ - pDeviceArray = pRole->pSourceEndpoints; - } - else{ - pDeviceArray = pRole->pSinkEndpoints; - } - if (pDeviceArray == NULL || pDeviceArray->len == 0) { - afb_req_fail_f(req, "No available devices", "No available devices for role:%s and device type:%s",audioRole,endpointTypeStr); - return; - } - - if (endpointID == AHL_UNDEFINED) - { - // Assign a device based on configuration priority (first in the list for requested role and endpoint type) - pEndpointInfo = g_ptr_array_index(pDeviceArray,0); - endpointSelMode = ENDPOINTSELMODE_AUTO; - } - else{ - endpointSelMode = ENDPOINTSELMODE_MANUAL; - // Find specified endpoint ID in list of devices - int iNumberDevices = pDeviceArray->len; - for ( int j = 0 ; j < iNumberDevices; j++) - { - pEndpointInfo = g_ptr_array_index(pDeviceArray,j); - if (pEndpointInfo && pEndpointInfo->endpointID == endpointID) { - break; - } - pEndpointInfo = NULL; - } - } - - if (pEndpointInfo == NULL) { - afb_req_fail_f(req, "Endpoint not available", "Specified endpoint not available for role:%s and device type:%d endpoint id %d",audioRole,endpointType,endpointID); - return; - } - - pStreamInfo = (StreamInfoT*) malloc(sizeof(StreamInfoT)); - memset(pStreamInfo,0,sizeof(StreamInfoT)); - - // Create stream - pStreamInfo->streamID = CreateNewStreamID(); // create new ID - pStreamInfo->streamState = STREAM_STATE_IDLE; - pStreamInfo->streamMute = STREAM_UNMUTED; - pStreamInfo->pEndpointInfo = pEndpointInfo; - pStreamInfo->endpointSelMode = endpointSelMode; - // Directly from role config for now, but could be programmatically overriden in the future - pStreamInfo->pRoleName = pRole->pRoleName; - pStreamInfo->iPriority = pRole->iPriority; - pStreamInfo->eInterruptBehavior = pRole->eInterruptBehavior; - -#ifndef AHL_DISCONNECT_POLICY - // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions - json_object *pPolicyStreamJ = NULL; - err = StreamInfoToJSON(pStreamInfo,&pPolicyStreamJ); - if (err) - { - afb_req_fail(req, "Cannot convert stream info to JSON", "Unable to get JSON object for Policy_OpenStream"); - return; - } - - int policyAllowed = Policy_OpenStream(pPolicyStreamJ); - if (policyAllowed == AHL_POLICY_REJECT) - { - afb_req_fail_f(req, "Audio policy violation", "Open stream for role: %s on endpoint_id:%d not allowed in current context",pRole->pRoleName,pEndpointInfo->endpointID); - return; - } -#endif - - char streamEventName[128]; - snprintf(streamEventName,128,"ahl_streamstate_%d",pStreamInfo->streamID); - - pStreamInfo->streamStateEvent = afb_daemon_make_event(streamEventName); - err = !afb_event_is_valid(pStreamInfo->streamStateEvent); - if (err) { - afb_req_fail_f(req, "Stream event creation failure", "Could not create stream specific state change event: %s",streamEventName); - return; - } - - err = afb_req_subscribe(req,pStreamInfo->streamStateEvent); - if (err) { - afb_req_fail_f(req, "Stream event subscription failure", "Could not subscribe to stream specific state change event for event name: %s",streamEventName); - return; - } - - // Add to client context stream ID access rights - g_array_append_val(pClientCtx->pStreamAccessList, pStreamInfo->streamID); - - // Push stream on active stream list - if (g_AHLCtx.policyCtx.pStreams) - g_hash_table_insert( g_AHLCtx.policyCtx.pStreams, GINT_TO_POINTER(&pStreamInfo->streamID), pStreamInfo ); - - // Package and return stream information to client - JSONPublicPackageStream(pStreamInfo,&streamInfoJ); - - afb_req_success(req, streamInfoJ, "Stream info structure"); -} - -PUBLIC void audiohlapi_stream_close(struct afb_req req) -{ - json_object *queryJ = NULL; - streamID_t streamID = AHL_UNDEFINED; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s?i}", "stream_id", &streamID); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - // Check if there is already an existing context for this client - AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure - if (pClientCtx == NULL) - { - afb_req_fail(req, "Missing client info context", "No client context associated with the request (is there an opened stream by this client?)"); - return; - } - - if (streamID == AHL_UNDEFINED) { - AFB_DEBUG("StreamID not specified. Closing all stream for client"); - err = CloseAllClientStreams(pClientCtx,&req); - if (err) { - afb_req_fail(req, "Error closing streams", "Streams cannot close"); - return; - } - } - else { - AFB_DEBUG("Closing streamID:%d",streamID); - err = CloseStream(pClientCtx,streamID,&req); - if (err) { - afb_req_fail_f(req, "Error closing stream", "Specified stream cannot close stream_id -> %d",streamID); - return; - } - } - - afb_req_success(req, NULL, "Stream close completed"); -} - -static int SetStreamState(AHLClientCtxT * in_pClientCtx,struct afb_req * pReq, streamID_t streamID, char * pStreamStateStr, int iMuteValue) { - - StreamInfoT * pStreamInfo = GetStream(streamID); - if (pStreamInfo == NULL) { - afb_req_fail_f(*pReq, "Stream not found", "Specified stream not found stream_id -> %d",streamID); - return AHL_FAIL; - } - - // Verify that this client can control the stream - int iStreamAccessControl = CheckStreamAccessControl( in_pClientCtx, streamID ); - if (iStreamAccessControl == AHL_ACCESS_CONTROL_DENIED) - { - afb_req_fail_f(*pReq, "Access control denied", "Current client is not the owner of streamID:%d", streamID); - return AHL_FAIL; - } - - if (pStreamStateStr != NULL) { - StreamStateT streamState = StreamStateToEnum(pStreamStateStr); -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyStreamJ = NULL; - int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail_f(*pReq, "JSON packaging error", "Unable to create stream JSON object for Policy_SetStreamState for streamID:%d",streamID); - return AHL_FAIL; - } - - json_object *paramJ= json_object_new_int(streamState); - json_object_object_add(pPolicyStreamJ, "arg_stream_state", paramJ); - - int policyAllowed = Policy_SetStreamState(pPolicyStreamJ); - if (policyAllowed == AHL_POLICY_REJECT) - { - afb_req_fail_f(*pReq, "Audio policy violation", "Change stream state not allowed by policy in current context for streamID:%d",streamID); - return AHL_FAIL; - } -#else - // Simulate that policy returns target state (accepted) - pStreamInfo->streamState = streamState; -#endif - } - -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyStreamJ = NULL; - int err = StreamInfoToJSON(pStreamInfo, &pPolicyStreamJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail_f((*pReq), "JSON packaging error", "Unable to create stream JSON object for Policy_SetStreamMute for streamID:%d",streamID); - return AHL_FAIL; - } - - json_object *paramJ= json_object_new_int(iMuteValue); - json_object_object_add(pPolicyStreamJ, "mute_state", paramJ); - - int policyAllowed = Policy_SetStreamMute(pPolicyStreamJ); - if (policyAllowed == AHL_POLICY_REJECT) - { - afb_req_fail_f(*pReq, "Audio policy violation", "Mute stream not allowed by policy in current context for streamID:%d",streamID); - return AHL_FAIL; - } -#else - // Simulate that policy returns target state (accepted) - pStreamInfo->streamMute = (StreamMuteT)(*piMuteValue); -#endif - - return AHL_SUCCESS; -} - - PUBLIC void audiohlapi_set_stream_state(struct afb_req req) - { - json_object *queryJ = NULL; - streamID_t streamID = AHL_UNDEFINED; - char * streamStateStr = NULL; - int iMuteValue = 0; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s?i,s?s,s?i}", "stream_id", &streamID,"state",&streamStateStr,"mute",&iMuteValue); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - // Check if there is already an existing context for this client - AHLClientCtxT * pClientCtx = afb_req_context_get(req); // Retrieve client-specific data structure - if (pClientCtx == NULL) - { - afb_req_fail(req, "Missing client info context", "No client context associated with the request (is there an opened stream by this client?)"); - return; - } - - if (streamID == AHL_UNDEFINED) { - // All stream for this client - if (pClientCtx->pStreamAccessList != NULL) { - for (int i = 0; i < pClientCtx->pStreamAccessList->len; i++) - { - streamID_t curStreamID = g_array_index(pClientCtx->pStreamAccessList,streamID_t,i); - err = SetStreamState(pClientCtx,&req,curStreamID,streamStateStr,iMuteValue); - if (err) { - // Error displayed within SetStreamState - return; - } - } - } - } - else { - err = SetStreamState(pClientCtx,&req,streamID,streamStateStr,iMuteValue); - if (err) { - // Error displayed within SetStreamState - return; - } - } - - afb_req_success(req, NULL, "Set stream state"); - } - - PUBLIC void audiohlapi_get_stream_info(struct afb_req req) - { - json_object *queryJ = NULL; - streamID_t streamID = AHL_UNDEFINED; - json_object * streamInfoJ = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i}", "stream_id", &streamID); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - StreamInfoT * pStreamInfo = GetStream(streamID); - if (pStreamInfo == NULL) { - afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); - return; - } - - JSONPublicPackageStream(pStreamInfo,&streamInfoJ); - - afb_req_success(req, streamInfoJ, "Get stream info completed"); - } - -PUBLIC void audiohlapi_volume(struct afb_req req) -{ - json_object *queryJ = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - char * pEndpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - char * volumeStr = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:i,s?s}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"volume",&volumeStr); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - - EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); - if (pEndpointInfo == NULL) - { - afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for endpoint_id:%d type:%d",endpointID,endpointType); - return; - } - - if (volumeStr != NULL) { -#ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyEndpointJ = NULL; - err = EndpointInfoToJSON(pEndpointInfo, &pPolicyEndpointJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail_f(req, "JSON packaging error", "Unable to create endpoint JSON object for Policy_SetVolume for endpoint_id:%d type:%d",endpointID,endpointType); - return; - } - - json_object *paramJ= json_object_new_string(volumeStr); - json_object_object_add(pPolicyEndpointJ, "arg_volume", paramJ); - - json_object * pPolicyVolumeReply = NULL; - int policyAllowed = Policy_SetVolume(pPolicyEndpointJ,&pPolicyVolumeReply); - if (!policyAllowed) - { - afb_req_fail_f(req, "Audio policy violation", "Set volume not allowed by policy in current context for endpoint_id:%d type:%d",endpointID,endpointType); - return; - } - - err = wrap_json_unpack(pPolicyVolumeReply,"{s:i}","volume",&pEndpointInfo->iVolume); - if (err) { - afb_req_fail_f(req, "Invalid policy reply", "Policy volume change reply for endpoint_id:%d type:%d not a valid JSON object=%s", endpointID,endpointType,json_object_get_string(pPolicyVolumeReply)); - return; - } -#else - // Simulate that policy returns target state (accepted) - pEndpointInfo->iVolume = atoi(volumeStr); -#endif - } - - json_object * volumeJ = json_object_new_int(pEndpointInfo->iVolume); - - afb_req_success(req, volumeJ, "Set/get volume completed"); -} - -PUBLIC void audiohlapi_get_endpoint_info(struct afb_req req) -{ - json_object *queryJ = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - char * pEndpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:i}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - - EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); - if (pEndpointInfo == NULL) - { - afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType); - return; - } - - json_object *endpointInfoJ = NULL; - EndpointInfoToJSON(pEndpointInfo,&endpointInfoJ); - - afb_req_success(req, endpointInfoJ, "Retrieved endpoint information and properties"); -} - -PUBLIC void audiohlapi_property(struct afb_req req) -{ - json_object *queryJ = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - char * pEndpointTypeStr = NULL; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - char * propertyName = NULL; - json_object * propValueJ = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:i,s:s,s?o}", "endpoint_type", &pEndpointTypeStr,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueJ); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - endpointType = EndpointTypeToEnum(pEndpointTypeStr); - - EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); - if (pEndpointInfo == NULL) - { - afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type:%d",endpointID,endpointType); - return; - } - - if (propValueJ != NULL) { - #ifndef AHL_DISCONNECT_POLICY - json_object *pPolicyEndpointJ = NULL; - err = EndpointInfoToJSON(pEndpointInfo, &pPolicyEndpointJ); - if (err == AHL_POLICY_UTIL_FAIL) - { - afb_req_fail_f(req, "JSON packaging error", "Unable to create endpoint JSON object for Policy_SetProperty for endpoint_id:%d type:%d",endpointID,endpointType); - return; - } - - json_object *paramJ= json_object_new_string(propertyName); - json_object_object_add(pPolicyEndpointJ, "arg_property_name", paramJ); - json_object_object_add(pPolicyEndpointJ, "arg_property_value", propValueJ); - - // Call policy to allow custom policy actions in current context - json_object * pPropertyReply = NULL; - int policyAllowed = Policy_SetProperty(pPolicyEndpointJ,&pPropertyReply); - if (!policyAllowed) - { - afb_req_fail_f(req, "Policy violation", "Policy rejected set property control in current context for endpointID: %d type:%d",endpointID,endpointType); - return; - } - - json_object * pPropReplyValue = NULL; - err = wrap_json_unpack(pPropertyReply,"{s:o}","value",&pPropReplyValue); - if (err) { - afb_req_fail_f(req, "JSON parse error", "Policy returned property value for endpointID:%d type:%d not a valid JSON object=%s", endpointID,endpointType,json_object_get_string(pPropertyReply)); - return; - } - if (pEndpointInfo->pPropTable && pPropReplyValue) { - json_object_get(pPropReplyValue); - g_hash_table_insert(pEndpointInfo->pPropTable, propertyName, pPropReplyValue); - } - #else - // Simulate that policy returns target state (accepted) - if (pEndpointInfo->pPropTable && propValueJ) - json_object_get(propValueJ); - g_hash_table_insert(pEndpointInfo->pPropTable, propertyName, propValueJ); - #endif - } - - // Retrieve cached property value - json_object * propertyValJ = (json_object *)g_hash_table_lookup(pEndpointInfo->pPropTable,propertyName); - if (propertyValJ == NULL) { - afb_req_fail_f(req, "Property information not found", "Property information not found for property:%s endpointID:%d type:%d",propertyName,endpointID,endpointType); - return; - } - - json_object_get(propertyValJ); // Increase ref count so that framework does not free our JSON object - - afb_req_success(req, propertyValJ, "Set/get property completed"); -} - -PUBLIC void audiohlapi_get_list_actions(struct afb_req req) -{ - json_object *queryJ = NULL; - char * audioRole = NULL; - json_object * roleActionsJ = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role",&audioRole); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - // Build and return list of actions for specific audio role - RoleInfoT * pRole = GetRole(audioRole); - if ( pRole == NULL ) - { - afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); - return; - } - - roleActionsJ = json_object_new_array(); - if (pRole->pActionList) { - int iNumberActions = pRole->pActionList->len; - for ( int i = 0 ; i < iNumberActions; i++) - { - char * pActionName = g_ptr_array_index(pRole->pActionList,i); - json_object * actionJ = json_object_new_string(pActionName); - json_object_array_add(roleActionsJ, actionJ); - } - } - - afb_req_success(req, roleActionsJ, "Retrieved action list for audio role"); -} - -PUBLIC void audiohlapi_post_action(struct afb_req req) -{ - json_object *queryJ = NULL; - char * actionName = NULL; - char * audioRole = NULL; - char * mediaName = NULL; - json_object *actionContext = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - // Verify if known action for audio role - RoleInfoT * pRole = GetRole(audioRole); - if ( pRole == NULL ) - { - afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); - return; - } - - // Check to find specific action - int iActionFound = 0; - if (pRole->pActionList) { - int iNumberActions = pRole->pActionList->len; - char * pTargetActionName = NULL; - for ( int i = 0 ; i < iNumberActions; i++) - { - pTargetActionName = g_ptr_array_index(pRole->pActionList,i); - if ( strcasecmp(pTargetActionName,actionName)==0) { - iActionFound = 1; - break; - } - } - } - - if (!iActionFound) { - afb_req_fail_f(req, "Action not found for audio role", "Action -> %s not found for role:%s",actionName,audioRole); - return; - } - - // Disable this to avoid problems (temporary solution) -// #ifndef AHL_DISCONNECT_POLICY -// // Call policy to allow custom policy actions in current context (e.g. cancel playback) -// json_object * pActionInfo = NULL; -// err = wrap_json_pack(&pActionInfo, "{s:s,s:s,s?s,s?o}", "action_name", &actionName,"audio_role",&audioRole,"media_name",&mediaName,"action_context",&actionContext); -// if (err) { -// afb_req_fail_f(req, "Invalid arguments", "Could not create action JSON object arguments"); -// return; -// } -// json_object_get(pActionInfo); -// int policyAllowed = Policy_PostAction(pActionInfo); -// if (!policyAllowed) -// { -// afb_req_fail_f(req, "Audio policy violation", "Post sound action not allowed by policy in current context for action:%s and audiorole:%s",actionName,audioRole); -// return; -// } -// #endif - - afb_req_success(req, NULL, "Posted sound action"); - } - -PUBLIC void audiohlapi_event_subscription(struct afb_req req) -{ - json_object *queryJ = NULL; - json_object * eventArrayJ = NULL; - int iSubscribe = 1; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:o,s:i}", "events", &eventArrayJ,"subscribe",&iSubscribe); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); - return; - } - - json_type jType = json_object_get_type(eventArrayJ); - int iNumEvents = 0; - if(jType == json_type_array) - { - iNumEvents = json_object_array_length(eventArrayJ); - } - for (int i = 0; i < iNumEvents; i++) - { - char * pEventName = NULL; - json_object * jEvent = json_object_array_get_idx(eventArrayJ,i); - pEventName = (char *)json_object_get_string(jEvent); - if(pEventName == NULL) { - afb_req_fail(req, "JSON arguments parse error", "Empty event"); - return; - } - else if(!strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)) { - if (iSubscribe) - afb_req_subscribe(req, g_AHLCtx.policyCtx.propertyEvent); - else - afb_req_unsubscribe(req, g_AHLCtx.policyCtx.propertyEvent); - } - else if(!strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)) { - if (iSubscribe) - afb_req_subscribe(req, g_AHLCtx.policyCtx.volumeEvent); - else - afb_req_unsubscribe(req, g_AHLCtx.policyCtx.volumeEvent); - } - else if(!strcasecmp(pEventName, AHL_POST_ACTION_EVENT)) { - if (iSubscribe) - afb_req_subscribe(req, g_AHLCtx.policyCtx.postActionEvent); - else - afb_req_unsubscribe(req, g_AHLCtx.policyCtx.postActionEvent); - } - else { - afb_req_fail_f(req, "JSON arguments parse error", "Invalid event:%s",pEventName); - return; - } - } - - afb_req_success(req, NULL, "Event subscription update finished"); -} - -// Since the policy is currently in the same binding, it cannot raise events on its own -// This is a first step toward isolation, when policy is migrated in its own binding it can simply raise AGL events -// This binding will register for these policy events and will execute the code below upon event reception -PUBLIC void audiohlapi_raise_event(json_object * pEventDataJ) -{ - char * pEventName = NULL; - - int err = wrap_json_unpack(pEventDataJ,"{s:s}","event_name", &pEventName); - if(err) - { - AFB_ERROR("Unable to retrieve event name %s",json_object_get_string(pEventDataJ)); - return; - } - - if(strcasecmp(pEventName, AHL_ENDPOINT_PROPERTY_EVENT)==0) { - char * pAudioRole = NULL; - char * pPropertyName = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - json_object * propValueJ = NULL; - int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:s,s:o,s:s}", - "endpoint_id", &endpointID, - "endpoint_type", &endpointType, - "property_name", &pPropertyName, - "value",&propValueJ, - "audio_role", &pAudioRole); - if(err) - { - AFB_ERROR("Unable to unpack property event for event: %s",pEventName); - return; - } - RoleInfoT * pRole = GetRole(pAudioRole); - if ( pRole == NULL ){ - AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); - return; - } - EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); - // update property value - if ((pEndpointInfo!=NULL) && (pEndpointInfo->pPropTable!=NULL)) - { - json_type jType = json_object_get_type(propValueJ); - switch (jType) { - case json_type_double: - g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_double(json_object_get_double(propValueJ))); - break; - case json_type_int: - g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_int(json_object_get_int(propValueJ))); - break; - case json_type_string: - g_hash_table_insert(pEndpointInfo->pPropTable, pPropertyName, json_object_new_string(json_object_get_string(propValueJ))); - break; - default: - AFB_ERROR("Invalid property argument property value not a valid JSON object query=%s", json_object_get_string(propValueJ)); - return ; - } - } - // Remove event name from object - json_object_object_del(pEventDataJ,"event_name"); - afb_event_push(g_AHLCtx.policyCtx.propertyEvent,pEventDataJ); - } - else if(strcasecmp(pEventName, AHL_ENDPOINT_VOLUME_EVENT)==0) { - char * pAudioRole = NULL; - endpointID_t endpointID = AHL_UNDEFINED; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - int iVolume = 0; - int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i,s:i,s:s}", - "endpoint_id", &endpointID, - "endpoint_type", &endpointType, - "value",&iVolume, - "audio_role", &pAudioRole); - if(err) - { - AFB_ERROR("Unable to unpack volume event data"); - return; - } - RoleInfoT * pRole = GetRole(pAudioRole); - if ( pRole == NULL ){ - AFB_ERROR("Requested audio role does not exist in current configuration -> %s", pAudioRole); - return; - } - EndpointInfoT * pEndpointInfo = GetEndpointInfoWithRole(endpointID,endpointType,pRole); - // update volume value - if(pEndpointInfo) - { - pEndpointInfo->iVolume = iVolume; - } - else - { - AFB_ERROR("Unable to find endpoint id:%d type:%d for role:%s",endpointID,endpointType,pAudioRole); - } - // Remove event name from object - json_object_object_del(pEventDataJ,"event_name"); - afb_event_push(g_AHLCtx.policyCtx.volumeEvent,pEventDataJ); - } - else if(strcasecmp(pEventName, AHL_POST_ACTION_EVENT)==0) { - // Remove event name from object - json_object_object_del(pEventDataJ,"event_name"); - // BUG: This crashes... - afb_event_push(g_AHLCtx.policyCtx.postActionEvent,pEventDataJ); - } - else if(strcasecmp(pEventName, AHL_STREAM_STATE_EVENT)==0) { - streamID_t streamID = AHL_UNDEFINED; - StreamEventT streamEvent = STREAM_EVENT_MAXVALUE; - int err = wrap_json_unpack(pEventDataJ,"{s:i,s:i}", - "stream_id", &streamID, - "state_event", &streamEvent); - if(err) - { - AFB_ERROR("Unable to unpack stream event data for event %s",pEventName); - return; - } - - StreamInfoT * pStreamInfo = GetStream(streamID); - if (pStreamInfo == NULL) { - AFB_ERROR("Specified stream not currently active stream_id -> %d",streamID); - return; - } - - // update streamstate value - switch (streamEvent) { - case STREAM_EVENT_START: - pStreamInfo->streamState = STREAM_STATE_RUNNING; - break; - case STREAM_EVENT_STOP: - pStreamInfo->streamState = STREAM_STATE_IDLE; - break; - case STREAM_EVENT_PAUSE: - pStreamInfo->streamState = STREAM_STATE_PAUSED; - break; - case STREAM_EVENT_RESUME: - pStreamInfo->streamState = STREAM_STATE_RUNNING; - break; - case STREAM_EVENT_MUTED: - pStreamInfo->streamMute = STREAM_MUTED; - break; - case STREAM_EVENT_UNMUTED: - pStreamInfo->streamMute = STREAM_UNMUTED; - break; - default: - AFB_ERROR("Unknown stream event"); - } - - // Remove event name from object - json_object_object_del(pEventDataJ,"event_name"); - afb_event_push(pStreamInfo->streamStateEvent,pEventDataJ); - } - else { - AFB_ERROR("Unknown event name %s",pEventName); - } -}
\ No newline at end of file diff --git a/ahl-binding/ahl-binding.cpp b/ahl-binding/ahl-binding.cpp new file mode 100644 index 0000000..6493267 --- /dev/null +++ b/ahl-binding/ahl-binding.cpp @@ -0,0 +1,344 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@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. + */ + +#include <algorithm> +#include "ahl-binding.hpp" + +afb_dynapi* AFB_default; // BUG: Is it possible to get rid of this ? + +ahl_binding_t::ahl_binding_t() + : handle_{nullptr} + , apihandle_{nullptr} +{ +} + +ahl_binding_t& ahl_binding_t::instance() +{ + static ahl_binding_t s; + return s; +} + +int ahl_binding_t::build(afb_dynapi* handle) +{ + using namespace std::placeholders; + + if (!handle || handle_) return -1; + handle_ = handle; + AFB_default = handle; + + return afb_dynapi_new_api( + handle, + HL_API_NAME, + HL_API_INFO, + 1, + [](void*, afb_dynapi* h) { + return ahl_binding_t::instance().preinit(h); + }, + nullptr); +} + +int ahl_binding_t::preinit(afb_dynapi* handle) +{ + apihandle_ = handle; + + try + { + load_static_verbs(); + load_controller_api(); + + if (afb_dynapi_on_event(handle, + [](afb_dynapi*, const char* e, struct json_object* o) + { ahl_binding_t::instance().event(e, o); } + ) + ) throw std::runtime_error("Failed to register event handler callback."); + + if (afb_dynapi_on_init(handle, + [](afb_dynapi*) { return ahl_binding_t::instance().init(); } + )) throw std::runtime_error("Failed to register init handler callback."); + } + catch(std::exception& e) + { + AFB_DYNAPI_ERROR(handle, "%s", e.what()); + return -1; + } + + return 0; +} + +int ahl_binding_t::init() +{ + using namespace std::placeholders; + + if (afb_dynapi_require_api(apihandle_, HAL_MGR_API, 1)) + { + AFB_DYNAPI_ERROR(apihandle_, "Failed to require '%s' API!", HAL_MGR_API); + return -1; + } + AFB_DYNAPI_NOTICE(apihandle_, "Required '%s' API found!", HAL_MGR_API); + + // Requires corresponding API + for(const auto& h : config_.hals()) + { + if (afb_dynapi_require_api(apihandle_, h.c_str(), 1)) + { + AFB_DYNAPI_ERROR(apihandle_, "Failed to require '%s' API!", h.c_str()); + return -1; + } + AFB_DYNAPI_NOTICE(apihandle_, "Required '%s' API found!", h.c_str()); + json_object* result = nullptr; + if(afb_dynapi_call_sync(apihandle_, h.c_str(), "init-mixer", nullptr, &result)) + { + AFB_DYNAPI_ERROR(apihandle_, "Failed to call 'init-mixer' verb on '%s' API!", h.c_str()); + if (result) AFB_DYNAPI_NOTICE(apihandle_, "%s", json_object_to_json_string(result)); + return -1; + } + AFB_DYNAPI_NOTICE(apihandle_, "Mixer initialized using '%s' API.", h.c_str()); + + json_object* response = nullptr; + json_object* verbose = json_object_new_object(); + json_object_object_add(verbose, "verbose", json_object_new_int(1)); + if (afb_dynapi_call_sync(apihandle_, h.c_str(), "list", verbose, &response)) + { + AFB_DYNAPI_ERROR(apihandle_, "Failed to call 'list' verb on '%s' API!", h.c_str()); + if (result) AFB_DYNAPI_NOTICE(apihandle_, "%s", json_object_to_json_string(result)); + return -1; + } + json_object* streams = json_object_object_get(response, "response"); + AFB_DYNAPI_DEBUG(apihandle_, "Called 'list' verb on '%s' API: %s", h.c_str(), json_object_to_json_string(streams)); + json_object* array = json_object_object_get(streams, "streams"); + size_t streams_count = json_object_array_length(array); + for(size_t i = 0; i < streams_count; ++i) + { + std::string stream_name; + std::string device_uri; + json_object* item = json_object_array_get_idx(array, i); + + jcast(stream_name, item, "name"); + jcast(device_uri, item, "cardId"); + + config_.set_device_uri(stream_name, device_uri); + } + } + + afb_dynapi_seal(apihandle_); + AFB_DYNAPI_NOTICE(apihandle_, "API is now sealed!"); + + actions_["volume"] = std::bind(&ahl_binding_t::volume, this, _1, _2, _3, _4); + actions_["open"] = std::bind(&ahl_binding_t::open, this, _1, _2, _3, _4); + + return 0; +} + +void ahl_binding_t::event(std::string name, json_object* arg) +{ + AFB_DYNAPI_DEBUG(apihandle_, "Event '%s' received with the following arg: %s", name.c_str(), json_object_to_json_string(arg)); +} + +void ahl_binding_t::load_static_verbs() +{ + if (afb_dynapi_add_verb( + apihandle_, + "get_roles", + "Retrieve array of available audio roles", + [](afb_request* r) { ahl_binding_t::instance().get_roles(r); }, + nullptr, + nullptr, + AFB_SESSION_NONE_V2)) + { + throw std::runtime_error("Failed to add 'get_role' verb to the API."); + } +} + +void ahl_binding_t::load_controller_api() +{ + char* dir_list = getenv("CONTROL_CONFIG_PATH"); + if (!dir_list) dir_list = strdup(CONTROL_CONFIG_PATH); + struct json_object* config_files = CtlConfigScan(dir_list, "policy"); + if (!config_files) throw std::runtime_error("No config files found!"); + + // Only one file should be found this way, but read all just in case + size_t config_files_count = json_object_array_length(config_files); + for(size_t i = 0; i < config_files_count; ++i) + { + config_entry_t file {json_object_array_get_idx(config_files, i)}; + + if(load_controller_config(file.filepath()) < 0) + { + std::stringstream ss; + ss << "Failed to load config file '" + << file.filename() + << "' from '" + << file.fullpath() + << "'!"; + throw std::runtime_error(ss.str()); + } + } +} + +int ahl_binding_t::load_controller_config(const std::string& path) +{ + CtlConfigT* controller_config; + + controller_config = CtlLoadMetaData(apihandle_, path.c_str()); + if (!controller_config) + { + AFB_DYNAPI_ERROR(apihandle_, "Failed to load controller from config file!"); + return -1; + } + + static CtlSectionT controller_sections[] = + { + {.key = "plugins", .uid = nullptr, .info = nullptr, .loadCB = PluginConfig, .handle = nullptr, .actions = nullptr}, + {.key = "onload", .uid = nullptr, .info = nullptr, .loadCB = OnloadConfig, .handle = nullptr, .actions = nullptr}, + {.key = "controls", .uid = nullptr, .info = nullptr, .loadCB = ControlConfig, .handle = nullptr, .actions = nullptr}, + {.key = "events", .uid = nullptr, .info = nullptr, .loadCB = EventConfig, .handle = nullptr, .actions = nullptr}, + { + .key = "ahl-4a", + .uid = nullptr, + .info = nullptr, + .loadCB = [](afb_dynapi*, CtlSectionT* s, json_object* o){ + return ahl_binding_t::instance().load_config(s, o); + }, + .handle = nullptr, + .actions = nullptr + }, + {.key = nullptr, .uid = nullptr, .info = nullptr, .loadCB = nullptr, .handle = nullptr, .actions = nullptr} + }; + + CtlLoadSections(apihandle_, controller_config, controller_sections); + + return 0; +} + +int ahl_binding_t::load_config(CtlSectionT* section, json_object* o) +{ + config_ << o; + + // Add corresponding verbs + for(const auto& r : config_.roles()) + { + AFB_DYNAPI_NOTICE(apihandle_, "New audio role: %s", r.name().c_str()); + + if (afb_dynapi_add_verb( + apihandle_, + r.name().c_str(), + r.description().c_str(), + [](afb_request* r) { ahl_binding_t::instance().audiorole(r); }, + nullptr, + nullptr, + AFB_SESSION_NONE_V2)) + { + std::stringstream ss; + ss << "Failed to add '" << r.name() << "' verb to the API."; + throw std::runtime_error(ss.str()); + } + } + + return 0; +} + +int afbBindingVdyn(afb_dynapi* handle) +{ + if (!handle) return -1; + return ahl_binding_t::instance().build(handle); +} + +void ahl_binding_t::audiorole(afb_request* req) +{ + std::string verb = afb_request_get_verb(req); + const auto& roles = config_.roles(); + auto r = std::find_if(roles.cbegin(), roles.cend(), [&verb](const role_t& r) { return r.name() == verb; }); + + if (r == roles.cend()) + { + afb_request_fail(req, "The requested role doesn't exist!", nullptr); + return; + } + + json_object* query = json_object_get(afb_request_json(req)); + + std::string action; + jcast(action, query, "action"); + std::transform(action.begin(), action.end(), action.begin(), ::tolower); + + auto a = actions_.find(action); + if (a == actions_.end()) + { + afb_request_fail(req, "The requested action doesn't exist!", nullptr); + return; + } + a->second(req, verb, r->stream(), query); +} + +void ahl_binding_t::get_roles(afb_request* req) +{ + json_object* result = json_object_new_array(); + for(const auto& r : config_.roles()) + json_object_array_add(result, json_object_new_string(r.name().c_str())); + afb_request_success(req, result, nullptr); +} + +void ahl_binding_t::volume(afb_request* req, std::string role, std::string stream, json_object* arg) +{ + json_object* value = json_object_object_get(arg, "value"); + json_object_get(value); + + if (!value) + { + afb_request_fail(req, "No value given!", nullptr); + return; + } + + json_object* a = json_object_new_object(); + json_object_object_add(a, "volume", value); + AFB_DYNAPI_DEBUG(apihandle_, "Call the HAL with the following argument: %s", json_object_to_json_string(a)); + + afb_dynapi_call( + apihandle_, + config_.hals()[0].c_str(), // BUG: What to do if multiple hals ? + stream.c_str(), + a, + [](void* closure, int status, json_object* result, afb_dynapi* handle) + { + AFB_DYNAPI_DEBUG(handle, "Got the following answer: %s", json_object_to_json_string(result)); + afb_request* r = (afb_request*)closure; + + json_object_get(result); + if (status) afb_request_fail(r, json_object_to_json_string(result), nullptr); + else afb_request_success(r, result, nullptr); + afb_request_unref(r); + }, + afb_request_addref(req)); +} + +void ahl_binding_t::open(afb_request* req, std::string role, std::string stream, json_object* arg) +{ + for(const auto& r : config_.roles()) + { + if (r.name() == role) + { + AFB_DYNAPI_DEBUG(apihandle_, "HELLO"); + + json_object* result = json_object_new_object(); + json_object_object_add(result, "device_uri", json_object_new_string(r.device_uri().c_str())); + + afb_request_success(req, result, nullptr); + return; + } + } + afb_request_fail(req, "Can't open the specified role!", nullptr); +} diff --git a/ahl-binding/ahl-binding.h b/ahl-binding/ahl-binding.h deleted file mode 100644 index 6ceb040..0000000 --- a/ahl-binding/ahl-binding.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * 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. - */ - -#ifndef AHL_BINDING_INCLUDE -#define AHL_BINDING_INCLUDE - -//#define AHL_DISCONNECT_POLICY // define for debugging HLB in standalone only - -#include <json-c/json.h> -#include <glib.h> -#define AFB_BINDING_VERSION 2 -#include <afb/afb-binding.h> -#include "ahl-interface.h" -#include "ahl-policy-utils.h" - -#ifndef PUBLIC - #define PUBLIC -#endif - -#define AHL_SUCCESS 0 -#define AHL_WARNING 1 -#define AHL_FAIL -1 - -#define AHL_ACCESS_CONTROL_GRANTED 1 -#define AHL_ACCESS_CONTROL_DENIED 0 - -#define AHL_STR_MAX_LENGTH 256 - -typedef struct EndpointInfo -{ - endpointID_t endpointID; // Unique endpoint ID (per type) - EndpointTypeT type; // Source or sink device - char * gsDeviceName; // Unique device card name - char * gsDisplayName; // Application display name - char * gsDeviceURI; // Associated URI - char * gsDeviceDomain; // Device URI domain (e.g. alsa or pulse) - char * pRoleName; // Role assigned to this endpoint - DeviceURITypeT deviceURIType; // Device URI type (includes audio domain information) - char * gsHALAPIName; // HAL associated with the device (for volume control) - AlsaDeviceInfoT alsaInfo; // ALSA specific device information - AudioFormatT format; // Preferred audio format supported (later could be array of supported formats) - int iVolume; // Storage for current endpoint volume (policy effected). - GHashTable * pPropTable; // Storage for array of properties (policy effected) -} EndpointInfoT; - -typedef struct StreamInfo { - streamID_t streamID; // Stream unique ID - EndpointInfoT * pEndpointInfo; // Associated endpoint information (reference) - StreamStateT streamState; // Stream activity state - StreamMuteT streamMute; // Stream mute state - struct afb_event streamStateEvent; // Stream specific event for stream state changes - EndpointSelectionModeT endpointSelMode; // Automatic (priority based) or manual endpoint selection - char * pRoleName; // Role string identifier (from role config but could be programatically overriden later) - int iPriority; // Role normalized priority (0-100) (from role config but could be programatically overriden later) - InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams (from role config but could be programatically overriden later) -} StreamInfoT; - -typedef struct RoleInfo { - char * pRoleName; // Role string identifier - int iPriority; // Role normalized priority (0-100) - InterruptBehaviorT eInterruptBehavior; // Role behavior when interrupting lower priority streams - GPtrArray * pActionList; // List of supported actions for the role (gchar*) - GPtrArray * pSourceEndpoints; // Source endpoints info (EndpointInfoT*) - GPtrArray * pSinkEndpoints; // Sink endpoints info (EndpointInfoT*) -} RoleInfoT; - -// Parts of the context that are visible to the policy (for state based decisions) -typedef struct AHLPolicyCtx { - GHashTable * pRoleInfo; // Hash table of role information structure (RoleInfoT*) accessed by role name - GHashTable * pStreams; // List of active streams (StreamInfoT*) accessed by streamID - GPtrArray * pHALList; // List of HAL dependencies - // TODO: Events need to be sent directly by HLB when separation with policy complete - struct afb_event propertyEvent; // AGL event used when property changes - struct afb_event volumeEvent; // AGL event used when volume changes - struct afb_event postActionEvent; // AGL event used on post action call -} AHLPolicyCtxT; - -// Global binding context -typedef struct AHLCtx { - AHLPolicyCtxT policyCtx; - endpointID_t nextSourceEndpointID; // Counter to assign new ID - endpointID_t nextSinkEndpointID; // Counter to assign new ID - endpointID_t nextStreamID; // Counter to assign new ID -} AHLCtxT; - -// Client specific binding context -typedef struct AHLClientCtx { - GArray * pStreamAccessList; // List of streams that client has control over -} AHLClientCtxT; - -// ahl-binding.c -PUBLIC int AhlBindingInit(); -PUBLIC void AhlOnEvent(const char *evtname, json_object *eventJ); - -// ahl-deviceenum.c -int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, EndpointTypeT in_deviceType, GPtrArray * out_pEndpointArray); -EndpointInfoT * InitEndpointInfo(); -void TermEndpointInfo( gpointer data ); -// ahl-config.c -int ParseHLBConfig(); -// ahl-policy.c -#ifndef AHL_DISCONNECT_POLICY -PUBLIC void audiohlapi_raise_event(json_object *EventDataJ); -#endif - -#endif // AHL_BINDING_INCLUDE diff --git a/ahl-binding/ahl-binding.hpp b/ahl-binding/ahl-binding.hpp new file mode 100644 index 0000000..34d6492 --- /dev/null +++ b/ahl-binding/ahl-binding.hpp @@ -0,0 +1,74 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@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. + */ + +#include <exception> +#include <functional> +#include <sstream> +#include <vector> +#include <map> + +#include "config_entry.hpp" +#include "ahl-4a.hpp" + +#define HL_API_NAME "ahl-4a" +#define HL_API_INFO "Audio high level API for AGL applications" +#define HAL_MGR_API "4a-hal-manager" + +extern "C" { + + #define AFB_BINDING_VERSION 0 + #include <afb/afb-binding.h> + #include <string.h> + #include <ctl-config.h> + + int afbBindingVdyn(afb_dynapi* handle); +}; + +class ahl_binding_t +{ + using role_action = std::function<void(afb_request*, std::string, std::string, json_object*)>; + +private: + afb_dynapi* handle_; + afb_dynapi* apihandle_; + + ahl_4a_t config_; + std::map<std::string, role_action> actions_; + + explicit ahl_binding_t(); + + void load_static_verbs(); + void load_controller_api(); + int load_controller_config(const std::string& path); + int load_config(CtlSectionT* section, json_object* o); + + void volume(afb_request* req, std::string role, std::string stream, json_object* arg); + void open(afb_request* req, std::string role, std::string stream, json_object* arg); + +public: + static ahl_binding_t& instance(); + int build(afb_dynapi* handle); + int preinit(afb_dynapi* handle); + int init(); + void event(std::string name, json_object* arg); + void audiorole(afb_request* req); + void get_roles(afb_request* req); +}; + + diff --git a/ahl-binding/ahl-config.c b/ahl-binding/ahl-config.c deleted file mode 100644 index 0bd16f2..0000000 --- a/ahl-binding/ahl-config.c +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdio.h> -#include <string.h> -#include <json-c/json.h> -#include "wrap-json.h" -#include "filescan-utils.h" - -#include "ahl-binding.h" - -extern AHLCtxT g_AHLCtx; - -static InterruptBehaviorT InterruptBehaviorToEnum(char * in_pInterruptBehaviorStr) -{ - g_assert_nonnull(in_pInterruptBehaviorStr); - if (strcasecmp(in_pInterruptBehaviorStr,AHL_INTERRUPTBEHAVIOR_CONTINUE)==0) { - return INTERRUPTBEHAVIOR_CONTINUE; - } - else if (strcasecmp(in_pInterruptBehaviorStr,AHL_INTERRUPTBEHAVIOR_CANCEL)==0) { - return INTERRUPTBEHAVIOR_CANCEL; - } - else if (strcasecmp(in_pInterruptBehaviorStr,AHL_INTERRUPTBEHAVIOR_PAUSE)==0) { - return INTERRUPTBEHAVIOR_PAUSE; - } - else - return INTERRUPTBEHAVIOR_MAXVALUE; -} - -static json_object* CtlConfigScan(const char *dirList, const char *prefix) { - char controlFile [CONTROL_MAXPATH_LEN]; - strncpy(controlFile, prefix, CONTROL_MAXPATH_LEN); - strncat(controlFile, GetBinderName(), CONTROL_MAXPATH_LEN); - - // search for default dispatch config file - json_object* responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, controlFile, "-config.json"); - - return responseJ; -} - -static char* CtlConfigSearch(const char *dirList, const char *prefix) { - int index, err; - - // search for default dispatch config file - json_object* responseJ = CtlConfigScan (dirList, prefix); - if (!responseJ) return NULL; - - // We load 1st file others are just warnings - for (index = 0; index < json_object_array_length(responseJ); index++) { - json_object *entryJ = json_object_array_get_idx(responseJ, index); - - char *filename; - char*fullpath; - err = wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename); - if (err) { - AFB_ERROR("CTL-INIT HOOPs invalid JSON entry= %s", json_object_get_string(entryJ)); - return NULL; - } - - if (index == 0) { - char filepath[CONTROL_MAXPATH_LEN]; - strncpy(filepath, fullpath, sizeof (filepath)); - strncat(filepath, "/", sizeof (filepath)); - strncat(filepath, filename, sizeof (filepath)); - return (strdup(filepath)); - } - } - // no config found - return NULL; -} - -int ParseHLBConfig() { - char * versionStr = NULL; - json_object * jAudioRoles = NULL; - json_object * jHALList = NULL; - char * policyModule = NULL; - - const char *dirList=getenv("AAAA_CONFIG_PATH"); - if (!dirList) dirList=CONTROL_CONFIG_PATH; - - const char *configfile_path =CtlConfigSearch(dirList, "ahl-"); - if (!configfile_path) { - AFB_ERROR("Error: No ahl-* config found invalid JSON %s ", dirList); - return AHL_FAIL; - } - - AFB_NOTICE("High-level config file -> %s\n", configfile_path); - - // Open configuration file - json_object *config_JFile=json_object_from_file(configfile_path); - if(config_JFile == NULL) - { - AFB_ERROR("Error: Can't open configuration file -> %s",configfile_path); - return AHL_FAIL; - } - - int err = wrap_json_unpack(config_JFile, "{s:s,s:s,s:o,s:o}", "version", &versionStr,"policy_module", &policyModule,"audio_roles",&jAudioRoles,"hal_list",&jHALList); - if (err) { - AFB_ERROR("Invalid configuration file -> %s", configfile_path); - return AHL_FAIL; - } - AFB_INFO("High-level audio API version: %s", "1.0.0"); - AFB_INFO("Config version: %s", versionStr); - AFB_INFO("Policy module: %s", policyModule); - - int iHALListLength = 0; - int iNumberOfRoles = 0; - - if(jHALList) - { - json_type jTypeHalList = json_object_get_type(jHALList); - if(jTypeHalList ==json_type_array) - { - iHALListLength = json_object_array_length(jHALList); - } - } - - if(jAudioRoles) - { - json_type jTypeAudioRole = json_object_get_type(jAudioRoles); - if(jTypeAudioRole ==json_type_array) - { - iNumberOfRoles = json_object_array_length(jAudioRoles); - } - } - g_AHLCtx.policyCtx.pHALList = g_ptr_array_new_with_free_func(g_free); - g_AHLCtx.policyCtx.pRoleInfo = g_hash_table_new(g_str_hash, g_str_equal); - - for (int i = 0; i < iHALListLength; i++) - { - char * pHAL = NULL; - json_object * jHAL = json_object_array_get_idx(jHALList,i); - if (jHAL) { - pHAL = (char *)json_object_get_string(jHAL); - char * pHALName = g_strdup( pHAL ); - g_ptr_array_add( g_AHLCtx.policyCtx.pHALList, pHALName ); - AFB_INFO("High-level binding add dependency on HAL: %s", pHALName); - - // Set dependency on HAL specified - err = afb_daemon_require_api_v2(pHAL,1) ; - if( err != 0 ) - { - AFB_ERROR("Audio high level API could not set dependency on API: %s",pHAL); - return AHL_FAIL; - } - } - } - - for (int i = 0; i < iNumberOfRoles; i++) - { - int priority = 0; - json_object * jAudioRole = json_object_array_get_idx(jAudioRoles,i); - json_object * jOutputDevices = NULL; - json_object * jInputDevices = NULL; - json_object * jActions = NULL; - char * pRoleName = NULL; - char * pInteruptBehavior = NULL; - - int iNumOutDevices = 0; - int iNumInDevices = 0; - int iNumActions = 0; - - err = wrap_json_unpack(jAudioRole, "{s:s,s:i,s:s,s?o,s?o,s?o}", - "name", &pRoleName, - "priority",&priority, - "interupt_behavior",&pInteruptBehavior, - "output",&jOutputDevices, - "input",&jInputDevices, - "actions",&jActions - ); - if (err) { - AFB_ERROR("Invalid audio role configuration : %s", json_object_to_json_string(jAudioRole)); - return AHL_FAIL; - } - - AFB_DEBUG("Parsing configuration audio role: %s", pRoleName); - - if (jOutputDevices) - { - json_type jTypeOutputDevices = json_object_get_type(jOutputDevices); - if(jTypeOutputDevices == json_type_array) - iNumOutDevices = json_object_array_length(jOutputDevices); - } - if (jInputDevices) - { - json_type jTypeInputDevices = json_object_get_type(jInputDevices); - if(jTypeInputDevices == json_type_array) - iNumInDevices = json_object_array_length(jInputDevices); - } - if (jActions) - { - json_type jTypeActions = json_object_get_type(jActions); - if(jTypeActions == json_type_array) - iNumActions = json_object_array_length(jActions); - } - RoleInfoT * pRoleInfo = (RoleInfoT*) malloc(sizeof(RoleInfoT)); - memset(pRoleInfo,0,sizeof(RoleInfoT)); - pRoleInfo->pRoleName = g_strdup( pRoleName ); - pRoleInfo->iPriority = priority; - pRoleInfo->eInterruptBehavior = InterruptBehaviorToEnum(pInteruptBehavior); - - // Actions - pRoleInfo->pActionList = g_ptr_array_new_with_free_func(g_free); - // Parse and validate list of available actions - for (int i = 0; i < iNumActions; i++) - { - json_object * jAction = json_object_array_get_idx(jActions,i); - char * pActionName = (char *)json_object_get_string(jAction); - if (pActionName) - g_ptr_array_add(pRoleInfo->pActionList, g_strdup(pActionName)); - } - - // Sources - pRoleInfo->pSourceEndpoints = g_ptr_array_new_with_free_func(TermEndpointInfo); - if (iNumInDevices) { - err = EnumerateDevices(jInputDevices,pRoleName,ENDPOINTTYPE_SOURCE,pRoleInfo->pSourceEndpoints); - if (err) { - AFB_ERROR("Invalid input devices for role %s : %s", pRoleName, json_object_to_json_string(jInputDevices)); - return AHL_FAIL; - } - } - // Sinks - pRoleInfo->pSinkEndpoints = g_ptr_array_new_with_free_func(TermEndpointInfo); - if (iNumOutDevices) { - err = EnumerateDevices(jOutputDevices,pRoleName,ENDPOINTTYPE_SINK,pRoleInfo->pSinkEndpoints); - if (err) { - AFB_ERROR("Invalid output devices for role %s: %s", pRoleName, json_object_to_json_string(jOutputDevices)); - return AHL_FAIL; - } - } - - g_hash_table_insert(g_AHLCtx.policyCtx.pRoleInfo, pRoleInfo->pRoleName, pRoleInfo); - } - - // Build lists of all device URI referenced in config file (input/output) - AFB_DEBUG ("Audio high-level - Parse high-level audio configuration done"); - return AHL_SUCCESS; -} diff --git a/ahl-binding/ahl-deviceenum.c b/ahl-binding/ahl-deviceenum.c deleted file mode 100644 index 75b0715..0000000 --- a/ahl-binding/ahl-deviceenum.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <alsa/asoundlib.h> -#include <alsa/pcm.h> -#include "ahl-binding.h" - -extern AHLCtxT g_AHLCtx; - -// TODO: Hash from endpoint ID information instead -static endpointID_t CreateNewSourceID() -{ - endpointID_t newID = g_AHLCtx.nextSourceEndpointID; - g_AHLCtx.nextSourceEndpointID++; - return newID; -} - -// TODO: Hash from endpoint ID information instead -static endpointID_t CreateNewSinkID() -{ - endpointID_t newID = g_AHLCtx.nextSinkEndpointID; - g_AHLCtx.nextSinkEndpointID++; - return newID; -} - -// Watchout: This function uses strtok and is destructive on the input string (use a copy) -static int SeparateDomainFromDeviceURI( char * in_pDeviceURI, char ** out_pDomain, char ** out_pDevice) -{ - *out_pDomain = strtok(in_pDeviceURI, "."); - if (*out_pDomain == NULL) - { - AFB_ERROR("Error tokenizing device URI -> %s",in_pDeviceURI); - return AHL_FAIL; - } - // TODO: Validate domain is known string (e.g. ALSA,Pulse,GStreamer) - *out_pDevice = strtok(NULL, "."); - if (*out_pDevice == NULL) - { - AFB_ERROR("Error tokenizing device URI -> %s",in_pDeviceURI); - return AHL_FAIL; - } - return AHL_SUCCESS; -} - -static int IsAlsaDomain(const char * in_pDomainStr) -{ - return (int) (strcasecmp(in_pDomainStr,AHL_DOMAIN_ALSA) == 0); -} - -static int IsPulseDomain(const char * in_pDomainStr) -{ - return (int) (strcasecmp(in_pDomainStr,AHL_DOMAIN_PULSE) == 0); -} - -static int IsGStreamerDomain(const char * in_pDomainStr) -{ - return (int) (strcasecmp(in_pDomainStr,AHL_DOMAIN_GSTREAMER) == 0); -} - -static int IsExternalDomain(const char * in_pDomainStr) -{ - return (int) (strcasecmp(in_pDomainStr,AHL_DOMAIN_EXTERNAL) == 0); -} - -static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndpointInfo ) -{ - g_assert_nonnull(in_pPcmHandle); - g_assert_nonnull(out_pEndpointInfo); - snd_pcm_type_t pcmType = 0; - snd_pcm_info_t * pPcmInfo = NULL; - int iAlsaRet = 0; - const char * pCardName = NULL; - snd_ctl_t * ctlHandle = NULL; - snd_ctl_card_info_t * ctlInfo = NULL; - - snd_pcm_info_alloca(&pPcmInfo); - snd_ctl_card_info_alloca(&ctlInfo); - - // retrieve PCM type - pcmType = snd_pcm_type(in_pPcmHandle); - switch (pcmType) { - case SND_PCM_TYPE_HW: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_HW; - break; - case SND_PCM_TYPE_DMIX: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_DMIX; - break; - case SND_PCM_TYPE_SOFTVOL: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_SOFTVOL; - break; - case SND_PCM_TYPE_PLUG: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_PLUG; - break; - default: - out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_OTHER; - break; - } - - iAlsaRet = snd_pcm_info(in_pPcmHandle,pPcmInfo); - if (iAlsaRet < 0) - { - AFB_WARNING("Error retrieving PCM device info"); - return AHL_FAIL; - } - - // get card number - out_pEndpointInfo->alsaInfo.cardNum = snd_pcm_info_get_card(pPcmInfo); - if ( out_pEndpointInfo->alsaInfo.cardNum < 0 ) - { - AFB_WARNING("No Alsa card number available"); - return AHL_FAIL; - } - - // get device number - out_pEndpointInfo->alsaInfo.deviceNum = snd_pcm_info_get_device(pPcmInfo); - if ( out_pEndpointInfo->alsaInfo.deviceNum < 0 ) - { - AFB_WARNING("No Alsa device number available"); - return AHL_FAIL; - } - - // get sub-device number - out_pEndpointInfo->alsaInfo.subDeviceNum = snd_pcm_info_get_subdevice(pPcmInfo); - if ( out_pEndpointInfo->alsaInfo.subDeviceNum < 0 ) - { - AFB_WARNING("No Alsa subdevice number available"); - return AHL_FAIL; - } - - char cardName[32]; - sprintf(cardName, "hw:%d", out_pEndpointInfo->alsaInfo.cardNum); - iAlsaRet = snd_ctl_open(&ctlHandle, cardName, 0); - if ( iAlsaRet < 0 ) - { - AFB_WARNING("Could not open ALSA card control"); - return AHL_FAIL; - } - - iAlsaRet = snd_ctl_card_info(ctlHandle, ctlInfo); - if ( iAlsaRet < 0 ) - { - AFB_WARNING("Could not retrieve ALSA card info"); - snd_ctl_close(ctlHandle); - return AHL_FAIL; - } - - // Populate unique target card name - pCardName = snd_ctl_card_info_get_id(ctlInfo); - if (pCardName == NULL) - { - AFB_WARNING("No Alsa card name available"); - snd_ctl_close(ctlHandle); - return AHL_FAIL; - } - g_strlcpy(out_pEndpointInfo->gsDeviceName,pCardName,AHL_STR_MAX_LENGTH); - - snd_ctl_close(ctlHandle); - - return AHL_SUCCESS; -} - -EndpointInfoT * InitEndpointInfo() -{ - EndpointInfoT * pEndpointInfo = (EndpointInfoT*) malloc(sizeof(EndpointInfoT)); - memset(pEndpointInfo,0,sizeof(EndpointInfoT)); - pEndpointInfo->endpointID = AHL_UNDEFINED; - pEndpointInfo->type = ENDPOINTTYPE_MAXVALUE; - pEndpointInfo->deviceURIType = DEVICEURITYPE_MAXVALUE; - pEndpointInfo->alsaInfo.cardNum = AHL_UNDEFINED; - pEndpointInfo->alsaInfo.deviceNum = AHL_UNDEFINED; - pEndpointInfo->alsaInfo.subDeviceNum = AHL_UNDEFINED; - pEndpointInfo->format.sampleRate = AHL_UNDEFINED; - pEndpointInfo->format.numChannels = AHL_UNDEFINED; - pEndpointInfo->format.sampleType = AHL_FORMAT_UNKNOWN; - // Assigned by device enumeration - pEndpointInfo->gsDeviceName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDeviceName,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->gsDeviceDomain = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDeviceDomain,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->pRoleName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->pRoleName,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->gsDeviceURI = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDeviceURI,0,AHL_STR_MAX_LENGTH*sizeof(char)); - // Assigned by policy initialization - pEndpointInfo->gsDisplayName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsDisplayName,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->gsHALAPIName = malloc(AHL_STR_MAX_LENGTH*sizeof(char)); - memset(pEndpointInfo->gsHALAPIName,0,AHL_STR_MAX_LENGTH*sizeof(char)); - pEndpointInfo->pPropTable = g_hash_table_new(g_str_hash, g_str_equal); - return pEndpointInfo; -} - -void TermEndpointInfo( gpointer data ) -{ - EndpointInfoT * out_pEndpointInfo = (EndpointInfoT *)data; - #define SAFE_FREE(__ptr__) if(__ptr__) g_free(__ptr__); __ptr__ = NULL; - if(out_pEndpointInfo) - { - SAFE_FREE(out_pEndpointInfo->gsDeviceName); - SAFE_FREE(out_pEndpointInfo->gsDeviceDomain); - SAFE_FREE(out_pEndpointInfo->pRoleName); - SAFE_FREE(out_pEndpointInfo->gsDeviceURI); - SAFE_FREE(out_pEndpointInfo->gsHALAPIName); - SAFE_FREE(out_pEndpointInfo->gsDisplayName); - - if (out_pEndpointInfo->pPropTable) { - // Free json_object for all property values - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, out_pEndpointInfo->pPropTable); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - if (value) - json_object_put(value); - } - g_hash_table_remove_all(out_pEndpointInfo->pPropTable); - g_hash_table_destroy(out_pEndpointInfo->pPropTable); - out_pEndpointInfo->pPropTable = NULL; - } - } -} - -// For a given audio role -int EnumerateDevices(json_object * in_jDeviceArray, char * in_pAudioRole, EndpointTypeT in_deviceType, GPtrArray * out_pEndpointArray) { - - g_assert_nonnull(in_jDeviceArray); - g_assert_nonnull(in_pAudioRole); - int iNumberDevices = json_object_array_length(in_jDeviceArray); - - // Parse and validate list of available devices - for (int i = 0; i < iNumberDevices; i++) - { - char * pDeviceURIDomain = NULL; - char * pFullDeviceURI = NULL; - char * pDeviceURIPCM = NULL; - int err = AHL_SUCCESS; - - json_object * jDevice = json_object_array_get_idx(in_jDeviceArray,i); - if (jDevice == NULL) { - AFB_WARNING("Invalid device array for audiorole:%s -> %s",in_pAudioRole,json_object_to_json_string(in_jDeviceArray)); - continue; - } - // strip domain name from URI - pFullDeviceURI = (char *)json_object_get_string(jDevice); - char * pFullDeviceURICopy = g_strdup(pFullDeviceURI); // strtok is destructive - err = SeparateDomainFromDeviceURI(pFullDeviceURICopy,&pDeviceURIDomain,&pDeviceURIPCM); - if (err) - { - AFB_WARNING("Invalid device URI string -> %s for audiorole:%s",pFullDeviceURICopy,in_pAudioRole); - continue; - } - - EndpointInfoT * pEndpointInfo = InitEndpointInfo(); - g_assert_nonnull(pEndpointInfo); - - // non ALSA URI are simply passed to application (no validation) at this time - // In Non ALSA case devices in config are assumed to be available, if not application can fallback on explicit device selection - g_strlcpy(pEndpointInfo->gsDeviceName,pDeviceURIPCM,AHL_STR_MAX_LENGTH); - g_strlcpy(pEndpointInfo->gsDeviceDomain,pDeviceURIDomain,AHL_STR_MAX_LENGTH); - g_strlcpy(pEndpointInfo->gsDeviceURI,pDeviceURIPCM,AHL_STR_MAX_LENGTH); - g_strlcpy(pEndpointInfo->pRoleName ,in_pAudioRole,AHL_STR_MAX_LENGTH); - - g_free(pFullDeviceURICopy); - pFullDeviceURICopy = NULL; - pDeviceURIDomain = NULL; //Derived from above mem - pDeviceURIPCM = NULL; //Derived from above mem - - if (IsAlsaDomain(pEndpointInfo->gsDeviceDomain)) - { - // TODO: Missing support for loose name matching - // This will require using ALSA hints to get PCM names - // And would iterate over all available devices matching string (possibly all if no filtering is desired for a certain role) - - // Get PCM handle - snd_pcm_t * pPcmHandle = NULL; - snd_pcm_stream_t streamType = in_deviceType == ENDPOINTTYPE_SOURCE ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK; - err = snd_pcm_open(&pPcmHandle, pEndpointInfo->gsDeviceURI, streamType, 0); - if (err < 0) - { - AFB_NOTICE("Alsa PCM device was not found -> %s for audiorole:%s", pEndpointInfo->gsDeviceURI,in_pAudioRole); - continue; - } - - err = FillALSAPCMInfo(pPcmHandle,pEndpointInfo); - if (err) { - AFB_WARNING("Unable to retrieve PCM information for PCM -> %s for audiorole:%s",pEndpointInfo->gsDeviceURI,in_pAudioRole); - snd_pcm_close(pPcmHandle); - continue; - } - - snd_pcm_close(pPcmHandle); - } - else if (IsPulseDomain(pEndpointInfo->gsDeviceDomain)) { - // Pulse domain - // For now display name is device URI directly, could extrapolated using more heuristics or even usins Pulse API later on - pEndpointInfo->deviceURIType = DEVICEURITYPE_NOT_ALSA; - } - else if (IsGStreamerDomain(pEndpointInfo->gsDeviceDomain)){ - // GStreamer domain - // For now display name is device URI directly, could extrapolated using more heuristics or even usins GStreamer API later on - pEndpointInfo->deviceURIType = DEVICEURITYPE_NOT_ALSA; - } - else if (IsExternalDomain(pEndpointInfo->gsDeviceDomain)){ - // External domain - pEndpointInfo->deviceURIType = DEVICEURITYPE_NOT_ALSA; - } - else { - // Unknown domain - AFB_WARNING("Unknown domain in device URI string -> %s for audiorole:%s",pFullDeviceURI,in_pAudioRole); - continue; - } - - pEndpointInfo->endpointID = in_deviceType == ENDPOINTTYPE_SOURCE ? CreateNewSourceID() : CreateNewSinkID(); - pEndpointInfo->type = in_deviceType; - - // add to structure to list of available devices - g_ptr_array_add(out_pEndpointArray, pEndpointInfo); - - } // for all devices - - AFB_DEBUG ("Audio high-level - Enumerate devices done"); - return AHL_SUCCESS; -}
\ No newline at end of file diff --git a/ahl-binding/ahl-json.c b/ahl-binding/ahl-json.c deleted file mode 100644 index b711b43..0000000 --- a/ahl-binding/ahl-json.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * 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 AFB_BINDING_VERSION 2 -#include <afb/afb-binding.h> -#include "wrap-json.h" -#include <json-c/json.h> -#include <glib.h> -#include "ahl-binding.h" - -static char * DeviceURITypeEnumToStr(DeviceURITypeT in_eDeviceURIType) { - switch(in_eDeviceURIType) { - case DEVICEURITYPE_ALSA_HW: // Alsa hardware device URI - return AHL_DEVICEURITYPE_ALSA_HW; - case DEVICEURITYPE_ALSA_DMIX: // Alsa Dmix device URI (only for playback devices) - return AHL_DEVICEURITYPE_ALSA_DMIX; - case DEVICEURITYPE_ALSA_DSNOOP: // Alsa DSnoop device URI (only for capture devices) - return AHL_DEVICEURITYPE_ALSA_DSNOOP; - case DEVICEURITYPE_ALSA_SOFTVOL: // Alsa softvol device URI - return AHL_DEVICEURITYPE_ALSA_SOFTVOL; - case DEVICEURITYPE_ALSA_PLUG: // Alsa plug device URI - return AHL_DEVICEURITYPE_ALSA_PLUG; - case DEVICEURITYPE_ALSA_OTHER: // Alsa domain URI device of unspecified type - return AHL_DEVICEURITYPE_ALSA_OTHER; - case DEVICEURITYPE_NOT_ALSA: // Unknown (not ALSA domain) - return AHL_DEVICEURITYPE_NOT_ALSA; - default: - return "Unknown"; - } -} - -static char * StreamStateEnumToStr(StreamStateT in_eStreamState) { - switch(in_eStreamState) { - case STREAM_STATE_IDLE: - return AHL_STREAM_STATE_IDLE; - case STREAM_STATE_RUNNING: - return AHL_STREAM_STATE_RUNNING; - case STREAM_STATE_PAUSED: - return AHL_STREAM_STATE_PAUSED; - default: - return "Unknown"; - } -} - -static char * StreamMuteEnumToStr(StreamMuteT in_eStreamMute) { - switch(in_eStreamMute) { - case STREAM_UNMUTED: - return AHL_STREAM_UNMUTED; - case STREAM_MUTED: - return AHL_STREAM_MUTED; - default: - return "Unknown"; - } -} - -static int EndpointPropTableToJSON(GHashTable * pPropTable, json_object **ppProptableJ) -{ - if(pPropTable == NULL) - { - AFB_ERROR("Invalid EndpointPropTableToJSON arguments"); - return AHL_FAIL; - } - - // Create json object for PropTable - *ppProptableJ = json_object_new_array(); - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init (&iter, pPropTable); - while (g_hash_table_iter_next (&iter, &key, &value)) - { - if ( key != NULL && value != NULL) { - json_object *pPropertyJ = NULL; - json_object_get(value); - int err = wrap_json_pack(&pPropertyJ, "{s:s,s:o}", - "property_name", (char*)key, - "property_value", value - ); - if(err) - { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); - return AHL_FAIL; - } - json_object_array_add(*ppProptableJ, pPropertyJ); - } - } - - return AHL_SUCCESS; -} - -int EndpointInfoToJSON(EndpointInfoT * pEndpointInfo, json_object **ppEndpointInfoJ) -{ - if(pEndpointInfo == NULL || pEndpointInfo->pPropTable == NULL) - { - AFB_ERROR("Invalid EndpointInfoToJSON arguments"); - return AHL_FAIL; - } - - json_object * pPropTableJ = NULL; - int err = EndpointPropTableToJSON(pEndpointInfo->pPropTable,&pPropTableJ); - if (err) { - return AHL_FAIL; - } - - // Create json object for EndpointInfo - err = wrap_json_pack(ppEndpointInfoJ, "{s:i,s:i,s:s,s:s,s:s,s:s,s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:o}", - "endpoint_id", pEndpointInfo->endpointID, - "endpoint_type", pEndpointInfo->type, - "device_name", pEndpointInfo->gsDeviceName, - "display_name", pEndpointInfo->gsDisplayName, - "device_uri", pEndpointInfo->gsDeviceURI, - "device_domain", pEndpointInfo->gsDeviceDomain, - "audio_role",pEndpointInfo->pRoleName, - "device_uri_type", pEndpointInfo->deviceURIType, - "hal_api_name", pEndpointInfo->gsHALAPIName, - "alsa_cardNum", pEndpointInfo->alsaInfo.cardNum, - "alsa_deviceNum", pEndpointInfo->alsaInfo.deviceNum, - "alsa_subDeviceNum", pEndpointInfo->alsaInfo.subDeviceNum, - "format_samplerate", pEndpointInfo->format.sampleRate, - "format_numchannels", pEndpointInfo->format.numChannels, - "format_sampletype",pEndpointInfo->format.sampleType, - "volume", pEndpointInfo->iVolume, - "property_table", pPropTableJ - ); - if (err) { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); - return AHL_FAIL; - } - - return AHL_SUCCESS; -} - -int StreamInfoToJSON(StreamInfoT * pStreamInfo, json_object **ppStreamInfoJ) -{ - if(pStreamInfo == NULL) - { - AFB_ERROR("Invalid arguments to StreamInfoToJSON"); - return AHL_FAIL; - } - - json_object * pEndpointInfoJ = NULL; - int err = EndpointInfoToJSON(pStreamInfo->pEndpointInfo, &pEndpointInfoJ); - if (err) { - return AHL_FAIL; - } - - // Create json object for stream - err = wrap_json_pack(ppStreamInfoJ, "{s:i,s:i,s:i,s:I,s:i,s:s,s:i,s:i,s:o}", - "stream_id", pStreamInfo->streamID, - "stream_state", pStreamInfo->streamState, - "stream_mute", pStreamInfo->streamMute, - "stream_state_event", &pStreamInfo->streamStateEvent, - "endpoint_sel_mod", pStreamInfo->endpointSelMode, - "role_name", pStreamInfo->pRoleName, - "priority", pStreamInfo->iPriority, - "interrupt_behavior", pStreamInfo->eInterruptBehavior, - "endpoint_info", pEndpointInfoJ - ); - if (err) { - AFB_ERROR("Unable to pack JSON endpoint, =%s", wrap_json_get_error_string(err)); - return AHL_FAIL; - } - - return AHL_SUCCESS; -} - -static int UpdatePropertyList(GHashTable * pPropTable, json_object * pPropTableJ) { - if (pPropTable == NULL || pPropTableJ == NULL) { - AFB_ERROR("Invalid arguments to UpdatePropertyList"); - return AHL_FAIL; - } - // Unpack prop table - int nbProperties = json_object_array_length(pPropTableJ); - for(int i = 0; i < nbProperties; i++) - { - json_object * propJ = json_object_array_get_idx(pPropTableJ,i); - if (propJ) { - char * pPropertyName = NULL; - json_object * pPropertyValueJ = NULL; - int err = wrap_json_unpack(propJ, "{s:s,s:o}", - "property_name", &pPropertyName, - "property_value", &pPropertyValueJ); - if (err) { - AFB_ERROR("Unable to unpack JSON property, = %s", wrap_json_get_error_string(err)); - return AHL_FAIL; - } - - // Object type detection for property value (string = state, numeric = property) - json_type jType = json_object_get_type(pPropertyValueJ); - switch (jType) { - case json_type_double: - g_hash_table_insert(pPropTable, pPropertyName, json_object_new_double(json_object_get_double(pPropertyValueJ))); - break; - case json_type_int: - g_hash_table_insert(pPropTable, pPropertyName, json_object_new_int(json_object_get_int(pPropertyValueJ))); - break; - case json_type_string: - g_hash_table_insert(pPropTable, pPropertyName, json_object_new_string(json_object_get_string(pPropertyValueJ))); - break; - default: - AFB_ERROR("Invalid property argument Property value not a valid json object query=%s", json_object_get_string(pPropertyValueJ)); - return AHL_FAIL; - } - } - } - - return AHL_SUCCESS; -} - -int UpdateEndpointInfo(EndpointInfoT * pEndpoint, json_object * pEndpointInfoJ) { - - if(pEndpoint == NULL || pEndpointInfoJ == NULL) - { - AFB_ERROR("Invalid arguments to UpdateEndpointInfo"); - return AHL_FAIL; - } - - // Push information to endpoint info struct - json_object * pPropTableJ = NULL; - char * pDisplayName = NULL; - char * pHALName = NULL; - int err = wrap_json_unpack(pEndpointInfoJ,"{s:i,s:s,s:s,s:o}", - "init_volume",&pEndpoint->iVolume, - "display_name",&pDisplayName, - "hal_name", &pHALName, - "property_table",&pPropTableJ); - if (err) { - AFB_ERROR("Unable to create Endpoint Json object error:%s ",wrap_json_get_error_string(err)); - return AHL_FAIL; - } - g_strlcpy(pEndpoint->gsDisplayName,pDisplayName,AHL_STR_MAX_LENGTH); - g_strlcpy(pEndpoint->gsHALAPIName,pHALName,AHL_STR_MAX_LENGTH); - - if (pEndpoint->pPropTable && pPropTableJ) { - err = UpdatePropertyList(pEndpoint->pPropTable,pPropTableJ); - if (err) { - AFB_ERROR("Unable to update property table Json object error:%s ",wrap_json_get_error_string(err)); - return AHL_FAIL; - } - } - - return AHL_SUCCESS; -} - -static void AudioFormatStructToJSON(json_object **audioFormatJ, AudioFormatT * pAudioFormat) -{ - wrap_json_pack(audioFormatJ, "{s:i,s:i,s:i}", - "sample_rate", pAudioFormat->sampleRate, - "num_channels", pAudioFormat->numChannels, - "sample_type", pAudioFormat->sampleType); -} - -// Package only information that can useful to application clients when selecting endpoint -void JSONPublicPackageEndpoint(EndpointInfoT * pEndpointInfo,json_object **endpointInfoJ) -{ - json_object *formatInfoJ = NULL; - wrap_json_pack(endpointInfoJ, "{s:i,s:s,s:s,s:s,s:s,s:s,s:s}", - "endpoint_id", pEndpointInfo->endpointID, - "endpoint_type", (pEndpointInfo->type == ENDPOINTTYPE_SOURCE) ? AHL_ENDPOINTTYPE_SOURCE : AHL_ENDPOINTTYPE_SINK, - "device_name", pEndpointInfo->gsDeviceName, - "display_name", pEndpointInfo->gsDisplayName, - "audio_role", pEndpointInfo->pRoleName, - "device_domain",pEndpointInfo->gsDeviceDomain, - "device_uri_type", DeviceURITypeEnumToStr(pEndpointInfo->deviceURIType)); - AudioFormatStructToJSON(&formatInfoJ,&pEndpointInfo->format); - json_object_object_add(*endpointInfoJ,"format",formatInfoJ); - - json_object *pPropTableJ = NULL; - EndpointPropTableToJSON(pEndpointInfo->pPropTable,&pPropTableJ); - json_object_object_add(*endpointInfoJ,"property_table",pPropTableJ); -} - -// Package only information that can useful to application clients when opening a stream -void JSONPublicPackageStream(StreamInfoT * pStreamInfo,json_object **streamInfoJ) -{ - json_object *endpointInfoJ = NULL; - JSONPublicPackageEndpoint(pStreamInfo->pEndpointInfo,&endpointInfoJ); - wrap_json_pack(streamInfoJ, "{s:i,s:s,s:s,s:s}", - "stream_id", pStreamInfo->streamID, - "state", StreamStateEnumToStr(pStreamInfo->streamState), - "mute", StreamMuteEnumToStr(pStreamInfo->streamMute), - "device_uri",pStreamInfo->pEndpointInfo->gsDeviceURI); // Need to open a stream to have access to the device URI - json_object_object_add(*streamInfoJ,"endpoint_info",endpointInfoJ); -}
\ No newline at end of file diff --git a/ahl-binding/ahl-json.h b/ahl-binding/ahl-json.h deleted file mode 100644 index ffd683a..0000000 --- a/ahl-binding/ahl-json.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2017 "Audiokinetic Inc" - * - * 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. - */ - -#ifndef AHL_POLICY_JSON_INCLUDE -#define AHL_POLICY_JSON_INCLUDE - -int EndpointInfoToJSON(EndpointInfoT * pEndpointInfo, json_object **ppEndpointInfoJ); -int StreamInfoToJSON(StreamInfoT * pStreamInfo, json_object **ppStreamInfoJ); -int UpdateEndpointInfo(EndpointInfoT * pEndpoint,json_object * pEndpointInfoJ); -void JSONPublicPackageEndpoint(EndpointInfoT * pEndpointInfo,json_object **endpointInfoJ); -void JSONPublicPackageStream(StreamInfoT * pStreamInfo,json_object **streamInfoJ); - -#endif // AHL_POLICY_JSON_INCLUDE diff --git a/ahl-binding/config_entry.cpp b/ahl-binding/config_entry.cpp new file mode 100644 index 0000000..65d7a40 --- /dev/null +++ b/ahl-binding/config_entry.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@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. + */ + +#include "config_entry.hpp" + +config_entry_t::config_entry_t(std::string fp, std::string fn) + : fullpath_{fp} + , filename_{fn} +{ +} + +config_entry_t::config_entry_t(struct json_object* j) +{ + jcast(fullpath_, j, "fullpath"); + jcast(filename_, j, "filename"); +} + +config_entry_t& config_entry_t::operator<<(struct json_object* j) +{ + jcast(fullpath_, j, "fullpath"); + jcast(filename_, j, "filename"); + return *this; +} + +std::string config_entry_t::fullpath() const +{ + return fullpath_; +} + +std::string config_entry_t::filename() const +{ + return filename_; +} + +std::string config_entry_t::filepath() const +{ + return fullpath_ + '/' + filename_; +} + +void config_entry_t::fullpath(std::string fp) +{ + fullpath_ = fp; +} + +void config_entry_t::finename(std::string fn) +{ + filename_ = fn; +} diff --git a/ahl-binding/config_entry.hpp b/ahl-binding/config_entry.hpp new file mode 100644 index 0000000..492eed2 --- /dev/null +++ b/ahl-binding/config_entry.hpp @@ -0,0 +1,49 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@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. + */ + +#include "jsonc_utils.hpp" + +class config_entry_t +{ +private: + std::string fullpath_; + std::string filename_; + +public: + explicit config_entry_t() = default; + explicit config_entry_t(const config_entry_t&) = default; + explicit config_entry_t(config_entry_t&&) = default; + + ~config_entry_t() = default; + + config_entry_t& operator=(const config_entry_t&) = default; + config_entry_t& operator=(config_entry_t&&) = default; + + explicit config_entry_t(std::string fp, std::string fn); + explicit config_entry_t(struct json_object* j); + config_entry_t& operator<<(struct json_object* j); + + std::string fullpath() const; + std::string filename() const; + std::string filepath() const; + + void fullpath(std::string fp); + void finename(std::string fn); + +}; diff --git a/ahl-binding/jsonc_utils.hpp b/ahl-binding/jsonc_utils.hpp new file mode 100644 index 0000000..02e113b --- /dev/null +++ b/ahl-binding/jsonc_utils.hpp @@ -0,0 +1,90 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@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. + */ + +#include <string> +#include <vector> +#include <json-c/json.h> + +template<class T> +inline T& jcast(T& v, json_object* o) +{ + v << o; + return v; +} + +template<class T> +inline T& jcast_array(T& v, json_object* o) +{ + auto sz = json_object_array_length(o); + for(auto i = 0; i < sz ; ++i) + { + typename T::value_type item; + jcast(item, json_object_array_get_idx(o, i)); + v.push_back(item); + } + return v; +} + +template<> +inline bool& jcast<bool>(bool& v, json_object* o) +{ + v = (json_object_get_boolean(o) == TRUE); + return v; +} + +template<> +inline double& jcast<double>(double& v, json_object* o) +{ + v = json_object_get_double(o); + return v; +} + +template<> +inline int32_t& jcast<int32_t>(int32_t& v, json_object* o) +{ + v = json_object_get_int(o); + return v; +} + +template<> +inline int64_t& jcast<int64_t>(int64_t& v, json_object* o) +{ + v = json_object_get_int(o); + return v; +} + +template<> +inline std::string& jcast<std::string>(std::string& v, json_object* o) +{ + const char* tmp = json_object_get_string(o); + v = tmp ? tmp : ""; + return v; +} + +template<class T> +inline T& jcast(T& v, json_object* o, std::string field) +{ + return jcast<T>(v, json_object_object_get(o, field.c_str())); +} + +template<class T> +inline T& jcast_array(T& v, json_object* o, std::string field) +{ + return jcast_array<T>(v, json_object_object_get(o, field.c_str())); +} diff --git a/ahl-binding/role.cpp b/ahl-binding/role.cpp new file mode 100644 index 0000000..f0f744f --- /dev/null +++ b/ahl-binding/role.cpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@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. + */ + +#include "role.hpp" +#include "jsonc_utils.hpp" + +role_t::role_t(std::string uid, std::string name, std::string description, std::string stream, int priority) + : uid_{uid} + , name_{name} + , description_{description} + , stream_{stream} + , priority_{priority} +{ +} + +role_t::role_t(json_object* j) +{ + jcast(uid_, j, "uid"); + jcast(name_, j, "name"); + jcast(description_, j, "description"); + jcast(priority_, j, "priority"); + jcast(stream_, j, "stream"); +} + +role_t& role_t::operator<<(json_object* j) +{ + jcast(uid_, j, "uid"); + jcast(name_, j, "name"); + jcast(description_, j, "description"); + jcast(priority_, j, "priority"); + jcast(stream_, j, "stream"); + return *this; +} + +std::string role_t::uid() const +{ + return uid_; +} + +std::string role_t::name() const +{ + return name_; +} + +std::string role_t::description() const +{ + return description_; +} + +std::string role_t::stream() const +{ + return stream_; +} + +int role_t::priority() const +{ + return priority_; +} + +std::string role_t::device_uri() const +{ + return device_uri_; +} + +void role_t::uid(std::string v) +{ + uid_ = v; +} + +void role_t::name(std::string v) +{ + name_ = v; +} + +void role_t::description(std::string v) +{ + description_ = v; +} + +void role_t::stream(std::string v) +{ + stream_ = v; +} + +void role_t::priority(int v) +{ + priority_ = v; +} + +void role_t::device_uri(std::string v) +{ + device_uri_ = v; +} diff --git a/ahl-binding/role.hpp b/ahl-binding/role.hpp new file mode 100644 index 0000000..5b3d6b1 --- /dev/null +++ b/ahl-binding/role.hpp @@ -0,0 +1,60 @@ +#pragma once + +/* + * Copyright (C) 2018 "IoT.bzh" + * Author Loïc Collignon <loic.collignon@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. + */ + +#include "jsonc_utils.hpp" + +class role_t +{ +private: + std::string uid_; + std::string name_; + std::string description_; + std::string stream_; + std::string device_uri_; + int priority_; + +public: + explicit role_t() = default; + explicit role_t(const role_t&) = default; + explicit role_t(role_t&&) = default; + + ~role_t() = default; + + role_t& operator=(const role_t&) = default; + role_t& operator=(role_t&&) = default; + + explicit role_t(std::string uid, std::string name, std::string description, std::string stream, int priority); + explicit role_t(json_object* j); + + role_t& operator<<(json_object* j); + + std::string uid() const; + std::string name() const; + std::string description() const; + std::string stream() const; + int priority() const; + std::string device_uri() const; + + void uid(std::string v); + void name(std::string v); + void description(std::string v); + void stream(std::string v); + void priority(int v); + void device_uri(std::string v); +}; |