blob: 2eb809ec07d6797e5b2fad2f26970827151ba72f [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) {
157 if (args[i].compare("&&")) {
158 *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{
192 bool found = !name.compare("");
193 if (property_triggers_.empty()) {
194 return true;
195 }
196
197 for (const auto& t : property_triggers_) {
198 if (!t.first.compare(name)) {
199 if (t.second.compare("*") &&
200 t.second.compare(value)) {
201 return false;
202 } else {
203 found = true;
204 }
205 } else {
206 std::string prop_val = property_get(t.first.c_str());
207 if (prop_val.empty() ||
208 (t.second.compare("*") &&
209 t.second.compare(prop_val))) {
210 return false;
211 }
212 }
213 }
214 return found;
215}
216
217bool Action::CheckEventTrigger(const std::string& trigger) const
218{
219 return !event_trigger_.empty() &&
220 !trigger.compare(event_trigger_) &&
221 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{
232 return property_triggers_.size() == other.property_triggers_.size() &&
233 std::equal(property_triggers_.begin(), property_triggers_.end(),
234 other.property_triggers_.begin()) &&
235 !event_trigger_.compare(other.event_trigger_);
236}
237
238std::string Action::BuildTriggersString() const
239{
240 std::string result;
241
242 for (const auto& t : property_triggers_) {
243 result += t.first;
244 result += '=';
245 result += t.second;
246 result += ' ';
247 }
248 if (!event_trigger_.empty()) {
249 result += event_trigger_;
250 result += ' ';
251 }
252 result.pop_back();
253 return result;
254}
255
256void Action::DumpState() const
257{
258 INFO("on ");
259 std::string trigger_name = BuildTriggersString();
260 INFO("%s", trigger_name.c_str());
261 INFO("\n");
262
263 for (const auto& c : commands_) {
264 std::string cmd_str = c->BuildCommandString();
265 INFO(" %s", cmd_str.c_str());
266 }
267 INFO("\n");
268}
269
270ActionManager::ActionManager() : cur_command_(0)
271{
272}
273
274ActionManager& ActionManager::GetInstance() {
275 static ActionManager instance;
276 return instance;
277}
278
279void ActionManager::QueueEventTrigger(const std::string& trigger)
280{
281 for (const auto& a : action_list_) {
282 if (a->CheckEventTrigger(trigger)) {
283 action_queue_.push(a);
284 }
285 }
286}
287
288void ActionManager::QueuePropertyTrigger(const std::string& name,
289 const std::string& value)
290{
291 for (const auto& a : action_list_) {
292 if (a->CheckPropertyTrigger(name, value)) {
293 action_queue_.push(a);
294 }
295 }
296}
297
298void ActionManager::QueueAllPropertyTriggers()
299{
300 QueuePropertyTrigger("", "");
301}
302
Tom Cherry96f67312015-07-30 13:52:55 -0700303void ActionManager::QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
Tom Cherryfa0c21c2015-07-23 17:53:11 -0700304 const std::string& name)
305{
306 Action* act = new Action();
307 std::vector<std::string> name_vector{name};
308
309 if (!act->InitSingleTrigger(name)) {
310 return;
311 }
312
313 act->AddCommand(func, name_vector);
314
315 action_queue_.push(act);
316}
317
318void ActionManager::ExecuteOneCommand() {
319 if (action_queue_.empty()) {
320 return;
321 }
322
323 Action* action = action_queue_.front();
324 if (!action->NumCommands()) {
325 action_queue_.pop();
326 return;
327 }
328
329 if (cur_command_ == 0) {
330 std::string trigger_name = action->BuildTriggersString();
331 INFO("processing action %p (%s)\n", action, trigger_name.c_str());
332 }
333
334 action->ExecuteOneCommand(cur_command_++);
335 if (cur_command_ == action->NumCommands()) {
336 cur_command_ = 0;
337 action_queue_.pop();
338 }
339}
340
341bool ActionManager::HasMoreCommands() const
342{
343 return !action_queue_.empty();
344}
345
346Action* ActionManager::AddNewAction(const std::vector<std::string>& triggers,
347 std::string* err)
348{
349 if (triggers.size() < 1) {
350 *err = "actions must have a trigger\n";
351 return nullptr;
352 }
353
354 Action* act = new Action();
355 if (!act->InitTriggers(triggers, err)) {
356 return nullptr;
357 }
358
359 auto old_act_it =
360 std::find_if(action_list_.begin(), action_list_.end(),
361 [&act] (Action* a) { return act->TriggersEqual(*a); });
362
363 if (old_act_it != action_list_.end()) {
364 delete act;
365 return *old_act_it;
366 }
367
368 action_list_.push_back(act);
369 return act;
370}
371
372void ActionManager::DumpState() const
373{
374 for (const auto& a : action_list_) {
375 a->DumpState();
376 }
377 INFO("\n");
378}