aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Collignon <loic.collignon@iot.bzh>2018-06-12 11:00:25 +0200
committerJan-Simon Moeller <jsmoeller@linuxfoundation.org>2018-06-12 14:40:08 +0000
commit22c2633ac833ae78ff84beafce19cec0f05758a2 (patch)
treedca31bcfc6bf27aaca93e6f9f676361c6f6ed1e1
parent9a631c30c9c8792865ce2aa0ec06a1bb5fd16751 (diff)
Update to last softmixer and hal, plus some fixes
Use the last 4a-softmixer and 4a-hal-generic versions. Fix some potential crash and performance bottleneck. Now support sessions to disallow multiple opening or closing by not owner. Change-Id: I95b020a3fab03b1e1058812adae1d8d5986f282b Signed-off-by: Loïc Collignon <loic.collignon@iot.bzh>
-rw-r--r--ahl-binding/CMakeLists.txt1
-rw-r--r--ahl-binding/ahl-4a.cpp49
-rw-r--r--ahl-binding/ahl-4a.hpp47
-rw-r--r--ahl-binding/ahl-binding.cpp428
-rw-r--r--ahl-binding/ahl-binding.hpp52
-rw-r--r--ahl-binding/role.cpp200
-rw-r--r--ahl-binding/role.hpp30
-rw-r--r--conf.d/project/policy-4a-sample1.json37
8 files changed, 435 insertions, 409 deletions
diff --git a/ahl-binding/CMakeLists.txt b/ahl-binding/CMakeLists.txt
index 0e5df43..bd2be0c 100644
--- a/ahl-binding/CMakeLists.txt
+++ b/ahl-binding/CMakeLists.txt
@@ -23,7 +23,6 @@ PROJECT_TARGET_ADD(audiohighlevel)
ADD_LIBRARY(${TARGET_NAME} MODULE
config_entry.cpp
role.cpp
- ahl-4a.cpp
interrupt.cpp
ahl-binding.cpp
)
diff --git a/ahl-binding/ahl-4a.cpp b/ahl-binding/ahl-4a.cpp
deleted file mode 100644
index b5fdc16..0000000
--- a/ahl-binding/ahl-4a.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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
deleted file mode 100644
index acab71e..0000000
--- a/ahl-binding/ahl-4a.hpp
+++ /dev/null
@@ -1,47 +0,0 @@
-#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-binding.cpp b/ahl-binding/ahl-binding.cpp
index fa4c74b..2bf1664 100644
--- a/ahl-binding/ahl-binding.cpp
+++ b/ahl-binding/ahl-binding.cpp
@@ -21,6 +21,16 @@
afb_dynapi* AFB_default; // BUG: Is it possible to get rid of this ?
/**
+ * @brief Callback invoked on new api creation.
+ * @param[in] handle Handle to the new api.
+ * @return Status code, zero if success.
+ */
+int ahl_api_create(void*, struct afb_dynapi* handle)
+{
+ return ahl_binding_t::instance().preinit(handle);
+}
+
+/**
* @brief Entry point for dynamic API.
* @param[in] handle Handle to start with for API creation.
* @return Status code, zero if success.
@@ -43,26 +53,80 @@ int afbBindingVdyn(afb_dynapi* handle)
}
/**
- * @brief Callback to create the new api.
- * @param[in] handle Handle to the new api.
+ * @brief Callback invoked when API enter the init phase.
* @return Status code, zero if success.
*/
-int ahl_api_create(void*, struct afb_dynapi* handle)
+int ahl_api_init(afb_dynapi*)
{
- return ahl_binding_t::instance().preinit(handle);
+ return ahl_binding_t::instance().init();
}
+/**
+ * @brief Callback invoked when an event is received.
+ * @param[in] e Event's name.
+ * @param[in] o Event's args.
+ */
+void ahl_api_on_event(afb_dynapi*, const char* e, struct json_object* o)
+{
+ ahl_binding_t::instance().event(e, o);
+}
+
+/**
+ * @brief Callback invoked when a 'roles' section is found in config file.
+ * @param[in] o Config section to handle.
+ * @return Status code, zero if success.
+ */
+int ahl_api_config_roles(afb_dynapi*, CtlSectionT*, json_object* o)
+{
+ return ahl_binding_t::instance().parse_roles_config(o);
+}
+
+/**
+ * @brief Callback invoked when clients call the verb 'get_roles'.
+ * @param[in] req Request to handle.
+ */
+void ahl_api_get_roles(afb_request* req)
+{
+ ahl_binding_t::instance().get_roles(req);
+}
+
+/**
+ * @brief Callback invoked when clients call a 'role' verb.
+ * @param[in] req Request to handle.
+ *
+ * Handle dynamic verbs based on role name ('multimedia', 'navigation', ...)
+ */
+void ahl_api_role(afb_request* req)
+{
+ role_t* role = (role_t*)req->vcbdata;
+ assert(role != nullptr);
+
+ role->invoke(req);
+}
+
+/**
+ * @brief Default constructor.
+ */
ahl_binding_t::ahl_binding_t()
: handle_{nullptr}
{
}
+/**
+ * @brief Get the singleton instance.
+ * @return The unique instance.
+ */
ahl_binding_t& ahl_binding_t::instance()
{
static ahl_binding_t s;
return s;
}
+/**
+ * @brief This method is called during the pre-init phase of loading the binding.
+ * @param[in] handle Handle to the api.
+ * @return Status code, zero if success.
+ */
int ahl_binding_t::preinit(afb_dynapi* handle)
{
handle_ = handle;
@@ -70,17 +134,13 @@ int ahl_binding_t::preinit(afb_dynapi* handle)
try
{
load_static_verbs();
- load_controller_api();
+ load_controller_configs();
- 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_event(handle_, ahl_api_on_event))
+ 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.");
+ if (afb_dynapi_on_init(handle_, ahl_api_init))
+ throw std::runtime_error("Failed to register init handler callback.");
}
catch(std::exception& e)
{
@@ -91,6 +151,9 @@ int ahl_binding_t::preinit(afb_dynapi* handle)
return 0;
}
+/**
+ * @brief Initialize the API.
+ */
int ahl_binding_t::init()
{
using namespace std::placeholders;
@@ -109,73 +172,86 @@ int ahl_binding_t::init()
}
AFB_DYNAPI_NOTICE(handle_, "Required 'smixer' API found!");
- // Requires corresponding API
- for(const auto& h : config_.hals())
+ afb_dynapi_seal(handle_);
+ AFB_DYNAPI_NOTICE(handle_, "API is now sealed!");
+
+ if (update_streams()) return -1;
+ return 0;
+}
+
+/**
+ * @brief Update audio roles definition by binding to streams.
+ * @return Status code, zero if success.
+ */
+int ahl_binding_t::update_streams()
+{
+ json_object* loaded = nullptr;
+ json_object* response = nullptr;
+ size_t i = 0, j = 0;
+ size_t hals_count = 0, streams_count = 0;
+
+ if (afb_dynapi_call_sync(handle_, "4a-hal-manager", "loaded", json_object_new_object(), &loaded))
+ {
+ AFB_DYNAPI_ERROR(handle_, "Failed to call 'loaded' verb on '4a-hal-manager' API!");
+ if (loaded) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(loaded));
+ return -1;
+ }
+ response = json_object_object_get(loaded, "response");
+ hals_count = json_object_array_length(response);
+
+ for(i = 0; i < hals_count; ++i)
{
- if (afb_dynapi_require_api(handle_, h.c_str(), 1))
+ json_object* info = nullptr;
+ json_object* streams = nullptr;
+ const char* halname = json_object_get_string(json_object_array_get_idx(response, i));
+ AFB_DYNAPI_DEBUG(handle_, "Found an active HAL: %s", halname);
+
+ if (afb_dynapi_call_sync(handle_, halname, "info", json_object_new_object(), &info))
{
- AFB_DYNAPI_ERROR(handle_, "Failed to require '%s' API!", h.c_str());
+ AFB_DYNAPI_ERROR(handle_, "Failed to call 'info' verb on '%s' API!", halname);
+ if (info) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(info));
return -1;
}
- AFB_DYNAPI_NOTICE(handle_, "Required '%s' API found!", h.c_str());
- json_object* result = nullptr;
-
- // if(afb_dynapi_call_sync(handle_, h.c_str(), "init-mixer", nullptr, &result))
- // {
- // AFB_DYNAPI_ERROR(handle_, "Failed to call 'init-mixer' verb on '%s' API!", h.c_str());
- // if (result) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(result));
- // return -1;
- // }
- // AFB_DYNAPI_NOTICE(handle_, "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(handle_, h.c_str(), "list", verbose, &response))
- // {
- // AFB_DYNAPI_ERROR(handle_, "Failed to call 'list' verb on '%s' API!", h.c_str());
- // if (result) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(result));
- // return -1;
- // }
-
- json_object* response = nullptr;
- json_object* arg = json_object_new_object();
- json_object_object_add(arg, "streams", json_object_new_boolean(TRUE));
-
- if (afb_dynapi_call_sync(handle_, "smixer", "info", arg, &response))
+
+ streams = json_object_object_get(json_object_object_get(info, "response"), "streams");
+ streams_count = json_object_array_length(streams);
+ for(j = 0; j < streams_count; ++j)
{
- AFB_DYNAPI_ERROR(handle_, "Failed to call 'list' verb on '%s' API!", h.c_str());
- if (result) AFB_DYNAPI_NOTICE(handle_, "%s", json_object_to_json_string(result));
- return -1;
- }
+ update_stream(
+ halname,
+ json_object_get_string(json_object_object_get(json_object_array_get_idx(streams, j), "name")),
+ json_object_get_string(json_object_object_get(json_object_array_get_idx(streams, j), "cardId"))
+ );
+ }
+ json_object_put(info);
+ }
+ json_object_put(loaded);
+
+ return 0;
+}
- json_object* streams = json_object_object_get(response, "response");
- AFB_DYNAPI_DEBUG(handle_, "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)
+/**
+ * @brief Update the stream info for audio roles.
+ * @param[in] halname The hal on which the stream is.
+ * @param[in] stream The name of the stream.
+ * @param[in] deviceid The device ID to return when opening an audio role.
+ */
+void ahl_binding_t::update_stream(std::string halname, std::string stream, std::string deviceid)
+{
+ for(auto& r : roles_)
+ {
+ if(r.stream() == stream)
{
- std::string stream_name;
- std::string device_uri;
- json_object* item = json_object_array_get_idx(array, i);
-
- jcast(stream_name, item, "uid");
- jcast(device_uri, item, "alsa");
-
- config_.set_device_uri(stream_name, device_uri);
+ if (r.device_uri().size())
+ AFB_DYNAPI_WARNING(handle_, "Multiple stream with same name: '%s'.", stream.c_str());
+ else
+ {
+ r.device_uri(deviceid);
+ r.hal(halname);
+ }
}
}
-
- afb_dynapi_seal(handle_);
- AFB_DYNAPI_NOTICE(handle_, "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);
- actions_["close"] = std::bind(&ahl_binding_t::close, this, _1, _2, _3, _4);
- actions_["interrupt"] = std::bind(&ahl_binding_t::interrupt, this, _1, _2, _3, _4);
-
- return 0;
}
void ahl_binding_t::event(std::string name, json_object* arg)
@@ -189,7 +265,7 @@ void ahl_binding_t::load_static_verbs()
handle_,
"get_roles",
"Retrieve array of available audio roles",
- [](afb_request* r) { ahl_binding_t::instance().get_roles(r); },
+ ahl_api_get_roles,
nullptr,
nullptr,
AFB_SESSION_NONE_V2))
@@ -198,7 +274,7 @@ void ahl_binding_t::load_static_verbs()
}
}
-void ahl_binding_t::load_controller_api()
+void ahl_binding_t::load_controller_configs()
{
char* dir_list = getenv("CONTROL_CONFIG_PATH");
if (!dir_list) dir_list = strdup(CONTROL_CONFIG_PATH);
@@ -241,17 +317,8 @@ int ahl_binding_t::load_controller_config(const std::string& path)
{.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}
+ {.key = "roles", .uid = nullptr, .info = nullptr, .loadCB = ahl_api_config_roles, .handle = nullptr, .actions = nullptr },
+ {.key = nullptr, .uid = nullptr, .info = nullptr, .loadCB = nullptr, .handle = nullptr, .actions = nullptr}
};
CtlLoadSections(handle_, controller_config, controller_sections);
@@ -259,187 +326,62 @@ int ahl_binding_t::load_controller_config(const std::string& path)
return 0;
}
-int ahl_binding_t::load_config(CtlSectionT* section, json_object* o)
+int ahl_binding_t::parse_roles_config(json_object* o)
{
- config_ << o;
+ assert(o != nullptr);
+ assert(json_object_is_type(o, json_type_array));
- // Add corresponding verbs
- for(const auto& r : config_.roles())
+ size_t count = json_object_array_length(o);
+ roles_.reserve(count);
+ for(size_t i = 0; i < count; ++i)
{
- AFB_DYNAPI_NOTICE(handle_, "New audio role: %s", r.name().c_str());
+ json_object* jr = json_object_array_get_idx(o, i);
+ assert(jr != nullptr);
- if (afb_dynapi_add_verb(
- handle_,
- 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());
- }
+ roles_.push_back(role_t(jr));
+ role_t& r = roles_[roles_.size() - 1];
+ if(create_api_verb(&r))
+ return -1;
}
-
- return 0;
-}
-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);
+ return 0;
}
-void ahl_binding_t::get_roles(afb_request* req)
+int ahl_binding_t::create_api_verb(role_t* r)
{
- 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);
-}
+ AFB_DYNAPI_NOTICE(handle_, "New audio role: %s", r->uid().c_str());
-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(handle_, "Call the HAL with the following argument: %s", json_object_to_json_string(a));
-
- afb_dynapi_call(
+ if (afb_dynapi_add_verb(
handle_,
- 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())
+ r->uid().c_str(),
+ r->description().c_str(),
+ ahl_api_role,
+ r,
+ nullptr,
+ AFB_SESSION_NONE_V2))
{
- if (r.name() == role)
- {
- if (
- ext::cfind_if(opened_roles_,
- [&role](const role_t& r){ return r.name() == role;}) != opened_roles_.end()
- )
- {
- afb_request_fail(req, "This role is already opened!", nullptr);
- return;
- }
- // Execute policy for current asked role
- policy_open(req, r);
- return;
- }
+ AFB_DYNAPI_ERROR(handle_, "Failed to add '%s' verb to the API.",
+ r->uid().c_str());
+ return -1;
}
- afb_request_fail(req, "Can't open the specified role!", nullptr);
+
+ return 0;
}
-void ahl_binding_t::policy_open(afb_request* req, const role_t& role)
+void ahl_binding_t::get_roles(afb_request* req)
{
- if(role.interrupts().size())
- {
- const interrupt_t& i = role.interrupts()[0];
- /*if (i.type() == "mute")
- {
- }
- else if (i.type() == "continue")
- {
- }
- else if (i.type() == "cancel")
- {
- }
- else */if (i.type() == "ramp")
- {
- for(const auto& r: opened_roles_)
- {
- if (role.priority() > r.priority())
- {
- // { "ramp" : { "uid" : "ramp-slow", "volume" : 30 } }
- json_object* arg = json_object_new_object();
- json_object_object_add(arg, "ramp", i.args());
- json_object_get(i.args());
- json_object* result = nullptr;
-
- AFB_DYNAPI_NOTICE(handle_, "Call 'smixer'/'%s' '%s", r.stream().c_str(), json_object_to_json_string(arg));
-
- if(afb_dynapi_call_sync(handle_, "smixer", r.stream().c_str(), arg, &result))
- {
- afb_request_fail(req, "Failed to call 'ramp' action on stream", nullptr);
- return;
- }
- AFB_DYNAPI_NOTICE(handle_, "POLICY: Applying a ramp to '%s' stream because '%s' is opened and have higher priority!", r.stream().c_str(), role.stream().c_str());
- }
- }
- }
- else
- {
- afb_request_fail(req, "Unkown interrupt uid!", nullptr);
- return;
- }
- }
-
- json_object* result = json_object_new_object();
- json_object_object_add(result, "device_uri", json_object_new_string(role.device_uri().c_str()));
+ json_object* result = json_object_new_array();
+ for(const auto& r : roles_)
+ json_object_array_add(result, json_object_new_string(r.uid().c_str()));
afb_request_success(req, result, nullptr);
- opened_roles_.push_back(role);
}
-void ahl_binding_t::close(afb_request* req, std::string role, std::string stream, json_object* arg)
+const std::vector<role_t> ahl_binding_t::roles() const
{
- AFB_DYNAPI_DEBUG(handle_, "Got this arg: %s", json_object_to_json_string(arg));
- auto it = ext::cfind_if(opened_roles_, [&role](const role_t& r) { return r.name() == role; });
- if (it == opened_roles_.cend())
- {
- afb_request_fail(req, "This role is already closed!", nullptr);
- return;
- }
- opened_roles_.erase(it);
- afb_request_success(req, nullptr, "Role closed!");
+ return roles_;
}
-void ahl_binding_t::interrupt(afb_request* req, std::string role, std::string stream, json_object* arg)
+afb_dynapi* ahl_binding_t::handle() const
{
- afb_request_fail(req, "Not implemented yet!", nullptr);
+ return handle_;
}
diff --git a/ahl-binding/ahl-binding.hpp b/ahl-binding/ahl-binding.hpp
index 62a518e..66fce84 100644
--- a/ahl-binding/ahl-binding.hpp
+++ b/ahl-binding/ahl-binding.hpp
@@ -21,11 +21,12 @@
#include <functional>
#include <sstream>
#include <vector>
+#include <list>
#include <map>
#include <cassert>
#include "config_entry.hpp"
-#include "ahl-4a.hpp"
+#include "role.hpp"
#define HL_API_NAME "ahl-4a"
#define HL_API_INFO "Audio high level API for AGL applications"
@@ -37,55 +38,27 @@ extern "C" {
#include <afb/afb-binding.h>
#include <string.h>
#include <ctl-config.h>
-
- int afbBindingVdyn(afb_dynapi* handle);
- int ahl_api_create(void*, struct afb_dynapi*);
};
-namespace ext
-{
- template<class T>
- typename T::iterator find(T& container, const typename T::value_type& value)
- {
- return std::find(std::begin(container), std::end(container), value);
- }
-
- template<class T, class UnaryPredicate>
- typename T::iterator find_if(T& container, UnaryPredicate pred)
- {
- return std::find_if(std::begin(container), std::end(container), pred);
- }
-
- template<class T, class UnaryPredicate>
- typename T::iterator cfind_if(T& container, UnaryPredicate pred)
- {
- return std::find_if(std::begin(container), std::end(container), pred);
- }
-}
-
class ahl_binding_t
{
using role_action = std::function<void(afb_request*, std::string, std::string, json_object*)>;
private:
afb_dynapi* handle_;
-
- ahl_4a_t config_;
- std::map<std::string, role_action> actions_;
- std::vector<role_t> opened_roles_;
+ std::vector<role_t> roles_;
explicit ahl_binding_t();
void load_static_verbs();
- void load_controller_api();
+
+
+ void load_controller_configs();
int load_controller_config(const std::string& path);
- int load_config(CtlSectionT* section, json_object* o);
+ int update_streams();
+ void update_stream(std::string hal, std::string stream, std::string deviceuri);
+ int create_api_verb(role_t* r);
- 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);
- void close(afb_request* req, std::string role, std::string stream, json_object* arg);
- void interrupt(afb_request* req, std::string role, std::string stream, json_object* arg);
-
void policy_open(afb_request* req, const role_t& role);
public:
@@ -93,8 +66,13 @@ public:
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);
+
+ const std::vector<role_t> roles() const;
+ afb_dynapi* handle() const;
+
+ void audiorole(afb_request* req);
+ int parse_roles_config(json_object* o);
};
diff --git a/ahl-binding/role.cpp b/ahl-binding/role.cpp
index 9338436..b6a9c1f 100644
--- a/ahl-binding/role.cpp
+++ b/ahl-binding/role.cpp
@@ -17,30 +17,21 @@
#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}
-{
-}
+#include "ahl-binding.hpp"
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");
jcast_array(interrupts_, j, "interrupts");
+ opened_ = false;
}
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");
@@ -53,14 +44,14 @@ std::string role_t::uid() const
return uid_;
}
-std::string role_t::name() const
+std::string role_t::description() const
{
- return name_;
+ return description_;
}
-std::string role_t::description() const
+std::string role_t::hal() const
{
- return description_;
+ return hal_;
}
std::string role_t::stream() const
@@ -78,14 +69,14 @@ std::string role_t::device_uri() const
return device_uri_;
}
-void role_t::uid(std::string v)
+bool role_t::opened() const
{
- uid_ = v;
+ return opened_;
}
-void role_t::name(std::string v)
+void role_t::uid(std::string v)
{
- name_ = v;
+ uid_ = v;
}
void role_t::description(std::string v)
@@ -93,6 +84,11 @@ void role_t::description(std::string v)
description_ = v;
}
+void role_t::hal(std::string v)
+{
+ hal_ = v;
+}
+
void role_t::stream(std::string v)
{
stream_ = v;
@@ -112,3 +108,169 @@ const std::vector<interrupt_t>& role_t::interrupts() const
{
return interrupts_;
}
+
+int role_t::apply_policy(afb_request* req)
+{
+ if(interrupts_.size())
+ {
+ const interrupt_t& i = interrupts_[0];
+ /*if (i.type() == "mute")
+ {
+ }
+ else if (i.type() == "continue")
+ {
+ }
+ else if (i.type() == "cancel")
+ {
+ }
+ else */if (i.type() == "ramp")
+ {
+ for(const auto& r: ahl_binding_t::instance().roles())
+ {
+ if (r.opened() && priority_ > r.priority())
+ {
+ // { "ramp" : { "uid" : "ramp-slow", "volume" : 30 } }
+ json_object* arg = json_object_new_object();
+ json_object_object_add(arg, "ramp", i.args());
+ json_object_get(i.args());
+ json_object* result = nullptr;
+
+ AFB_DYNAPI_NOTICE(ahl_binding_t::instance().handle(),
+ "Call '%s'/'%s' '%s",
+ r.hal().c_str(), r.stream().c_str(), json_object_to_json_string(arg));
+
+ if(afb_dynapi_call_sync(ahl_binding_t::instance().handle(), r.hal().c_str(), r.stream().c_str(), arg, &result))
+ {
+ afb_request_fail(req, "Failed to call 'ramp' action on stream", nullptr);
+ return -1;
+ }
+ AFB_DYNAPI_NOTICE(ahl_binding_t::instance().handle(),
+ "POLICY: Applying a ramp to '%s' stream because '%s' is opened and have higher priority!",
+ r.stream().c_str(), stream_.c_str());
+ }
+ }
+ }
+ else
+ {
+ afb_request_fail(req, "Unkown interrupt uid!", nullptr);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void role_t::invoke(afb_request* req)
+{
+ json_object* arg = afb_request_json(req);
+ if (arg == nullptr)
+ {
+ afb_request_fail(req, "No valid argument!", nullptr);
+ return;
+ }
+
+ json_object* jaction = json_object_object_get(arg, "action");
+ if (jaction == nullptr)
+ {
+ afb_request_fail(req, "No valid action!", nullptr);
+ return;
+ }
+
+ std::string action = json_object_get_string(jaction);
+ if (action.size() == 0)
+ {
+ afb_request_fail(req, "No valid action!", nullptr);
+ return;
+ }
+
+ if (action == "open") open(req, arg);
+ else if (action == "close") close(req, arg);
+ else if (action == "volume") volume(req, arg);
+ else if (action == "interrupt") interrupt(req, arg);
+ else afb_request_fail(req, "Unknown action!", nullptr);
+}
+
+void role_t::open(afb_request* r, json_object* o)
+{
+ if (opened_)
+ {
+ afb_request_fail(r, "Already opened!", nullptr);
+ return;
+ }
+
+ if (!apply_policy(r))
+ {
+ afb_request_context_set(r, this, nullptr);
+ opened_ = true;
+
+ json_object* result = json_object_new_object();
+ json_object_object_add(result, "device_uri", json_object_new_string(device_uri_.c_str()));
+
+ afb_request_success(r, result, nullptr);
+ }
+}
+
+void role_t::close(afb_request* r, json_object* o)
+{
+ if (!opened_)
+ {
+ afb_request_success(r, nullptr, "Already closed!");
+ return;
+ }
+
+ if(!afb_request_context_get(r))
+ {
+ afb_request_fail(r, "Stream is opened by another client!", nullptr);
+ return;
+ }
+
+ opened_ = false;
+ afb_request_success(r, nullptr, "Stream closed!");
+}
+
+void role_t::volume(afb_request* r, json_object* o)
+{
+ if (!opened_)
+ {
+ afb_request_fail(r, "You have to open the stream first!", nullptr);
+ return;
+ }
+
+ if(!afb_request_context_get(r))
+ {
+ afb_request_fail(r, "Stream is opened by another client!", nullptr);
+ return;
+ }
+
+ json_object* value = json_object_object_get(o, "value");
+ if(!value)
+ {
+ afb_request_fail(r, "No value given!", nullptr);
+ return;
+ }
+
+ json_object_get(value);
+
+ json_object* a = json_object_new_object();
+ json_object_object_add(a, "volume", value);
+
+ afb_dynapi_call(
+ r->dynapi,
+ hal_.c_str(),
+ 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(r));
+}
+
+void role_t::interrupt(afb_request* r, json_object* o)
+{
+}
diff --git a/ahl-binding/role.hpp b/ahl-binding/role.hpp
index fa1bcd4..1469b68 100644
--- a/ahl-binding/role.hpp
+++ b/ahl-binding/role.hpp
@@ -20,17 +20,24 @@
#include <vector>
#include "interrupt.hpp"
+struct afb_request;
+
class role_t
{
private:
+ // Members filled by config
std::string uid_;
- std::string name_;
std::string description_;
+ std::string hal_;
std::string stream_;
- std::string device_uri_;
int priority_;
std::vector<interrupt_t> interrupts_;
+ std::string device_uri_;
+ bool opened_;
+
+ int apply_policy(afb_request* req);
+
public:
explicit role_t() = default;
explicit role_t(const role_t&) = default;
@@ -41,23 +48,32 @@ public:
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);
+ static role_t from_json(json_object* o);
+
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 hal() const;
std::string stream() const;
int priority() const;
- std::string device_uri() const;
const std::vector<interrupt_t>& interrupts() const;
+ std::string device_uri() const;
+ bool opened() const;
void uid(std::string v);
- void name(std::string v);
void description(std::string v);
+ void hal(std::string v);
void stream(std::string v);
- void priority(int v);
void device_uri(std::string v);
+ void priority(int v);
+
+ void invoke(afb_request* r);
+
+ void open(afb_request* r, json_object* o);
+ void close(afb_request* r, json_object* o);
+ void volume(afb_request* r, json_object* o);
+ void interrupt(afb_request* r, json_object* o);
};
diff --git a/conf.d/project/policy-4a-sample1.json b/conf.d/project/policy-4a-sample1.json
index d71bbe9..8aa7a03 100644
--- a/conf.d/project/policy-4a-sample1.json
+++ b/conf.d/project/policy-4a-sample1.json
@@ -16,15 +16,13 @@
"hals": ["4a-hal-ctxnop-laptop"],
"roles": [
{
- "uid": "role-multimedia",
- "name": "multimedia",
+ "uid": "multimedia",
"description": "Multimedia content (e.g. tuner, media player, etc.)",
"priority": 0,
"stream": "multimedia"
},
{
- "uid": "role-emergency",
- "name": "emergency",
+ "uid": "emergency",
"description": "Safety-relevant or critical alerts/alarms",
"priority": 100,
"stream": "emergency",
@@ -33,7 +31,35 @@
]
},
{
- "uid": "role-navigation",
+ "uid": "navigation",
+ "name": "navigation",
+ "description": "Navigation instructions (GPS, turn directions, etc...)",
+ "priority": 25,
+ "stream": "navigation",
+ "interrupts":[
+ {"type": "ramp", "args": { "uid": "ramp-slow", "volume": 30} }
+ ]
+ }
+ ]
+ },
+ "roles":[
+ {
+ "uid": "multimedia",
+ "description": "Multimedia content (e.g. tuner, media player, etc.)",
+ "priority": 0,
+ "stream": "multimedia"
+ },
+ {
+ "uid": "emergency",
+ "description": "Safety-relevant or critical alerts/alarms",
+ "priority": 100,
+ "stream": "emergency",
+ "interrupts":[
+ {"type": "ramp", "args": { "uid": "ramp-slow", "volume": 30} }
+ ]
+ },
+ {
+ "uid": "navigation",
"name": "navigation",
"description": "Navigation instructions (GPS, turn directions, etc...)",
"priority": 25,
@@ -43,5 +69,4 @@
]
}
]
- }
}