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
19ede0d538501dfc78c741fb3b0645406636d1d1fdTom Cherry#include <android-base/chrono_utils.h>
203f5eaae526413a29de899270714469c76dc91ec8Tom Cherry#include <android-base/logging.h>
21ccf23537eeacfa47e5f18dd3b75089886d177c1bTom Cherry#include <android-base/properties.h>
22ccf23537eeacfa47e5f18dd3b75089886d177c1bTom Cherry#include <android-base/strings.h>
23fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
24fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry#include "util.h"
25fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
26b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryusing android::base::Join;
27fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
2881f5d3ebef2c3789737bf718fc2a2cdd7b9e8b33Tom Cherrynamespace android {
2981f5d3ebef2c3789737bf718fc2a2cdd7b9e8b33Tom Cherrynamespace init {
3081f5d3ebef2c3789737bf718fc2a2cdd7b9e8b33Tom Cherry
31012c573e267b8dd70de14237cb470bd7301ee8eaTom CherryCommand::Command(BuiltinFunction f, const std::vector<std::string>& args, int line)
32012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry    : func_(f), args_(args), line_(line) {}
33fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
34b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryint Command::InvokeFunc() const {
3596f67316a22bc9236aed70b198e91a5406389e5bTom Cherry    std::vector<std::string> expanded_args;
3696f67316a22bc9236aed70b198e91a5406389e5bTom Cherry    expanded_args.resize(args_.size());
3796f67316a22bc9236aed70b198e91a5406389e5bTom Cherry    expanded_args[0] = args_[0];
38fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (std::size_t i = 1; i < args_.size(); ++i) {
39b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        if (!expand_props(args_[i], &expanded_args[i])) {
40f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes            LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
41fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            return -EINVAL;
42fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
43fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
44fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
4596f67316a22bc9236aed70b198e91a5406389e5bTom Cherry    return func_(expanded_args);
46fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
47fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
48b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Command::BuildCommandString() const {
49b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return Join(args_, ' ');
50fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
51fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
52012c573e267b8dd70de14237cb470bd7301ee8eaTom CherryAction::Action(bool oneshot, const std::string& filename, int line)
53012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry    : oneshot_(oneshot), filename_(filename), line_(line) {}
54fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
55b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryconst KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
56b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
57012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherrybool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
58b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!function_map_) {
59b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        *err = "no function map available";
60b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
61b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    }
62b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
63fe062055cb11fcb1a6178b046173fc0361ad5b96Tom Cherry    auto function = function_map_->FindFunction(args, err);
64b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!function) {
65b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
66b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    }
67b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
68012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry    AddCommand(function, args, line);
69b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return true;
70b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
71b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
72012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherryvoid Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
73012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry    commands_.emplace_back(f, args, line);
74fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
75fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
76b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::size_t Action::NumCommands() const {
77fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return commands_.size();
78fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
79fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
80b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteOneCommand(std::size_t command) const {
81d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang    // We need a copy here since some Command execution may result in
82d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang    // changing commands_ vector by importing .rc files through parser
83d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang    Command cmd = commands_[command];
84d67a4abc647d5ed7235ff7ee1695b31340e63a1cWei Wang    ExecuteCommand(cmd);
85fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
86fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
87b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteAllCommands() const {
88fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& c : commands_) {
89b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        ExecuteCommand(c);
90fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
91fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
92fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
93b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::ExecuteCommand(const Command& command) const {
94ede0d538501dfc78c741fb3b0645406636d1d1fdTom Cherry    android::base::Timer t;
95fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    int result = command.InvokeFunc();
96fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
97ede0d538501dfc78c741fb3b0645406636d1d1fdTom Cherry    auto duration = t.duration();
988b1d526a72c1e6705b03f4b3267ee02fe84ce765Wei Wang    // Any action longer than 50ms will be warned to user as slow operation
99ede0d538501dfc78c741fb3b0645406636d1d1fdTom Cherry    if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
100fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string trigger_name = BuildTriggersString();
101fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string cmd_str = command.BuildCommandString();
102fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
1031c3a53f03ca3c2c647f83cd8b8ae7e18c5c7bc69Tom Cherry        LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
104ede0d538501dfc78c741fb3b0645406636d1d1fdTom Cherry                  << ":" << command.line() << ") returned " << result << " took "
105ede0d538501dfc78c741fb3b0645406636d1d1fdTom Cherry                  << duration.count() << "ms.";
106fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
107fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
108fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
109b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
110fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    const static std::string prop_str("property:");
111fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string prop_name(trigger.substr(prop_str.length()));
112fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    size_t equal_pos = prop_name.find('=');
113fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (equal_pos == std::string::npos) {
114fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        *err = "property trigger found without matching '='";
115fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return false;
116fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
117fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
118fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string prop_value(prop_name.substr(equal_pos + 1));
119fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    prop_name.erase(equal_pos);
120fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
1212bc00140be22f08964102068a736358fe8dde5daTom Cherry    if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
122fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        *err = "multiple property triggers found for same property";
123fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return false;
124fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
125fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return true;
126fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
127fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
128b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
129fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    const static std::string prop_str("property:");
130fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (std::size_t i = 0; i < args.size(); ++i) {
13193df4e18a255262595acb862ab870e9fed721fb8Wei Wang        if (args[i].empty()) {
13293df4e18a255262595acb862ab870e9fed721fb8Wei Wang            *err = "empty trigger is not valid";
13393df4e18a255262595acb862ab870e9fed721fb8Wei Wang            return false;
13493df4e18a255262595acb862ab870e9fed721fb8Wei Wang        }
13593df4e18a255262595acb862ab870e9fed721fb8Wei Wang
136fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        if (i % 2) {
137cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (args[i] != "&&") {
138fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                *err = "&& is the only symbol allowed to concatenate actions";
139fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
140fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            } else {
141fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                continue;
142fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
143fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
144fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
145fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        if (!args[i].compare(0, prop_str.length(), prop_str)) {
146fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            if (!ParsePropertyTrigger(args[i], err)) {
147fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
148fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
149fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        } else {
150fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            if (!event_trigger_.empty()) {
151fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                *err = "multiple event triggers are not allowed";
152fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
153fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
154fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
155fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            event_trigger_ = args[i];
156fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
157fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
158fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
159fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return true;
160fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
161fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
162b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool Action::InitSingleTrigger(const std::string& trigger) {
163fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::vector<std::string> name_vector{trigger};
164fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string err;
16593df4e18a255262595acb862ab870e9fed721fb8Wei Wang    bool ret = InitTriggers(name_vector, &err);
16693df4e18a255262595acb862ab870e9fed721fb8Wei Wang    if (!ret) {
16793df4e18a255262595acb862ab870e9fed721fb8Wei Wang        LOG(ERROR) << "InitSingleTrigger failed due to: " << err;
16893df4e18a255262595acb862ab870e9fed721fb8Wei Wang    }
16993df4e18a255262595acb862ab870e9fed721fb8Wei Wang    return ret;
170fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
171fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
172b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// This function checks that all property triggers are satisfied, that is
173b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// for each (name, value) in property_triggers_, check that the current
174b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// value of the property 'name' == value.
175b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry//
176b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// It takes an optional (name, value) pair, which if provided must
177b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// be present in property_triggers_; it skips the check of the current
178b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry// property value for this pair.
179fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherrybool Action::CheckPropertyTriggers(const std::string& name,
180b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                                   const std::string& value) const {
181fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (property_triggers_.empty()) {
182fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return true;
183fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
184fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
185b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    bool found = name.empty();
1862bc00140be22f08964102068a736358fe8dde5daTom Cherry    for (const auto& [trigger_name, trigger_value] : property_triggers_) {
187cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        if (trigger_name == name) {
188cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry            if (trigger_value != "*" && trigger_value != value) {
189fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
190fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            } else {
191fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                found = true;
192fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
193fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        } else {
194ccf23537eeacfa47e5f18dd3b75089886d177c1bTom Cherry            std::string prop_val = android::base::GetProperty(trigger_name, "");
195ccf23537eeacfa47e5f18dd3b75089886d177c1bTom Cherry            if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
196fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry                return false;
197fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry            }
198fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        }
199fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
200fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return found;
201fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
202fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
20326ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherrybool Action::CheckEvent(const EventTrigger& event_trigger) const {
20426ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    return event_trigger == event_trigger_ && CheckPropertyTriggers();
205fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
206fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
20726ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherrybool Action::CheckEvent(const PropertyChange& property_change) const {
20826ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    const auto& [name, value] = property_change;
209fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return event_trigger_.empty() && CheckPropertyTriggers(name, value);
210fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
211fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
21226ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherrybool Action::CheckEvent(const BuiltinAction& builtin_action) const {
21326ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    return this == builtin_action;
21426ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry}
21526ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry
216b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrystd::string Action::BuildTriggersString() const {
217d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry    std::vector<std::string> triggers;
218fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
2192bc00140be22f08964102068a736358fe8dde5daTom Cherry    for (const auto& [trigger_name, trigger_value] : property_triggers_) {
220d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry        triggers.emplace_back(trigger_name + '=' + trigger_value);
221fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
222fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    if (!event_trigger_.empty()) {
223d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry        triggers.emplace_back(event_trigger_);
224fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
225d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry
226d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry    return Join(triggers, " && ");
227fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
228fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
229b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid Action::DumpState() const {
230fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::string trigger_name = BuildTriggersString();
231f86b5a6b90619e02d1d034ef7b0adc3b439f4abbElliott Hughes    LOG(INFO) << "on " << trigger_name;
232fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
233fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    for (const auto& c : commands_) {
234b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        std::string cmd_str = c.BuildCommandString();
235d8a7257b14086a9070aa521b41118570ee4f4aaaTom Cherry        LOG(INFO) << "  " << cmd_str;
236fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
237fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
238fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
239b7349902a945903f9e36a569051f5131beb0bc24Tom CherryActionManager::ActionManager() : current_command_(0) {
240fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
241fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
242fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom CherryActionManager& ActionManager::GetInstance() {
243fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    static ActionManager instance;
244fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    return instance;
245fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
246fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
247b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::AddAction(std::unique_ptr<Action> action) {
248012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry    actions_.emplace_back(std::move(action));
249b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
250b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
251b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::QueueEventTrigger(const std::string& trigger) {
25226ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    event_queue_.emplace(trigger);
253fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
254fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
25526ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherryvoid ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
25626ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    event_queue_.emplace(std::make_pair(name, value));
257fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
258fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
25926ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherryvoid ActionManager::QueueAllPropertyActions() {
26026ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    QueuePropertyChange("", "");
261fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
262fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
263012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherryvoid ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
264012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry    auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
265fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    std::vector<std::string> name_vector{name};
266fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
267b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!action->InitSingleTrigger(name)) {
268fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return;
269fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
270fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
271012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry    action->AddCommand(func, name_vector, 0);
272fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
27326ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    event_queue_.emplace(action.get());
274b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    actions_.emplace_back(std::move(action));
275fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
276fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
277fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherryvoid ActionManager::ExecuteOneCommand() {
27826ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    // Loop through the event queue until we have an action to execute
27926ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    while (current_executing_actions_.empty() && !event_queue_.empty()) {
280b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        for (const auto& action : actions_) {
28126ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry            if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
28226ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry                           event_queue_.front())) {
283b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                current_executing_actions_.emplace(action.get());
284b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            }
285b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        }
28626ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry        event_queue_.pop();
287cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    }
288cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry
289cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_executing_actions_.empty()) {
290fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        return;
291fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
292fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
293b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    auto action = current_executing_actions_.front();
294fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
295cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_command_ == 0) {
296fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry        std::string trigger_name = action->BuildTriggersString();
297012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry        LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
298012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry                  << ":" << action->line() << ")";
299fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
300fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
301b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action->ExecuteOneCommand(current_command_);
302b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
303b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // If this was the last command in the current action, then remove
304b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // the action from the executing list.
305b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    // If this action was oneshot, then also remove it from actions_.
306b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    ++current_command_;
307cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry    if (current_command_ == action->NumCommands()) {
308b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        current_executing_actions_.pop();
309cb716f976b078dff72aa3a61f9435d45e4beb9f5Tom Cherry        current_command_ = 0;
310b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        if (action->oneshot()) {
311b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            auto eraser = [&action] (std::unique_ptr<Action>& a) {
312b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry                return a.get() == action;
313b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            };
314b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
315b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        }
316fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
317fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
318fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
319b7349902a945903f9e36a569051f5131beb0bc24Tom Cherrybool ActionManager::HasMoreCommands() const {
32026ed9cb7062c852749b18cd4b5873d07a3389d00Tom Cherry    return !current_executing_actions_.empty() || !event_queue_.empty();
321fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
322fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
323b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionManager::DumpState() const {
324b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    for (const auto& a : actions_) {
325b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        a->DumpState();
326fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
327b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
328fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
329eeab491efd8f456324f88e444f228b1016712e45Wei Wangvoid ActionManager::ClearQueue() {
330eeab491efd8f456324f88e444f228b1016712e45Wei Wang    // We are shutting down so don't claim the oneshot builtin actions back
331eeab491efd8f456324f88e444f228b1016712e45Wei Wang    current_executing_actions_ = {};
332eeab491efd8f456324f88e444f228b1016712e45Wei Wang    event_queue_ = {};
333eeab491efd8f456324f88e444f228b1016712e45Wei Wang    current_command_ = 0;
334eeab491efd8f456324f88e444f228b1016712e45Wei Wang}
335eeab491efd8f456324f88e444f228b1016712e45Wei Wang
33630a6f276fd8850b0a78689d7bff3cb06a18cb286Tom Cherrybool ActionParser::ParseSection(std::vector<std::string>&& args, const std::string& filename,
337012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry                                int line, std::string* err) {
338b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    std::vector<std::string> triggers(args.begin() + 1, args.end());
339b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (triggers.size() < 1) {
340b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        *err = "actions must have a trigger";
341b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
342fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
343fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
344012c573e267b8dd70de14237cb470bd7301ee8eaTom Cherry    auto action = std::make_unique<Action>(false, filename, line);
345b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (!action->InitTriggers(triggers, err)) {
346b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry        return false;
347fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
348fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
349b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    action_ = std::move(action);
350b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    return true;
351fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
352fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry
35330a6f276fd8850b0a78689d7bff3cb06a18cb286Tom Cherrybool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
35430a6f276fd8850b0a78689d7bff3cb06a18cb286Tom Cherry    return action_ ? action_->AddCommand(std::move(args), line, err) : false;
355b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry}
356b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry
357b7349902a945903f9e36a569051f5131beb0bc24Tom Cherryvoid ActionParser::EndSection() {
358b7349902a945903f9e36a569051f5131beb0bc24Tom Cherry    if (action_ && action_->NumCommands() > 0) {
35930a6f276fd8850b0a78689d7bff3cb06a18cb286Tom Cherry        action_manager_->AddAction(std::move(action_));
360fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry    }
361fa0c21c94ccb98bfa5cf3cc7a6b220be4a5fa378Tom Cherry}
36281f5d3ebef2c3789737bf718fc2a2cdd7b9e8b33Tom Cherry
36381f5d3ebef2c3789737bf718fc2a2cdd7b9e8b33Tom Cherry}  // namespace init
36481f5d3ebef2c3789737bf718fc2a2cdd7b9e8b33Tom Cherry}  // namespace android
365