blob: dd366d3ae8a9f2ff89f8a8b54eacd152c65ac21f [file] [log] [blame]
Tom Cherryfa0c21c2015-07-23 17:53:11 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "action.h"
18
19#include <errno.h>
20
21#include <base/strings.h>
22#include <base/stringprintf.h>
23
24#include "error.h"
25#include "init_parser.h"
26#include "log.h"
27#include "property_service.h"
28#include "util.h"
29
30class Action::Command
31{
32public:
Tom Cherry96f67312015-07-30 13:52:55 -070033 Command(int (*f)(const std::vector<std::string>& args),
Tom Cherryfa0c21c2015-07-23 17:53:11 -070034 const std::vector<std::string>& args,
35 const std::string& filename,
36 int line);
37
38 int InvokeFunc() const;
39 std::string BuildCommandString() const;
40 std::string BuildSourceString() const;
41
42private:
Tom Cherry96f67312015-07-30 13:52:55 -070043 int (*func_)(const std::vector<std::string>& args);
Tom Cherryfa0c21c2015-07-23 17:53:11 -070044 const std::vector<std::string> args_;
45 const std::string filename_;
46 int line_;
47};
48
Tom Cherry96f67312015-07-30 13:52:55 -070049Action::Command::Command(int (*f)(const std::vector<std::string>& args),
Tom Cherryfa0c21c2015-07-23 17:53:11 -070050 const std::vector<std::string>& args,
51 const std::string& filename,
52 int line) :
53 func_(f), args_(args), filename_(filename), line_(line)
54{
55}
56
57int Action::Command::InvokeFunc() const
58{
Tom Cherry96f67312015-07-30 13:52:55 -070059 std::vector<std::string> expanded_args;
60 expanded_args.resize(args_.size());
61 expanded_args[0] = args_[0];
Tom Cherryfa0c21c2015-07-23 17:53:11 -070062 for (std::size_t i = 1; i < args_.size(); ++i) {
Tom Cherry96f67312015-07-30 13:52:55 -070063 if (expand_props(args_[i], &expanded_args[i]) == -1) {
Tom Cherryfa0c21c2015-07-23 17:53:11 -070064 ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
65 return -EINVAL;
66 }
67 }
68
Tom Cherry96f67312015-07-30 13:52:55 -070069 return func_(expanded_args);
Tom Cherryfa0c21c2015-07-23 17:53:11 -070070}
71
72std::string Action::Command::BuildCommandString() const
73{
74 return android::base::Join(args_, ' ');
75}
76
77std::string Action::Command::BuildSourceString() const
78{
79 if (!filename_.empty()) {
80 return android::base::StringPrintf(" (%s:%d)", filename_.c_str(), line_);
81 } else {
82 return std::string();
83 }
84}
85
86Action::Action()
87{
88}
89
Tom Cherry96f67312015-07-30 13:52:55 -070090void Action::AddCommand(int (*f)(const std::vector<std::string>& args),
Tom Cherryfa0c21c2015-07-23 17:53:11 -070091 const std::vector<std::string>& args,
92 const std::string& filename, int line)
93{
94 Action::Command* cmd = new Action::Command(f, args, filename, line);
95 commands_.push_back(cmd);
96}
97
98std::size_t Action::NumCommands() const
99{
100 return commands_.size();
101}
102
103void Action::ExecuteOneCommand(std::size_t command) const
104{
105 ExecuteCommand(*commands_[command]);
106}
107
108void Action::ExecuteAllCommands() const
109{
110 for (const auto& c : commands_) {
111 ExecuteCommand(*c);
112 }
113}
114
115void Action::ExecuteCommand(const Command& command) const
116{
117 Timer t;
118 int result = command.InvokeFunc();
119
120 if (klog_get_level() >= KLOG_INFO_LEVEL) {
121 std::string trigger_name = BuildTriggersString();
122 std::string cmd_str = command.BuildCommandString();
123 std::string source = command.BuildSourceString();
124
125 INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
126 cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
127 result, t.duration());
128 }
129}
130
131bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err)
132{
133 const static std::string prop_str("property:");
134 std::string prop_name(trigger.substr(prop_str.length()));
135 size_t equal_pos = prop_name.find('=');
136 if (equal_pos == std::string::npos) {
137 *err = "property trigger found without matching '='";
138 return false;
139 }
140
141 std::string prop_value(prop_name.substr(equal_pos + 1));
142 prop_name.erase(equal_pos);
143
144 auto res = property_triggers_.emplace(prop_name, prop_value);
145 if (res.second == false) {
146 *err = "multiple property triggers found for same property";
147 return false;
148 }
149 return true;
150}
151
152bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err)
153{
154 const static std::string prop_str("property:");
155 for (std::size_t i = 0; i < args.size(); ++i) {
156 if (i % 2) {
Tom Cherrycb716f92015-08-11 12:34:22 -0700157 if (args[i] != "&&") {
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700158 *err = "&& is the only symbol allowed to concatenate actions";
159 return false;
160 } else {
161 continue;
162 }
163 }
164
165 if (!args[i].compare(0, prop_str.length(), prop_str)) {
166 if (!ParsePropertyTrigger(args[i], err)) {
167 return false;
168 }
169 } else {
170 if (!event_trigger_.empty()) {
171 *err = "multiple event triggers are not allowed";
172 return false;
173 }
174
175 event_trigger_ = args[i];
176 }
177 }
178
179 return true;
180}
181
182bool Action::InitSingleTrigger(const std::string& trigger)
183{
184 std::vector<std::string> name_vector{trigger};
185 std::string err;
186 return InitTriggers(name_vector, &err);
187}
188
189bool Action::CheckPropertyTriggers(const std::string& name,
190 const std::string& value) const
191{
Tom Cherrycb716f92015-08-11 12:34:22 -0700192 bool found = name.empty();
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700193 if (property_triggers_.empty()) {
194 return true;
195 }
196
197 for (const auto& t : property_triggers_) {
Tom Cherrycb716f92015-08-11 12:34:22 -0700198 const auto& trigger_name = t.first;
199 const auto& trigger_value = t.second;
200 if (trigger_name == name) {
201 if (trigger_value != "*" && trigger_value != value) {
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700202 return false;
203 } else {
204 found = true;
205 }
206 } else {
Tom Cherrycb716f92015-08-11 12:34:22 -0700207 std::string prop_val = property_get(trigger_name.c_str());
208 if (prop_val.empty() || (trigger_value != "*" &&
209 trigger_value != prop_val)) {
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700210 return false;
211 }
212 }
213 }
214 return found;
215}
216
217bool Action::CheckEventTrigger(const std::string& trigger) const
218{
219 return !event_trigger_.empty() &&
Tom Cherrycb716f92015-08-11 12:34:22 -0700220 trigger == event_trigger_ &&
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700221 CheckPropertyTriggers();
222}
223
224bool Action::CheckPropertyTrigger(const std::string& name,
225 const std::string& value) const
226{
227 return event_trigger_.empty() && CheckPropertyTriggers(name, value);
228}
229
230bool Action::TriggersEqual(const class Action& other) const
231{
Tom Cherrycb716f92015-08-11 12:34:22 -0700232 return property_triggers_ == other.property_triggers_ &&
233 event_trigger_ == other.event_trigger_;
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700234}
235
236std::string Action::BuildTriggersString() const
237{
238 std::string result;
239
240 for (const auto& t : property_triggers_) {
241 result += t.first;
242 result += '=';
243 result += t.second;
244 result += ' ';
245 }
246 if (!event_trigger_.empty()) {
247 result += event_trigger_;
248 result += ' ';
249 }
250 result.pop_back();
251 return result;
252}
253
254void Action::DumpState() const
255{
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700256 std::string trigger_name = BuildTriggersString();
Tom Cherrycb716f92015-08-11 12:34:22 -0700257 INFO("on %s\n", trigger_name.c_str());
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700258
259 for (const auto& c : commands_) {
260 std::string cmd_str = c->BuildCommandString();
Tom Cherrycb716f92015-08-11 12:34:22 -0700261 INFO(" %s\n", cmd_str.c_str());
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700262 }
263 INFO("\n");
264}
265
Tom Cherrycb716f92015-08-11 12:34:22 -0700266
267class EventTrigger : public Trigger {
268public:
269 EventTrigger(const std::string& trigger) : trigger_(trigger) {
270 }
271 bool CheckTriggers(const Action* action) override {
272 return action->CheckEventTrigger(trigger_);
273 }
274private:
275 std::string trigger_;
276};
277
278class PropertyTrigger : public Trigger {
279public:
280 PropertyTrigger(const std::string& name, const std::string& value)
281 : name_(name), value_(value) {
282 }
283 bool CheckTriggers(const Action* action) override {
284 return action->CheckPropertyTrigger(name_, value_);
285 }
286private:
287 std::string name_;
288 std::string value_;
289};
290
291class BuiltinTrigger : public Trigger {
292public:
293 BuiltinTrigger(Action* action) : action_(action) {
294 }
295 bool CheckTriggers(const Action* action) override {
296 return action == action_;
297 }
298private:
299 Action* action_;
300};
301
302ActionManager::ActionManager() : current_command_(0)
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700303{
304}
305
306ActionManager& ActionManager::GetInstance() {
307 static ActionManager instance;
308 return instance;
309}
310
311void ActionManager::QueueEventTrigger(const std::string& trigger)
312{
Tom Cherrycb716f92015-08-11 12:34:22 -0700313 trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700314}
315
316void ActionManager::QueuePropertyTrigger(const std::string& name,
317 const std::string& value)
318{
Tom Cherrycb716f92015-08-11 12:34:22 -0700319 trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700320}
321
322void ActionManager::QueueAllPropertyTriggers()
323{
324 QueuePropertyTrigger("", "");
325}
326
Tom Cherry96f67312015-07-30 13:52:55 -0700327void ActionManager::QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700328 const std::string& name)
329{
330 Action* act = new Action();
331 std::vector<std::string> name_vector{name};
332
333 if (!act->InitSingleTrigger(name)) {
334 return;
335 }
336
337 act->AddCommand(func, name_vector);
338
Tom Cherrycb716f92015-08-11 12:34:22 -0700339 actions_.push_back(act);
340 trigger_queue_.push(std::make_unique<BuiltinTrigger>(act));
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700341}
342
343void ActionManager::ExecuteOneCommand() {
Tom Cherrycb716f92015-08-11 12:34:22 -0700344 while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
345 std::copy_if(actions_.begin(), actions_.end(),
346 std::back_inserter(current_executing_actions_),
347 [this] (Action* act) {
348 return trigger_queue_.front()->CheckTriggers(act);
349 });
350 trigger_queue_.pop();
351 }
352
353 if (current_executing_actions_.empty()) {
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700354 return;
355 }
356
Tom Cherrycb716f92015-08-11 12:34:22 -0700357 Action* action = current_executing_actions_.back();
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700358 if (!action->NumCommands()) {
Tom Cherrycb716f92015-08-11 12:34:22 -0700359 current_executing_actions_.pop_back();
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700360 return;
361 }
362
Tom Cherrycb716f92015-08-11 12:34:22 -0700363 if (current_command_ == 0) {
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700364 std::string trigger_name = action->BuildTriggersString();
365 INFO("processing action %p (%s)\n", action, trigger_name.c_str());
366 }
367
Tom Cherrycb716f92015-08-11 12:34:22 -0700368 action->ExecuteOneCommand(current_command_++);
369 if (current_command_ == action->NumCommands()) {
370 current_command_ = 0;
371 current_executing_actions_.pop_back();
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700372 }
373}
374
375bool ActionManager::HasMoreCommands() const
376{
Tom Cherrycb716f92015-08-11 12:34:22 -0700377 return !current_executing_actions_.empty() || !trigger_queue_.empty();
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700378}
379
380Action* ActionManager::AddNewAction(const std::vector<std::string>& triggers,
381 std::string* err)
382{
383 if (triggers.size() < 1) {
384 *err = "actions must have a trigger\n";
385 return nullptr;
386 }
387
388 Action* act = new Action();
389 if (!act->InitTriggers(triggers, err)) {
390 return nullptr;
391 }
392
393 auto old_act_it =
Tom Cherrycb716f92015-08-11 12:34:22 -0700394 std::find_if(actions_.begin(), actions_.end(),
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700395 [&act] (Action* a) { return act->TriggersEqual(*a); });
396
Tom Cherrycb716f92015-08-11 12:34:22 -0700397 if (old_act_it != actions_.end()) {
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700398 delete act;
399 return *old_act_it;
400 }
401
Tom Cherrycb716f92015-08-11 12:34:22 -0700402 actions_.push_back(act);
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700403 return act;
404}
405
406void ActionManager::DumpState() const
407{
Tom Cherrycb716f92015-08-11 12:34:22 -0700408 for (const auto& a : actions_) {
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700409 a->DumpState();
410 }
411 INFO("\n");
412}