blob: 027ac8ff0000240b083a9c1638f9b89d2c67da89 [file] [log] [blame]
Adam Lesinski8780eb62017-10-31 17:44:39 -07001/*
2 * Copyright (C) 2017 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 <vector>
18
19#include "android-base/macros.h"
20#include "androidfw/StringPiece.h"
21
22#include "Flags.h"
23#include "LoadedApk.h"
24#include "ValueVisitor.h"
25#include "cmd/Util.h"
26#include "format/binary/TableFlattener.h"
27#include "format/binary/XmlFlattener.h"
28#include "format/proto/ProtoDeserialize.h"
29#include "io/BigBufferStream.h"
30#include "io/Util.h"
31#include "process/IResourceTableConsumer.h"
32#include "process/SymbolTable.h"
33
34using ::android::StringPiece;
35using ::std::unique_ptr;
36using ::std::vector;
37
38namespace aapt {
39
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000040class ISerializer {
41 public:
42 ISerializer(IAaptContext* context, const Source& source) : context_(context), source_(source) {}
43
44 virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
45 IArchiveWriter* writer) = 0;
46 virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0;
47 virtual bool SerializeFile(const FileReference* file, IArchiveWriter* writer) = 0;
48
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000049 virtual ~ISerializer() = default;
50
51 protected:
52 IAaptContext* context_;
53 Source source_;
54};
Adam Lesinski8780eb62017-10-31 17:44:39 -070055
56bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk,
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000057 ISerializer* serializer, IArchiveWriter* writer) {
Pierre Lecesned55bef72017-11-10 22:31:01 +000058 // AndroidManifest.xml
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000059 if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
60 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
61 << "failed to serialize AndroidManifest.xml");
Adam Lesinski8780eb62017-10-31 17:44:39 -070062 return false;
63 }
64
Pierre Lecesned55bef72017-11-10 22:31:01 +000065 // Resource table
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000066 if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) {
67 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
68 << "failed to serialize the resource table");
Adam Lesinski8780eb62017-10-31 17:44:39 -070069 return false;
70 }
71
Pierre Lecesned55bef72017-11-10 22:31:01 +000072 // Resources
Adam Lesinski8780eb62017-10-31 17:44:39 -070073 for (const auto& package : apk->GetResourceTable()->packages) {
74 for (const auto& type : package->types) {
75 for (const auto& entry : type->entries) {
76 for (const auto& config_value : entry->values) {
77 const FileReference* file = ValueCast<FileReference>(config_value->value.get());
78 if (file != nullptr) {
79 if (file->file == nullptr) {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000080 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
Adam Lesinski8780eb62017-10-31 17:44:39 -070081 << "no file associated with " << *file);
82 return false;
83 }
84
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000085 if (!serializer->SerializeFile(file, writer)) {
86 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
87 << "failed to serialize file " << *file->path);
88 return false;
Adam Lesinski8780eb62017-10-31 17:44:39 -070089 }
Adam Lesinski8780eb62017-10-31 17:44:39 -070090 } // file
91 } // config_value
92 } // entry
93 } // type
94 } // package
Pierre Lecesned55bef72017-11-10 22:31:01 +000095
96 // Other files
97 std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator();
98 while (iterator->HasNext()) {
99 io::IFile* file = iterator->Next();
100
101 std::string path = file->GetSource().path;
102 // The name of the path has the format "<zip-file-name>@<path-to-file>".
103 path = path.substr(path.find('@') + 1);
104
105 // Manifest, resource table and resources have already been taken care of.
106 if (path == kAndroidManifestPath ||
107 path == kApkResourceTablePath ||
108 path == kProtoResourceTablePath ||
109 path.find("res/") == 0) {
110 continue;
111 }
112
113 if (!io::CopyFileToArchivePreserveCompression(context, file, path, writer)) {
114 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
115 << "failed to copy file " << path);
116 return false;
117 }
118 }
119
Adam Lesinski8780eb62017-10-31 17:44:39 -0700120 return true;
121}
122
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000123
124class BinarySerializer : public ISerializer {
125 public:
126 BinarySerializer(IAaptContext* context, const Source& source,
127 const TableFlattenerOptions& options)
128 : ISerializer(context, source), tableFlattenerOptions_(options) {}
129
130 bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
131 IArchiveWriter* writer) {
132 BigBuffer buffer(4096);
133 XmlFlattenerOptions options = {};
134 options.use_utf16 = utf16;
135 XmlFlattener flattener(&buffer, options);
136 if (!flattener.Consume(context_, xml)) {
137 return false;
138 }
139
140 io::BigBufferInputStream input_stream(&buffer);
141 return io::CopyInputStreamToArchive(context_, &input_stream, path, ArchiveEntry::kCompress,
142 writer);
143 }
144
145 bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) {
146 BigBuffer buffer(4096);
147 TableFlattener table_flattener(tableFlattenerOptions_, &buffer);
148 if (!table_flattener.Consume(context_, table)) {
149 return false;
150 }
151
152 io::BigBufferInputStream input_stream(&buffer);
153 return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
154 ArchiveEntry::kAlign, writer);
155 }
156
157 bool SerializeFile(const FileReference* file, IArchiveWriter* writer) {
158 if (file->type == ResourceFile::Type::kProtoXml) {
159 unique_ptr<io::InputStream> in = file->file->OpenInputStream();
160 if (in == nullptr) {
161 context_->GetDiagnostics()->Error(DiagMessage(source_)
162 << "failed to open file " << *file->path);
163 return false;
164 }
165
166 pb::XmlNode pb_node;
167 io::ZeroCopyInputAdaptor adaptor(in.get());
168 if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
169 context_->GetDiagnostics()->Error(DiagMessage(source_)
170 << "failed to parse proto XML " << *file->path);
171 return false;
172 }
173
174 std::string error;
175 unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
176 if (xml == nullptr) {
177 context_->GetDiagnostics()->Error(DiagMessage(source_)
178 << "failed to deserialize proto XML "
179 << *file->path << ": " << error);
180 return false;
181 }
182
183 if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
184 context_->GetDiagnostics()->Error(DiagMessage(source_)
185 << "failed to serialize proto XML " << *file->path);
186 return false;
187 }
188 } else {
Pierre Lecesned55bef72017-11-10 22:31:01 +0000189 if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) {
190 context_->GetDiagnostics()->Error(DiagMessage(source_)
191 << "failed to copy file " << *file->path);
192 return false;
193 }
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000194 }
195
196 return true;
197 }
198
199 private:
200 TableFlattenerOptions tableFlattenerOptions_;
201
202 DISALLOW_COPY_AND_ASSIGN(BinarySerializer);
203};
204
Adam Lesinski8780eb62017-10-31 17:44:39 -0700205class Context : public IAaptContext {
206 public:
207 Context() : mangler_({}), symbols_(&mangler_) {
208 }
209
210 PackageType GetPackageType() override {
211 return PackageType::kApp;
212 }
213
214 SymbolTable* GetExternalSymbols() override {
215 return &symbols_;
216 }
217
218 IDiagnostics* GetDiagnostics() override {
219 return &diag_;
220 }
221
222 const std::string& GetCompilationPackage() override {
223 return package_;
224 }
225
226 uint8_t GetPackageId() override {
227 // Nothing should call this.
228 UNIMPLEMENTED(FATAL) << "PackageID should not be necessary";
229 return 0;
230 }
231
232 NameMangler* GetNameMangler() override {
233 UNIMPLEMENTED(FATAL);
234 return nullptr;
235 }
236
237 bool IsVerbose() override {
238 return verbose_;
239 }
240
241 int GetMinSdkVersion() override {
242 return 0u;
243 }
244
245 bool verbose_ = false;
246 std::string package_;
247
248 private:
249 DISALLOW_COPY_AND_ASSIGN(Context);
250
251 NameMangler mangler_;
252 SymbolTable symbols_;
253 StdErrDiagnostics diag_;
254};
255
256int Convert(const vector<StringPiece>& args) {
257 Context context;
258 std::string output_path;
259 TableFlattenerOptions options;
260 Flags flags =
261 Flags()
262 .RequiredFlag("-o", "Output path", &output_path)
263 .OptionalSwitch("--enable-sparse-encoding",
264 "Enables encoding sparse entries using a binary search tree.\n"
265 "This decreases APK size at the cost of resource retrieval performance.",
266 &options.use_sparse_entries)
267 .OptionalSwitch("-v", "Enables verbose logging", &context.verbose_);
268 if (!flags.Parse("aapt2 convert", args, &std::cerr)) {
269 return 1;
270 }
271
272 if (flags.GetArgs().size() != 1) {
273 std::cerr << "must supply a single proto APK\n";
274 flags.Usage("aapt2 convert", &std::cerr);
275 return 1;
276 }
277
278 const StringPiece& path = flags.GetArgs()[0];
279 unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
280 if (apk == nullptr) {
281 context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK");
282 return 1;
283 }
284
285 Maybe<AppInfo> app_info =
286 ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
287 if (!app_info) {
288 return 1;
289 }
290
291 context.package_ = app_info.value().package;
292
293 unique_ptr<IArchiveWriter> writer =
294 CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path);
295 if (writer == nullptr) {
296 return 1;
297 }
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000298 BinarySerializer serializer(&context, apk->GetSource(), options);
299 return ConvertProtoApkToBinaryApk(&context, std::move(apk), &serializer, writer.get()) ? 0 : 1;
Adam Lesinski8780eb62017-10-31 17:44:39 -0700300}
301
302} // namespace aapt