blob: 76792ced86b00b3638fd35c31dfd78ba913de26e [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"
21#include "flatten/Archive.h"
22#include "flatten/TableFlattener.h"
23
Pierre Lecesneff759e62017-02-01 00:29:25 +000024namespace aapt {
25
Pierre Lecesne2599aa42017-02-01 22:47:03 +000026std::unique_ptr<LoadedApk> LoadedApk::LoadApkFromPath(IAaptContext* context,
27 const android::StringPiece& path) {
Pierre Lecesneff759e62017-02-01 00:29:25 +000028 Source source(path);
29 std::string error;
30 std::unique_ptr<io::ZipFileCollection> apk =
31 io::ZipFileCollection::Create(path, &error);
32 if (!apk) {
33 context->GetDiagnostics()->Error(DiagMessage(source) << error);
34 return {};
35 }
36
37 io::IFile* file = apk->FindFile("resources.arsc");
38 if (!file) {
39 context->GetDiagnostics()->Error(DiagMessage(source)
40 << "no resources.arsc found");
41 return {};
42 }
43
44 std::unique_ptr<io::IData> data = file->OpenAsData();
45 if (!data) {
46 context->GetDiagnostics()->Error(DiagMessage(source)
47 << "could not open resources.arsc");
48 return {};
49 }
50
51 std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
52 BinaryResourceParser parser(context, table.get(), source, data->data(),
53 data->size());
54 if (!parser.Parse()) {
55 return {};
56 }
57
58 return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table));
59}
60
Pierre Lecesne2599aa42017-02-01 22:47:03 +000061bool LoadedApk::WriteToArchive(IAaptContext* context, IArchiveWriter* writer) {
62 std::set<std::string> referenced_resources;
63 // List the files being referenced in the resource table.
64 for (auto& pkg : table_->packages) {
65 for (auto& type : pkg->types) {
66 for (auto& entry : type->entries) {
67 for (auto& config_value : entry->values) {
68 FileReference* file_ref = ValueCast<FileReference>(config_value->value.get());
69 if (file_ref) {
70 referenced_resources.insert(*file_ref->path);
71 }
72 }
73 }
74 }
75 }
76
77 std::unique_ptr<io::IFileCollectionIterator> iterator = apk_->Iterator();
78 while (iterator->HasNext()) {
79 io::IFile* file = iterator->Next();
80
81 std::string path = file->GetSource().path;
82 // The name of the path has the format "<zip-file-name>@<path-to-file>".
83 path = path.substr(path.find("@") + 1);
84
85 // Skip resources that are not referenced if requested.
86 if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
87 if (context->IsVerbose()) {
88 context->GetDiagnostics()->Note(DiagMessage()
89 << "Resource '" << path << "' not referenced in "
90 << "resource table; removing from APK.");
91 }
92 continue;
93 }
94
95 // The resource table needs to be reserialized since it might have changed.
96 if (path == "resources.arsc") {
97 BigBuffer buffer = BigBuffer(1024);
98 TableFlattener flattener(&buffer);
99 if (!flattener.Consume(context, table_.get())) {
100 return false;
101 }
102
103 if (!writer->StartEntry(path, ArchiveEntry::kAlign) || !writer->WriteEntry(buffer) ||
104 !writer->FinishEntry()) {
105 context->GetDiagnostics()->Error(DiagMessage()
106 << "Error when writing file '" << path << "' in APK.");
107 return false;
108 }
109 continue;
110 }
111
112 std::unique_ptr<io::IData> data = file->OpenAsData();
113 // TODO(lecesne): Only compress the files that were compressed in the original APK.
114 if (!writer->StartEntry(path, ArchiveEntry::kCompress) ||
115 !writer->WriteEntry(data->data(), data->size()) || !writer->FinishEntry()) {
116 context->GetDiagnostics()->Error(DiagMessage()
117 << "Error when writing file '" << path << "' in APK.");
118 return false;
119 }
120 }
121
122 return true;
123}
124
Pierre Lecesneff759e62017-02-01 00:29:25 +0000125} // namespace aapt