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