action.cpp revision ccf23537eeacfa47e5f18dd3b75089886d177c1b
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
21ccf23537eeacfa47e5f18dd3b75089886d177c1bTom Cherry#include <android-base/properties.h>
224f71319df011d796a60a43fc1bc68e16fbf7d321Elliott Hughes#include <android-base/stringprintf.h>
23ccf23537eeacfa47e5f18dd3b75089886d177c1bTom Cherry#include <android-base/strings.h>
24fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
25b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry#include "builtins.h"
26fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "error.h"
27fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "init_parser.h"
28fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "log.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
1492bc00140be22f08964102068a736358fe8dde5daTom Cherry    if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
150fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        *err = "multiple property triggers found for same property";
151fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return false;
152fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
153fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return true;
154fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
155fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
156b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
157fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    const static std::string prop_str("property:");
158fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (std::size_t i = 0; i < args.size(); ++i) {
15993df4e18a255262595acb862ab870e9fed721fb8Wei Wang        if (args[i].empty()) {
16093df4e18a255262595acb862ab870e9fed721fb8Wei Wang            *err = "empty trigger is not valid";
16193df4e18a255262595acb862ab870e9fed721fb8Wei Wang            return false;
16293df4e18a255262595acb862ab870e9fed721fb8Wei Wang        }
16393df4e18a255262595acb862ab870e9fed721fb8Wei Wang
164fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        if (i % 2) {
165cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (args[i] != "&&") {
166fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                *err = "&& is the only symbol allowed to concatenate actions";
167fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
168fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            } else {
169fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                continue;
170fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
171fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
172fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
173fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        if (!args[i].compare(0, prop_str.length(), prop_str)) {
174fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            if (!ParsePropertyTrigger(args[i], err)) {
175fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
176fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
177fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        } else {
178fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            if (!event_trigger_.empty()) {
179fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                *err = "multiple event triggers are not allowed";
180fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
181fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
182fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
183fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            event_trigger_ = args[i];
184fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
185fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
186fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
187fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return true;
188fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
189fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
190b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitSingleTrigger(const std::string& trigger) {
191fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::vector<std::string> name_vector{trigger};
192fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string err;
19393df4e18a255262595acb862ab870e9fed721fb8Wei Wang    bool ret = InitTriggers(name_vector, &err);
19493df4e18a255262595acb862ab870e9fed721fb8Wei Wang    if (!ret) {
19593df4e18a255262595acb862ab870e9fed721fb8Wei Wang        LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
19693df4e18a255262595acb862ab870e9fed721fb8Wei Wang    }
19793df4e18a255262595acb862ab870e9fed721fb8Wei Wang    return ret;
198fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
199fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
200b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// This function checks that all property triggers are satisfied, that is
201b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// for each (name, value) in property_triggers_, check that the current
202b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// value of the property 'name' == value.
203b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry//
204b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// It takes an optional (name, value) pair, which if provided must
205b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// be present in property_triggers_; it skips the check of the current
206b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// property value for this pair.
207fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherrybool Action::CheckPropertyTriggers(const std::string& name,
208b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                   const std::string& value) const {
209fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (property_triggers_.empty()) {
210fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return true;
211fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
212fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
213b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool found = name.empty();
2142bc00140be22f08964102068a736358fe8dde5daTom Cherry    for (const auto& [trigger_name, trigger_value] : property_triggers_) {
215cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        if (trigger_name == name) {
216cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (trigger_value != "*" && trigger_value != value) {
217fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
218fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            } else {
219fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                found = true;
220fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
221fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        } else {
222ccf23537eeacfa47e5f18dd3b75089886d177c1bTom Cherry            std::string prop_val = android::base::GetProperty(trigger_name, "");
223ccf23537eeacfa47e5f18dd3b75089886d177c1bTom Cherry            if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
224fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
225fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
226fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
227fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
228fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return found;
229fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
230fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
231b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::CheckEventTrigger(const std::string& trigger) const {
232fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return !event_trigger_.empty() &&
233cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        trigger == event_trigger_ &&
234fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        CheckPropertyTriggers();
235fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
236fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
237fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherrybool Action::CheckPropertyTrigger(const std::string& name,
238b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                  const std::string& value) const {
239fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return event_trigger_.empty() && CheckPropertyTriggers(name, value);
240fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
241fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
242b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::TriggersEqual(const Action& other) const {
243cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    return property_triggers_ == other.property_triggers_ &&
244cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        event_trigger_ == other.event_trigger_;
245fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
246fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
247b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Action::BuildTriggersString() const {
248d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry    std::vector<std::string> triggers;
249fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
2502bc00140be22f08964102068a736358fe8dde5daTom Cherry    for (const auto& [trigger_name, trigger_value] : property_triggers_) {
251d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry        triggers.emplace_back(trigger_name + '=' + trigger_value);
252fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
253fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (!event_trigger_.empty()) {
254d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry        triggers.emplace_back(event_trigger_);
255fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
256d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry
257d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry    return Join(triggers, " && ");
258fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
259fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
260b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::DumpState() const {
261fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string trigger_name = BuildTriggersString();
262f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes    LOG(INFO) << "on " << trigger_name;
263fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
264fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& c : commands_) {
265b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        std::string cmd_str = c.BuildCommandString();
266d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry        LOG(INFO) << "  " << cmd_str;
267fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
268fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
269fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
270cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass EventTrigger : public Trigger {
271cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic:
2721c563d96f000876d77b2d33fbfb03c241bc503e1Chih-Hung Hsieh    explicit EventTrigger(const std::string& trigger) : trigger_(trigger) {
273cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
274b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool CheckTriggers(const Action& action) const override {
275b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return action.CheckEventTrigger(trigger_);
276cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
277cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate:
278b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const std::string trigger_;
279cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry};
280cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
281cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass PropertyTrigger : public Trigger {
282cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic:
283cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    PropertyTrigger(const std::string& name, const std::string& value)
284cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        : name_(name), value_(value) {
285cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
286b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool CheckTriggers(const Action& action) const override {
287b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return action.CheckPropertyTrigger(name_, value_);
288cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
289cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate:
290b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const std::string name_;
291b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const std::string value_;
292cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry};
293cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
294cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryclass BuiltinTrigger : public Trigger {
295cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherrypublic:
2961c563d96f000876d77b2d33fbfb03c241bc503e1Chih-Hung Hsieh    explicit BuiltinTrigger(Action* action) : action_(action) {
297cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
298b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool CheckTriggers(const Action& action) const override {
299b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return action_ == &action;
300cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
301cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherryprivate:
302b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    const Action* action_;
303cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry};
304cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
305b7349902a945903f9e36a569051f5131beb0bc24Tom CherryActionManager::ActionManager() : current_command_(0) {
306fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
307fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
308fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom CherryActionManager& ActionManager::GetInstance() {
309fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    static ActionManager instance;
310fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return instance;
311fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
312fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
313b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::AddAction(std::unique_ptr<Action> action) {
314b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto old_action_it =
315b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        std::find_if(actions_.begin(), actions_.end(),
316b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                     [&action] (std::unique_ptr<Action>& a) {
317b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                         return action->TriggersEqual(*a);
318b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                     });
319b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
320b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (old_action_it != actions_.end()) {
321b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        (*old_action_it)->CombineAction(*action);
322b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    } else {
323b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        actions_.emplace_back(std::move(action));
324b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    }
325b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
326b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
327b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueEventTrigger(const std::string& trigger) {
328cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
329fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
330fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
331fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherryvoid ActionManager::QueuePropertyTrigger(const std::string& name,
332b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                         const std::string& value) {
333cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
334fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
335fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
336b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueAllPropertyTriggers() {
337fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    QueuePropertyTrigger("", "");
338fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
339fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
340b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueBuiltinAction(BuiltinFunction func,
341b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                       const std::string& name) {
342b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = std::make_unique<Action>(true);
343fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::vector<std::string> name_vector{name};
344fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
345b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!action->InitSingleTrigger(name)) {
346fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return;
347fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
348fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
349b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action->AddCommand(func, name_vector);
350fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
351b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
352b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    actions_.emplace_back(std::move(action));
353fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
354fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
355fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherryvoid ActionManager::ExecuteOneCommand() {
356b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // Loop through the trigger queue until we have an action to execute
357cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
358b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        for (const auto& action : actions_) {
359b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            if (trigger_queue_.front()->CheckTriggers(*action)) {
360b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                current_executing_actions_.emplace(action.get());
361b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            }
362b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        }
363cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        trigger_queue_.pop();
364cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
365cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
366cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_executing_actions_.empty()) {
367fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return;
368fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
369fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
370b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = current_executing_actions_.front();
371fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
372cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_command_ == 0) {
373fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string trigger_name = action->BuildTriggersString();
374f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes        LOG(INFO) << "processing action (" << trigger_name << ")";
375fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
376fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
377b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action->ExecuteOneCommand(current_command_);
378b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
379b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // If this was the last command in the current action, then remove
380b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // the action from the executing list.
381b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // If this action was oneshot, then also remove it from actions_.
382b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    ++current_command_;
383cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_command_ == action->NumCommands()) {
384b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        current_executing_actions_.pop();
385cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        current_command_ = 0;
386b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        if (action->oneshot()) {
387b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            auto eraser = [&action] (std::unique_ptr<Action>& a) {
388b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                return a.get() == action;
389b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            };
390b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
391b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        }
392fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
393fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
394fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
395b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionManager::HasMoreCommands() const {
396cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    return !current_executing_actions_.empty() || !trigger_queue_.empty();
397fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
398fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
399b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::DumpState() const {
400b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    for (const auto& a : actions_) {
401b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        a->DumpState();
402fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
403b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
404fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
405b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionParser::ParseSection(const std::vector<std::string>& args,
406b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                std::string* err) {
407b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    std::vector<std::string> triggers(args.begin() + 1, args.end());
408b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (triggers.size() < 1) {
409b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        *err = "actions must have a trigger";
410b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
411fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
412fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
413b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = std::make_unique<Action>(false);
414b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!action->InitTriggers(triggers, err)) {
415b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
416fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
417fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
418b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action_ = std::move(action);
419b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return true;
420fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
421fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
422b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionParser::ParseLineSection(const std::vector<std::string>& args,
423b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                    const std::string& filename, int line,
424b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                    std::string* err) const {
425b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return action_ ? action_->AddCommand(args, filename, line, err) : false;
426b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
427b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
428b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionParser::EndSection() {
429b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (action_ && action_->NumCommands() > 0) {
430b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        ActionManager::GetInstance().AddAction(std::move(action_));
431fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
432fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
433