| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 1 | /* | 
|  | 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 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 17 | #include "Convert.h" | 
|  | 18 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 19 | #include <vector> | 
|  | 20 |  | 
|  | 21 | #include "android-base/macros.h" | 
|  | 22 | #include "androidfw/StringPiece.h" | 
|  | 23 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 24 | #include "LoadedApk.h" | 
|  | 25 | #include "ValueVisitor.h" | 
|  | 26 | #include "cmd/Util.h" | 
|  | 27 | #include "format/binary/TableFlattener.h" | 
|  | 28 | #include "format/binary/XmlFlattener.h" | 
|  | 29 | #include "format/proto/ProtoDeserialize.h" | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 30 | #include "format/proto/ProtoSerialize.h" | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 31 | #include "io/BigBufferStream.h" | 
|  | 32 | #include "io/Util.h" | 
|  | 33 | #include "process/IResourceTableConsumer.h" | 
|  | 34 | #include "process/SymbolTable.h" | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 35 | #include "util/Util.h" | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 36 |  | 
|  | 37 | using ::android::StringPiece; | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 38 | using ::android::base::StringPrintf; | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 39 | using ::std::unique_ptr; | 
|  | 40 | using ::std::vector; | 
|  | 41 |  | 
|  | 42 | namespace aapt { | 
|  | 43 |  | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 44 | class IApkSerializer { | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 45 | public: | 
| Ryan Mitchell | 90b7a08 | 2019-02-15 17:39:58 +0000 | [diff] [blame] | 46 | IApkSerializer(IAaptContext* context, const Source& source) : context_(context), | 
|  | 47 | source_(source) {} | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 48 |  | 
|  | 49 | virtual bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16, | 
| Ryan Mitchell | 05aebf4 | 2018-10-09 15:08:41 -0700 | [diff] [blame] | 50 | IArchiveWriter* writer, uint32_t compression_flags) = 0; | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 51 | virtual bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) = 0; | 
| David Chaloupka | b66db4e | 2018-01-15 12:35:41 +0000 | [diff] [blame] | 52 | virtual bool SerializeFile(FileReference* file, IArchiveWriter* writer) = 0; | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 53 |  | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 54 | virtual ~IApkSerializer() = default; | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 55 |  | 
|  | 56 | protected: | 
|  | 57 | IAaptContext* context_; | 
|  | 58 | Source source_; | 
|  | 59 | }; | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 60 |  | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 61 | class BinaryApkSerializer : public IApkSerializer { | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 62 | public: | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 63 | BinaryApkSerializer(IAaptContext* context, const Source& source, | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 64 | const TableFlattenerOptions& table_flattener_options, | 
|  | 65 | const XmlFlattenerOptions& xml_flattener_options) | 
|  | 66 | : IApkSerializer(context, source), | 
|  | 67 | table_flattener_options_(table_flattener_options), | 
|  | 68 | xml_flattener_options_(xml_flattener_options) {} | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 69 |  | 
|  | 70 | bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16, | 
| Ryan Mitchell | 05aebf4 | 2018-10-09 15:08:41 -0700 | [diff] [blame] | 71 | IArchiveWriter* writer, uint32_t compression_flags) override { | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 72 | BigBuffer buffer(4096); | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 73 | xml_flattener_options_.use_utf16 = utf16; | 
|  | 74 | XmlFlattener flattener(&buffer, xml_flattener_options_); | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 75 | if (!flattener.Consume(context_, xml)) { | 
|  | 76 | return false; | 
|  | 77 | } | 
|  | 78 |  | 
|  | 79 | io::BigBufferInputStream input_stream(&buffer); | 
| Ryan Mitchell | 05aebf4 | 2018-10-09 15:08:41 -0700 | [diff] [blame] | 80 | return io::CopyInputStreamToArchive(context_, &input_stream, path, compression_flags, writer); | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 81 | } | 
|  | 82 |  | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 83 | bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override { | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 84 | BigBuffer buffer(4096); | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 85 | TableFlattener table_flattener(table_flattener_options_, &buffer); | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 86 | if (!table_flattener.Consume(context_, table)) { | 
|  | 87 | return false; | 
|  | 88 | } | 
|  | 89 |  | 
|  | 90 | io::BigBufferInputStream input_stream(&buffer); | 
|  | 91 | return io::CopyInputStreamToArchive(context_, &input_stream, kApkResourceTablePath, | 
|  | 92 | ArchiveEntry::kAlign, writer); | 
|  | 93 | } | 
|  | 94 |  | 
| David Chaloupka | b66db4e | 2018-01-15 12:35:41 +0000 | [diff] [blame] | 95 | bool SerializeFile(FileReference* file, IArchiveWriter* writer) override { | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 96 | if (file->type == ResourceFile::Type::kProtoXml) { | 
|  | 97 | unique_ptr<io::InputStream> in = file->file->OpenInputStream(); | 
|  | 98 | if (in == nullptr) { | 
|  | 99 | context_->GetDiagnostics()->Error(DiagMessage(source_) | 
|  | 100 | << "failed to open file " << *file->path); | 
|  | 101 | return false; | 
|  | 102 | } | 
|  | 103 |  | 
|  | 104 | pb::XmlNode pb_node; | 
| Ryan Mitchell | e0eba7a | 2018-09-12 08:54:07 -0700 | [diff] [blame] | 105 | io::ProtoInputStreamReader proto_reader(in.get()); | 
|  | 106 | if (!proto_reader.ReadMessage(&pb_node)) { | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 107 | context_->GetDiagnostics()->Error(DiagMessage(source_) | 
|  | 108 | << "failed to parse proto XML " << *file->path); | 
|  | 109 | return false; | 
|  | 110 | } | 
|  | 111 |  | 
|  | 112 | std::string error; | 
|  | 113 | unique_ptr<xml::XmlResource> xml = DeserializeXmlResourceFromPb(pb_node, &error); | 
|  | 114 | if (xml == nullptr) { | 
|  | 115 | context_->GetDiagnostics()->Error(DiagMessage(source_) | 
|  | 116 | << "failed to deserialize proto XML " | 
|  | 117 | << *file->path << ": " << error); | 
|  | 118 | return false; | 
|  | 119 | } | 
|  | 120 |  | 
| Ryan Mitchell | 05aebf4 | 2018-10-09 15:08:41 -0700 | [diff] [blame] | 121 | if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer, | 
|  | 122 | file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) { | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 123 | context_->GetDiagnostics()->Error(DiagMessage(source_) | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 124 | << "failed to serialize to binary XML: " << *file->path); | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 125 | return false; | 
|  | 126 | } | 
| David Chaloupka | b66db4e | 2018-01-15 12:35:41 +0000 | [diff] [blame] | 127 |  | 
|  | 128 | file->type = ResourceFile::Type::kBinaryXml; | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 129 | } else { | 
| Pierre Lecesne | d55bef7 | 2017-11-10 22:31:01 +0000 | [diff] [blame] | 130 | if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) { | 
|  | 131 | context_->GetDiagnostics()->Error(DiagMessage(source_) | 
|  | 132 | << "failed to copy file " << *file->path); | 
|  | 133 | return false; | 
|  | 134 | } | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 135 | } | 
|  | 136 |  | 
|  | 137 | return true; | 
|  | 138 | } | 
|  | 139 |  | 
|  | 140 | private: | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 141 | TableFlattenerOptions table_flattener_options_; | 
|  | 142 | XmlFlattenerOptions xml_flattener_options_; | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 143 |  | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 144 | DISALLOW_COPY_AND_ASSIGN(BinaryApkSerializer); | 
|  | 145 | }; | 
|  | 146 |  | 
|  | 147 | class ProtoApkSerializer : public IApkSerializer { | 
|  | 148 | public: | 
|  | 149 | ProtoApkSerializer(IAaptContext* context, const Source& source) | 
|  | 150 | : IApkSerializer(context, source) {} | 
|  | 151 |  | 
|  | 152 | bool SerializeXml(const xml::XmlResource* xml, const std::string& path, bool utf16, | 
| Ryan Mitchell | 05aebf4 | 2018-10-09 15:08:41 -0700 | [diff] [blame] | 153 | IArchiveWriter* writer, uint32_t compression_flags) override { | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 154 | pb::XmlNode pb_node; | 
|  | 155 | SerializeXmlResourceToPb(*xml, &pb_node); | 
| Ryan Mitchell | 05aebf4 | 2018-10-09 15:08:41 -0700 | [diff] [blame] | 156 | return io::CopyProtoToArchive(context_, &pb_node, path, compression_flags, writer); | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 157 | } | 
|  | 158 |  | 
|  | 159 | bool SerializeTable(ResourceTable* table, IArchiveWriter* writer) override { | 
|  | 160 | pb::ResourceTable pb_table; | 
| Ryan Mitchell | a15c2a8 | 2018-03-26 11:05:31 -0700 | [diff] [blame] | 161 | SerializeTableToPb(*table, &pb_table, context_->GetDiagnostics()); | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 162 | return io::CopyProtoToArchive(context_, &pb_table, kProtoResourceTablePath, | 
|  | 163 | ArchiveEntry::kCompress, writer); | 
|  | 164 | } | 
|  | 165 |  | 
| David Chaloupka | b66db4e | 2018-01-15 12:35:41 +0000 | [diff] [blame] | 166 | bool SerializeFile(FileReference* file, IArchiveWriter* writer) override { | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 167 | if (file->type == ResourceFile::Type::kBinaryXml) { | 
|  | 168 | std::unique_ptr<io::IData> data = file->file->OpenAsData(); | 
|  | 169 | if (!data) { | 
|  | 170 | context_->GetDiagnostics()->Error(DiagMessage(source_) | 
| Ryan Mitchell | 90b7a08 | 2019-02-15 17:39:58 +0000 | [diff] [blame] | 171 | << "failed to open file " << *file->path); | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 172 | return false; | 
|  | 173 | } | 
|  | 174 |  | 
|  | 175 | std::string error; | 
|  | 176 | std::unique_ptr<xml::XmlResource> xml = xml::Inflate(data->data(), data->size(), &error); | 
|  | 177 | if (xml == nullptr) { | 
|  | 178 | context_->GetDiagnostics()->Error(DiagMessage(source_) << "failed to parse binary XML: " | 
| Ryan Mitchell | 90b7a08 | 2019-02-15 17:39:58 +0000 | [diff] [blame] | 179 | << error); | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 180 | return false; | 
|  | 181 | } | 
|  | 182 |  | 
| Ryan Mitchell | 05aebf4 | 2018-10-09 15:08:41 -0700 | [diff] [blame] | 183 | if (!SerializeXml(xml.get(), *file->path, false /*utf16*/, writer, | 
|  | 184 | file->file->WasCompressed() ? ArchiveEntry::kCompress : 0u)) { | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 185 | context_->GetDiagnostics()->Error(DiagMessage(source_) | 
|  | 186 | << "failed to serialize to proto XML: " << *file->path); | 
|  | 187 | return false; | 
|  | 188 | } | 
| David Chaloupka | b66db4e | 2018-01-15 12:35:41 +0000 | [diff] [blame] | 189 |  | 
|  | 190 | file->type = ResourceFile::Type::kProtoXml; | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 191 | } else { | 
|  | 192 | if (!io::CopyFileToArchivePreserveCompression(context_, file->file, *file->path, writer)) { | 
|  | 193 | context_->GetDiagnostics()->Error(DiagMessage(source_) | 
|  | 194 | << "failed to copy file " << *file->path); | 
|  | 195 | return false; | 
|  | 196 | } | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | return true; | 
|  | 200 | } | 
|  | 201 |  | 
|  | 202 | private: | 
|  | 203 | DISALLOW_COPY_AND_ASSIGN(ProtoApkSerializer); | 
| Pierre Lecesne | 7e8549d | 2017-11-10 01:22:12 +0000 | [diff] [blame] | 204 | }; | 
|  | 205 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 206 | class Context : public IAaptContext { | 
|  | 207 | public: | 
|  | 208 | Context() : mangler_({}), symbols_(&mangler_) { | 
|  | 209 | } | 
|  | 210 |  | 
|  | 211 | PackageType GetPackageType() override { | 
|  | 212 | return PackageType::kApp; | 
|  | 213 | } | 
|  | 214 |  | 
|  | 215 | SymbolTable* GetExternalSymbols() override { | 
|  | 216 | return &symbols_; | 
|  | 217 | } | 
|  | 218 |  | 
|  | 219 | IDiagnostics* GetDiagnostics() override { | 
|  | 220 | return &diag_; | 
|  | 221 | } | 
|  | 222 |  | 
|  | 223 | const std::string& GetCompilationPackage() override { | 
|  | 224 | return package_; | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | uint8_t GetPackageId() override { | 
|  | 228 | // Nothing should call this. | 
|  | 229 | UNIMPLEMENTED(FATAL) << "PackageID should not be necessary"; | 
|  | 230 | return 0; | 
|  | 231 | } | 
|  | 232 |  | 
|  | 233 | NameMangler* GetNameMangler() override { | 
|  | 234 | UNIMPLEMENTED(FATAL); | 
|  | 235 | return nullptr; | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | bool IsVerbose() override { | 
|  | 239 | return verbose_; | 
|  | 240 | } | 
|  | 241 |  | 
|  | 242 | int GetMinSdkVersion() override { | 
|  | 243 | return 0u; | 
|  | 244 | } | 
|  | 245 |  | 
| Udam Saini | b228df3 | 2019-06-18 16:50:34 -0700 | [diff] [blame] | 246 | const std::set<std::string>& GetSplitNameDependencies() override { | 
|  | 247 | UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary"; | 
|  | 248 | static std::set<std::string> empty; | 
|  | 249 | return empty; | 
|  | 250 | } | 
|  | 251 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 252 | bool verbose_ = false; | 
|  | 253 | std::string package_; | 
|  | 254 |  | 
|  | 255 | private: | 
|  | 256 | DISALLOW_COPY_AND_ASSIGN(Context); | 
|  | 257 |  | 
|  | 258 | NameMangler mangler_; | 
|  | 259 | SymbolTable symbols_; | 
|  | 260 | StdErrDiagnostics diag_; | 
|  | 261 | }; | 
|  | 262 |  | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 263 | int Convert(IAaptContext* context, LoadedApk* apk, IArchiveWriter* output_writer, | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 264 | ApkFormat output_format, TableFlattenerOptions table_flattener_options, | 
|  | 265 | XmlFlattenerOptions xml_flattener_options) { | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 266 | unique_ptr<IApkSerializer> serializer; | 
|  | 267 | if (output_format == ApkFormat::kBinary) { | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 268 | serializer.reset(new BinaryApkSerializer(context, apk->GetSource(), table_flattener_options, | 
|  | 269 | xml_flattener_options)); | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 270 | } else if (output_format == ApkFormat::kProto) { | 
|  | 271 | serializer.reset(new ProtoApkSerializer(context, apk->GetSource())); | 
|  | 272 | } else { | 
|  | 273 | context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) | 
|  | 274 | << "Cannot convert APK to unknown format"); | 
|  | 275 | return 1; | 
|  | 276 | } | 
|  | 277 |  | 
|  | 278 | io::IFile* manifest = apk->GetFileCollection()->FindFile(kAndroidManifestPath); | 
|  | 279 | if (!serializer->SerializeXml(apk->GetManifest(), kAndroidManifestPath, true /*utf16*/, | 
|  | 280 | output_writer, (manifest != nullptr && manifest->WasCompressed()) | 
| Ryan Mitchell | 90b7a08 | 2019-02-15 17:39:58 +0000 | [diff] [blame] | 281 | ? ArchiveEntry::kCompress : 0u)) { | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 282 | context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) | 
|  | 283 | << "failed to serialize AndroidManifest.xml"); | 
|  | 284 | return 1; | 
|  | 285 | } | 
|  | 286 |  | 
|  | 287 | if (apk->GetResourceTable() != nullptr) { | 
|  | 288 | // The table might be modified by below code. | 
|  | 289 | auto converted_table = apk->GetResourceTable(); | 
|  | 290 |  | 
| Winson | f54c9a1 | 2019-01-23 12:39:40 -0800 | [diff] [blame] | 291 | std::unordered_set<std::string> files_written; | 
|  | 292 |  | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 293 | // Resources | 
|  | 294 | for (const auto& package : converted_table->packages) { | 
|  | 295 | for (const auto& type : package->types) { | 
|  | 296 | for (const auto& entry : type->entries) { | 
|  | 297 | for (const auto& config_value : entry->values) { | 
|  | 298 | FileReference* file = ValueCast<FileReference>(config_value->value.get()); | 
|  | 299 | if (file != nullptr) { | 
|  | 300 | if (file->file == nullptr) { | 
|  | 301 | context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) | 
|  | 302 | << "no file associated with " << *file); | 
|  | 303 | return 1; | 
|  | 304 | } | 
|  | 305 |  | 
| Winson | f54c9a1 | 2019-01-23 12:39:40 -0800 | [diff] [blame] | 306 | // Only serialize if we haven't seen this file before | 
|  | 307 | if (files_written.insert(*file->path).second) { | 
|  | 308 | if (!serializer->SerializeFile(file, output_writer)) { | 
|  | 309 | context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) | 
| Ryan Mitchell | 90b7a08 | 2019-02-15 17:39:58 +0000 | [diff] [blame] | 310 | << "failed to serialize file " << *file->path); | 
| Winson | f54c9a1 | 2019-01-23 12:39:40 -0800 | [diff] [blame] | 311 | return 1; | 
|  | 312 | } | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 313 | } | 
|  | 314 | } // file | 
|  | 315 | } // config_value | 
|  | 316 | } // entry | 
|  | 317 | } // type | 
|  | 318 | } // package | 
|  | 319 |  | 
|  | 320 | // Converted resource table | 
|  | 321 | if (!serializer->SerializeTable(converted_table, output_writer)) { | 
|  | 322 | context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) | 
|  | 323 | << "failed to serialize the resource table"); | 
|  | 324 | return 1; | 
|  | 325 | } | 
|  | 326 | } | 
|  | 327 |  | 
|  | 328 | // Other files | 
|  | 329 | std::unique_ptr<io::IFileCollectionIterator> iterator = apk->GetFileCollection()->Iterator(); | 
|  | 330 | while (iterator->HasNext()) { | 
|  | 331 | io::IFile* file = iterator->Next(); | 
|  | 332 | std::string path = file->GetSource().path; | 
|  | 333 |  | 
|  | 334 | // Manifest, resource table and resources have already been taken care of. | 
|  | 335 | if (path == kAndroidManifestPath || | 
|  | 336 | path == kApkResourceTablePath || | 
|  | 337 | path == kProtoResourceTablePath || | 
|  | 338 | path.find("res/") == 0) { | 
|  | 339 | continue; | 
|  | 340 | } | 
|  | 341 |  | 
|  | 342 | if (!io::CopyFileToArchivePreserveCompression(context, file, path, output_writer)) { | 
|  | 343 | context->GetDiagnostics()->Error(DiagMessage(apk->GetSource()) | 
| Ryan Mitchell | 90b7a08 | 2019-02-15 17:39:58 +0000 | [diff] [blame] | 344 | << "failed to copy file " << path); | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 345 | return 1; | 
|  | 346 | } | 
|  | 347 | } | 
|  | 348 |  | 
|  | 349 | return 0; | 
|  | 350 | } | 
|  | 351 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 352 | const char* ConvertCommand::kOutputFormatProto = "proto"; | 
|  | 353 | const char* ConvertCommand::kOutputFormatBinary = "binary"; | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 354 |  | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 355 | int ConvertCommand::Action(const std::vector<std::string>& args) { | 
|  | 356 | if (args.size() != 1) { | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 357 | std::cerr << "must supply a single APK\n"; | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 358 | Usage(&std::cerr); | 
|  | 359 | return 1; | 
|  | 360 | } | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 361 |  | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 362 | Context context; | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 363 | const StringPiece& path = args[0]; | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 364 | unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(path, context.GetDiagnostics()); | 
|  | 365 | if (apk == nullptr) { | 
|  | 366 | context.GetDiagnostics()->Error(DiagMessage(path) << "failed to load APK"); | 
|  | 367 | return 1; | 
|  | 368 | } | 
|  | 369 |  | 
| Ryan Mitchell | 4382e44 | 2021-07-14 12:53:01 -0700 | [diff] [blame] | 370 | auto app_info = ExtractAppInfoFromBinaryManifest(*apk->GetManifest(), context.GetDiagnostics()); | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 371 | if (!app_info) { | 
|  | 372 | return 1; | 
|  | 373 | } | 
|  | 374 |  | 
|  | 375 | context.package_ = app_info.value().package; | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 376 | unique_ptr<IArchiveWriter> writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), | 
|  | 377 | output_path_); | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 378 | if (writer == nullptr) { | 
|  | 379 | return 1; | 
|  | 380 | } | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 381 |  | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 382 | ApkFormat format; | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 383 | if (!output_format_ || output_format_.value() == ConvertCommand::kOutputFormatBinary) { | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 384 | format = ApkFormat::kBinary; | 
| Ryan Mitchell | 833a1a6 | 2018-07-10 13:51:36 -0700 | [diff] [blame] | 385 | } else if (output_format_.value() == ConvertCommand::kOutputFormatProto) { | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 386 | format = ApkFormat::kProto; | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 387 | } else { | 
| Ryan Mitchell | 4e9a922 | 2018-11-13 10:40:07 -0800 | [diff] [blame] | 388 | context.GetDiagnostics()->Error(DiagMessage(path) << "Invalid value for flag --output-format: " | 
|  | 389 | << output_format_.value()); | 
| Pierre Lecesne | d8b4bea | 2017-11-10 23:50:17 +0000 | [diff] [blame] | 390 | return 1; | 
|  | 391 | } | 
|  | 392 |  | 
| Ryan Mitchell | 479fa39 | 2019-01-02 17:15:39 -0800 | [diff] [blame] | 393 | return Convert(&context, apk.get(), writer.get(), format, table_flattener_options_, | 
|  | 394 | xml_flattener_options_); | 
| Adam Lesinski | 8780eb6 | 2017-10-31 17:44:39 -0700 | [diff] [blame] | 395 | } | 
|  | 396 |  | 
|  | 397 | }  // namespace aapt |