blob: cb56a5172a4557835b3166dc9bf94eb7fe8d59ad [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
Adam Lesinski7ad11102016-10-28 16:39:15 -070017#include "androidfw/ApkAssets.h"
18
Adam Lesinskid1ecd7a2017-01-23 12:58:11 -080019#include <algorithm>
20
Adam Lesinski970bd8d2017-09-25 13:21:55 -070021#include "android-base/errors.h"
22#include "android-base/file.h"
Adam Lesinski7ad11102016-10-28 16:39:15 -070023#include "android-base/logging.h"
Ryan Mitchellc07aa702020-03-10 13:49:12 -070024#include "android-base/stringprintf.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070025#include "android-base/unique_fd.h"
26#include "android-base/utf8.h"
27#include "utils/Compat.h"
Adam Lesinski7ad11102016-10-28 16:39:15 -070028#include "ziparchive/zip_archive.h"
29
30#include "androidfw/Asset.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070031#include "androidfw/Idmap.h"
Winsonb0085ce2019-02-19 12:48:22 -080032#include "androidfw/misc.h"
Adam Lesinski7ad11102016-10-28 16:39:15 -070033#include "androidfw/Util.h"
34
35namespace android {
36
Adam Lesinski970bd8d2017-09-25 13:21:55 -070037using base::SystemErrorCodeToString;
38using base::unique_fd;
39
40static const std::string kResourcesArsc("resources.arsc");
41
Ryan Mitchellc07aa702020-03-10 13:49:12 -070042ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070043 std::string path,
Winson9947f1e2019-08-16 10:20:39 -070044 time_t last_mod_time,
Ryan Mitchell73bfe412019-11-12 16:22:04 -080045 package_property_t property_flags)
Ryan Mitchellc07aa702020-03-10 13:49:12 -070046 : assets_provider_(std::move(assets_provider)),
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070047 path_(std::move(path)),
48 last_mod_time_(last_mod_time),
Ryan Mitchell73bfe412019-11-12 16:22:04 -080049 property_flags_(property_flags) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -070050}
Adam Lesinski03ebac82017-09-25 13:10:14 -070051
Ryan Mitchellc07aa702020-03-10 13:49:12 -070052// Provides asset files from a zip file.
53class ZipAssetsProvider : public AssetsProvider {
54 public:
55 ~ZipAssetsProvider() override = default;
56
57 static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
58 ::ZipArchiveHandle unmanaged_handle;
59 const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
60 if (result != 0) {
61 LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
62 ::CloseArchive(unmanaged_handle);
63 return {};
64 }
65
66 return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle));
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070067 }
68
Ryan Mitchellc07aa702020-03-10 13:49:12 -070069 static std::unique_ptr<const AssetsProvider> Create(
70 unique_fd fd, const std::string& friendly_name, const off64_t offset = 0,
71 const off64_t length = ApkAssets::kUnknownLength) {
72
73 ::ZipArchiveHandle unmanaged_handle;
74 const int32_t result = (length == ApkAssets::kUnknownLength)
75 ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle)
76 : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length,
77 offset);
78
79 if (result != 0) {
80 LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
81 << " and length " << length << ": " << ::ErrorCodeString(result);
82 ::CloseArchive(unmanaged_handle);
83 return {};
84 }
85
86 return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name,
87 unmanaged_handle));
88 }
89
90 // Iterate over all files and directories within the zip. The order of iteration is not
91 // guaranteed to be the same as the order of elements in the central directory but is stable for a
92 // given zip file.
93 bool ForEachFile(const std::string& root_path,
94 const std::function<void(const StringPiece&, FileType)>& f) const override {
95 // If this is a resource loader from an .arsc, there will be no zip handle
96 if (zip_handle_ == nullptr) {
97 return false;
98 }
99
100 std::string root_path_full = root_path;
101 if (root_path_full.back() != '/') {
102 root_path_full += '/';
103 }
104
105 void* cookie;
106 if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
107 return false;
108 }
109
110 std::string name;
111 ::ZipEntry entry{};
112
113 // We need to hold back directories because many paths will contain them and we want to only
114 // surface one.
115 std::set<std::string> dirs{};
116
117 int32_t result;
118 while ((result = ::Next(cookie, &entry, &name)) == 0) {
119 StringPiece full_file_path(name);
120 StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
121
122 if (!leaf_file_path.empty()) {
123 auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
124 if (iter != leaf_file_path.end()) {
125 std::string dir =
126 leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
127 dirs.insert(std::move(dir));
128 } else {
129 f(leaf_file_path, kFileTypeRegular);
130 }
131 }
132 }
133 ::EndIteration(cookie);
134
135 // Now present the unique directories.
136 for (const std::string& dir : dirs) {
137 f(dir, kFileTypeDirectory);
138 }
139
140 // -1 is end of iteration, anything else is an error.
141 return result == -1;
142 }
143
144 protected:
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700145 std::unique_ptr<Asset> OpenInternal(
146 const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700147 if (file_exists) {
148 *file_exists = false;
149 }
150
151 ::ZipEntry entry;
152 int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
153 if (result != 0) {
154 return {};
155 }
156
157 if (file_exists) {
158 *file_exists = true;
159 }
160
161 const int fd = ::GetFileDescriptor(zip_handle_.get());
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700162 const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
163 incfs::IncFsFileMap asset_map;
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700164 if (entry.method == kCompressDeflated) {
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700165 if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) {
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700166 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
167 return {};
168 }
169
170 std::unique_ptr<Asset> asset =
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700171 Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode);
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700172 if (asset == nullptr) {
173 LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
174 return {};
175 }
176 return asset;
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700177 }
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700178
179 if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) {
180 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
181 return {};
182 }
183
184 unique_fd ufd;
185 if (!GetPath()) {
186 // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
187 // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
188 // to create new file descriptors.
189 ufd = unique_fd(dup(fd));
190 if (!ufd.ok()) {
191 LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
192 return {};
193 }
194 }
195
196 auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd));
197 if (asset == nullptr) {
198 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
199 return {};
200 }
201 return asset;
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700202 }
203
204 private:
205 DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider);
206
207 explicit ZipAssetsProvider(std::string path,
208 std::string friendly_name,
209 ZipArchiveHandle unmanaged_handle)
210 : zip_handle_(unmanaged_handle, ::CloseArchive),
211 path_(std::move(path)),
212 friendly_name_(std::move(friendly_name)) { }
213
214 const char* GetPath() const {
215 return path_.empty() ? nullptr : path_.c_str();
216 }
217
218 using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
219 ZipArchivePtr zip_handle_;
220 std::string path_;
221 std::string friendly_name_;
222};
223
224class DirectoryAssetsProvider : AssetsProvider {
225 public:
226 ~DirectoryAssetsProvider() override = default;
227
228 static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
229 struct stat sb{};
230 const int result = stat(path.c_str(), &sb);
231 if (result == -1) {
232 LOG(ERROR) << "Failed to find directory '" << path << "'.";
233 return nullptr;
234 }
235
236 if (!S_ISDIR(sb.st_mode)) {
237 LOG(ERROR) << "Path '" << path << "' is not a directory.";
238 return nullptr;
239 }
240
241 return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path));
242 }
243
244 protected:
245 std::unique_ptr<Asset> OpenInternal(
246 const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override {
247 const std::string resolved_path = ResolvePath(path);
248 if (file_exists) {
249 struct stat sb{};
250 const int result = stat(resolved_path.c_str(), &sb);
251 *file_exists = result != -1 && S_ISREG(sb.st_mode);
252 }
253
254 return ApkAssets::CreateAssetFromFile(resolved_path);
255 }
256
257 private:
258 DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider);
259
260 explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { }
261
262 inline std::string ResolvePath(const std::string& path) const {
263 return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str());
264 }
265
266 const std::string path_;
267};
268
269// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty.
270class EmptyAssetsProvider : public AssetsProvider {
271 public:
272 EmptyAssetsProvider() = default;
273 ~EmptyAssetsProvider() override = default;
274
275 protected:
276 std::unique_ptr<Asset> OpenInternal(const std::string& /*path */,
277 Asset::AccessMode /* mode */,
278 bool* file_exists) const override {
279 if (file_exists) {
280 *file_exists = false;
281 }
282 return nullptr;
283 }
284
285 private:
286 DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider);
287};
288
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700289// AssetProvider implementation
290class MultiAssetsProvider : public AssetsProvider {
291 public:
292 ~MultiAssetsProvider() override = default;
293
294 static std::unique_ptr<const AssetsProvider> Create(
295 std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) {
296 CHECK(parent != nullptr) << "parent provider must not be null";
297 return (!child) ? std::move(parent)
298 : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider(
299 std::move(child), std::move(parent)));
300 }
301
302 bool ForEachFile(const std::string& root_path,
303 const std::function<void(const StringPiece&, FileType)>& f) const override {
304 // TODO: Only call the function once for files defined in the parent and child
305 return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f);
306 }
307
308 protected:
309 std::unique_ptr<Asset> OpenInternal(
310 const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
311 auto asset = child_->Open(path, mode, file_exists);
312 return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists);
313 }
314
315 private:
316 DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider);
317
318 MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child,
319 std::unique_ptr<const AssetsProvider> parent)
320 : child_(std::move(child)), parent_(std::move(parent)) { }
321
322 std::unique_ptr<const AssetsProvider> child_;
323 std::unique_ptr<const AssetsProvider> parent_;
324};
325
326// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the
327// file.
328std::unique_ptr<const ApkAssets> ApkAssets::Load(
329 const std::string& path, const package_property_t flags,
330 std::unique_ptr<const AssetsProvider> override_asset) {
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700331 auto assets = ZipAssetsProvider::Create(path);
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700332 return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700333 : nullptr;
Adam Lesinskida431a22016-12-29 16:08:16 -0500334}
335
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700336// Opens the archive using the file file descriptor with the specified file offset and read length.
337// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file.
338std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(
339 unique_fd fd, const std::string& friendly_name, const package_property_t flags,
340 std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
341 const off64_t length) {
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700342 CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
343 CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
344 << kUnknownLength;
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700345
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700346 auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length);
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700347 return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset))
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700348 : nullptr;
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700349}
350
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700351std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(
352 const std::string& path, const package_property_t flags,
353 std::unique_ptr<const AssetsProvider> override_asset) {
354
355 auto assets = CreateAssetFromFile(path);
356 return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset))
357 : nullptr;
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700358}
359
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700360std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(
361 unique_fd fd, const std::string& friendly_name, const package_property_t flags,
362 std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
363 const off64_t length) {
364
365 auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
366 return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags,
367 std::move(override_asset))
368 : nullptr;
Adam Lesinskida431a22016-12-29 16:08:16 -0500369}
370
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700371std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700372 const package_property_t flags) {
373 CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700374 std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
375 if (idmap_asset == nullptr) {
376 return {};
377 }
378
379 const StringPiece idmap_data(
380 reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
381 static_cast<size_t>(idmap_asset->getLength()));
Ryan Mitchella9093052020-03-26 17:15:01 -0700382 std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700383 if (loaded_idmap == nullptr) {
384 LOG(ERROR) << "failed to load IDMAP " << idmap_path;
385 return {};
386 }
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700387
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700388 auto overlay_path = loaded_idmap->OverlayApkPath();
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700389 auto assets = ZipAssetsProvider::Create(overlay_path);
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700390 return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY,
391 nullptr /* override_asset */, std::move(idmap_asset),
392 std::move(loaded_idmap))
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700393 : nullptr;
394}
Adam Lesinski7ad11102016-10-28 16:39:15 -0700395
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700396std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(
397 const std::string& path, const package_property_t flags,
398 std::unique_ptr<const AssetsProvider> override_asset) {
399
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700400 auto assets = DirectoryAssetsProvider::Create(path);
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700401 return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700402 : nullptr;
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700403}
404
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700405std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(
406 const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) {
407
408 auto assets = (override_asset) ? std::move(override_asset)
409 : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider());
410 std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */,
411 -1 /* last_mod-time */, flags));
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700412 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
413 // Need to force a move for mingw32.
414 return std::move(loaded_apk);
415}
416
417std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
418 unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
419 if (!fd.ok()) {
420 LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
421 return {};
422 }
423
424 return CreateAssetFromFd(std::move(fd), path.c_str());
425}
426
427std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
428 const char* path,
429 off64_t offset,
430 off64_t length) {
431 CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
432 CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
433 << kUnknownLength;
434 if (length == kUnknownLength) {
435 length = lseek64(fd, 0, SEEK_END);
436 if (length < 0) {
437 LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
438 << SystemErrorCodeToString(errno);
439 return {};
440 }
441 }
442
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700443 incfs::IncFsFileMap file_map;
444 if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) {
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700445 LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
446 << SystemErrorCodeToString(errno);
447 return {};
448 }
449
450 // If `path` is set, do not pass ownership of the `fd` to the new Asset since
451 // Asset::openFileDescriptor can use `path` to create new file descriptors.
452 return Asset::createFromUncompressedMap(std::move(file_map),
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700453 Asset::AccessMode::ACCESS_RANDOM,
454 (path) ? base::unique_fd(-1) : std::move(fd));
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700455}
456
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700457std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
458 std::unique_ptr<const AssetsProvider> assets, const std::string& path,
459 package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets,
460 std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) {
461
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700462 const time_t last_mod_time = getFileModDate(path.c_str());
Winsonb0085ce2019-02-19 12:48:22 -0800463
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700464 // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
465 bool resources_asset_exists = false;
466 auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
467 &resources_asset_exists);
Ryan Mitchell39cacf22020-03-16 14:54:02 -0700468
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700469 assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets));
470
Adam Lesinski7ad11102016-10-28 16:39:15 -0700471 // Wrap the handle in a unique_ptr so it gets automatically closed.
Winson9947f1e2019-08-16 10:20:39 -0700472 std::unique_ptr<ApkAssets>
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700473 loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
Adam Lesinski7ad11102016-10-28 16:39:15 -0700474
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700475 if (!resources_asset_exists) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700476 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
477 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700478 }
479
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700480 loaded_apk->resources_asset_ = std::move(resources_asset_);
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700481 if (!loaded_apk->resources_asset_) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700482 LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700483 return {};
484 }
485
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700486 // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
487 loaded_apk->idmap_asset_ = std::move(idmap_asset);
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700488 loaded_apk->loaded_idmap_ = std::move(idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700489
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700490 const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
491 const size_t length = loaded_apk->resources_asset_->getLength();
492 if (!data || length == 0) {
Ryan Mitchelld338dfe2019-02-23 15:33:08 +0800493 LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'.";
494 return {};
495 }
496
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700497 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(),
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800498 property_flags);
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700499 if (!loaded_apk->loaded_arsc_) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700500 LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700501 return {};
502 }
Adam Lesinski0c405242017-01-13 20:47:26 -0800503
504 // Need to force a move for mingw32.
505 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700506}
507
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700508std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(
509 std::unique_ptr<Asset> resources_asset, const std::string& path,
510 package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) {
511
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700512 const time_t last_mod_time = getFileModDate(path.c_str());
Winson9947f1e2019-08-16 10:20:39 -0700513
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700514 auto assets = (override_assets) ? std::move(override_assets)
515 : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider());
516
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800517 std::unique_ptr<ApkAssets> loaded_apk(
Ryan Mitchell4ea1e422020-03-11 13:15:28 -0700518 new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
Winson9947f1e2019-08-16 10:20:39 -0700519 loaded_apk->resources_asset_ = std::move(resources_asset);
520
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700521 const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */);
522 const size_t length = loaded_apk->resources_asset_->getLength();
523 if (!data || length == 0) {
Ryan Mitchelld338dfe2019-02-23 15:33:08 +0800524 LOG(ERROR) << "Failed to read resources table data in '" << path << "'.";
525 return {};
526 }
527
Ryan Mitchellc75c2e02020-08-17 08:42:48 -0700528 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */,
529 property_flags);
Winson9947f1e2019-08-16 10:20:39 -0700530 if (loaded_apk->loaded_arsc_ == nullptr) {
Ryan Mitchelld338dfe2019-02-23 15:33:08 +0800531 LOG(ERROR) << "Failed to read resources table in '" << path << "'.";
Winson9947f1e2019-08-16 10:20:39 -0700532 return {};
533 }
534
535 // Need to force a move for mingw32.
536 return std::move(loaded_apk);
537}
538
Winsonb0085ce2019-02-19 12:48:22 -0800539bool ApkAssets::IsUpToDate() const {
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800540 if (IsLoader()) {
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700541 // Loaders are invalidated by the app, not the system, so assume they are up to date.
Winson9947f1e2019-08-16 10:20:39 -0700542 return true;
543 }
Ryan Mitchella9093052020-03-26 17:15:01 -0700544 return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) &&
545 last_mod_time_ == getFileModDate(path_.c_str());
Winsonb0085ce2019-02-19 12:48:22 -0800546}
547
Adam Lesinski7ad11102016-10-28 16:39:15 -0700548} // namespace android