/* * Copyright (C) 2018 "IoT.bzh" * Author Loïc Collignon * * 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 #include "role.hpp" #include "jsonc_utils.hpp" #include "ahl-binding.hpp" using session_t = std::vector; role_t::role_t(json_object* j) { jcast(uid_, j, "uid"); 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(description_, j, "description"); jcast(priority_, j, "priority"); jcast(stream_, j, "stream"); jcast_array(interrupts_, j, "interrupts"); return *this; } std::string role_t::uid() const { return uid_; } std::string role_t::description() const { return description_; } std::string role_t::hal() const { return hal_; } 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_; } bool role_t::opened() const { return opened_; } void role_t::uid(std::string v) { uid_ = v; } 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; } void role_t::priority(int v) { priority_ = v; } void role_t::device_uri(std::string v) { device_uri_ = v; } const std::vector& role_t::interrupts() const { return interrupts_; } int role_t::apply_policy(afb_req_t req) { return interrupts_.size() ? interrupts_[0].apply(req, *this) : 0; } void role_t::invoke(afb_req_t req) { json_object* arg = afb_req_json(req); if (arg == nullptr) { afb_req_fail(req, "No valid argument!", nullptr); return; } json_object* jaction; json_bool ret = json_object_object_get_ex(arg, "action", &jaction); if (!ret) { afb_req_fail(req, "No valid action!", nullptr); return; } std::string action = json_object_get_string(jaction); if (action.size() == 0) { afb_req_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 if (action == "mute") mute(req, arg); else if (action == "unmute") unmute(req, arg); else afb_req_fail(req, "Unknown action!", nullptr); } void role_t::open(afb_req_t r, json_object* o) { if (opened_) { afb_req_fail(r, "Already opened!", nullptr); return; } if (!apply_policy(r)) { afb_req_context(r, 0, // Do not replace previous context if any [](void* arg) -> void* { return new session_t(); }, [](void* arg) { afb_api_t api = ahl_binding_t::instance().handle(); session_t* opened_roles = reinterpret_cast(arg); for(role_t* role : *opened_roles) { AFB_API_DEBUG(api, "Released role %s\n", role->uid_.c_str()); role->opened_ = false; if(role->interrupts_.size()) role->interrupts_[0].clear(); // send a mute command to the HAL. We cannot reuse the do_mute function, // because in the context here, the afb_request is no longer valid. json_object* a = json_object_new_object(); json_object_object_add(a, "mute", json_object_new_boolean(true)); afb_api_call( api, role->hal_.c_str(), role->stream_.c_str(), a, NULL, NULL); } delete opened_roles; }, nullptr ); opened_ = true; // Add the current role to the session session_t* context = reinterpret_cast(afb_req_context_get(r)); if(context) context->push_back(this); json_object* result = json_object_new_object(); json_object_object_add(result, "device_uri", json_object_new_string(device_uri_.c_str())); afb_req_success(r, result, nullptr); } } void role_t::close(afb_req_t r, json_object* o) { if (!opened_) { afb_req_success(r, nullptr, "Already closed!"); return; } session_t* context = reinterpret_cast(afb_req_context_get(r)); if(!context) { afb_req_fail(r, "Stream is opened by another client!", nullptr); return; } // Remove the current role from the session. std::string uid = uid_; std::remove_if(context->begin(), context->end(), [uid](role_t* r) { return r->uid_ == uid; }); context->push_back(this); opened_ = false; if(interrupts_.size()) interrupts_[0].clear(); afb_req_success(r, nullptr, "Stream closed!"); } void role_t::mute(afb_req_t r, json_object* o) { do_mute(r, true); } void role_t::unmute(afb_req_t r, json_object *o) { do_mute(r, false); } void role_t::do_mute(afb_req_t r, bool v) { json_object* a = json_object_new_object(); json_object_object_add(a, "mute", json_object_new_boolean(v)); afb_api_t api = ahl_binding_t::instance().handle(); afb_api_call( api, hal_.c_str(), stream_.c_str(), a, [](void* closure, json_object* result, const char* error, const char* info, afb_api_t handle) { AFB_API_DEBUG(handle, "Got the following answer: %s", json_object_to_json_string(result)); afb_req_t r = (afb_req_t)closure; json_object_get(result); if (error) afb_req_fail(r, json_object_to_json_string(result), nullptr); else afb_req_success(r, result, nullptr); afb_req_unref(r); }, afb_req_addref(r)); } struct volumeclosure { std::string role; afb_req_t req; }; void role_t::volume(afb_req_t r, json_object* o) { afb_api_t api = ahl_binding_t::instance().handle(); if(!afb_req_has_permission(r, "urn:AGL:permission::public:4a-audio-mixer")) { if (!opened_) { afb_req_fail(r, "You have to open the stream first!", nullptr); return; } if(!afb_req_context_get(r)) { afb_req_fail(r, "Stream is opened by another client!", nullptr); return; } } else { AFB_API_DEBUG(api, "Granted special audio-mixer permission to change volume"); } json_object* value; json_bool ret = json_object_object_get_ex(o, "value", &value); if (!ret) { afb_req_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); volumeclosure* userdata = new volumeclosure(); userdata->role = uid_; userdata->req = afb_req_addref(r); afb_api_call( api, hal_.c_str(), stream_.c_str(), a, [](void* closure, json_object* result, const char* error, const char* info, afb_api_t handle) { AFB_API_DEBUG(handle, "Got the following answer: %s", json_object_to_json_string(result)); volumeclosure* r = reinterpret_cast(closure); json_object_get(result); if (error) afb_req_fail(r->req, json_object_to_json_string(result), nullptr); else { json_object* volnew; if (json_object_object_get_ex(result, "volnew", &volnew)) { ahl_binding_t::instance().emit_volume_changed(r->role, json_object_get_int(volnew)); } afb_req_success(r->req, result, nullptr); } afb_req_unref(r->req); delete r; }, userdata ); } void role_t::interrupt(afb_req_t r, json_object* o) { }