blob: 89ae9e82f603114156c5cad669fcb1025745dc86 [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
40static bool FlattenXml(IAaptContext* context, const xml::XmlResource& xml,
41 const std::string& entry_path, bool utf16, IArchiveWriter* writer) {
42 BigBuffer buffer(4096);
43 XmlFlattenerOptions options = {};
44 options.use_utf16 = utf16;
45 XmlFlattener flattener(&buffer, options);
46 if (!flattener.Consume(context, &xml)) {
47 return false;
48 }
49 io::BigBufferInputStream input_stream(&buffer);
50 return io::CopyInputStreamToArchive(context, &input_stream, entry_path, ArchiveEntry::kCompress,
51 writer);
52}
53
54bool ConvertProtoApkToBinaryApk(IAaptContext* context, unique_ptr<LoadedApk> apk,
55 const TableFlattenerOptions& options, IArchiveWriter* writer) {
56 if (!FlattenXml(context, *apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, writer)) {
57 return false;
58 }
59
60 BigBuffer buffer(4096);
61 TableFlattener table_flattener(options, &buffer);
62 if (!table_flattener.Consume(context, apk->GetResourceTable())) {
63 return false;
64 }
65
66 io::BigBufferInputStream input_stream(&buffer);
67 if (!io::CopyInputStreamToArchive(context, &input_stream, kApkResourceTablePath,
68 ArchiveEntry::kAlign, writer)) {
69 return false;
70 }
71
72 for (const auto& package : apk->GetResourceTable()->packages) {
73 for (const auto& type : package->types) {
74 for (const auto& entry : type->entries) {
75 for (const auto& config_value : entry->values) {
76 const FileReference* file = ValueCast<FileReference>(config_value->value.get());
77 if (file != nullptr) {
78 if (file->file == nullptr) {
79 context->GetDiagnostics()->Warn(DiagMessage(apk->GetSource())
80 << "no file associated with " << *file);
81 return false;
82 }
83
84 if (file->type == ResourceFile::Type::kProtoXml) {
85 unique_ptr<io::InputStream> in = file->file->OpenInputStream();
86 if (in == nullptr) {
87 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
88 << "failed to open file " << *file->path);
89 return false;
90 }
91
92 pb::XmlNode pb_node;
93 io::ZeroCopyInputAdaptor adaptor(in.get());
94 if (!pb_node.ParseFromZeroCopyStream(&adaptor)) {
95 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
96 << "failed to parse proto XML " << *file->path);
97 return false;
98 }
99
100 std::string error;
101 unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error);
102 if (xml == nullptr) {
103 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
104 << "failed to deserialize proto XML "
105 << *file->path << ": " << error);
106 return false;
107 }
108
109 if (!FlattenXml(context, *xml, *file->path, false /*utf16*/, writer)) {
110 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
111 << "failed to serialize XML " << *file->path);
112 return false;
113 }
114 } else {
115 if (!io::CopyFileToArchive(context, file->file, *file->path,
116 file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u,
117 writer)) {
118 context->GetDiagnostics()->Error(DiagMessage(apk->GetSource())
119 << "failed to copy file " << *file->path);
120 return false;
121 }
122 }
123
124 } // file
125 } // config_value
126 } // entry
127 } // type
128 } // package
129 return true;
130}
131
132class Context : public IAaptContext {
133 public:
134 Context() : mangler_({}), symbols_(&mangler_) {
135 }
136
137 PackageType GetPackageType() override {
138 return PackageType::kApp;
139 }
140
141 SymbolTable* GetExternalSymbols() override {
142 return &symbols_;
143 }
144
145 IDiagnostics* GetDiagnostics() override {
146 return &diag_;
147 }
148
149 const std::string& GetCompilationPackage() override {
150 return package_;
151 }
152
153 uint8_t GetPackageId() override {
154 // Nothing should call this.
155 UNIMPLEMENTED(FATAL) << "PackageID should not be necessary";
156 return 0;
157 }
158
159 NameMangler* GetNameMangler() override {
160 UNIMPLEMENTED(FATAL);
161 return nullptr;
162 }
163
164 bool IsVerbose() override {
165 return verbose_;
166 }
167
168 int GetMinSdkVersion() override {
169 return 0u;
170 }
171
172 bool verbose_ = false;
173 std::string package_;
174
175 private:
176 DISALLOW_COPY_AND_ASSIGN(Context);
177
178 NameMangler mangler_;
179 SymbolTable symbols_;
180 StdErrDiagnostics diag_;
181};
182
183int Convert(const vector<StringPiece>& args) {
184 Context context;
185 std::string output_path;
186 TableFlattenerOptions options;
187 Flags flags =
188 Flags()
189 .RequiredFlag("-o", "Output path", &output_path)
190 .OptionalSwitch("--enable-sparse-encoding",
191 "Enables encoding sparse entries using a binary search tree.\n"
192 "This decreases APK size at the cost of resource retrieval performance.",
193 &options.use_sparse_entries)
194 .OptionalSwitch("-v", "Enables verbose logging", &context.verbose_);
195 if (!flags.Parse("aapt2 convert", args, &std::cerr)) {
196 return 1;
197 }
198
199 if (flags.GetArgs().size() != 1) {
200 std::cerr << "must supply a single proto APK\n";
201 flags.Usage("aapt2 convert", &std::cerr);
202 return 1;
203 }
204
205 const StringPiece& path = flags.GetArgs()[0];
206 unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics());
207 if (apk == nullptr) {
208 context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK");
209 return 1;
210 }
211
212 Maybe<AppInfo> app_info =
213 ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics());
214 if (!app_info) {
215 return 1;
216 }
217
218 context.package_ = app_info.value().package;
219
220 unique_ptr<IArchiveWriter> writer =
221 CreateZipFileArchiveWriter(context.GetDiagnostics(), output_path);
222 if (writer == nullptr) {
223 return 1;
224 }
225 return ConvertProtoApkToBinaryApk(&context, std::move(apk), options, writer.get()) ? 0 : 1;
226}
227
228} // namespace aapt