1/* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "action_parser.h" 18 19#include <android-base/strings.h> 20 21#include "stable_properties.h" 22 23#if defined(__ANDROID__) 24#include <android-base/properties.h> 25#else 26#include "host_init_stubs.h" 27#endif 28 29using android::base::GetBoolProperty; 30using android::base::StartsWith; 31 32namespace android { 33namespace init { 34 35namespace { 36 37bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) { 38 static bool enabled = GetBoolProperty("ro.actionable_compatible_property.enabled", false); 39 40 if (subcontext == nullptr || !enabled) { 41 return true; 42 } 43 44 if (kExportedActionableProperties.count(prop_name) == 1) { 45 return true; 46 } 47 for (const auto& prefix : kPartnerPrefixes) { 48 if (android::base::StartsWith(prop_name, prefix)) { 49 return true; 50 } 51 } 52 return false; 53} 54 55Result<Success> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext, 56 std::map<std::string, std::string>* property_triggers) { 57 const static std::string prop_str("property:"); 58 std::string prop_name(trigger.substr(prop_str.length())); 59 size_t equal_pos = prop_name.find('='); 60 if (equal_pos == std::string::npos) { 61 return Error() << "property trigger found without matching '='"; 62 } 63 64 std::string prop_value(prop_name.substr(equal_pos + 1)); 65 prop_name.erase(equal_pos); 66 67 if (!IsActionableProperty(subcontext, prop_name)) { 68 return Error() << "unexported property tigger found: " << prop_name; 69 } 70 71 if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) { 72 return Error() << "multiple property triggers found for same property"; 73 } 74 return Success(); 75} 76 77Result<Success> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext, 78 std::string* event_trigger, 79 std::map<std::string, std::string>* property_triggers) { 80 const static std::string prop_str("property:"); 81 for (std::size_t i = 0; i < args.size(); ++i) { 82 if (args[i].empty()) { 83 return Error() << "empty trigger is not valid"; 84 } 85 86 if (i % 2) { 87 if (args[i] != "&&") { 88 return Error() << "&& is the only symbol allowed to concatenate actions"; 89 } else { 90 continue; 91 } 92 } 93 94 if (!args[i].compare(0, prop_str.length(), prop_str)) { 95 if (auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers); 96 !result) { 97 return result; 98 } 99 } else { 100 if (!event_trigger->empty()) { 101 return Error() << "multiple event triggers are not allowed"; 102 } 103 104 *event_trigger = args[i]; 105 } 106 } 107 108 return Success(); 109} 110 111} // namespace 112 113Result<Success> ActionParser::ParseSection(std::vector<std::string>&& args, 114 const std::string& filename, int line) { 115 std::vector<std::string> triggers(args.begin() + 1, args.end()); 116 if (triggers.size() < 1) { 117 return Error() << "Actions must have a trigger"; 118 } 119 120 Subcontext* action_subcontext = nullptr; 121 if (subcontexts_) { 122 for (auto& subcontext : *subcontexts_) { 123 if (StartsWith(filename, subcontext.path_prefix())) { 124 action_subcontext = &subcontext; 125 break; 126 } 127 } 128 } 129 130 std::string event_trigger; 131 std::map<std::string, std::string> property_triggers; 132 133 if (auto result = ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers); 134 !result) { 135 return Error() << "ParseTriggers() failed: " << result.error(); 136 } 137 138 auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger, 139 property_triggers); 140 141 action_ = std::move(action); 142 return Success(); 143} 144 145Result<Success> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) { 146 return action_ ? action_->AddCommand(std::move(args), line) : Success(); 147} 148 149Result<Success> ActionParser::EndSection() { 150 if (action_ && action_->NumCommands() > 0) { 151 action_manager_->AddAction(std::move(action_)); 152 } 153 154 return Success(); 155} 156 157} // namespace init 158} // namespace android 159