action.cpp revision 331cf2fb7c16b5b25064f8d2f00284105a9b413f
1fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry/* 2fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * Copyright (C) 2015 The Android Open Source Project 3fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * 4fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * Licensed under the Apache License, Version 2.0 (the "License"); 5fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * you may not use this file except in compliance with the License. 6fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * You may obtain a copy of the License at 7fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * 8fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * http://www.apache.org/licenses/LICENSE-2.0 9fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * 10fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * Unless required by applicable law or agreed to in writing, software 11fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * distributed under the License is distributed on an "AS IS" BASIS, 12fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * See the License for the specific language governing permissions and 14fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry * limitations under the License. 15fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry */ 16fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 17fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "action.h" 18fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 19fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include <errno.h> 20fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 214f71319df011d796a60a43fc1bc68e16fbf7d321Elliott Hughes#include <android-base/strings.h> 224f71319df011d796a60a43fc1bc68e16fbf7d321Elliott Hughes#include <android-base/stringprintf.h> 23fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 24b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry#include "builtins.h" 25fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "error.h" 26fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "init_parser.h" 27fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "log.h" 28fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "property_service.h" 29fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "util.h" 30fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 31b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryusing android::base::Join; 32b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryusing android::base::StringPrintf; 33fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 34b7349902a945903f9e36a569051f5131beb0bc24Tom CherryCommand::Command(BuiltinFunction f, const std::vector<std::string>& args, 35b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string& filename, int line) 36b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry : func_(f), args_(args), filename_(filename), line_(line) { 37fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 38fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 39b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryint Command::InvokeFunc() const { 4096f67316a22bc9236aed70b198e91a5406389e5bTom Cherry std::vector<std::string> expanded_args; 4196f67316a22bc9236aed70b198e91a5406389e5bTom Cherry expanded_args.resize(args_.size()); 4296f67316a22bc9236aed70b198e91a5406389e5bTom Cherry expanded_args[0] = args_[0]; 43fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry for (std::size_t i = 1; i < args_.size(); ++i) { 44b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (!expand_props(args_[i], &expanded_args[i])) { 45f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'"; 46fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return -EINVAL; 47fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 48fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 49fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 5096f67316a22bc9236aed70b198e91a5406389e5bTom Cherry return func_(expanded_args); 51fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 52fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 53b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Command::BuildCommandString() const { 54b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return Join(args_, ' '); 55fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 56fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 57b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Command::BuildSourceString() const { 58fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry if (!filename_.empty()) { 59b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return StringPrintf(" (%s:%d)", filename_.c_str(), line_); 60fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } else { 61fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return std::string(); 62fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 63fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 64fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 65b7349902a945903f9e36a569051f5131beb0bc24Tom CherryAction::Action(bool oneshot) : oneshot_(oneshot) { 66fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 67fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 68b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryconst KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr; 69b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 70b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::AddCommand(const std::vector<std::string>& args, 71b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string& filename, int line, std::string* err) { 72b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (!function_map_) { 73b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry *err = "no function map available"; 74b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return false; 75b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry } 76b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 77b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (args.empty()) { 78b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry *err = "command needed, but not provided"; 79b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return false; 80b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry } 81b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 82b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry auto function = function_map_->FindFunction(args[0], args.size() - 1, err); 83b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (!function) { 84b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return false; 85b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry } 86b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 87b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry AddCommand(function, args, filename, line); 88b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return true; 89b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry} 90b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 91b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::AddCommand(BuiltinFunction f, 92fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry const std::vector<std::string>& args, 93b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string& filename, int line) { 94b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry commands_.emplace_back(f, args, filename, line); 95b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry} 96b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 97b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::CombineAction(const Action& action) { 98b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry for (const auto& c : action.commands_) { 99b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry commands_.emplace_back(c); 100b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry } 101fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 102fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 103b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::size_t Action::NumCommands() const { 104fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return commands_.size(); 105fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 106fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 107b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteOneCommand(std::size_t command) const { 108d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang // We need a copy here since some Command execution may result in 109d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang // changing commands_ vector by importing .rc files through parser 110d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang Command cmd = commands_[command]; 111d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang ExecuteCommand(cmd); 112fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 113fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 114b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteAllCommands() const { 115fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry for (const auto& c : commands_) { 116b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry ExecuteCommand(c); 117fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 118fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 119fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 120b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteCommand(const Command& command) const { 121fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry Timer t; 122fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry int result = command.InvokeFunc(); 123fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 124331cf2fb7c16b5b25064f8d2f00284105a9b413fElliott Hughes double duration_ms = t.duration_s() * 1000; 1258b1d526a72c1e6705b03f4b3267ee02fe84ce765Wei Wang // Any action longer than 50ms will be warned to user as slow operation 1268b1d526a72c1e6705b03f4b3267ee02fe84ce765Wei Wang if (duration_ms > 50.0 || 1278b1d526a72c1e6705b03f4b3267ee02fe84ce765Wei Wang android::base::GetMinimumLogSeverity() <= android::base::DEBUG) { 128fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::string trigger_name = BuildTriggersString(); 129fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::string cmd_str = command.BuildCommandString(); 130fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::string source = command.BuildSourceString(); 131fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 132f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << source 1338b1d526a72c1e6705b03f4b3267ee02fe84ce765Wei Wang << " returned " << result << " took " << duration_ms << "ms."; 134fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 135fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 136fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 137b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) { 138fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry const static std::string prop_str("property:"); 139fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::string prop_name(trigger.substr(prop_str.length())); 140fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry size_t equal_pos = prop_name.find('='); 141fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry if (equal_pos == std::string::npos) { 142fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry *err = "property trigger found without matching '='"; 143fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return false; 144fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 145fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 146fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::string prop_value(prop_name.substr(equal_pos + 1)); 147fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry prop_name.erase(equal_pos); 148fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 149fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry auto res = property_triggers_.emplace(prop_name, prop_value); 150fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry if (res.second == false) { 151fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry *err = "multiple property triggers found for same property"; 152fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return false; 153fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 154fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return true; 155fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 156fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 157b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) { 158fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry const static std::string prop_str("property:"); 159fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry for (std::size_t i = 0; i < args.size(); ++i) { 16093df4e18a255262595acb862ab870e9fed721fb8Wei Wang if (args[i].empty()) { 16193df4e18a255262595acb862ab870e9fed721fb8Wei Wang *err = "empty trigger is not valid"; 16293df4e18a255262595acb862ab870e9fed721fb8Wei Wang return false; 16393df4e18a255262595acb862ab870e9fed721fb8Wei Wang } 16493df4e18a255262595acb862ab870e9fed721fb8Wei Wang 165fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry if (i % 2) { 166cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry if (args[i] != "&&") { 167fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry *err = "&& is the only symbol allowed to concatenate actions"; 168fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return false; 169fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } else { 170fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry continue; 171fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 172fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 173fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 174fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry if (!args[i].compare(0, prop_str.length(), prop_str)) { 175fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry if (!ParsePropertyTrigger(args[i], err)) { 176fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return false; 177fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 178fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } else { 179fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry if (!event_trigger_.empty()) { 180fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry *err = "multiple event triggers are not allowed"; 181fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return false; 182fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 183fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 184fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry event_trigger_ = args[i]; 185fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 186fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 187fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 188fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return true; 189fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 190fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 191b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitSingleTrigger(const std::string& trigger) { 192fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::vector<std::string> name_vector{trigger}; 193fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::string err; 19493df4e18a255262595acb862ab870e9fed721fb8Wei Wang bool ret = InitTriggers(name_vector, &err); 19593df4e18a255262595acb862ab870e9fed721fb8Wei Wang if (!ret) { 19693df4e18a255262595acb862ab870e9fed721fb8Wei Wang LOG(ERROR) << "InitSingleTrigger failed due to: " << err; 19793df4e18a255262595acb862ab870e9fed721fb8Wei Wang } 19893df4e18a255262595acb862ab870e9fed721fb8Wei Wang return ret; 199fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 200fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 201b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// This function checks that all property triggers are satisfied, that is 202b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// for each (name, value) in property_triggers_, check that the current 203b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// value of the property 'name' == value. 204b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// 205b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// It takes an optional (name, value) pair, which if provided must 206b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// be present in property_triggers_; it skips the check of the current 207b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// property value for this pair. 208fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherrybool Action::CheckPropertyTriggers(const std::string& name, 209b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string& value) const { 210fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry if (property_triggers_.empty()) { 211fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return true; 212fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 213fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 214b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry bool found = name.empty(); 215fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry for (const auto& t : property_triggers_) { 216cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry const auto& trigger_name = t.first; 217cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry const auto& trigger_value = t.second; 218cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry if (trigger_name == name) { 219cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry if (trigger_value != "*" && trigger_value != value) { 220fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return false; 221fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } else { 222fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry found = true; 223fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 224fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } else { 225cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry std::string prop_val = property_get(trigger_name.c_str()); 226cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry if (prop_val.empty() || (trigger_value != "*" && 227cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry trigger_value != prop_val)) { 228fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return false; 229fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 230fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 231fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 232fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return found; 233fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 234fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 235b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::CheckEventTrigger(const std::string& trigger) const { 236fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return !event_trigger_.empty() && 237cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry trigger == event_trigger_ && 238fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry CheckPropertyTriggers(); 239fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 240fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 241fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherrybool Action::CheckPropertyTrigger(const std::string& name, 242b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string& value) const { 243fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return event_trigger_.empty() && CheckPropertyTriggers(name, value); 244fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 245fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 246b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::TriggersEqual(const Action& other) const { 247cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry return property_triggers_ == other.property_triggers_ && 248cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry event_trigger_ == other.event_trigger_; 249fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 250fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 251b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Action::BuildTriggersString() const { 252fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::string result; 253fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 254fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry for (const auto& t : property_triggers_) { 255fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry result += t.first; 256fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry result += '='; 257fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry result += t.second; 258fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry result += ' '; 259fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 260fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry if (!event_trigger_.empty()) { 261fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry result += event_trigger_; 262fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry result += ' '; 263fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 26493df4e18a255262595acb862ab870e9fed721fb8Wei Wang result.pop_back(); 265fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return result; 266fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 267fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 268b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::DumpState() const { 269fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::string trigger_name = BuildTriggersString(); 270f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes LOG(INFO) << "on " << trigger_name; 271fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 272fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry for (const auto& c : commands_) { 273b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry std::string cmd_str = c.BuildCommandString(); 274f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes LOG(INFO) << " %s" << cmd_str; 275fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 276fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 277fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 278cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass EventTrigger : public Trigger { 279cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic: 2801c563d96f000876d77b2d33fbfb03c241bc503e1Chih-Hung Hsieh explicit EventTrigger(const std::string& trigger) : trigger_(trigger) { 281cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry } 282b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry bool CheckTriggers(const Action& action) const override { 283b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return action.CheckEventTrigger(trigger_); 284cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry } 285cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate: 286b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string trigger_; 287cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry}; 288cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry 289cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass PropertyTrigger : public Trigger { 290cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic: 291cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry PropertyTrigger(const std::string& name, const std::string& value) 292cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry : name_(name), value_(value) { 293cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry } 294b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry bool CheckTriggers(const Action& action) const override { 295b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return action.CheckPropertyTrigger(name_, value_); 296cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry } 297cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate: 298b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string name_; 299b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string value_; 300cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry}; 301cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry 302cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass BuiltinTrigger : public Trigger { 303cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic: 3041c563d96f000876d77b2d33fbfb03c241bc503e1Chih-Hung Hsieh explicit BuiltinTrigger(Action* action) : action_(action) { 305cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry } 306b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry bool CheckTriggers(const Action& action) const override { 307b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return action_ == &action; 308cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry } 309cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate: 310b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const Action* action_; 311cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry}; 312cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry 313b7349902a945903f9e36a569051f5131beb0bc24Tom CherryActionManager::ActionManager() : current_command_(0) { 314fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 315fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 316fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom CherryActionManager& ActionManager::GetInstance() { 317fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry static ActionManager instance; 318fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return instance; 319fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 320fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 321b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::AddAction(std::unique_ptr<Action> action) { 322b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry auto old_action_it = 323b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry std::find_if(actions_.begin(), actions_.end(), 324b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry [&action] (std::unique_ptr<Action>& a) { 325b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return action->TriggersEqual(*a); 326b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry }); 327b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 328b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (old_action_it != actions_.end()) { 329b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry (*old_action_it)->CombineAction(*action); 330b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry } else { 331b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry actions_.emplace_back(std::move(action)); 332b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry } 333b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry} 334b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 335b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueEventTrigger(const std::string& trigger) { 336cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry trigger_queue_.push(std::make_unique<EventTrigger>(trigger)); 337fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 338fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 339fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherryvoid ActionManager::QueuePropertyTrigger(const std::string& name, 340b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string& value) { 341cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value)); 342fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 343fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 344b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueAllPropertyTriggers() { 345fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry QueuePropertyTrigger("", ""); 346fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 347fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 348b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueBuiltinAction(BuiltinFunction func, 349b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string& name) { 350b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry auto action = std::make_unique<Action>(true); 351fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::vector<std::string> name_vector{name}; 352fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 353b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (!action->InitSingleTrigger(name)) { 354fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return; 355fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 356fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 357b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry action->AddCommand(func, name_vector); 358fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 359b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get())); 360b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry actions_.emplace_back(std::move(action)); 361fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 362fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 363fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherryvoid ActionManager::ExecuteOneCommand() { 364b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry // Loop through the trigger queue until we have an action to execute 365cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry while (current_executing_actions_.empty() && !trigger_queue_.empty()) { 366b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry for (const auto& action : actions_) { 367b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (trigger_queue_.front()->CheckTriggers(*action)) { 368b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry current_executing_actions_.emplace(action.get()); 369b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry } 370b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry } 371cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry trigger_queue_.pop(); 372cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry } 373cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry 374cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry if (current_executing_actions_.empty()) { 375fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry return; 376fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 377fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 378b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry auto action = current_executing_actions_.front(); 379fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 380cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry if (current_command_ == 0) { 381fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry std::string trigger_name = action->BuildTriggersString(); 382f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes LOG(INFO) << "processing action (" << trigger_name << ")"; 383fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 384fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 385b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry action->ExecuteOneCommand(current_command_); 386b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 387b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry // If this was the last command in the current action, then remove 388b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry // the action from the executing list. 389b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry // If this action was oneshot, then also remove it from actions_. 390b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry ++current_command_; 391cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry if (current_command_ == action->NumCommands()) { 392b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry current_executing_actions_.pop(); 393cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry current_command_ = 0; 394b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (action->oneshot()) { 395b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry auto eraser = [&action] (std::unique_ptr<Action>& a) { 396b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return a.get() == action; 397b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry }; 398b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser)); 399b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry } 400fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 401fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 402fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 403b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionManager::HasMoreCommands() const { 404cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry return !current_executing_actions_.empty() || !trigger_queue_.empty(); 405fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 406fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 407b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::DumpState() const { 408b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry for (const auto& a : actions_) { 409b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry a->DumpState(); 410fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 411b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry} 412fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 413b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionParser::ParseSection(const std::vector<std::string>& args, 414b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry std::string* err) { 415b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry std::vector<std::string> triggers(args.begin() + 1, args.end()); 416b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (triggers.size() < 1) { 417b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry *err = "actions must have a trigger"; 418b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return false; 419fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 420fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 421b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry auto action = std::make_unique<Action>(false); 422b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (!action->InitTriggers(triggers, err)) { 423b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return false; 424fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 425fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 426b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry action_ = std::move(action); 427b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return true; 428fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 429fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry 430b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionParser::ParseLineSection(const std::vector<std::string>& args, 431b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry const std::string& filename, int line, 432b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry std::string* err) const { 433b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry return action_ ? action_->AddCommand(args, filename, line, err) : false; 434b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry} 435b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry 436b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionParser::EndSection() { 437b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry if (action_ && action_->NumCommands() > 0) { 438b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry ActionManager::GetInstance().AddAction(std::move(action_)); 439fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry } 440fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry} 441