/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ApiChecker.h" #include #include using android::base::Result; namespace { Result CompareProps(const sysprop::Properties& latest, const sysprop::Properties& current) { std::unordered_map props; std::unordered_map legacy_props; for (int i = 0; i < current.prop_size(); ++i) { const auto& prop = current.prop(i); props[prop.api_name()] = prop; if (!prop.legacy_prop_name().empty()) { legacy_props[prop.legacy_prop_name()] = prop; } } std::string err; bool latest_empty = true; for (int i = 0; i < latest.prop_size(); ++i) { const auto& latest_prop = latest.prop(i); if (latest_prop.deprecated() || latest_prop.scope() == sysprop::Internal) { continue; } latest_empty = false; // Error if a prop from the latest API doesn't exist in the current API. // But allow removal if any legacy_prop_name in the current API is // identical to prop_name. bool legacy = false; auto itr = props.find(latest_prop.api_name()); sysprop::Property current_prop; if (itr != props.end()) { current_prop = itr->second; } else { auto legacy_itr = legacy_props.find(latest_prop.prop_name()); if (legacy_itr != legacy_props.end()) { legacy = true; current_prop = legacy_itr->second; } else { err += "Prop " + latest_prop.api_name() + " has been removed\n"; continue; } } if (latest_prop.type() != current_prop.type()) { err += "Type of prop " + latest_prop.api_name() + " has been changed\n"; } // Readonly > Writeonce > ReadWrite if (latest_prop.access() > current_prop.access()) { err += "Accessibility of prop " + latest_prop.api_name() + " has become more restrictive\n"; } // Public < Internal if (latest_prop.scope() < current_prop.scope()) { err += "Scope of prop " + latest_prop.api_name() + " has become more restrictive\n"; } if (latest_prop.enum_values() != current_prop.enum_values()) { err += "Enum values of prop " + latest_prop.api_name() + " has been changed\n"; } if (latest_prop.integer_as_bool() != current_prop.integer_as_bool()) { err += "Integer-as-bool of prop " + latest_prop.api_name() + " has been changed\n"; } // If current_prop is new and latest_prop is legacy, skip prop name compare // because latest_prop.prop_name() == current_prop.legacy_prop_name() if (!legacy) { if (latest_prop.prop_name() != current_prop.prop_name()) { err += "Underlying property of prop " + latest_prop.api_name() + " has been changed\n"; } if (latest_prop.legacy_prop_name() != current_prop.legacy_prop_name()) { err += "Legacy prop of prop " + latest_prop.api_name() + " has been changed\n"; } } } if (!latest_empty) { if (latest.owner() != current.owner()) { err += "owner of module " + latest.module() + " has been changed\n"; } } if (err.empty()) return {}; else return Errorf("{}", err); } } // namespace Result CompareApis(const sysprop::SyspropLibraryApis& latest, const sysprop::SyspropLibraryApis& current) { std::unordered_map propsMap; for (int i = 0; i < current.props_size(); ++i) { propsMap[current.props(i).module()] = current.props(i); } for (int i = 0; i < latest.props_size(); ++i) { // Checking whether propsMap contains latest.props(i)->module() or not // is intentionally skipped to handle the case that latest.props(i) has // only deprecated properties. if (auto res = CompareProps(latest.props(i), propsMap[latest.props(i).module()]); !res.ok()) { return res; } } return {}; }