1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2016 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 "LoadedApk.h"
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include "ResourceValues.h"
20*d57664e9SAndroid Build Coastguard Worker #include "ValueVisitor.h"
21*d57664e9SAndroid Build Coastguard Worker #include "androidfw/BigBufferStream.h"
22*d57664e9SAndroid Build Coastguard Worker #include "format/Archive.h"
23*d57664e9SAndroid Build Coastguard Worker #include "format/binary/TableFlattener.h"
24*d57664e9SAndroid Build Coastguard Worker #include "format/binary/XmlFlattener.h"
25*d57664e9SAndroid Build Coastguard Worker #include "format/proto/ProtoDeserialize.h"
26*d57664e9SAndroid Build Coastguard Worker #include "format/proto/ProtoSerialize.h"
27*d57664e9SAndroid Build Coastguard Worker #include "io/Util.h"
28*d57664e9SAndroid Build Coastguard Worker #include "xml/XmlDom.h"
29*d57664e9SAndroid Build Coastguard Worker
30*d57664e9SAndroid Build Coastguard Worker using ::aapt::io::IFile;
31*d57664e9SAndroid Build Coastguard Worker using ::aapt::io::IFileCollection;
32*d57664e9SAndroid Build Coastguard Worker using ::aapt::xml::XmlResource;
33*d57664e9SAndroid Build Coastguard Worker using ::android::StringPiece;
34*d57664e9SAndroid Build Coastguard Worker using ::std::unique_ptr;
35*d57664e9SAndroid Build Coastguard Worker
36*d57664e9SAndroid Build Coastguard Worker namespace aapt {
37*d57664e9SAndroid Build Coastguard Worker
DetermineApkFormat(io::IFileCollection * apk)38*d57664e9SAndroid Build Coastguard Worker static ApkFormat DetermineApkFormat(io::IFileCollection* apk) {
39*d57664e9SAndroid Build Coastguard Worker if (apk->FindFile(kApkResourceTablePath) != nullptr) {
40*d57664e9SAndroid Build Coastguard Worker return ApkFormat::kBinary;
41*d57664e9SAndroid Build Coastguard Worker } else if (apk->FindFile(kProtoResourceTablePath) != nullptr) {
42*d57664e9SAndroid Build Coastguard Worker return ApkFormat::kProto;
43*d57664e9SAndroid Build Coastguard Worker } else {
44*d57664e9SAndroid Build Coastguard Worker // If the resource table is not present, attempt to read the manifest.
45*d57664e9SAndroid Build Coastguard Worker io::IFile* manifest_file = apk->FindFile(kAndroidManifestPath);
46*d57664e9SAndroid Build Coastguard Worker if (manifest_file == nullptr) {
47*d57664e9SAndroid Build Coastguard Worker return ApkFormat::kUnknown;
48*d57664e9SAndroid Build Coastguard Worker }
49*d57664e9SAndroid Build Coastguard Worker
50*d57664e9SAndroid Build Coastguard Worker // First try in proto format.
51*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<android::InputStream> manifest_in = manifest_file->OpenInputStream();
52*d57664e9SAndroid Build Coastguard Worker if (manifest_in != nullptr) {
53*d57664e9SAndroid Build Coastguard Worker pb::XmlNode pb_node;
54*d57664e9SAndroid Build Coastguard Worker io::ProtoInputStreamReader proto_reader(manifest_in.get());
55*d57664e9SAndroid Build Coastguard Worker if (proto_reader.ReadMessage(&pb_node)) {
56*d57664e9SAndroid Build Coastguard Worker return ApkFormat::kProto;
57*d57664e9SAndroid Build Coastguard Worker }
58*d57664e9SAndroid Build Coastguard Worker }
59*d57664e9SAndroid Build Coastguard Worker
60*d57664e9SAndroid Build Coastguard Worker // If it didn't work, try in binary format.
61*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
62*d57664e9SAndroid Build Coastguard Worker if (manifest_data != nullptr) {
63*d57664e9SAndroid Build Coastguard Worker std::string error;
64*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> manifest =
65*d57664e9SAndroid Build Coastguard Worker xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
66*d57664e9SAndroid Build Coastguard Worker if (manifest != nullptr) {
67*d57664e9SAndroid Build Coastguard Worker return ApkFormat::kBinary;
68*d57664e9SAndroid Build Coastguard Worker }
69*d57664e9SAndroid Build Coastguard Worker }
70*d57664e9SAndroid Build Coastguard Worker
71*d57664e9SAndroid Build Coastguard Worker return ApkFormat::kUnknown;
72*d57664e9SAndroid Build Coastguard Worker }
73*d57664e9SAndroid Build Coastguard Worker }
74*d57664e9SAndroid Build Coastguard Worker
LoadApkFromPath(StringPiece path,android::IDiagnostics * diag)75*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(StringPiece path,
76*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* diag) {
77*d57664e9SAndroid Build Coastguard Worker android::Source source(path);
78*d57664e9SAndroid Build Coastguard Worker std::string error;
79*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::Create(path, &error);
80*d57664e9SAndroid Build Coastguard Worker if (apk == nullptr) {
81*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(path) << "failed opening zip: " << error);
82*d57664e9SAndroid Build Coastguard Worker return {};
83*d57664e9SAndroid Build Coastguard Worker }
84*d57664e9SAndroid Build Coastguard Worker
85*d57664e9SAndroid Build Coastguard Worker ApkFormat apkFormat = DetermineApkFormat(apk.get());
86*d57664e9SAndroid Build Coastguard Worker switch (apkFormat) {
87*d57664e9SAndroid Build Coastguard Worker case ApkFormat::kBinary:
88*d57664e9SAndroid Build Coastguard Worker return LoadBinaryApkFromFileCollection(source, std::move(apk), diag);
89*d57664e9SAndroid Build Coastguard Worker case ApkFormat::kProto:
90*d57664e9SAndroid Build Coastguard Worker return LoadProtoApkFromFileCollection(source, std::move(apk), diag);
91*d57664e9SAndroid Build Coastguard Worker default:
92*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(path) << "could not identify format of APK");
93*d57664e9SAndroid Build Coastguard Worker return {};
94*d57664e9SAndroid Build Coastguard Worker }
95*d57664e9SAndroid Build Coastguard Worker }
96*d57664e9SAndroid Build Coastguard Worker
LoadProtoApkFromFileCollection(const android::Source & source,unique_ptr<io::IFileCollection> collection,android::IDiagnostics * diag)97*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<LoadedApk> LoadedApk::LoadProtoApkFromFileCollection(
98*d57664e9SAndroid Build Coastguard Worker const android::Source& source, unique_ptr<io::IFileCollection> collection,
99*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* diag) {
100*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<ResourceTable> table;
101*d57664e9SAndroid Build Coastguard Worker
102*d57664e9SAndroid Build Coastguard Worker io::IFile* table_file = collection->FindFile(kProtoResourceTablePath);
103*d57664e9SAndroid Build Coastguard Worker if (table_file != nullptr) {
104*d57664e9SAndroid Build Coastguard Worker pb::ResourceTable pb_table;
105*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<android::InputStream> in = table_file->OpenInputStream();
106*d57664e9SAndroid Build Coastguard Worker if (in == nullptr) {
107*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source) << "failed to open " << kProtoResourceTablePath);
108*d57664e9SAndroid Build Coastguard Worker return {};
109*d57664e9SAndroid Build Coastguard Worker }
110*d57664e9SAndroid Build Coastguard Worker
111*d57664e9SAndroid Build Coastguard Worker io::ProtoInputStreamReader proto_reader(in.get());
112*d57664e9SAndroid Build Coastguard Worker if (!proto_reader.ReadMessage(&pb_table)) {
113*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source) << "failed to read " << kProtoResourceTablePath);
114*d57664e9SAndroid Build Coastguard Worker return {};
115*d57664e9SAndroid Build Coastguard Worker }
116*d57664e9SAndroid Build Coastguard Worker
117*d57664e9SAndroid Build Coastguard Worker std::string error;
118*d57664e9SAndroid Build Coastguard Worker table = util::make_unique<ResourceTable>(ResourceTable::Validation::kDisabled);
119*d57664e9SAndroid Build Coastguard Worker if (!DeserializeTableFromPb(pb_table, collection.get(), table.get(), &error)) {
120*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source)
121*d57664e9SAndroid Build Coastguard Worker << "failed to deserialize " << kProtoResourceTablePath << ": " << error);
122*d57664e9SAndroid Build Coastguard Worker return {};
123*d57664e9SAndroid Build Coastguard Worker }
124*d57664e9SAndroid Build Coastguard Worker }
125*d57664e9SAndroid Build Coastguard Worker
126*d57664e9SAndroid Build Coastguard Worker io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
127*d57664e9SAndroid Build Coastguard Worker if (manifest_file == nullptr) {
128*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source) << "failed to find " << kAndroidManifestPath);
129*d57664e9SAndroid Build Coastguard Worker return {};
130*d57664e9SAndroid Build Coastguard Worker }
131*d57664e9SAndroid Build Coastguard Worker
132*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<android::InputStream> manifest_in = manifest_file->OpenInputStream();
133*d57664e9SAndroid Build Coastguard Worker if (manifest_in == nullptr) {
134*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source) << "failed to open " << kAndroidManifestPath);
135*d57664e9SAndroid Build Coastguard Worker return {};
136*d57664e9SAndroid Build Coastguard Worker }
137*d57664e9SAndroid Build Coastguard Worker
138*d57664e9SAndroid Build Coastguard Worker pb::XmlNode pb_node;
139*d57664e9SAndroid Build Coastguard Worker io::ProtoInputStreamReader proto_reader(manifest_in.get());
140*d57664e9SAndroid Build Coastguard Worker if (!proto_reader.ReadMessage(&pb_node)) {
141*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source) << "failed to read proto " << kAndroidManifestPath);
142*d57664e9SAndroid Build Coastguard Worker return {};
143*d57664e9SAndroid Build Coastguard Worker }
144*d57664e9SAndroid Build Coastguard Worker
145*d57664e9SAndroid Build Coastguard Worker std::string error;
146*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> manifest = DeserializeXmlResourceFromPb(pb_node, &error);
147*d57664e9SAndroid Build Coastguard Worker if (manifest == nullptr) {
148*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source)
149*d57664e9SAndroid Build Coastguard Worker << "failed to deserialize proto " << kAndroidManifestPath << ": " << error);
150*d57664e9SAndroid Build Coastguard Worker return {};
151*d57664e9SAndroid Build Coastguard Worker }
152*d57664e9SAndroid Build Coastguard Worker return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table),
153*d57664e9SAndroid Build Coastguard Worker std::move(manifest), ApkFormat::kProto);
154*d57664e9SAndroid Build Coastguard Worker }
155*d57664e9SAndroid Build Coastguard Worker
LoadBinaryApkFromFileCollection(const android::Source & source,unique_ptr<io::IFileCollection> collection,android::IDiagnostics * diag)156*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<LoadedApk> LoadedApk::LoadBinaryApkFromFileCollection(
157*d57664e9SAndroid Build Coastguard Worker const android::Source& source, unique_ptr<io::IFileCollection> collection,
158*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* diag) {
159*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<ResourceTable> table;
160*d57664e9SAndroid Build Coastguard Worker
161*d57664e9SAndroid Build Coastguard Worker io::IFile* table_file = collection->FindFile(kApkResourceTablePath);
162*d57664e9SAndroid Build Coastguard Worker if (table_file != nullptr) {
163*d57664e9SAndroid Build Coastguard Worker table = util::make_unique<ResourceTable>(ResourceTable::Validation::kDisabled);
164*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IData> data = table_file->OpenAsData();
165*d57664e9SAndroid Build Coastguard Worker if (data == nullptr) {
166*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source) << "failed to open " << kApkResourceTablePath);
167*d57664e9SAndroid Build Coastguard Worker return {};
168*d57664e9SAndroid Build Coastguard Worker }
169*d57664e9SAndroid Build Coastguard Worker BinaryResourceParser parser(diag, table.get(), source, data->data(), data->size(),
170*d57664e9SAndroid Build Coastguard Worker collection.get());
171*d57664e9SAndroid Build Coastguard Worker if (!parser.Parse()) {
172*d57664e9SAndroid Build Coastguard Worker return {};
173*d57664e9SAndroid Build Coastguard Worker }
174*d57664e9SAndroid Build Coastguard Worker }
175*d57664e9SAndroid Build Coastguard Worker
176*d57664e9SAndroid Build Coastguard Worker io::IFile* manifest_file = collection->FindFile(kAndroidManifestPath);
177*d57664e9SAndroid Build Coastguard Worker if (manifest_file == nullptr) {
178*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source) << "failed to find " << kAndroidManifestPath);
179*d57664e9SAndroid Build Coastguard Worker return {};
180*d57664e9SAndroid Build Coastguard Worker }
181*d57664e9SAndroid Build Coastguard Worker
182*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
183*d57664e9SAndroid Build Coastguard Worker if (manifest_data == nullptr) {
184*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source) << "failed to open " << kAndroidManifestPath);
185*d57664e9SAndroid Build Coastguard Worker return {};
186*d57664e9SAndroid Build Coastguard Worker }
187*d57664e9SAndroid Build Coastguard Worker
188*d57664e9SAndroid Build Coastguard Worker std::string error;
189*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> manifest =
190*d57664e9SAndroid Build Coastguard Worker xml::Inflate(manifest_data->data(), manifest_data->size(), &error);
191*d57664e9SAndroid Build Coastguard Worker if (manifest == nullptr) {
192*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage(source)
193*d57664e9SAndroid Build Coastguard Worker << "failed to parse binary " << kAndroidManifestPath << ": " << error);
194*d57664e9SAndroid Build Coastguard Worker return {};
195*d57664e9SAndroid Build Coastguard Worker }
196*d57664e9SAndroid Build Coastguard Worker return util::make_unique<LoadedApk>(source, std::move(collection), std::move(table),
197*d57664e9SAndroid Build Coastguard Worker std::move(manifest), ApkFormat::kBinary);
198*d57664e9SAndroid Build Coastguard Worker }
199*d57664e9SAndroid Build Coastguard Worker
WriteToArchive(IAaptContext * context,const TableFlattenerOptions & options,IArchiveWriter * writer)200*d57664e9SAndroid Build Coastguard Worker bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
201*d57664e9SAndroid Build Coastguard Worker IArchiveWriter* writer) {
202*d57664e9SAndroid Build Coastguard Worker FilterChain empty;
203*d57664e9SAndroid Build Coastguard Worker return WriteToArchive(context, table_.get(), options, &empty, writer);
204*d57664e9SAndroid Build Coastguard Worker }
205*d57664e9SAndroid Build Coastguard Worker
WriteToArchive(IAaptContext * context,ResourceTable * split_table,const TableFlattenerOptions & options,FilterChain * filters,IArchiveWriter * writer,XmlResource * manifest)206*d57664e9SAndroid Build Coastguard Worker bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table,
207*d57664e9SAndroid Build Coastguard Worker const TableFlattenerOptions& options, FilterChain* filters,
208*d57664e9SAndroid Build Coastguard Worker IArchiveWriter* writer, XmlResource* manifest) {
209*d57664e9SAndroid Build Coastguard Worker std::set<std::string> referenced_resources;
210*d57664e9SAndroid Build Coastguard Worker // List the files being referenced in the resource table.
211*d57664e9SAndroid Build Coastguard Worker for (auto& pkg : split_table->packages) {
212*d57664e9SAndroid Build Coastguard Worker for (auto& type : pkg->types) {
213*d57664e9SAndroid Build Coastguard Worker for (auto& entry : type->entries) {
214*d57664e9SAndroid Build Coastguard Worker for (auto& config_value : entry->values) {
215*d57664e9SAndroid Build Coastguard Worker FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
216*d57664e9SAndroid Build Coastguard Worker if (file_ref) {
217*d57664e9SAndroid Build Coastguard Worker referenced_resources.insert(*file_ref->path);
218*d57664e9SAndroid Build Coastguard Worker }
219*d57664e9SAndroid Build Coastguard Worker }
220*d57664e9SAndroid Build Coastguard Worker }
221*d57664e9SAndroid Build Coastguard Worker }
222*d57664e9SAndroid Build Coastguard Worker }
223*d57664e9SAndroid Build Coastguard Worker
224*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IFileCollectionIterator> iterator = apk_->Iterator();
225*d57664e9SAndroid Build Coastguard Worker while (iterator->HasNext()) {
226*d57664e9SAndroid Build Coastguard Worker io::IFile* file = iterator->Next();
227*d57664e9SAndroid Build Coastguard Worker std::string path = file->GetSource().path;
228*d57664e9SAndroid Build Coastguard Worker
229*d57664e9SAndroid Build Coastguard Worker std::string output_path = path;
230*d57664e9SAndroid Build Coastguard Worker bool is_resource = path.find("res/") == 0;
231*d57664e9SAndroid Build Coastguard Worker if (is_resource) {
232*d57664e9SAndroid Build Coastguard Worker auto it = options.shortened_path_map.find(path);
233*d57664e9SAndroid Build Coastguard Worker if (it != options.shortened_path_map.end()) {
234*d57664e9SAndroid Build Coastguard Worker output_path = it->second;
235*d57664e9SAndroid Build Coastguard Worker }
236*d57664e9SAndroid Build Coastguard Worker }
237*d57664e9SAndroid Build Coastguard Worker
238*d57664e9SAndroid Build Coastguard Worker // Skip resources that are not referenced if requested.
239*d57664e9SAndroid Build Coastguard Worker if (is_resource && referenced_resources.find(output_path) == referenced_resources.end()) {
240*d57664e9SAndroid Build Coastguard Worker if (context->IsVerbose()) {
241*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Note(android::DiagMessage()
242*d57664e9SAndroid Build Coastguard Worker << "Removing resource '" << path << "' from APK.");
243*d57664e9SAndroid Build Coastguard Worker }
244*d57664e9SAndroid Build Coastguard Worker continue;
245*d57664e9SAndroid Build Coastguard Worker }
246*d57664e9SAndroid Build Coastguard Worker
247*d57664e9SAndroid Build Coastguard Worker if (!filters->Keep(path)) {
248*d57664e9SAndroid Build Coastguard Worker if (context->IsVerbose()) {
249*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Note(android::DiagMessage()
250*d57664e9SAndroid Build Coastguard Worker << "Filtered '" << path << "' from APK.");
251*d57664e9SAndroid Build Coastguard Worker }
252*d57664e9SAndroid Build Coastguard Worker continue;
253*d57664e9SAndroid Build Coastguard Worker }
254*d57664e9SAndroid Build Coastguard Worker
255*d57664e9SAndroid Build Coastguard Worker // The resource table needs to be re-serialized since it might have changed.
256*d57664e9SAndroid Build Coastguard Worker if (format_ == ApkFormat::kBinary && path == kApkResourceTablePath) {
257*d57664e9SAndroid Build Coastguard Worker android::BigBuffer buffer(4096);
258*d57664e9SAndroid Build Coastguard Worker // TODO(adamlesinski): How to determine if there were sparse entries (and if to encode
259*d57664e9SAndroid Build Coastguard Worker // with sparse entries) b/35389232.
260*d57664e9SAndroid Build Coastguard Worker TableFlattener flattener(options, &buffer);
261*d57664e9SAndroid Build Coastguard Worker if (!flattener.Consume(context, split_table)) {
262*d57664e9SAndroid Build Coastguard Worker return false;
263*d57664e9SAndroid Build Coastguard Worker }
264*d57664e9SAndroid Build Coastguard Worker
265*d57664e9SAndroid Build Coastguard Worker android::BigBufferInputStream input_stream(&buffer);
266*d57664e9SAndroid Build Coastguard Worker if (!io::CopyInputStreamToArchive(context,
267*d57664e9SAndroid Build Coastguard Worker &input_stream,
268*d57664e9SAndroid Build Coastguard Worker path,
269*d57664e9SAndroid Build Coastguard Worker ArchiveEntry::kAlign,
270*d57664e9SAndroid Build Coastguard Worker writer)) {
271*d57664e9SAndroid Build Coastguard Worker return false;
272*d57664e9SAndroid Build Coastguard Worker }
273*d57664e9SAndroid Build Coastguard Worker } else if (format_ == ApkFormat::kProto && path == kProtoResourceTablePath) {
274*d57664e9SAndroid Build Coastguard Worker SerializeTableOptions proto_serialize_options;
275*d57664e9SAndroid Build Coastguard Worker proto_serialize_options.collapse_key_stringpool =
276*d57664e9SAndroid Build Coastguard Worker options.collapse_key_stringpool;
277*d57664e9SAndroid Build Coastguard Worker proto_serialize_options.name_collapse_exemptions =
278*d57664e9SAndroid Build Coastguard Worker options.name_collapse_exemptions;
279*d57664e9SAndroid Build Coastguard Worker pb::ResourceTable pb_table;
280*d57664e9SAndroid Build Coastguard Worker SerializeTableToPb(*split_table, &pb_table, context->GetDiagnostics(),
281*d57664e9SAndroid Build Coastguard Worker proto_serialize_options);
282*d57664e9SAndroid Build Coastguard Worker if (!io::CopyProtoToArchive(context,
283*d57664e9SAndroid Build Coastguard Worker &pb_table,
284*d57664e9SAndroid Build Coastguard Worker path,
285*d57664e9SAndroid Build Coastguard Worker ArchiveEntry::kAlign, writer)) {
286*d57664e9SAndroid Build Coastguard Worker return false;
287*d57664e9SAndroid Build Coastguard Worker }
288*d57664e9SAndroid Build Coastguard Worker } else if (manifest != nullptr && path == "AndroidManifest.xml") {
289*d57664e9SAndroid Build Coastguard Worker android::BigBuffer buffer(8192);
290*d57664e9SAndroid Build Coastguard Worker XmlFlattenerOptions xml_flattener_options;
291*d57664e9SAndroid Build Coastguard Worker xml_flattener_options.use_utf16 = true;
292*d57664e9SAndroid Build Coastguard Worker XmlFlattener xml_flattener(&buffer, xml_flattener_options);
293*d57664e9SAndroid Build Coastguard Worker if (!xml_flattener.Consume(context, manifest)) {
294*d57664e9SAndroid Build Coastguard Worker context->GetDiagnostics()->Error(android::DiagMessage(path) << "flattening failed");
295*d57664e9SAndroid Build Coastguard Worker return false;
296*d57664e9SAndroid Build Coastguard Worker }
297*d57664e9SAndroid Build Coastguard Worker
298*d57664e9SAndroid Build Coastguard Worker uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
299*d57664e9SAndroid Build Coastguard Worker android::BigBufferInputStream manifest_buffer_in(&buffer);
300*d57664e9SAndroid Build Coastguard Worker if (!io::CopyInputStreamToArchive(context, &manifest_buffer_in, path, compression_flags,
301*d57664e9SAndroid Build Coastguard Worker writer)) {
302*d57664e9SAndroid Build Coastguard Worker return false;
303*d57664e9SAndroid Build Coastguard Worker }
304*d57664e9SAndroid Build Coastguard Worker } else {
305*d57664e9SAndroid Build Coastguard Worker if (!io::CopyFileToArchivePreserveCompression(
306*d57664e9SAndroid Build Coastguard Worker context, file, output_path, writer)) {
307*d57664e9SAndroid Build Coastguard Worker return false;
308*d57664e9SAndroid Build Coastguard Worker }
309*d57664e9SAndroid Build Coastguard Worker }
310*d57664e9SAndroid Build Coastguard Worker }
311*d57664e9SAndroid Build Coastguard Worker return true;
312*d57664e9SAndroid Build Coastguard Worker }
313*d57664e9SAndroid Build Coastguard Worker
LoadXml(const std::string & file_path,android::IDiagnostics * diag) const314*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> LoadedApk::LoadXml(const std::string& file_path,
315*d57664e9SAndroid Build Coastguard Worker android::IDiagnostics* diag) const {
316*d57664e9SAndroid Build Coastguard Worker io::IFile* file = apk_->FindFile(file_path);
317*d57664e9SAndroid Build Coastguard Worker if (file == nullptr) {
318*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage() << "failed to find file");
319*d57664e9SAndroid Build Coastguard Worker return nullptr;
320*d57664e9SAndroid Build Coastguard Worker }
321*d57664e9SAndroid Build Coastguard Worker
322*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<xml::XmlResource> doc;
323*d57664e9SAndroid Build Coastguard Worker if (format_ == ApkFormat::kProto) {
324*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<android::InputStream> in = file->OpenInputStream();
325*d57664e9SAndroid Build Coastguard Worker if (!in) {
326*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage() << "failed to open file");
327*d57664e9SAndroid Build Coastguard Worker return nullptr;
328*d57664e9SAndroid Build Coastguard Worker }
329*d57664e9SAndroid Build Coastguard Worker
330*d57664e9SAndroid Build Coastguard Worker pb::XmlNode pb_node;
331*d57664e9SAndroid Build Coastguard Worker io::ProtoInputStreamReader proto_reader(in.get());
332*d57664e9SAndroid Build Coastguard Worker if (!proto_reader.ReadMessage(&pb_node)) {
333*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage() << "failed to parse file as proto XML");
334*d57664e9SAndroid Build Coastguard Worker return nullptr;
335*d57664e9SAndroid Build Coastguard Worker }
336*d57664e9SAndroid Build Coastguard Worker
337*d57664e9SAndroid Build Coastguard Worker std::string err;
338*d57664e9SAndroid Build Coastguard Worker doc = DeserializeXmlResourceFromPb(pb_node, &err);
339*d57664e9SAndroid Build Coastguard Worker if (!doc) {
340*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage() << "failed to deserialize proto XML: " << err);
341*d57664e9SAndroid Build Coastguard Worker return nullptr;
342*d57664e9SAndroid Build Coastguard Worker }
343*d57664e9SAndroid Build Coastguard Worker } else if (format_ == ApkFormat::kBinary) {
344*d57664e9SAndroid Build Coastguard Worker std::unique_ptr<io::IData> data = file->OpenAsData();
345*d57664e9SAndroid Build Coastguard Worker if (!data) {
346*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage() << "failed to open file");
347*d57664e9SAndroid Build Coastguard Worker return nullptr;
348*d57664e9SAndroid Build Coastguard Worker }
349*d57664e9SAndroid Build Coastguard Worker
350*d57664e9SAndroid Build Coastguard Worker std::string err;
351*d57664e9SAndroid Build Coastguard Worker doc = xml::Inflate(data->data(), data->size(), &err);
352*d57664e9SAndroid Build Coastguard Worker if (!doc) {
353*d57664e9SAndroid Build Coastguard Worker diag->Error(android::DiagMessage() << "failed to parse file as binary XML: " << err);
354*d57664e9SAndroid Build Coastguard Worker return nullptr;
355*d57664e9SAndroid Build Coastguard Worker }
356*d57664e9SAndroid Build Coastguard Worker }
357*d57664e9SAndroid Build Coastguard Worker
358*d57664e9SAndroid Build Coastguard Worker return doc;
359*d57664e9SAndroid Build Coastguard Worker }
360*d57664e9SAndroid Build Coastguard Worker
361*d57664e9SAndroid Build Coastguard Worker } // namespace aapt
362