blob: f5bf84f18a89d40c0b961023d877c6fbf79231e1 [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 Lesinskid1ecd7a2017-01-23 12:58:11 -080028#include "utils/FileMap.h"
Adam Lesinski7ad11102016-10-28 16:39:15 -070029#include "ziparchive/zip_archive.h"
30
31#include "androidfw/Asset.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070032#include "androidfw/Idmap.h"
Winsonb0085ce2019-02-19 12:48:22 -080033#include "androidfw/misc.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070034#include "androidfw/ResourceTypes.h"
Adam Lesinski7ad11102016-10-28 16:39:15 -070035#include "androidfw/Util.h"
36
37namespace android {
38
Adam Lesinski970bd8d2017-09-25 13:21:55 -070039using base::SystemErrorCodeToString;
40using base::unique_fd;
41
42static const std::string kResourcesArsc("resources.arsc");
43
Ryan Mitchellc07aa702020-03-10 13:49:12 -070044ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070045 std::string path,
Winson9947f1e2019-08-16 10:20:39 -070046 time_t last_mod_time,
Ryan Mitchell73bfe412019-11-12 16:22:04 -080047 package_property_t property_flags)
Ryan Mitchellc07aa702020-03-10 13:49:12 -070048 : assets_provider_(std::move(assets_provider)),
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070049 path_(std::move(path)),
50 last_mod_time_(last_mod_time),
Ryan Mitchell73bfe412019-11-12 16:22:04 -080051 property_flags_(property_flags) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -070052}
Adam Lesinski03ebac82017-09-25 13:10:14 -070053
Ryan Mitchellc07aa702020-03-10 13:49:12 -070054// Provides asset files from a zip file.
55class ZipAssetsProvider : public AssetsProvider {
56 public:
57 ~ZipAssetsProvider() override = default;
58
59 static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
60 ::ZipArchiveHandle unmanaged_handle;
61 const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
62 if (result != 0) {
63 LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result);
64 ::CloseArchive(unmanaged_handle);
65 return {};
66 }
67
68 return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle));
Ryan Mitchellef40d2e2020-03-11 10:26:08 -070069 }
70
Ryan Mitchellc07aa702020-03-10 13:49:12 -070071 static std::unique_ptr<const AssetsProvider> Create(
72 unique_fd fd, const std::string& friendly_name, const off64_t offset = 0,
73 const off64_t length = ApkAssets::kUnknownLength) {
74
75 ::ZipArchiveHandle unmanaged_handle;
76 const int32_t result = (length == ApkAssets::kUnknownLength)
77 ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle)
78 : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length,
79 offset);
80
81 if (result != 0) {
82 LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset
83 << " and length " << length << ": " << ::ErrorCodeString(result);
84 ::CloseArchive(unmanaged_handle);
85 return {};
86 }
87
88 return std::unique_ptr<AssetsProvider>(new ZipAssetsProvider({}, friendly_name,
89 unmanaged_handle));
90 }
91
92 // Iterate over all files and directories within the zip. The order of iteration is not
93 // guaranteed to be the same as the order of elements in the central directory but is stable for a
94 // given zip file.
95 bool ForEachFile(const std::string& root_path,
96 const std::function<void(const StringPiece&, FileType)>& f) const override {
97 // If this is a resource loader from an .arsc, there will be no zip handle
98 if (zip_handle_ == nullptr) {
99 return false;
100 }
101
102 std::string root_path_full = root_path;
103 if (root_path_full.back() != '/') {
104 root_path_full += '/';
105 }
106
107 void* cookie;
108 if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) {
109 return false;
110 }
111
112 std::string name;
113 ::ZipEntry entry{};
114
115 // We need to hold back directories because many paths will contain them and we want to only
116 // surface one.
117 std::set<std::string> dirs{};
118
119 int32_t result;
120 while ((result = ::Next(cookie, &entry, &name)) == 0) {
121 StringPiece full_file_path(name);
122 StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
123
124 if (!leaf_file_path.empty()) {
125 auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
126 if (iter != leaf_file_path.end()) {
127 std::string dir =
128 leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string();
129 dirs.insert(std::move(dir));
130 } else {
131 f(leaf_file_path, kFileTypeRegular);
132 }
133 }
134 }
135 ::EndIteration(cookie);
136
137 // Now present the unique directories.
138 for (const std::string& dir : dirs) {
139 f(dir, kFileTypeDirectory);
140 }
141
142 // -1 is end of iteration, anything else is an error.
143 return result == -1;
144 }
145
146 protected:
147 std::unique_ptr<Asset> OpenInternal(
148 const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
149 if (file_exists) {
150 *file_exists = false;
151 }
152
153 ::ZipEntry entry;
154 int32_t result = ::FindEntry(zip_handle_.get(), path, &entry);
155 if (result != 0) {
156 return {};
157 }
158
159 if (file_exists) {
160 *file_exists = true;
161 }
162
163 const int fd = ::GetFileDescriptor(zip_handle_.get());
164 const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get());
165 if (entry.method == kCompressDeflated) {
166 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
167 if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length,
168 true /*readOnly*/)) {
169 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
170 return {};
171 }
172
173 std::unique_ptr<Asset> asset =
174 Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
175 if (asset == nullptr) {
176 LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'";
177 return {};
178 }
179 return asset;
180 } else {
181 std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
182 if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length,
183 true /*readOnly*/)) {
184 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
185 return {};
186 }
187
188 unique_fd ufd;
189 if (!GetPath()) {
190 // If the `path` is not set, create a new `fd` for the new Asset to own in order to create
191 // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used
192 // to create new file descriptors.
193 ufd = unique_fd(dup(fd));
194 if (!ufd.ok()) {
195 LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'";
196 return {};
197 }
198 }
199
200 std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map),
201 std::move(ufd), mode);
202 if (asset == nullptr) {
203 LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'";
204 return {};
205 }
206 return asset;
207 }
208 }
209
210 private:
211 DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider);
212
213 explicit ZipAssetsProvider(std::string path,
214 std::string friendly_name,
215 ZipArchiveHandle unmanaged_handle)
216 : zip_handle_(unmanaged_handle, ::CloseArchive),
217 path_(std::move(path)),
218 friendly_name_(std::move(friendly_name)) { }
219
220 const char* GetPath() const {
221 return path_.empty() ? nullptr : path_.c_str();
222 }
223
224 using ZipArchivePtr = std::unique_ptr<ZipArchive, void (*)(ZipArchiveHandle)>;
225 ZipArchivePtr zip_handle_;
226 std::string path_;
227 std::string friendly_name_;
228};
229
230class DirectoryAssetsProvider : AssetsProvider {
231 public:
232 ~DirectoryAssetsProvider() override = default;
233
234 static std::unique_ptr<const AssetsProvider> Create(const std::string& path) {
235 struct stat sb{};
236 const int result = stat(path.c_str(), &sb);
237 if (result == -1) {
238 LOG(ERROR) << "Failed to find directory '" << path << "'.";
239 return nullptr;
240 }
241
242 if (!S_ISDIR(sb.st_mode)) {
243 LOG(ERROR) << "Path '" << path << "' is not a directory.";
244 return nullptr;
245 }
246
247 return std::unique_ptr<AssetsProvider>(new DirectoryAssetsProvider(path));
248 }
249
250 protected:
251 std::unique_ptr<Asset> OpenInternal(
252 const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override {
253 const std::string resolved_path = ResolvePath(path);
254 if (file_exists) {
255 struct stat sb{};
256 const int result = stat(resolved_path.c_str(), &sb);
257 *file_exists = result != -1 && S_ISREG(sb.st_mode);
258 }
259
260 return ApkAssets::CreateAssetFromFile(resolved_path);
261 }
262
263 private:
264 DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider);
265
266 explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { }
267
268 inline std::string ResolvePath(const std::string& path) const {
269 return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str());
270 }
271
272 const std::string path_;
273};
274
275// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty.
276class EmptyAssetsProvider : public AssetsProvider {
277 public:
278 EmptyAssetsProvider() = default;
279 ~EmptyAssetsProvider() override = default;
280
281 protected:
282 std::unique_ptr<Asset> OpenInternal(const std::string& /*path */,
283 Asset::AccessMode /* mode */,
284 bool* file_exists) const override {
285 if (file_exists) {
286 *file_exists = false;
287 }
288 return nullptr;
289 }
290
291 private:
292 DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider);
293};
294
295std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path,
296 const package_property_t flags) {
297 auto assets = ZipAssetsProvider::Create(path);
298 return (assets) ? LoadImpl(std::move(assets), path, nullptr /*idmap_asset*/,
299 nullptr /*loaded_idmap*/, flags)
300 : nullptr;
Adam Lesinskida431a22016-12-29 16:08:16 -0500301}
302
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700303std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(unique_fd fd,
304 const std::string& friendly_name,
305 const package_property_t flags,
306 const off64_t offset,
307 const off64_t length) {
308 CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
309 CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
310 << kUnknownLength;
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700311 auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length);
312 return (assets) ? LoadImpl(std::move(assets), friendly_name, nullptr /*idmap_asset*/,
313 nullptr /*loaded_idmap*/, flags)
314 : nullptr;
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700315}
316
317std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(const std::string& path,
318 const package_property_t flags) {
319 auto resources_asset = CreateAssetFromFile(path);
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700320 return (resources_asset) ? LoadTableImpl(std::move(resources_asset), path, flags)
321 : nullptr;
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700322}
323
324std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(unique_fd fd,
325 const std::string& friendly_name,
326 const package_property_t flags,
327 const off64_t offset,
328 const off64_t length) {
329 auto resources_asset = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700330 return (resources_asset) ? LoadTableImpl(std::move(resources_asset), friendly_name, flags)
331 : nullptr;
Adam Lesinskida431a22016-12-29 16:08:16 -0500332}
333
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700334std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700335 const package_property_t flags) {
336 CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
337
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700338 std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
339 if (idmap_asset == nullptr) {
340 return {};
341 }
342
343 const StringPiece idmap_data(
344 reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
345 static_cast<size_t>(idmap_asset->getLength()));
346 std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_data);
347 if (loaded_idmap == nullptr) {
348 LOG(ERROR) << "failed to load IDMAP " << idmap_path;
349 return {};
350 }
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700351
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700352 auto overlay_path = loaded_idmap->OverlayApkPath();
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700353 auto assets = ZipAssetsProvider::Create(overlay_path);
354 return (assets) ? LoadImpl(std::move(assets), overlay_path, std::move(idmap_asset),
355 std::move(loaded_idmap), flags | PROPERTY_OVERLAY)
356 : nullptr;
357}
Adam Lesinski7ad11102016-10-28 16:39:15 -0700358
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700359std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(const std::string& path,
360 const package_property_t flags) {
361 auto assets = DirectoryAssetsProvider::Create(path);
362 return (assets) ? LoadImpl(std::move(assets), path, nullptr /*idmap_asset*/,
363 nullptr /*loaded_idmap*/, flags)
364 : nullptr;
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700365}
366
367std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(const package_property_t flags) {
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700368 std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(
369 std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider()), "empty" /* path */,
370 -1 /* last_mod-time */, flags));
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700371 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
372 // Need to force a move for mingw32.
373 return std::move(loaded_apk);
374}
375
376std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
377 unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
378 if (!fd.ok()) {
379 LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
380 return {};
381 }
382
383 return CreateAssetFromFd(std::move(fd), path.c_str());
384}
385
386std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
387 const char* path,
388 off64_t offset,
389 off64_t length) {
390 CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
391 CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
392 << kUnknownLength;
393 if (length == kUnknownLength) {
394 length = lseek64(fd, 0, SEEK_END);
395 if (length < 0) {
396 LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
397 << SystemErrorCodeToString(errno);
398 return {};
399 }
400 }
401
402 std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
403 if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) {
404 LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
405 << SystemErrorCodeToString(errno);
406 return {};
407 }
408
409 // If `path` is set, do not pass ownership of the `fd` to the new Asset since
410 // Asset::openFileDescriptor can use `path` to create new file descriptors.
411 return Asset::createFromUncompressedMap(std::move(file_map),
412 (path) ? base::unique_fd(-1) : std::move(fd),
413 Asset::AccessMode::ACCESS_RANDOM);
414}
415
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700416std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<const AssetsProvider> assets,
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700417 const std::string& path,
418 std::unique_ptr<Asset> idmap_asset,
419 std::unique_ptr<const LoadedIdmap> idmap,
420 package_property_t property_flags) {
421 const time_t last_mod_time = getFileModDate(path.c_str());
Winsonb0085ce2019-02-19 12:48:22 -0800422
Adam Lesinski7ad11102016-10-28 16:39:15 -0700423 // Wrap the handle in a unique_ptr so it gets automatically closed.
Winson9947f1e2019-08-16 10:20:39 -0700424 std::unique_ptr<ApkAssets>
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700425 loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
Adam Lesinski7ad11102016-10-28 16:39:15 -0700426
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700427 // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
428 bool resources_asset_exists = false;
429 loaded_apk->resources_asset_ = loaded_apk->assets_provider_->Open(
430 kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, &resources_asset_exists);
431
432 if (!resources_asset_exists) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700433 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
434 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700435 }
436
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700437 if (!loaded_apk->resources_asset_) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700438 LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700439 return {};
440 }
441
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700442 // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
443 loaded_apk->idmap_asset_ = std::move(idmap_asset);
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700444 loaded_apk->loaded_idmap_ = std::move(idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700445
446 const StringPiece data(
447 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
448 loaded_apk->resources_asset_->getLength());
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800449 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
450 property_flags);
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700451 if (!loaded_apk->loaded_arsc_) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700452 LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
Adam Lesinski7ad11102016-10-28 16:39:15 -0700453 return {};
454 }
Adam Lesinski0c405242017-01-13 20:47:26 -0800455
456 // Need to force a move for mingw32.
457 return std::move(loaded_apk);
Adam Lesinski7ad11102016-10-28 16:39:15 -0700458}
459
Ryan Mitchellef40d2e2020-03-11 10:26:08 -0700460std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(std::unique_ptr<Asset> resources_asset,
461 const std::string& path,
462 package_property_t property_flags) {
463 const time_t last_mod_time = getFileModDate(path.c_str());
Winson9947f1e2019-08-16 10:20:39 -0700464
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800465 std::unique_ptr<ApkAssets> loaded_apk(
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700466 new ApkAssets(std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider()), path, last_mod_time,
467 property_flags));
Winson9947f1e2019-08-16 10:20:39 -0700468 loaded_apk->resources_asset_ = std::move(resources_asset);
469
470 const StringPiece data(
471 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
472 loaded_apk->resources_asset_->getLength());
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800473 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
Winson9947f1e2019-08-16 10:20:39 -0700474 if (loaded_apk->loaded_arsc_ == nullptr) {
475 LOG(ERROR) << "Failed to load '" << kResourcesArsc << path;
476 return {};
477 }
478
479 // Need to force a move for mingw32.
480 return std::move(loaded_apk);
481}
482
Winsonb0085ce2019-02-19 12:48:22 -0800483bool ApkAssets::IsUpToDate() const {
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800484 if (IsLoader()) {
Ryan Mitchellc07aa702020-03-10 13:49:12 -0700485 // Loaders are invalidated by the app, not the system, so assume they are up to date.
Winson9947f1e2019-08-16 10:20:39 -0700486 return true;
487 }
488
Winsonb0085ce2019-02-19 12:48:22 -0800489 return last_mod_time_ == getFileModDate(path_.c_str());
490}
491
Adam Lesinski7ad11102016-10-28 16:39:15 -0700492} // namespace android