blob: 0e577d1c9e3cc3a68a5143e2ab5e99af0b9ec3a1 [file] [log] [blame]
Adam Lesinski7ad11102016-10-28 16:39:15 -07001/*
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#define ATRACE_TAG ATRACE_TAG_RESOURCES
18
19#include "androidfw/ApkAssets.h"
20
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -080021#include <algorithm>
22
Adam Lesinski7ad11102016-10-28 16:39:15 -070023#include "android-base/logging.h"
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -080024#include "utils/FileMap.h"
Adam Lesinski7ad11102016-10-28 16:39:15 -070025#include "utils/Trace.h"
26#include "ziparchive/zip_archive.h"
27
28#include "androidfw/Asset.h"
29#include "androidfw/Util.h"
30
31namespace android {
32
Adam Lesinski03ebac82017-09-25 13:10:14 -070033ApkAssets::ApkAssets() : zip_handle_(nullptr, ::CloseArchive) {}
34
Adam Lesinski0c405242017-01-13 20:47:26 -080035std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
36 return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/);
Adam Lesinskida431a22016-12-29 16:08:16 -050037}
38
Adam Lesinski0c405242017-01-13 20:47:26 -080039std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
40 bool system) {
41 return ApkAssets::LoadImpl(path, system, true /*load_as_shared_library*/);
Adam Lesinskida431a22016-12-29 16:08:16 -050042}
43
Adam Lesinski0c405242017-01-13 20:47:26 -080044std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bool system,
45 bool load_as_shared_library) {
Adam Lesinskida431a22016-12-29 16:08:16 -050046 ATRACE_CALL();
Adam Lesinski7ad11102016-10-28 16:39:15 -070047 ::ZipArchiveHandle unmanaged_handle;
48 int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
49 if (result != 0) {
50 LOG(ERROR) << ::ErrorCodeString(result);
51 return {};
52 }
53
54 // Wrap the handle in a unique_ptr so it gets automatically closed.
55 std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets());
56 loaded_apk->zip_handle_.reset(unmanaged_handle);
57
58 ::ZipString entry_name("resources.arsc");
59 ::ZipEntry entry;
60 result = ::FindEntry(loaded_apk->zip_handle_.get(), entry_name, &entry);
61 if (result != 0) {
62 LOG(ERROR) << ::ErrorCodeString(result);
63 return {};
64 }
65
66 if (entry.method == kCompressDeflated) {
67 LOG(WARNING) << "resources.arsc is compressed.";
68 }
69
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -080070 loaded_apk->path_ = path;
Adam Lesinski7ad11102016-10-28 16:39:15 -070071 loaded_apk->resources_asset_ =
72 loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER);
73 if (loaded_apk->resources_asset_ == nullptr) {
74 return {};
75 }
76
Adam Lesinski7ad11102016-10-28 16:39:15 -070077 loaded_apk->loaded_arsc_ =
78 LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
Adam Lesinski0c405242017-01-13 20:47:26 -080079 loaded_apk->resources_asset_->getLength(), system, load_as_shared_library);
Adam Lesinski7ad11102016-10-28 16:39:15 -070080 if (loaded_apk->loaded_arsc_ == nullptr) {
81 return {};
82 }
Adam Lesinski0c405242017-01-13 20:47:26 -080083
84 // Need to force a move for mingw32.
85 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -070086}
87
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -080088std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
89 ATRACE_CALL();
Adam Lesinski7ad11102016-10-28 16:39:15 -070090 CHECK(zip_handle_ != nullptr);
91
92 ::ZipString name(path.c_str());
93 ::ZipEntry entry;
94 int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
95 if (result != 0) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -080096 LOG(ERROR) << "No entry '" << path << "' found in APK '" << path_ << "'";
Adam Lesinski7ad11102016-10-28 16:39:15 -070097 return {};
98 }
99
100 if (entry.method == kCompressDeflated) {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800101 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
102 if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
103 entry.compressed_length, true /*readOnly*/)) {
104 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
105 return {};
106 }
107
108 std::unique_ptr<Asset> asset =
109 Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
110 if (asset == nullptr) {
Adam Lesinski7ad11102016-10-28 16:39:15 -0700111 LOG(ERROR) << "Failed to decompress '" << path << "'.";
112 return {};
113 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800114 return asset;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700115 } else {
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800116 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
117 if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
118 entry.uncompressed_length, true /*readOnly*/)) {
119 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700120 return {};
121 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800122
123 std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
124 if (asset == nullptr) {
125 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
126 return {};
127 }
128 return asset;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700129 }
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -0800130}
131
132bool ApkAssets::ForEachFile(const std::string& root_path,
133 const std::function<void(const StringPiece&, FileType)>& f) const {
134 CHECK(zip_handle_ != nullptr);
135
136 std::string root_path_full = root_path;
137 if (root_path_full.back() != '/') {
138 root_path_full += '/';
139 }
140
141 ::ZipString prefix(root_path_full.c_str());
142 void* cookie;
143 if (::StartIteration(zip_handle_.get(), &cookie, &prefix, nullptr) != 0) {
144 return false;
145 }
146
147 ::ZipString name;
148 ::ZipEntry entry;
149
150 // We need to hold back directories because many paths will contain them and we want to only
151 // surface one.
152 std::set<std::string> dirs;
153
154 int32_t result;
155 while ((result = ::Next(cookie, &entry, &name)) == 0) {
156 StringPiece full_file_path(reinterpret_cast<const char*>(name.name), name.name_length);
157 StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
158 auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
159 if (iter != leaf_file_path.end()) {
160 dirs.insert(
161 leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string());
162 } else if (!leaf_file_path.empty()) {
163 f(leaf_file_path, kFileTypeRegular);
164 }
165 }
166 ::EndIteration(cookie);
167
168 // Now present the unique directories.
169 for (const std::string& dir : dirs) {
170 f(dir, kFileTypeDirectory);
171 }
172
173 // -1 is end of iteration, anything else is an error.
174 return result == -1;
Adam Lesinski7ad11102016-10-28 16:39:15 -0700175}
176
177} // namespace android