blob: bd5fe757400c7ba85816e13a66ebe0af1bb954a0 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* 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 "action.h"
#include <errno.h>
#include <base/strings.h>
#include <base/stringprintf.h>
#include "error.h"
#include "init_parser.h"
#include "log.h"
#include "property_service.h"
#include "util.h"
class Action::Command
{
public:
Command(int (*f)(int nargs, char** args),
const std::vector<std::string>& args,
const std::string& filename,
int line);
int InvokeFunc() const;
std::string BuildCommandString() const;
std::string BuildSourceString() const;
private:
int (*func_)(int nargs, char** args);
const std::vector<std::string> args_;
const std::string filename_;
int line_;
};
Action::Command::Command(int (*f)(int nargs, char** args),
const std::vector<std::string>& args,
const std::string& filename,
int line) :
func_(f), args_(args), filename_(filename), line_(line)
{
}
int Action::Command::InvokeFunc() const
{
std::vector<std::string> strs;
strs.resize(args_.size());
strs[0] = args_[0];
for (std::size_t i = 1; i < args_.size(); ++i) {
if (expand_props(args_[i], &strs[i]) == -1) {
ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
return -EINVAL;
}
}
std::vector<char*> args;
for (auto& s : strs) {
args.push_back(&s[0]);
}
return func_(args.size(), &args[0]);
}
std::string Action::Command::BuildCommandString() const
{
return android::base::Join(args_, ' ');
}
std::string Action::Command::BuildSourceString() const
{
if (!filename_.empty()) {
return android::base::StringPrintf(" (%s:%d)", filename_.c_str(), line_);
} else {
return std::string();
}
}
Action::Action()
{
}
void Action::AddCommand(int (*f)(int nargs, char** args),
const std::vector<std::string>& args,
const std::string& filename, int line)
{
Action::Command* cmd = new Action::Command(f, args, filename, line);
commands_.push_back(cmd);
}
std::size_t Action::NumCommands() const
{
return commands_.size();
}
void Action::ExecuteOneCommand(std::size_t command) const
{
ExecuteCommand(*commands_[command]);
}
void Action::ExecuteAllCommands() const
{
for (const auto& c : commands_) {
ExecuteCommand(*c);
}
}
void Action::ExecuteCommand(const Command& command) const
{
Timer t;
int result = command.InvokeFunc();
if (klog_get_level() >= KLOG_INFO_LEVEL) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString();
std::string source = command.BuildSourceString();
INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
result, t.duration());
}
}
bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err)
{
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
size_t equal_pos = prop_name.find('=');
if (equal_pos == std::string::npos) {
*err = "property trigger found without matching '='";
return false;
}
std::string prop_value(prop_name.substr(equal_pos + 1));
prop_name.erase(equal_pos);
auto res = property_triggers_.emplace(prop_name, prop_value);
if (res.second == false) {
*err = "multiple property triggers found for same property";
return false;
}
return true;
}
bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err)
{
const static std::string prop_str("property:");
for (std::size_t i = 0; i < args.size(); ++i) {
if (i % 2) {
if (args[i].compare("&&")) {
*err = "&& is the only symbol allowed to concatenate actions";
return false;
} else {
continue;
}
}
if (!args[i].compare(0, prop_str.length(), prop_str)) {
if (!ParsePropertyTrigger(args[i], err)) {
return false;
}
} else {
if (!event_trigger_.empty()) {
*err = "multiple event triggers are not allowed";
return false;
}
event_trigger_ = args[i];
}
}
return true;
}
bool Action::InitSingleTrigger(const std::string& trigger)
{
std::vector<std::string> name_vector{trigger};
std::string err;
return InitTriggers(name_vector, &err);
}
bool Action::CheckPropertyTriggers(const std::string& name,
const std::string& value) const
{
bool found = !name.compare("");
if (property_triggers_.empty()) {
return true;
}
for (const auto& t : property_triggers_) {
if (!t.first.compare(name)) {
if (t.second.compare("*") &&
t.second.compare(value)) {
return false;
} else {
found = true;
}
} else {
std::string prop_val = property_get(t.first.c_str());
if (prop_val.empty() ||
(t.second.compare("*") &&
t.second.compare(prop_val))) {
return false;
}
}
}
return found;
}
bool Action::CheckEventTrigger(const std::string& trigger) const
{
return !event_trigger_.empty() &&
!trigger.compare(event_trigger_) &&
CheckPropertyTriggers();
}
bool Action::CheckPropertyTrigger(const std::string& name,
const std::string& value) const
{
return event_trigger_.empty() && CheckPropertyTriggers(name, value);
}
bool Action::TriggersEqual(const class Action& other) const
{
return property_triggers_.size() == other.property_triggers_.size() &&
std::equal(property_triggers_.begin(), property_triggers_.end(),
other.property_triggers_.begin()) &&
!event_trigger_.compare(other.event_trigger_);
}
std::string Action::BuildTriggersString() const
{
std::string result;
for (const auto& t : property_triggers_) {
result += t.first;
result += '=';
result += t.second;
result += ' ';
}
if (!event_trigger_.empty()) {
result += event_trigger_;
result += ' ';
}
result.pop_back();
return result;
}
void Action::DumpState() const
{
INFO("on ");
std::string trigger_name = BuildTriggersString();
INFO("%s", trigger_name.c_str());
INFO("\n");
for (const auto& c : commands_) {
std::string cmd_str = c->BuildCommandString();
INFO(" %s", cmd_str.c_str());
}
INFO("\n");
}
ActionManager::ActionManager() : cur_command_(0)
{
}
ActionManager& ActionManager::GetInstance() {
static ActionManager instance;
return instance;
}
void ActionManager::QueueEventTrigger(const std::string& trigger)
{
for (const auto& a : action_list_) {
if (a->CheckEventTrigger(trigger)) {
action_queue_.push(a);
}
}
}
void ActionManager::QueuePropertyTrigger(const std::string& name,
const std::string& value)
{
for (const auto& a : action_list_) {
if (a->CheckPropertyTrigger(name, value)) {
action_queue_.push(a);
}
}
}
void ActionManager::QueueAllPropertyTriggers()
{
QueuePropertyTrigger("", "");
}
void ActionManager::QueueBuiltinAction(int (*func)(int nargs, char** args),
const std::string& name)
{
Action* act = new Action();
std::vector<std::string> name_vector{name};
if (!act->InitSingleTrigger(name)) {
return;
}
act->AddCommand(func, name_vector);
action_queue_.push(act);
}
void ActionManager::ExecuteOneCommand() {
if (action_queue_.empty()) {
return;
}
Action* action = action_queue_.front();
if (!action->NumCommands()) {
action_queue_.pop();
return;
}
if (cur_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
INFO("processing action %p (%s)\n", action, trigger_name.c_str());
}
action->ExecuteOneCommand(cur_command_++);
if (cur_command_ == action->NumCommands()) {
cur_command_ = 0;
action_queue_.pop();
}
}
bool ActionManager::HasMoreCommands() const
{
return !action_queue_.empty();
}
Action* ActionManager::AddNewAction(const std::vector<std::string>& triggers,
std::string* err)
{
if (triggers.size() < 1) {
*err = "actions must have a trigger\n";
return nullptr;
}
Action* act = new Action();
if (!act->InitTriggers(triggers, err)) {
return nullptr;
}
auto old_act_it =
std::find_if(action_list_.begin(), action_list_.end(),
[&act] (Action* a) { return act->TriggersEqual(*a); });
if (old_act_it != action_list_.end()) {
delete act;
return *old_act_it;
}
action_list_.push_back(act);
return act;
}
void ActionManager::DumpState() const
{
for (const auto& a : action_list_) {
a->DumpState();
}
INFO("\n");
}