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