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