Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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 "idmap2/ResourceMapping.h" |
| 18 | |
| 19 | #include <map> |
| 20 | #include <memory> |
| 21 | #include <set> |
| 22 | #include <string> |
| 23 | #include <utility> |
| 24 | #include <vector> |
| 25 | |
| 26 | #include "android-base/stringprintf.h" |
Winson | 62ac8b5 | 2019-12-04 08:36:48 -0800 | [diff] [blame] | 27 | #include "androidfw/ResourceTypes.h" |
| 28 | #include "idmap2/PolicyUtils.h" |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 29 | #include "idmap2/ResourceUtils.h" |
| 30 | |
| 31 | using android::base::StringPrintf; |
Winson | 62ac8b5 | 2019-12-04 08:36:48 -0800 | [diff] [blame] | 32 | using android::idmap2::utils::BitmaskToPolicies; |
Winson | 62ac8b5 | 2019-12-04 08:36:48 -0800 | [diff] [blame] | 33 | using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask; |
| 34 | using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 35 | |
| 36 | namespace android::idmap2 { |
| 37 | |
| 38 | namespace { |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 39 | std::string ConcatPolicies(const std::vector<std::string>& policies) { |
| 40 | std::string message; |
| 41 | for (const std::string& policy : policies) { |
| 42 | if (!message.empty()) { |
| 43 | message.append("|"); |
| 44 | } |
| 45 | message.append(policy); |
| 46 | } |
| 47 | |
| 48 | return message; |
| 49 | } |
| 50 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 51 | Result<Unit> CheckOverlayable(const TargetResourceContainer& target, |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 52 | const OverlayManifestInfo& overlay_info, |
| 53 | const PolicyBitmask& fulfilled_policies, |
| 54 | const ResourceId& target_resource) { |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 55 | constexpr const PolicyBitmask kDefaultPolicies = |
Winson | 62ac8b5 | 2019-12-04 08:36:48 -0800 | [diff] [blame] | 56 | PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION | |
Zoran Jovanovic | 0f942f9 | 2020-06-09 18:51:57 +0200 | [diff] [blame] | 57 | PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE | |
| 58 | PolicyFlags::CONFIG_SIGNATURE; |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 59 | |
| 60 | // If the resource does not have an overlayable definition, allow the resource to be overlaid if |
Zoran Jovanovic | 0f942f9 | 2020-06-09 18:51:57 +0200 | [diff] [blame] | 61 | // the overlay is preinstalled, signed with the same signature as the target or signed with the |
| 62 | // same signature as reference package defined in SystemConfig under 'overlay-config-signature' |
| 63 | // tag. |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 64 | const Result<bool> defines_overlayable = target.DefinesOverlayable(); |
| 65 | if (!defines_overlayable) { |
| 66 | return Error(defines_overlayable.GetError(), "unable to retrieve overlayable info"); |
| 67 | } |
| 68 | |
| 69 | if (!*defines_overlayable) { |
| 70 | return (kDefaultPolicies & fulfilled_policies) != 0 |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 71 | ? Result<Unit>({}) |
Ryan Mitchell | db21f09a | 2020-11-16 23:08:18 +0000 | [diff] [blame] | 72 | : Error( |
| 73 | "overlay must be preinstalled, signed with the same signature as the target," |
| 74 | " or signed with the same signature as the package referenced through" |
| 75 | " <overlay-config-signature>."); |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 76 | } |
| 77 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 78 | const auto overlayable_info = target.GetOverlayableInfo(target_resource); |
| 79 | if (!overlayable_info) { |
| 80 | return overlayable_info.GetError(); |
| 81 | } |
| 82 | |
| 83 | if (*overlayable_info == nullptr) { |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 84 | // Do not allow non-overlayable resources to be overlaid. |
| 85 | return Error("target resource has no overlayable declaration"); |
| 86 | } |
| 87 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 88 | if (overlay_info.target_name != (*overlayable_info)->name) { |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 89 | // If the overlay supplies a target overlayable name, the resource must belong to the |
| 90 | // overlayable defined with the specified name to be overlaid. |
| 91 | return Error(R"(<overlay> android:targetName "%s" does not match overlayable name "%s")", |
Yurii Zubrytskyi | 9d22537 | 2022-11-29 11:12:18 -0800 | [diff] [blame] | 92 | overlay_info.target_name.c_str(), (*overlayable_info)->name.data()); |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 93 | } |
| 94 | |
| 95 | // Enforce policy restrictions if the resource is declared as overlayable. |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 96 | if (((*overlayable_info)->policy_flags & fulfilled_policies) == 0) { |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 97 | return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")", |
| 98 | ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(), |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 99 | ConcatPolicies(BitmaskToPolicies((*overlayable_info)->policy_flags)).c_str()); |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 100 | } |
| 101 | |
| 102 | return Result<Unit>({}); |
| 103 | } |
| 104 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 105 | std::string GetDebugResourceName(const ResourceContainer& container, ResourceId resid) { |
| 106 | auto name = container.GetResourceName(resid); |
| 107 | if (name) { |
| 108 | return *name; |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 109 | } |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 110 | return StringPrintf("0x%08x", resid); |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 111 | } |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 112 | } // namespace |
| 113 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 114 | Result<ResourceMapping> ResourceMapping::FromContainers(const TargetResourceContainer& target, |
| 115 | const OverlayResourceContainer& overlay, |
| 116 | const OverlayManifestInfo& overlay_info, |
| 117 | const PolicyBitmask& fulfilled_policies, |
| 118 | bool enforce_overlayable, |
| 119 | LogInfo& log_info) { |
| 120 | auto overlay_data = overlay.GetOverlayData(overlay_info); |
| 121 | if (!overlay_data) { |
| 122 | return overlay_data.GetError(); |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 123 | } |
| 124 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 125 | ResourceMapping mapping; |
| 126 | for (const auto& overlay_pair : overlay_data->pairs) { |
| 127 | const auto target_resid = target.GetResourceId(overlay_pair.resource_name); |
| 128 | if (!target_resid) { |
| 129 | log_info.Warning(LogMessage() << target_resid.GetErrorMessage()); |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 130 | continue; |
| 131 | } |
| 132 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 133 | if (enforce_overlayable) { |
| 134 | // Filter out resources the overlay is not allowed to override. |
| 135 | auto overlayable = CheckOverlayable(target, overlay_info, fulfilled_policies, *target_resid); |
| 136 | if (!overlayable) { |
| 137 | log_info.Warning(LogMessage() << "overlay '" << overlay.GetPath() |
| 138 | << "' is not allowed to overlay resource '" |
| 139 | << GetDebugResourceName(target, *target_resid) |
| 140 | << "' in target: " << overlayable.GetErrorMessage()); |
| 141 | continue; |
| 142 | } |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 143 | } |
| 144 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 145 | if (auto result = mapping.AddMapping(*target_resid, overlay_pair.value); !result) { |
| 146 | return Error(result.GetError(), "failed to add mapping for '%s'", |
| 147 | GetDebugResourceName(target, *target_resid).c_str()); |
Ryan Mitchell | e753ffe | 2019-09-23 09:47:02 -0700 | [diff] [blame] | 148 | } |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 149 | } |
| 150 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 151 | auto& string_pool_data = overlay_data->string_pool_data; |
| 152 | if (string_pool_data.has_value()) { |
| 153 | mapping.string_pool_offset_ = string_pool_data->string_pool_offset; |
| 154 | mapping.string_pool_data_ = std::move(string_pool_data->data); |
| 155 | mapping.string_pool_data_length_ = string_pool_data->data_length; |
| 156 | } |
| 157 | |
| 158 | return std::move(mapping); |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 159 | } |
| 160 | |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 161 | Result<Unit> ResourceMapping::AddMapping( |
| 162 | ResourceId target_resource, |
Jeremy Meyer | 77c8a93 | 2022-08-18 22:06:55 +0000 | [diff] [blame] | 163 | const std::variant<OverlayData::ResourceIdValue, TargetValueWithConfig>& value) { |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 164 | // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the |
| 165 | // runtime types are not compatible, it could cause runtime crashes when the resource is resolved. |
| 166 | |
| 167 | if (auto overlay_resource = std::get_if<OverlayData::ResourceIdValue>(&value)) { |
Jeremy Meyer | be2b779 | 2022-08-23 17:42:50 +0000 | [diff] [blame] | 168 | if (target_map_.find(target_resource) != target_map_.end()) { |
| 169 | return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource); |
| 170 | } |
Ryan Mitchell | 2ed8bfa | 2021-01-08 13:34:28 -0800 | [diff] [blame] | 171 | target_map_.insert(std::make_pair(target_resource, overlay_resource->overlay_id)); |
| 172 | if (overlay_resource->rewrite_id) { |
| 173 | // An overlay resource can override multiple target resources at once. Rewrite the overlay |
| 174 | // resource as the first target resource it overrides. |
| 175 | overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource)); |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 176 | } |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 177 | } else { |
Jeremy Meyer | be2b779 | 2022-08-23 17:42:50 +0000 | [diff] [blame] | 178 | auto[iter, inserted] = target_map_.try_emplace(target_resource, ConfigMap()); |
| 179 | auto& resource_value = iter->second; |
| 180 | if (!inserted && std::holds_alternative<ResourceId>(resource_value)) { |
| 181 | return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource); |
| 182 | } |
| 183 | auto& config_map = std::get<ConfigMap>(resource_value); |
| 184 | const auto& config_value = std::get<TargetValueWithConfig>(value); |
| 185 | if (config_map.find(config_value.config) != config_map.end()) { |
| 186 | return Error(R"(target resource id "0x%08x" mapped to multiple values with the same config)", |
| 187 | target_resource); |
| 188 | } |
| 189 | config_map.insert(config_value.to_pair()); |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 190 | } |
| 191 | |
Ryan Mitchell | bf1f45b | 2020-09-29 17:22:52 -0700 | [diff] [blame] | 192 | return Unit{}; |
| 193 | } |
| 194 | |
Ryan Mitchell | 9e4f52b | 2019-09-19 12:15:52 -0700 | [diff] [blame] | 195 | } // namespace android::idmap2 |