blob: c1815c82b5b5ccc6dd28d373595aea11ccb13ce2 [file] [log] [blame]
Pierre Lecesneff759e62017-02-01 00:29:25 +00001/*
2 * Copyright (C) 2016 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 "LoadedApk.h"
18
Pierre Lecesne2599aa42017-02-01 22:47:03 +000019#include "ResourceValues.h"
20#include "ValueVisitor.h"
Adam Lesinski46708052017-09-29 14:49:15 -070021#include "format/Archive.h"
22#include "format/binary/TableFlattener.h"
23#include "format/binary/XmlFlattener.h"
Adam Lesinski06460ef2017-03-14 18:52:13 -070024#include "io/BigBufferInputStream.h"
Adam Lesinskid0f492d2017-04-03 18:12:45 -070025#include "io/Util.h"
Shane Farmer3edd4722017-09-01 14:34:22 -070026#include "xml/XmlDom.h"
Pierre Lecesne2599aa42017-02-01 22:47:03 +000027
Pierre Lecesneff759e62017-02-01 00:29:25 +000028namespace aapt {
29
Shane Farmer3edd4722017-09-01 14:34:22 -070030using xml::XmlResource;
31
Pierre Lecesne2599aa42017-02-01 22:47:03 +000032std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
33 const android::StringPiece& path) {
Pierre Lecesneff759e62017-02-01 00:29:25 +000034 Source source(path);
35 std::string error;
Adam Lesinski06460ef2017-03-14 18:52:13 -070036 std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::Create(path, &error);
Pierre Lecesneff759e62017-02-01 00:29:25 +000037 if (!apk) {
38 context->GetDiagnostics()->Error(DiagMessage(source) << error);
39 return {};
40 }
41
42 io::IFile* file = apk->FindFile("resources.arsc");
43 if (!file) {
Adam Lesinski06460ef2017-03-14 18:52:13 -070044 context->GetDiagnostics()->Error(DiagMessage(source) << "no resources.arsc found");
Pierre Lecesneff759e62017-02-01 00:29:25 +000045 return {};
46 }
47
48 std::unique_ptr<io::IData> data = file->OpenAsData();
49 if (!data) {
Adam Lesinski06460ef2017-03-14 18:52:13 -070050 context->GetDiagnostics()->Error(DiagMessage(source) << "could not open resources.arsc");
Pierre Lecesneff759e62017-02-01 00:29:25 +000051 return {};
52 }
53
54 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
Adam Lesinskid0f492d2017-04-03 18:12:45 -070055 BinaryResourceParser parser(context, table.get(), source, data->data(), data->size(), apk.get());
Pierre Lecesneff759e62017-02-01 00:29:25 +000056 if (!parser.Parse()) {
57 return {};
58 }
Shane Farmer3edd4722017-09-01 14:34:22 -070059
Pierre Lecesneff759e62017-02-01 00:29:25 +000060 return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
61}
62
Adam Lesinskid48944a2017-02-21 14:22:30 -080063bool LoadedApk::WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options,
64 IArchiveWriter* writer) {
Shane Farmer57669432017-06-19 12:52:04 -070065 FilterChain empty;
Shane Farmer0a5b2012017-06-22 12:24:12 -070066 return WriteToArchive(context, table_.get(), options, &empty, writer);
Shane Farmer57669432017-06-19 12:52:04 -070067}
68
Shane Farmer0a5b2012017-06-22 12:24:12 -070069bool LoadedApk::WriteToArchive(IAaptContext* context, ResourceTable* split_table,
70 const TableFlattenerOptions& options, FilterChain* filters,
Shane Farmer3edd4722017-09-01 14:34:22 -070071 IArchiveWriter* writer, XmlResource* manifest) {
Pierre Lecesne2599aa42017-02-01 22:47:03 +000072 std::set<std::string> referenced_resources;
73 // List the files being referenced in the resource table.
Shane Farmer0a5b2012017-06-22 12:24:12 -070074 for (auto& pkg : split_table->packages) {
Pierre Lecesne2599aa42017-02-01 22:47:03 +000075 for (auto& type : pkg->types) {
76 for (auto& entry : type->entries) {
77 for (auto& config_value : entry->values) {
78 FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
79 if (file_ref) {
80 referenced_resources.insert(*file_ref->path);
81 }
82 }
83 }
84 }
85 }
86
87 std::unique_ptr<io::IFileCollectionIterator> iterator = apk_->Iterator();
88 while (iterator->HasNext()) {
89 io::IFile* file = iterator->Next();
90
91 std::string path = file->GetSource().path;
92 // The name of the path has the format "<zip-file-name>@<path-to-file>".
Chih-Hung Hsieh4dc58122017-08-03 16:28:10 -070093 path = path.substr(path.find('@') + 1);
Pierre Lecesne2599aa42017-02-01 22:47:03 +000094
95 // Skip resources that are not referenced if requested.
96 if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
97 if (context->IsVerbose()) {
98 context->GetDiagnostics()->Note(DiagMessage()
Pierre Lecesnefa131d52017-02-03 19:15:03 +000099 << "Removing resource '" << path << "' from APK.");
Pierre Lecesne2599aa42017-02-01 22:47:03 +0000100 }
101 continue;
102 }
103
Shane Farmer57669432017-06-19 12:52:04 -0700104 if (!filters->Keep(path)) {
105 if (context->IsVerbose()) {
106 context->GetDiagnostics()->Note(DiagMessage() << "Filtered '" << path << "' from APK.");
107 }
108 continue;
109 }
110
Adam Lesinski06460ef2017-03-14 18:52:13 -0700111 // The resource table needs to be re-serialized since it might have changed.
Pierre Lecesne2599aa42017-02-01 22:47:03 +0000112 if (path == "resources.arsc") {
Adam Lesinski06460ef2017-03-14 18:52:13 -0700113 BigBuffer buffer(4096);
Adam Lesinskic8f71aa2017-02-08 07:03:50 -0800114 // TODO(adamlesinski): How to determine if there were sparse entries (and if to encode
115 // with sparse entries) b/35389232.
Adam Lesinskid48944a2017-02-21 14:22:30 -0800116 TableFlattener flattener(options, &buffer);
Shane Farmer0a5b2012017-06-22 12:24:12 -0700117 if (!flattener.Consume(context, split_table)) {
Pierre Lecesne2599aa42017-02-01 22:47:03 +0000118 return false;
119 }
120
Adam Lesinski06460ef2017-03-14 18:52:13 -0700121 io::BigBufferInputStream input_stream(&buffer);
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700122 if (!io::CopyInputStreamToArchive(context, &input_stream, path, ArchiveEntry::kAlign,
123 writer)) {
Pierre Lecesne2599aa42017-02-01 22:47:03 +0000124 return false;
125 }
Pierre Lecesne2599aa42017-02-01 22:47:03 +0000126
Shane Farmer3edd4722017-09-01 14:34:22 -0700127 } else if (manifest != nullptr && path == "AndroidManifest.xml") {
128 BigBuffer buffer(8192);
129 XmlFlattener xml_flattener(&buffer, {});
130 if (!xml_flattener.Consume(context, manifest)) {
131 context->GetDiagnostics()->Error(DiagMessage(path) << "flattening failed");
132 return false;
133 }
134
135 uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
136 io::BigBufferInputStream manifest_buffer_in(&buffer);
137 if (!io::CopyInputStreamToArchive(context, &manifest_buffer_in, path, compression_flags,
138 writer)) {
139 return false;
140 }
Adam Lesinskid0f492d2017-04-03 18:12:45 -0700141 } else {
142 uint32_t compression_flags = file->WasCompressed() ? ArchiveEntry::kCompress : 0u;
143 if (!io::CopyFileToArchive(context, file, path, compression_flags, writer)) {
144 return false;
145 }
Pierre Lecesne2599aa42017-02-01 22:47:03 +0000146 }
147 }
Pierre Lecesne2599aa42017-02-01 22:47:03 +0000148 return true;
149}
150
Shane Farmer3edd4722017-09-01 14:34:22 -0700151std::unique_ptr<xml::XmlResource> LoadedApk::InflateManifest(IAaptContext* context) {
152 IDiagnostics* diag = context->GetDiagnostics();
153
154 io::IFile* manifest_file = GetFileCollection()->FindFile("AndroidManifest.xml");
155 if (manifest_file == nullptr) {
156 diag->Error(DiagMessage(source_) << "no AndroidManifest.xml found");
157 return {};
158 }
159
160 std::unique_ptr<io::IData> manifest_data = manifest_file->OpenAsData();
161 if (manifest_data == nullptr) {
162 diag->Error(DiagMessage(manifest_file->GetSource()) << "could not open AndroidManifest.xml");
163 return {};
164 }
165
166 std::unique_ptr<xml::XmlResource> manifest =
167 xml::Inflate(manifest_data->data(), manifest_data->size(), diag, manifest_file->GetSource());
168 if (manifest == nullptr) {
169 diag->Error(DiagMessage() << "failed to read binary AndroidManifest.xml");
170 }
171 return manifest;
172}
Pierre Lecesneff759e62017-02-01 00:29:25 +0000173} // namespace aapt