blob: a34d51be221b26bc1b5bb4083ab2a4fac79e545a [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
49 bool CopyFileToArchive(const FileReference* file, IArchiveWriter* writer) {
50 uint32_t compression_flags = file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
51 if (!io::CopyFileToArchive(context_, file->file, *file->path, compression_flags, writer)) {
52 context_->GetDiagnostics()->Error(DiagMessage(source_)
53 << "failed to copy file " << *file->path);
54 return false;
55 }
56
57 return true;
Adam Lesinski8780eb62017-10-31 17:44:39 -070058 }
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000059
60 virtual ~ISerializer() = default;
61
62 protected:
63 IAaptContext* context_;
64 Source source_;
65};
Adam Lesinski8780eb62017-10-31 17:44:39 -070066
67bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk,
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000068 ISerializer* serializer, IArchiveWriter* writer) {
69 if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
70 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
71 << "failed to serialize AndroidManifest.xml");
Adam Lesinski8780eb62017-10-31 17:44:39 -070072 return false;
73 }
74
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000075 if (!serializer->SerializeTable(apk->GetResourceTable(), writer)) {
76 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
77 << "failed to serialize the resource table");
Adam Lesinski8780eb62017-10-31 17:44:39 -070078 return false;
79 }
80
81 for (const auto& package : apk->GetResourceTable()->packages) {
82 for (const auto& type : package->types) {
83 for (const auto& entry : type->entries) {
84 for (const auto& config_value : entry->values) {
85 const FileReference* file = ValueCast<FileReference>(config_value->value.get());
86 if (file != nullptr) {
87 if (file->file == nullptr) {
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000088 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
Adam Lesinski8780eb62017-10-31 17:44:39 -070089 << "no file associated with " << *file);
90 return false;
91 }
92
Pierre Lecesne7e8549d2017-11-10 01:22:12 +000093 if (!serializer->SerializeFile(file, writer)) {
94 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
95 << "failed to serialize file " << *file->path);
96 return false;
Adam Lesinski8780eb62017-10-31 17:44:39 -070097 }
Adam Lesinski8780eb62017-10-31 17:44:39 -070098 } // file
99 } // config_value
100 } // entry
101 } // type
102 } // package
103 return true;
104}
105
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000106
107class BinarySerializer : public ISerializer {
108 public:
109 BinarySerializer(IAaptContext* context, const Source& source,
110 const TableFlattenerOptions& options)
111 : ISerializer(context, source), tableFlattenerOptions_(options) {}
112
113 bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16,
114 IArchiveWriter* writer) {
115 BigBuffer buffer(4096);
116 XmlFlattenerOptions options = {};
117 options.use_utf16 = utf16;
118 XmlFlattener flattener(&buffer, options);
119 if (!flattener.Consume(context_, xml)) {
120 return false;
121 }
122
123 io::BigBufferInputStream input_stream(&buffer);
124 return io::CopyInputStreamToArchive(context_, &input_stream, path, ArchiveEntry::kCompress,
125 writer);
126 }
127
128 bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) {
129 BigBuffer buffer(4096);
130 TableFlattener table_flattener(tableFlattenerOptions_, &buffer);
131 if (!table_flattener.Consume(context_, table)) {
132 return false;
133 }
134
135 io::BigBufferInputStream input_stream(&buffer);
136 return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath,
137 ArchiveEntry::kAlign, writer);
138 }
139
140 bool SerializeFile(const FileReference* file, IArchiveWriter* writer) {
141 if (file->type == ResourceFile::Type::kProtoXml) {
142 unique_ptr<io::InputStream> in = file->file->OpenInputStream();
143 if (in == nullptr) {
144 context_->GetDiagnostics()->Error(DiagMessage(source_)
145 << "failed to open file " << *file->path);
146 return false;
147 }
148
149 pb::XmlNode pb_node;
150 io::ZeroCopyInputAdaptor adaptor(in.get());
151 if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
152 context_->GetDiagnostics()->Error(DiagMessage(source_)
153 << "failed to parse proto XML " << *file->path);
154 return false;
155 }
156
157 std::string error;
158 unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
159 if (xml == nullptr) {
160 context_->GetDiagnostics()->Error(DiagMessage(source_)
161 << "failed to deserialize proto XML "
162 << *file->path << ": " << error);
163 return false;
164 }
165
166 if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer)) {
167 context_->GetDiagnostics()->Error(DiagMessage(source_)
168 << "failed to serialize proto XML " << *file->path);
169 return false;
170 }
171 } else {
172 return CopyFileToArchive(file, writer);
173 }
174
175 return true;
176 }
177
178 private:
179 TableFlattenerOptions tableFlattenerOptions_;
180
181 DISALLOW_COPY_AND_ASSIGN(BinarySerializer);
182};
183
Adam Lesinski8780eb62017-10-31 17:44:39 -0700184class Context : public IAaptContext {
185 public:
186 Context() : mangler_({}), symbols_(&mangler_) {
187 }
188
189 PackageType GetPackageType() override {
190 return PackageType::kApp;
191 }
192
193 SymbolTable* GetExternalSymbols() override {
194 return &symbols_;
195 }
196
197 IDiagnostics* GetDiagnostics() override {
198 return &diag_;
199 }
200
201 const std::string& GetCompilationPackage() override {
202 return package_;
203 }
204
205 uint8_t GetPackageId() override {
206 // Nothing should call this.
207 UNIMPLEMENTED(FATAL) << "PackageID should not be necessary";
208 return 0;
209 }
210
211 NameMangler* GetNameMangler() override {
212 UNIMPLEMENTED(FATAL);
213 return nullptr;
214 }
215
216 bool IsVerbose() override {
217 return verbose_;
218 }
219
220 int GetMinSdkVersion() override {
221 return 0u;
222 }
223
224 bool verbose_ = false;
225 std::string package_;
226
227 private:
228 DISALLOW_COPY_AND_ASSIGN(Context);
229
230 NameMangler mangler_;
231 SymbolTable symbols_;
232 StdErrDiagnostics diag_;
233};
234
235int Convert(const vector<StringPiece>& args) {
236 Context context;
237 std::string output_path;
238 TableFlattenerOptions options;
239 Flags flags =
240 Flags()
241 .RequiredFlag("-o", "Output path", &output_path)
242 .OptionalSwitch("--enable-sparse-encoding",
243 "Enables encoding sparse entries using a binary search tree.\n"
244 "This decreases APK size at the cost of resource retrieval performance.",
245 &options.use_sparse_entries)
246 .OptionalSwitch("-v", "Enables verbose logging", &context.verbose_);
247 if (!flags.Parse("aapt2 convert", args, &std::cerr)) {
248 return 1;
249 }
250
251 if (flags.GetArgs().size() != 1) {
252 std::cerr << "must supply a single proto APK\n";
253 flags.Usage("aapt2 convert", &std::cerr);
254 return 1;
255 }
256
257 const StringPiece& path = flags.GetArgs()[0];
258 unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
259 if (apk == nullptr) {
260 context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK");
261 return 1;
262 }
263
264 Maybe<AppInfo> app_info =
265 ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
266 if (!app_info) {
267 return 1;
268 }
269
270 context.package_ = app_info.value().package;
271
272 unique_ptr<IArchiveWriter> writer =
273 CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path);
274 if (writer == nullptr) {
275 return 1;
276 }
Pierre Lecesne7e8549d2017-11-10 01:22:12 +0000277 BinarySerializer serializer(&context, apk->GetSource(), options);
278 return ConvertProtoApkToBinaryApk(&context, std::move(apk), &serializer, writer.get()) ? 0 : 1;
Adam Lesinski8780eb62017-10-31 17:44:39 -0700279}
280
281} // namespace aapt