1 /*
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 "androidfw/ApkAssets.h"
18
19 #include <algorithm>
20
21 #include "android-base/errors.h"
22 #include "android-base/file.h"
23 #include "android-base/logging.h"
24 #include "android-base/stringprintf.h"
25 #include "android-base/unique_fd.h"
26 #include "android-base/utf8.h"
27 #include "utils/Compat.h"
28 #include "utils/FileMap.h"
29 #include "ziparchive/zip_archive.h"
30
31 #include "androidfw/Asset.h"
32 #include "androidfw/Idmap.h"
33 #include "androidfw/misc.h"
34 #include "androidfw/ResourceTypes.h"
35 #include "androidfw/Util.h"
36
37 namespace android {
38
39 using base::SystemErrorCodeToString;
40 using base::unique_fd;
41
42 static const std::string kResourcesArsc("resources.arsc");
43
ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,std::string path,time_t last_mod_time,package_property_t property_flags)44 ApkAssets::ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
45 std::string path,
46 time_t last_mod_time,
47 package_property_t property_flags)
48 : assets_provider_(std::move(assets_provider)),
49 path_(std::move(path)),
50 last_mod_time_(last_mod_time),
51 property_flags_(property_flags) {
52 }
53
54 // Provides asset files from a zip file.
55 class ZipAssetsProvider : public AssetsProvider {
56 public:
57 ~ZipAssetsProvider() override = default;
58
Create(const std::string & path)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));
69 }
70
Create(unique_fd fd,const std::string & friendly_name,const off64_t offset=0,const off64_t length=ApkAssets::kUnknownLength)71 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.
ForEachFile(const std::string & root_path,const std::function<void (const StringPiece &,FileType)> & f) const95 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:
OpenInternal(const std::string & path,Asset::AccessMode mode,bool * file_exists) const147 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
ZipAssetsProvider(std::string path,std::string friendly_name,ZipArchiveHandle unmanaged_handle)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
GetPath() const220 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
230 class DirectoryAssetsProvider : AssetsProvider {
231 public:
232 ~DirectoryAssetsProvider() override = default;
233
Create(const std::string & path)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:
OpenInternal(const std::string & path,Asset::AccessMode,bool * file_exists) const251 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
DirectoryAssetsProvider(std::string path)266 explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { }
267
ResolvePath(const std::string & path) const268 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.
276 class EmptyAssetsProvider : public AssetsProvider {
277 public:
278 EmptyAssetsProvider() = default;
279 ~EmptyAssetsProvider() override = default;
280
281 protected:
OpenInternal(const std::string &,Asset::AccessMode,bool * file_exists) const282 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
295 // AssetProvider implementation
296 class MultiAssetsProvider : public AssetsProvider {
297 public:
298 ~MultiAssetsProvider() override = default;
299
Create(std::unique_ptr<const AssetsProvider> child,std::unique_ptr<const AssetsProvider> parent)300 static std::unique_ptr<const AssetsProvider> Create(
301 std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) {
302 CHECK(parent != nullptr) << "parent provider must not be null";
303 return (!child) ? std::move(parent)
304 : std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider(
305 std::move(child), std::move(parent)));
306 }
307
ForEachFile(const std::string & root_path,const std::function<void (const StringPiece &,FileType)> & f) const308 bool ForEachFile(const std::string& root_path,
309 const std::function<void(const StringPiece&, FileType)>& f) const override {
310 // TODO: Only call the function once for files defined in the parent and child
311 return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f);
312 }
313
314 protected:
OpenInternal(const std::string & path,Asset::AccessMode mode,bool * file_exists) const315 std::unique_ptr<Asset> OpenInternal(
316 const std::string& path, Asset::AccessMode mode, bool* file_exists) const override {
317 auto asset = child_->Open(path, mode, file_exists);
318 return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists);
319 }
320
321 private:
322 DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider);
323
MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child,std::unique_ptr<const AssetsProvider> parent)324 MultiAssetsProvider(std::unique_ptr<const AssetsProvider> child,
325 std::unique_ptr<const AssetsProvider> parent)
326 : child_(std::move(child)), parent_(std::move(parent)) { }
327
328 std::unique_ptr<const AssetsProvider> child_;
329 std::unique_ptr<const AssetsProvider> parent_;
330 };
331
332 // Opens the archive using the file path. Calling CloseArchive on the zip handle will close the
333 // file.
Load(const std::string & path,const package_property_t flags,std::unique_ptr<const AssetsProvider> override_asset)334 std::unique_ptr<const ApkAssets> ApkAssets::Load(
335 const std::string& path, const package_property_t flags,
336 std::unique_ptr<const AssetsProvider> override_asset) {
337 auto assets = ZipAssetsProvider::Create(path);
338 return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
339 : nullptr;
340 }
341
342 // Opens the archive using the file file descriptor with the specified file offset and read length.
343 // If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file.
LoadFromFd(unique_fd fd,const std::string & friendly_name,const package_property_t flags,std::unique_ptr<const AssetsProvider> override_asset,const off64_t offset,const off64_t length)344 std::unique_ptr<const ApkAssets> ApkAssets::LoadFromFd(
345 unique_fd fd, const std::string& friendly_name, const package_property_t flags,
346 std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
347 const off64_t length) {
348 CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
349 CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
350 << kUnknownLength;
351
352 auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length);
353 return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset))
354 : nullptr;
355 }
356
LoadTable(const std::string & path,const package_property_t flags,std::unique_ptr<const AssetsProvider> override_asset)357 std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(
358 const std::string& path, const package_property_t flags,
359 std::unique_ptr<const AssetsProvider> override_asset) {
360
361 auto assets = CreateAssetFromFile(path);
362 return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset))
363 : nullptr;
364 }
365
LoadTableFromFd(unique_fd fd,const std::string & friendly_name,const package_property_t flags,std::unique_ptr<const AssetsProvider> override_asset,const off64_t offset,const off64_t length)366 std::unique_ptr<const ApkAssets> ApkAssets::LoadTableFromFd(
367 unique_fd fd, const std::string& friendly_name, const package_property_t flags,
368 std::unique_ptr<const AssetsProvider> override_asset, const off64_t offset,
369 const off64_t length) {
370
371 auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length);
372 return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags,
373 std::move(override_asset))
374 : nullptr;
375 }
376
LoadOverlay(const std::string & idmap_path,const package_property_t flags)377 std::unique_ptr<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
378 const package_property_t flags) {
379 CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
380 std::unique_ptr<Asset> idmap_asset = CreateAssetFromFile(idmap_path);
381 if (idmap_asset == nullptr) {
382 return {};
383 }
384
385 const StringPiece idmap_data(
386 reinterpret_cast<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
387 static_cast<size_t>(idmap_asset->getLength()));
388 std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
389 if (loaded_idmap == nullptr) {
390 LOG(ERROR) << "failed to load IDMAP " << idmap_path;
391 return {};
392 }
393
394 auto overlay_path = loaded_idmap->OverlayApkPath();
395 auto assets = ZipAssetsProvider::Create(overlay_path);
396 return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY,
397 nullptr /* override_asset */, std::move(idmap_asset),
398 std::move(loaded_idmap))
399 : nullptr;
400 }
401
LoadFromDir(const std::string & path,const package_property_t flags,std::unique_ptr<const AssetsProvider> override_asset)402 std::unique_ptr<const ApkAssets> ApkAssets::LoadFromDir(
403 const std::string& path, const package_property_t flags,
404 std::unique_ptr<const AssetsProvider> override_asset) {
405
406 auto assets = DirectoryAssetsProvider::Create(path);
407 return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
408 : nullptr;
409 }
410
LoadEmpty(const package_property_t flags,std::unique_ptr<const AssetsProvider> override_asset)411 std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(
412 const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) {
413
414 auto assets = (override_asset) ? std::move(override_asset)
415 : std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider());
416 std::unique_ptr<ApkAssets> loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */,
417 -1 /* last_mod-time */, flags));
418 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
419 // Need to force a move for mingw32.
420 return std::move(loaded_apk);
421 }
422
CreateAssetFromFile(const std::string & path)423 std::unique_ptr<Asset> ApkAssets::CreateAssetFromFile(const std::string& path) {
424 unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC));
425 if (!fd.ok()) {
426 LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno);
427 return {};
428 }
429
430 return CreateAssetFromFd(std::move(fd), path.c_str());
431 }
432
CreateAssetFromFd(base::unique_fd fd,const char * path,off64_t offset,off64_t length)433 std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd,
434 const char* path,
435 off64_t offset,
436 off64_t length) {
437 CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength;
438 CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is "
439 << kUnknownLength;
440 if (length == kUnknownLength) {
441 length = lseek64(fd, 0, SEEK_END);
442 if (length < 0) {
443 LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': "
444 << SystemErrorCodeToString(errno);
445 return {};
446 }
447 }
448
449 std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>();
450 if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) {
451 LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
452 << SystemErrorCodeToString(errno);
453 return {};
454 }
455
456 // If `path` is set, do not pass ownership of the `fd` to the new Asset since
457 // Asset::openFileDescriptor can use `path` to create new file descriptors.
458 return Asset::createFromUncompressedMap(std::move(file_map),
459 (path) ? base::unique_fd(-1) : std::move(fd),
460 Asset::AccessMode::ACCESS_RANDOM);
461 }
462
LoadImpl(std::unique_ptr<const AssetsProvider> assets,const std::string & path,package_property_t property_flags,std::unique_ptr<const AssetsProvider> override_assets,std::unique_ptr<Asset> idmap_asset,std::unique_ptr<const LoadedIdmap> idmap)463 std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(
464 std::unique_ptr<const AssetsProvider> assets, const std::string& path,
465 package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets,
466 std::unique_ptr<Asset> idmap_asset, std::unique_ptr<const LoadedIdmap> idmap) {
467
468 const time_t last_mod_time = getFileModDate(path.c_str());
469
470 // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open.
471 bool resources_asset_exists = false;
472 auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER,
473 &resources_asset_exists);
474
475 assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets));
476
477 // Wrap the handle in a unique_ptr so it gets automatically closed.
478 std::unique_ptr<ApkAssets>
479 loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
480
481 if (!resources_asset_exists) {
482 loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty();
483 return std::move(loaded_apk);
484 }
485
486 loaded_apk->resources_asset_ = std::move(resources_asset_);
487 if (!loaded_apk->resources_asset_) {
488 LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'.";
489 return {};
490 }
491
492 // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid.
493 loaded_apk->idmap_asset_ = std::move(idmap_asset);
494 loaded_apk->loaded_idmap_ = std::move(idmap);
495
496 const StringPiece data(
497 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
498 loaded_apk->resources_asset_->getLength());
499 if (data.data() == nullptr || data.empty()) {
500 LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'.";
501 return {};
502 }
503
504 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(),
505 property_flags);
506 if (!loaded_apk->loaded_arsc_) {
507 LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'.";
508 return {};
509 }
510
511 // Need to force a move for mingw32.
512 return std::move(loaded_apk);
513 }
514
LoadTableImpl(std::unique_ptr<Asset> resources_asset,const std::string & path,package_property_t property_flags,std::unique_ptr<const AssetsProvider> override_assets)515 std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl(
516 std::unique_ptr<Asset> resources_asset, const std::string& path,
517 package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) {
518
519 const time_t last_mod_time = getFileModDate(path.c_str());
520
521 auto assets = (override_assets) ? std::move(override_assets)
522 : std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider());
523
524 std::unique_ptr<ApkAssets> loaded_apk(
525 new ApkAssets(std::move(assets), path, last_mod_time, property_flags));
526 loaded_apk->resources_asset_ = std::move(resources_asset);
527
528 const StringPiece data(
529 reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)),
530 loaded_apk->resources_asset_->getLength());
531 if (data.data() == nullptr || data.empty()) {
532 LOG(ERROR) << "Failed to read resources table data in '" << path << "'.";
533 return {};
534 }
535
536 loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags);
537 if (loaded_apk->loaded_arsc_ == nullptr) {
538 LOG(ERROR) << "Failed to read resources table in '" << path << "'.";
539 return {};
540 }
541
542 // Need to force a move for mingw32.
543 return std::move(loaded_apk);
544 }
545
IsUpToDate() const546 bool ApkAssets::IsUpToDate() const {
547 if (IsLoader()) {
548 // Loaders are invalidated by the app, not the system, so assume they are up to date.
549 return true;
550 }
551 return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) &&
552 last_mod_time_ == getFileModDate(path_.c_str());
553
554 }
555
556 } // namespace android
557