blob: b2300cea3a6857b8a9495df71d55430586d1254a [file] [log] [blame]
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -07001/*
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"
Winson62ac8b52019-12-04 08:36:48 -080027#include "androidfw/ResourceTypes.h"
28#include "idmap2/PolicyUtils.h"
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -070029#include "idmap2/ResourceUtils.h"
30
31using android::base::StringPrintf;
Winson62ac8b52019-12-04 08:36:48 -080032using android::idmap2::utils::BitmaskToPolicies;
Winson62ac8b52019-12-04 08:36:48 -080033using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
34using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -070035
36namespace android::idmap2 {
37
38namespace {
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -070039std::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 Mitchell2ed8bfa2021-01-08 13:34:28 -080051Result<Unit> CheckOverlayable(const TargetResourceContainer& target,
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -070052 const OverlayManifestInfo& overlay_info,
53 const PolicyBitmask& fulfilled_policies,
54 const ResourceId& target_resource) {
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -080055 constexpr const PolicyBitmask kDefaultPolicies =
Winson62ac8b52019-12-04 08:36:48 -080056 PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
Zoran Jovanovic0f942f92020-06-09 18:51:57 +020057 PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE |
58 PolicyFlags::CONFIG_SIGNATURE;
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -070059
60 // If the resource does not have an overlayable definition, allow the resource to be overlaid if
Zoran Jovanovic0f942f92020-06-09 18:51:57 +020061 // 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 Mitchell2ed8bfa2021-01-08 13:34:28 -080064 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 Mitchell9e4f52b2019-09-19 12:15:52 -070071 ? Result<Unit>({})
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +000072 : 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 Mitchell9e4f52b2019-09-19 12:15:52 -070076 }
77
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -080078 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 Mitchell9e4f52b2019-09-19 12:15:52 -070084 // Do not allow non-overlayable resources to be overlaid.
85 return Error("target resource has no overlayable declaration");
86 }
87
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -080088 if (overlay_info.target_name != (*overlayable_info)->name) {
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -070089 // 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 Zubrytskyi9d225372022-11-29 11:12:18 -080092 overlay_info.target_name.c_str(), (*overlayable_info)->name.data());
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -070093 }
94
95 // Enforce policy restrictions if the resource is declared as overlayable.
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -080096 if (((*overlayable_info)->policy_flags & fulfilled_policies) == 0) {
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -070097 return Error(R"(overlay with policies "%s" does not fulfill any overlayable policies "%s")",
98 ConcatPolicies(BitmaskToPolicies(fulfilled_policies)).c_str(),
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -080099 ConcatPolicies(BitmaskToPolicies((*overlayable_info)->policy_flags)).c_str());
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -0700100 }
101
102 return Result<Unit>({});
103}
104
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800105std::string GetDebugResourceName(const ResourceContainer& container, ResourceId resid) {
106 auto name = container.GetResourceName(resid);
107 if (name) {
108 return *name;
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -0700109 }
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800110 return StringPrintf("0x%08x", resid);
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -0700111}
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -0700112} // namespace
113
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800114Result<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 Mitchell9e4f52b2019-09-19 12:15:52 -0700123 }
124
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800125 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 Mitchell9e4f52b2019-09-19 12:15:52 -0700130 continue;
131 }
132
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800133 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 Mitchell9e4f52b2019-09-19 12:15:52 -0700143 }
144
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800145 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 Mitchelle753ffe2019-09-23 09:47:02 -0700148 }
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -0700149 }
150
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800151 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 Mitchell9e4f52b2019-09-19 12:15:52 -0700159}
160
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800161Result<Unit> ResourceMapping::AddMapping(
162 ResourceId target_resource,
Jeremy Meyer77c8a932022-08-18 22:06:55 +0000163 const std::variant<OverlayData::ResourceIdValue, TargetValueWithConfig>& value) {
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800164 // 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 Meyerbe2b7792022-08-23 17:42:50 +0000168 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 Mitchell2ed8bfa2021-01-08 13:34:28 -0800171 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 Mitchell9e4f52b2019-09-19 12:15:52 -0700176 }
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -0700177 } else {
Jeremy Meyerbe2b7792022-08-23 17:42:50 +0000178 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 Mitchell9e4f52b2019-09-19 12:15:52 -0700190 }
191
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700192 return Unit{};
193}
194
Ryan Mitchell9e4f52b2019-09-19 12:15:52 -0700195} // namespace android::idmap2