• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
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 <android-base/strings.h>
22  #include <android-base/stringprintf.h>
23  
24  #include "builtins.h"
25  #include "error.h"
26  #include "init_parser.h"
27  #include "log.h"
28  #include "property_service.h"
29  #include "util.h"
30  
31  using android::base::Join;
32  using android::base::StringPrintf;
33  
Command(BuiltinFunction f,const std::vector<std::string> & args,const std::string & filename,int line)34  Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
35                   const std::string& filename, int line)
36      : func_(f), args_(args), filename_(filename), line_(line) {
37  }
38  
InvokeFunc() const39  int Command::InvokeFunc() const {
40      std::vector<std::string> expanded_args;
41      expanded_args.resize(args_.size());
42      expanded_args[0] = args_[0];
43      for (std::size_t i = 1; i < args_.size(); ++i) {
44          if (!expand_props(args_[i], &expanded_args[i])) {
45              ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
46              return -EINVAL;
47          }
48      }
49  
50      return func_(expanded_args);
51  }
52  
BuildCommandString() const53  std::string Command::BuildCommandString() const {
54      return Join(args_, ' ');
55  }
56  
BuildSourceString() const57  std::string Command::BuildSourceString() const {
58      if (!filename_.empty()) {
59          return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
60      } else {
61          return std::string();
62      }
63  }
64  
Action(bool oneshot)65  Action::Action(bool oneshot) : oneshot_(oneshot) {
66  }
67  
68  const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
69  
AddCommand(const std::vector<std::string> & args,const std::string & filename,int line,std::string * err)70  bool Action::AddCommand(const std::vector<std::string>& args,
71                          const std::string& filename, int line, std::string* err) {
72      if (!function_map_) {
73          *err = "no function map available";
74          return false;
75      }
76  
77      if (args.empty()) {
78          *err = "command needed, but not provided";
79          return false;
80      }
81  
82      auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
83      if (!function) {
84          return false;
85      }
86  
87      AddCommand(function, args, filename, line);
88      return true;
89  }
90  
AddCommand(BuiltinFunction f,const std::vector<std::string> & args,const std::string & filename,int line)91  void Action::AddCommand(BuiltinFunction f,
92                          const std::vector<std::string>& args,
93                          const std::string& filename, int line) {
94      commands_.emplace_back(f, args, filename, line);
95  }
96  
CombineAction(const Action & action)97  void Action::CombineAction(const Action& action) {
98      for (const auto& c : action.commands_) {
99          commands_.emplace_back(c);
100      }
101  }
102  
NumCommands() const103  std::size_t Action::NumCommands() const {
104      return commands_.size();
105  }
106  
ExecuteOneCommand(std::size_t command) const107  void Action::ExecuteOneCommand(std::size_t command) const {
108      ExecuteCommand(commands_[command]);
109  }
110  
ExecuteAllCommands() const111  void Action::ExecuteAllCommands() const {
112      for (const auto& c : commands_) {
113          ExecuteCommand(c);
114      }
115  }
116  
ExecuteCommand(const Command & command) const117  void Action::ExecuteCommand(const Command& command) const {
118      Timer t;
119      int result = command.InvokeFunc();
120  
121      if (klog_get_level() >= KLOG_INFO_LEVEL) {
122          std::string trigger_name = BuildTriggersString();
123          std::string cmd_str = command.BuildCommandString();
124          std::string source = command.BuildSourceString();
125  
126          INFO("Command '%s' action=%s%s returned %d took %.2fs\n",
127               cmd_str.c_str(), trigger_name.c_str(), source.c_str(),
128               result, t.duration());
129      }
130  }
131  
ParsePropertyTrigger(const std::string & trigger,std::string * err)132  bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
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  
InitTriggers(const std::vector<std::string> & args,std::string * err)152  bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
153      const static std::string prop_str("property:");
154      for (std::size_t i = 0; i < args.size(); ++i) {
155          if (i % 2) {
156              if (args[i] != "&&") {
157                  *err = "&& is the only symbol allowed to concatenate actions";
158                  return false;
159              } else {
160                  continue;
161              }
162          }
163  
164          if (!args[i].compare(0, prop_str.length(), prop_str)) {
165              if (!ParsePropertyTrigger(args[i], err)) {
166                  return false;
167              }
168          } else {
169              if (!event_trigger_.empty()) {
170                  *err = "multiple event triggers are not allowed";
171                  return false;
172              }
173  
174              event_trigger_ = args[i];
175          }
176      }
177  
178      return true;
179  }
180  
InitSingleTrigger(const std::string & trigger)181  bool Action::InitSingleTrigger(const std::string& trigger) {
182      std::vector<std::string> name_vector{trigger};
183      std::string err;
184      return InitTriggers(name_vector, &err);
185  }
186  
187  // This function checks that all property triggers are satisfied, that is
188  // for each (name, value) in property_triggers_, check that the current
189  // value of the property 'name' == value.
190  //
191  // It takes an optional (name, value) pair, which if provided must
192  // be present in property_triggers_; it skips the check of the current
193  // property value for this pair.
CheckPropertyTriggers(const std::string & name,const std::string & value) const194  bool Action::CheckPropertyTriggers(const std::string& name,
195                                     const std::string& value) const {
196      if (property_triggers_.empty()) {
197          return true;
198      }
199  
200      bool found = name.empty();
201      for (const auto& t : property_triggers_) {
202          const auto& trigger_name = t.first;
203          const auto& trigger_value = t.second;
204          if (trigger_name == name) {
205              if (trigger_value != "*" && trigger_value != value) {
206                  return false;
207              } else {
208                  found = true;
209              }
210          } else {
211              std::string prop_val = property_get(trigger_name.c_str());
212              if (prop_val.empty() || (trigger_value != "*" &&
213                                       trigger_value != prop_val)) {
214                  return false;
215              }
216          }
217      }
218      return found;
219  }
220  
CheckEventTrigger(const std::string & trigger) const221  bool Action::CheckEventTrigger(const std::string& trigger) const {
222      return !event_trigger_.empty() &&
223          trigger == event_trigger_ &&
224          CheckPropertyTriggers();
225  }
226  
CheckPropertyTrigger(const std::string & name,const std::string & value) const227  bool Action::CheckPropertyTrigger(const std::string& name,
228                                    const std::string& value) const {
229      return event_trigger_.empty() && CheckPropertyTriggers(name, value);
230  }
231  
TriggersEqual(const Action & other) const232  bool Action::TriggersEqual(const Action& other) const {
233      return property_triggers_ == other.property_triggers_ &&
234          event_trigger_ == other.event_trigger_;
235  }
236  
BuildTriggersString() const237  std::string Action::BuildTriggersString() const {
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  
DumpState() const254  void Action::DumpState() const {
255      std::string trigger_name = BuildTriggersString();
256      INFO("on %s\n", trigger_name.c_str());
257  
258      for (const auto& c : commands_) {
259          std::string cmd_str = c.BuildCommandString();
260          INFO(" %s\n", cmd_str.c_str());
261      }
262      INFO("\n");
263  }
264  
265  class EventTrigger : public Trigger {
266  public:
EventTrigger(const std::string & trigger)267      EventTrigger(const std::string& trigger) : trigger_(trigger) {
268      }
CheckTriggers(const Action & action) const269      bool CheckTriggers(const Action& action) const override {
270          return action.CheckEventTrigger(trigger_);
271      }
272  private:
273      const std::string trigger_;
274  };
275  
276  class PropertyTrigger : public Trigger {
277  public:
PropertyTrigger(const std::string & name,const std::string & value)278      PropertyTrigger(const std::string& name, const std::string& value)
279          : name_(name), value_(value) {
280      }
CheckTriggers(const Action & action) const281      bool CheckTriggers(const Action& action) const override {
282          return action.CheckPropertyTrigger(name_, value_);
283      }
284  private:
285      const std::string name_;
286      const std::string value_;
287  };
288  
289  class BuiltinTrigger : public Trigger {
290  public:
BuiltinTrigger(Action * action)291      BuiltinTrigger(Action* action) : action_(action) {
292      }
CheckTriggers(const Action & action) const293      bool CheckTriggers(const Action& action) const override {
294          return action_ == &action;
295      }
296  private:
297      const Action* action_;
298  };
299  
ActionManager()300  ActionManager::ActionManager() : current_command_(0) {
301  }
302  
GetInstance()303  ActionManager& ActionManager::GetInstance() {
304      static ActionManager instance;
305      return instance;
306  }
307  
AddAction(std::unique_ptr<Action> action)308  void ActionManager::AddAction(std::unique_ptr<Action> action) {
309      auto old_action_it =
310          std::find_if(actions_.begin(), actions_.end(),
311                       [&action] (std::unique_ptr<Action>& a) {
312                           return action->TriggersEqual(*a);
313                       });
314  
315      if (old_action_it != actions_.end()) {
316          (*old_action_it)->CombineAction(*action);
317      } else {
318          actions_.emplace_back(std::move(action));
319      }
320  }
321  
QueueEventTrigger(const std::string & trigger)322  void ActionManager::QueueEventTrigger(const std::string& trigger) {
323      trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
324  }
325  
QueuePropertyTrigger(const std::string & name,const std::string & value)326  void ActionManager::QueuePropertyTrigger(const std::string& name,
327                                           const std::string& value) {
328      trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
329  }
330  
QueueAllPropertyTriggers()331  void ActionManager::QueueAllPropertyTriggers() {
332      QueuePropertyTrigger("", "");
333  }
334  
QueueBuiltinAction(BuiltinFunction func,const std::string & name)335  void ActionManager::QueueBuiltinAction(BuiltinFunction func,
336                                         const std::string& name) {
337      auto action = std::make_unique<Action>(true);
338      std::vector<std::string> name_vector{name};
339  
340      if (!action->InitSingleTrigger(name)) {
341          return;
342      }
343  
344      action->AddCommand(func, name_vector);
345  
346      trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
347      actions_.emplace_back(std::move(action));
348  }
349  
ExecuteOneCommand()350  void ActionManager::ExecuteOneCommand() {
351      // Loop through the trigger queue until we have an action to execute
352      while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
353          for (const auto& action : actions_) {
354              if (trigger_queue_.front()->CheckTriggers(*action)) {
355                  current_executing_actions_.emplace(action.get());
356              }
357          }
358          trigger_queue_.pop();
359      }
360  
361      if (current_executing_actions_.empty()) {
362          return;
363      }
364  
365      auto action = current_executing_actions_.front();
366  
367      if (current_command_ == 0) {
368          std::string trigger_name = action->BuildTriggersString();
369          INFO("processing action (%s)\n", trigger_name.c_str());
370      }
371  
372      action->ExecuteOneCommand(current_command_);
373  
374      // If this was the last command in the current action, then remove
375      // the action from the executing list.
376      // If this action was oneshot, then also remove it from actions_.
377      ++current_command_;
378      if (current_command_ == action->NumCommands()) {
379          current_executing_actions_.pop();
380          current_command_ = 0;
381          if (action->oneshot()) {
382              auto eraser = [&action] (std::unique_ptr<Action>& a) {
383                  return a.get() == action;
384              };
385              actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
386          }
387      }
388  }
389  
HasMoreCommands() const390  bool ActionManager::HasMoreCommands() const {
391      return !current_executing_actions_.empty() || !trigger_queue_.empty();
392  }
393  
DumpState() const394  void ActionManager::DumpState() const {
395      for (const auto& a : actions_) {
396          a->DumpState();
397      }
398      INFO("\n");
399  }
400  
ParseSection(const std::vector<std::string> & args,std::string * err)401  bool ActionParser::ParseSection(const std::vector<std::string>& args,
402                                  std::string* err) {
403      std::vector<std::string> triggers(args.begin() + 1, args.end());
404      if (triggers.size() < 1) {
405          *err = "actions must have a trigger";
406          return false;
407      }
408  
409      auto action = std::make_unique<Action>(false);
410      if (!action->InitTriggers(triggers, err)) {
411          return false;
412      }
413  
414      action_ = std::move(action);
415      return true;
416  }
417  
ParseLineSection(const std::vector<std::string> & args,const std::string & filename,int line,std::string * err) const418  bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
419                                      const std::string& filename, int line,
420                                      std::string* err) const {
421      return action_ ? action_->AddCommand(args, filename, line, err) : false;
422  }
423  
EndSection()424  void ActionParser::EndSection() {
425      if (action_ && action_->NumCommands() > 0) {
426          ActionManager::GetInstance().AddAction(std::move(action_));
427      }
428  }
429