blob: 57ae3548123b738971ff1c70394e40f244fdd52a [file] [log] [blame]
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -08001/*
2 * Copyright (C) 2021 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/ResourceContainer.h"
18
Mårten Kongstad1195a6b2021-05-11 12:57:01 +000019#include <memory>
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -080020#include <mutex>
Mårten Kongstad1195a6b2021-05-11 12:57:01 +000021#include <string>
22#include <utility>
23#include <vector>
24
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -080025#include "androidfw/ApkAssets.h"
26#include "androidfw/AssetManager.h"
27#include "androidfw/Util.h"
28#include "idmap2/FabricatedOverlay.h"
29#include "idmap2/XmlParser.h"
30
31namespace android::idmap2 {
32namespace {
33#define REWRITE_PACKAGE(resid, package_id) \
34 (((resid)&0x00ffffffU) | (((uint32_t)(package_id)) << 24U))
35
36#define EXTRACT_PACKAGE(resid) ((0xff000000 & (resid)) >> 24)
37
38constexpr ResourceId kAttrName = 0x01010003;
39constexpr ResourceId kAttrResourcesMap = 0x01010609;
40constexpr ResourceId kAttrTargetName = 0x0101044d;
41constexpr ResourceId kAttrTargetPackage = 0x01010021;
42
43// idmap version 0x01 naively assumes that the package to use is always the first ResTable_package
44// in the resources.arsc blob. In most cases, there is only a single ResTable_package anyway, so
45// this assumption tends to work out. That said, the correct thing to do is to scan
46// resources.arsc for a package with a given name as read from the package manifest instead of
47// relying on a hard-coded index. This however requires storing the package name in the idmap
48// header, which in turn requires incrementing the idmap version. Because the initial version of
49// idmap2 is compatible with idmap, this will have to wait for now.
50const LoadedPackage* GetPackageAtIndex0(const LoadedArsc* loaded_arsc) {
51 const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
52 if (packages.empty()) {
53 return nullptr;
54 }
55 return loaded_arsc->GetPackageById(packages[0]->GetPackageId());
56}
57
58Result<uint32_t> CalculateCrc(const ZipAssetsProvider* zip_assets) {
59 constexpr const char* kResourcesArsc = "resources.arsc";
60 std::optional<uint32_t> res_crc = zip_assets->GetCrc(kResourcesArsc);
61 if (!res_crc) {
62 return Error("failed to get CRC for '%s'", kResourcesArsc);
63 }
64
65 constexpr const char* kManifest = "AndroidManifest.xml";
66 std::optional<uint32_t> man_crc = zip_assets->GetCrc(kManifest);
67 if (!man_crc) {
68 return Error("failed to get CRC for '%s'", kManifest);
69 }
70
71 return *res_crc ^ *man_crc;
72}
73
74Result<XmlParser> OpenXmlParser(const std::string& entry_path, const ZipAssetsProvider* zip) {
75 auto manifest = zip->Open(entry_path);
76 if (manifest == nullptr) {
77 return Error("failed to find %s ", entry_path.c_str());
78 }
79
80 auto size = manifest->getLength();
81 auto buffer = manifest->getIncFsBuffer(true /* aligned */).convert<uint8_t>();
82 if (!buffer.verify(size)) {
83 return Error("failed to read entire %s", entry_path.c_str());
84 }
85
86 return XmlParser::Create(buffer.unsafe_ptr(), size, true /* copyData */);
87}
88
89Result<XmlParser> OpenXmlParser(ResourceId id, const ZipAssetsProvider* zip,
90 const AssetManager2* am) {
91 const auto ref_table = am->GetDynamicRefTableForCookie(0);
92 if (ref_table == nullptr) {
93 return Error("failed to find dynamic ref table for cookie 0");
94 }
95
96 ref_table->lookupResourceId(&id);
97 auto value = am->GetResource(id);
98 if (!value.has_value()) {
99 return Error("failed to find resource for id 0x%08x", id);
100 }
101
102 if (value->type != Res_value::TYPE_STRING) {
103 return Error("resource for is 0x%08x is not a file", id);
104 }
105
106 auto string_pool = am->GetStringPoolForCookie(value->cookie);
107 auto file = string_pool->string8ObjectAt(value->data);
108 if (!file.has_value()) {
109 return Error("failed to find string for index %d", value->data);
110 }
111
112 return OpenXmlParser(file->c_str(), zip);
113}
114
115Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const ZipAssetsProvider* zip,
116 const std::string& name) {
117 Result<XmlParser> xml = OpenXmlParser("AndroidManifest.xml", zip);
118 if (!xml) {
119 return xml.GetError();
120 }
121
122 auto manifest_it = xml->tree_iterator();
123 if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") {
124 return Error("root element tag is not <manifest> in AndroidManifest.xml");
125 }
126
Ryan Mitchell6a2ca782021-01-19 13:51:15 -0800127 std::string package_name;
128 if (auto result_str = manifest_it->GetAttributeStringValue("package")) {
129 package_name = *result_str;
130 } else {
131 return result_str.GetError();
132 }
133
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800134 for (auto&& it : manifest_it) {
135 if (it.event() != XmlParser::Event::START_TAG || it.name() != "overlay") {
136 continue;
137 }
138
139 OverlayManifestInfo info{};
Ryan Mitchell6a2ca782021-01-19 13:51:15 -0800140 info.package_name = package_name;
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800141 if (auto result_str = it.GetAttributeStringValue(kAttrName, "android:name")) {
142 if (*result_str != name) {
Ryan Mitchell6a2ca782021-01-19 13:51:15 -0800143 // A value for android:name was found, but either a the name does not match the requested
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800144 // name, or an <overlay> tag with no name was requested.
145 continue;
146 }
147 info.name = *result_str;
148 } else if (!name.empty()) {
149 // This tag does not have a value for android:name, but an <overlay> tag with a specific name
150 // has been requested.
151 continue;
152 }
153
154 if (auto result_str = it.GetAttributeStringValue(kAttrTargetPackage, "android:targetPackage")) {
155 info.target_package = *result_str;
156 } else {
157 return Error("android:targetPackage missing from <overlay> in AndroidManifest.xml");
158 }
159
160 if (auto result_str = it.GetAttributeStringValue(kAttrTargetName, "android:targetName")) {
161 info.target_name = *result_str;
162 }
163
164 if (auto result_value = it.GetAttributeValue(kAttrResourcesMap, "android:resourcesMap")) {
165 if (utils::IsReference((*result_value).dataType)) {
166 info.resource_mapping = (*result_value).data;
167 } else {
168 return Error("android:resourcesMap is not a reference in AndroidManifest.xml");
169 }
170 }
171 return info;
172 }
173
174 return Error("<overlay> with android:name \"%s\" missing from AndroidManifest.xml", name.c_str());
175}
176
177Result<OverlayData> CreateResourceMapping(ResourceId id, const ZipAssetsProvider* zip,
178 const AssetManager2* overlay_am,
179 const LoadedArsc* overlay_arsc,
180 const LoadedPackage* overlay_package) {
181 auto parser = OpenXmlParser(id, zip, overlay_am);
182 if (!parser) {
183 return parser.GetError();
184 }
185
186 OverlayData overlay_data{};
187 const uint32_t string_pool_offset = overlay_arsc->GetStringPool()->size();
188 const uint8_t package_id = overlay_package->GetPackageId();
189 auto root_it = parser->tree_iterator();
190 if (root_it->event() != XmlParser::Event::START_TAG || root_it->name() != "overlay") {
191 return Error("root element is not <overlay> tag");
192 }
193
194 auto overlay_it_end = root_it.end();
195 for (auto overlay_it = root_it.begin(); overlay_it != overlay_it_end; ++overlay_it) {
196 if (overlay_it->event() == XmlParser::Event::BAD_DOCUMENT) {
197 return Error("failed to parse overlay xml document");
198 }
199
200 if (overlay_it->event() != XmlParser::Event::START_TAG) {
201 continue;
202 }
203
204 if (overlay_it->name() != "item") {
205 return Error("unexpected tag <%s> in <overlay>", overlay_it->name().c_str());
206 }
207
208 Result<std::string> target_resource = overlay_it->GetAttributeStringValue("target");
209 if (!target_resource) {
210 return Error(R"(<item> tag missing expected attribute "target")");
211 }
212
213 Result<android::Res_value> overlay_resource = overlay_it->GetAttributeValue("value");
214 if (!overlay_resource) {
215 return Error(R"(<item> tag missing expected attribute "value")");
216 }
217
218 if (overlay_resource->dataType == Res_value::TYPE_STRING) {
219 overlay_resource->data += string_pool_offset;
220 }
221
222 if (utils::IsReference(overlay_resource->dataType)) {
223 // Only rewrite resources defined within the overlay package to their corresponding target
224 // resource ids at runtime.
225 bool rewrite_id = package_id == EXTRACT_PACKAGE(overlay_resource->data);
226 overlay_data.pairs.emplace_back(OverlayData::Value{
227 *target_resource, OverlayData::ResourceIdValue{overlay_resource->data, rewrite_id}});
228 } else {
229 overlay_data.pairs.emplace_back(
Jeremy Meyer77c8a932022-08-18 22:06:55 +0000230 OverlayData::Value{*target_resource, TargetValueWithConfig{
Jeremy Meyer77c8a932022-08-18 22:06:55 +0000231 .value = TargetValue{.data_type = overlay_resource->dataType,
Yurii Zubrytskyi77e28bd2023-11-10 13:56:07 -0800232 .data_value = overlay_resource->data},
233 .config = std::string()}});
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800234 }
235 }
236
237 const auto& string_pool = parser->get_strings();
238 const uint32_t string_pool_data_length = string_pool.bytes();
239 overlay_data.string_pool_data = OverlayData::InlineStringPoolData{
240 .data = std::unique_ptr<uint8_t[]>(new uint8_t[string_pool_data_length]),
241 .data_length = string_pool_data_length,
242 .string_pool_offset = string_pool_offset,
243 };
244
245 // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here.
246 memcpy(overlay_data.string_pool_data->data.get(), string_pool.data().unsafe_ptr(),
247 string_pool_data_length);
248 return overlay_data;
249}
250
251OverlayData CreateResourceMappingLegacy(const AssetManager2* overlay_am,
252 const LoadedPackage* overlay_package) {
253 OverlayData overlay_data{};
254 for (const ResourceId overlay_resid : *overlay_package) {
255 if (auto name = utils::ResToTypeEntryName(*overlay_am, overlay_resid)) {
256 // Disable rewriting. Overlays did not support internal references before
257 // android:resourcesMap. Do not introduce new behavior.
258 overlay_data.pairs.emplace_back(OverlayData::Value{
259 *name, OverlayData::ResourceIdValue{overlay_resid, false /* rewrite_id */}});
260 }
261 }
262 return overlay_data;
263}
264
265struct ResState {
Yurii Zubrytskyib3455192023-05-01 14:35:48 -0700266 AssetManager2::ApkAssetsPtr apk_assets;
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800267 const LoadedArsc* arsc;
268 const LoadedPackage* package;
269 std::unique_ptr<AssetManager2> am;
270 ZipAssetsProvider* zip_assets;
271
Yurii Zubrytskyi596fa172023-11-10 16:05:38 -0800272 static Result<ResState> Initialize(std::unique_ptr<ZipAssetsProvider> zip,
273 package_property_t flags) {
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800274 ResState state;
275 state.zip_assets = zip.get();
Yurii Zubrytskyi596fa172023-11-10 16:05:38 -0800276 if ((state.apk_assets = ApkAssets::Load(std::move(zip), flags)) == nullptr) {
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800277 return Error("failed to load apk asset");
278 }
279
280 if ((state.arsc = state.apk_assets->GetLoadedArsc()) == nullptr) {
281 return Error("failed to retrieve loaded arsc");
282 }
283
284 if ((state.package = GetPackageAtIndex0(state.arsc)) == nullptr) {
285 return Error("failed to retrieve loaded package at index 0");
286 }
287
288 state.am = std::make_unique<AssetManager2>();
Yurii Zubrytskyi596fa172023-11-10 16:05:38 -0800289 if (!state.am->SetApkAssets({state.apk_assets}, false)) {
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800290 return Error("failed to create asset manager");
291 }
292
293 return state;
294 }
295};
296
297} // namespace
298
299struct ApkResourceContainer : public TargetResourceContainer, public OverlayResourceContainer {
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -0800300 static Result<std::unique_ptr<ApkResourceContainer>> FromPath(std::string path);
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800301
302 // inherited from TargetResourceContainer
303 Result<bool> DefinesOverlayable() const override;
304 Result<const android::OverlayableInfo*> GetOverlayableInfo(ResourceId id) const override;
305 Result<ResourceId> GetResourceId(const std::string& name) const override;
306
307 // inherited from OverlayResourceContainer
308 Result<OverlayData> GetOverlayData(const OverlayManifestInfo& info) const override;
309 Result<OverlayManifestInfo> FindOverlayInfo(const std::string& name) const override;
310
311 // inherited from ResourceContainer
312 Result<uint32_t> GetCrc() const override;
313 Result<std::string> GetResourceName(ResourceId id) const override;
314 const std::string& GetPath() const override;
315
316 ~ApkResourceContainer() override = default;
317
318 private:
319 ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets, std::string path);
320
321 Result<const ResState*> GetState() const;
322 ZipAssetsProvider* GetZipAssets() const;
323
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -0800324 mutable std::mutex state_lock_;
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800325 mutable std::variant<std::unique_ptr<ZipAssetsProvider>, ResState> state_;
326 std::string path_;
327};
328
329ApkResourceContainer::ApkResourceContainer(std::unique_ptr<ZipAssetsProvider> zip_assets,
330 std::string path)
331 : state_(std::move(zip_assets)), path_(std::move(path)) {
332}
333
334Result<std::unique_ptr<ApkResourceContainer>> ApkResourceContainer::FromPath(
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -0800335 std::string path) {
Ryan Mitchellc0416692021-05-11 12:21:29 -0700336 auto zip_assets = ZipAssetsProvider::Create(path, 0 /* flags */);
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800337 if (zip_assets == nullptr) {
338 return Error("failed to load zip assets");
339 }
340 return std::unique_ptr<ApkResourceContainer>(
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -0800341 new ApkResourceContainer(std::move(zip_assets), std::move(path)));
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800342}
343
344Result<const ResState*> ApkResourceContainer::GetState() const {
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -0800345 std::lock_guard lock(state_lock_);
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800346 if (auto state = std::get_if<ResState>(&state_); state != nullptr) {
347 return state;
348 }
349
Yurii Zubrytskyi596fa172023-11-10 16:05:38 -0800350 auto state = ResState::Initialize(std::move(std::get<std::unique_ptr<ZipAssetsProvider>>(state_)),
351 PROPERTY_OPTIMIZE_NAME_LOOKUPS);
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800352 if (!state) {
353 return state.GetError();
354 }
355
356 state_ = std::move(*state);
357 return &std::get<ResState>(state_);
358}
359
360ZipAssetsProvider* ApkResourceContainer::GetZipAssets() const {
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -0800361 std::lock_guard lock(state_lock_);
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800362 if (auto zip = std::get_if<std::unique_ptr<ZipAssetsProvider>>(&state_); zip != nullptr) {
363 return zip->get();
364 }
365 return std::get<ResState>(state_).zip_assets;
366}
367
368Result<bool> ApkResourceContainer::DefinesOverlayable() const {
369 auto state = GetState();
370 if (!state) {
371 return state.GetError();
372 }
373 return (*state)->package->DefinesOverlayable();
374}
375
376Result<const android::OverlayableInfo*> ApkResourceContainer::GetOverlayableInfo(
377 ResourceId id) const {
378 auto state = GetState();
379 if (!state) {
380 return state.GetError();
381 }
382 return (*state)->package->GetOverlayableInfo(id);
383}
384
385Result<OverlayManifestInfo> ApkResourceContainer::FindOverlayInfo(const std::string& name) const {
386 return ExtractOverlayManifestInfo(GetZipAssets(), name);
387}
388
389Result<OverlayData> ApkResourceContainer::GetOverlayData(const OverlayManifestInfo& info) const {
390 const auto state = GetState();
391 if (!state) {
392 return state.GetError();
393 }
394
395 if (info.resource_mapping != 0) {
396 return CreateResourceMapping(info.resource_mapping, GetZipAssets(), (*state)->am.get(),
397 (*state)->arsc, (*state)->package);
398 }
399 return CreateResourceMappingLegacy((*state)->am.get(), (*state)->package);
400}
401
402Result<uint32_t> ApkResourceContainer::GetCrc() const {
403 return CalculateCrc(GetZipAssets());
404}
405
406const std::string& ApkResourceContainer::GetPath() const {
407 return path_;
408}
409
410Result<ResourceId> ApkResourceContainer::GetResourceId(const std::string& name) const {
411 auto state = GetState();
412 if (!state) {
413 return state.GetError();
414 }
415 auto id = (*state)->am->GetResourceId(name, "", (*state)->package->GetPackageName());
416 if (!id.has_value()) {
417 return Error("failed to find resource '%s'", name.c_str());
418 }
419
420 // Retrieve the compile-time resource id of the target resource.
421 return REWRITE_PACKAGE(*id, (*state)->package->GetPackageId());
422}
423
424Result<std::string> ApkResourceContainer::GetResourceName(ResourceId id) const {
425 auto state = GetState();
426 if (!state) {
427 return state.GetError();
428 }
429 return utils::ResToTypeEntryName(*(*state)->am, id);
430}
431
432Result<std::unique_ptr<TargetResourceContainer>> TargetResourceContainer::FromPath(
433 std::string path) {
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -0800434 auto result = ApkResourceContainer::FromPath(std::move(path));
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800435 if (!result) {
436 return result.GetError();
437 }
438 return std::unique_ptr<TargetResourceContainer>(result->release());
439}
440
441Result<std::unique_ptr<OverlayResourceContainer>> OverlayResourceContainer::FromPath(
442 std::string path) {
443 // Load the path as a fabricated overlay if the file magic indicates this is a fabricated overlay.
444 if (android::IsFabricatedOverlay(path)) {
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -0800445 auto result = FabricatedOverlayContainer::FromPath(std::move(path));
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800446 if (!result) {
447 return result.GetError();
448 }
449 return std::unique_ptr<OverlayResourceContainer>(result->release());
450 }
451
452 // Fallback to loading the container as an APK.
Yurii Zubrytskyi536e0b42024-11-07 21:28:10 -0800453 auto result = ApkResourceContainer::FromPath(std::move(path));
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800454 if (!result) {
455 return result.GetError();
456 }
457 return std::unique_ptr<OverlayResourceContainer>(result->release());
458}
459
Mårten Kongstad1195a6b2021-05-11 12:57:01 +0000460} // namespace android::idmap2