From 1a48fa659bd877712dee5611524f903fd22aaae8 Mon Sep 17 00:00:00 2001 From: Ryan Mitchell Date: Sun, 10 Jan 2021 08:36:36 -0800 Subject: [PATCH] Dependency injection of AssetProviders into ApkAssets Creates ApkAssets creation methods that allow an AssetsProvider to be specified. During idmap verification and creation, idmap2 currently opens the target package and overlay package several times: 1) When the crc of the package is calculated in idmap2 verify 2) When the manifest of an overlay is parsed 3) When an ApkAssets is opened. Opening large zip files (like framework-res.apk) is slow. If we opened the zip one time as an ApkAssets, the resources.arsc would have to be parsed (which means mmaping/unmapping and touching a lot of resources.arsc pages). This would cause idmap2 to preform unnecessary work just to check the crc of some files. This change allows a ZipAssetsProvider to be created and then moved for the creation of an ApkAssets. The zip file only needs to be opened once and the resources.arsc is not parsed until reading resources is actually necessary. Bug: 172471315 Test: libandroidfw_tests Test: CtsResourcesLoaderTests Change-Id: I940bb2c13844c7f028776a623a9ecef45a4813bf --- core/jni/android_content_res_ApkAssets.cpp | 94 ++- libs/androidfw/Android.bp | 1 + libs/androidfw/ApkAssets.cpp | 566 +++--------------- libs/androidfw/AssetsProvider.cpp | 398 ++++++++++++ libs/androidfw/Idmap.cpp | 4 +- libs/androidfw/LoadedArsc.cpp | 13 +- libs/androidfw/include/androidfw/ApkAssets.h | 168 ++---- libs/androidfw/include/androidfw/Asset.h | 4 +- .../include/androidfw/AssetsProvider.h | 185 ++++++ libs/androidfw/include/androidfw/Idmap.h | 4 +- libs/androidfw/include/androidfw/LoadedArsc.h | 15 +- libs/androidfw/tests/Idmap_test.cpp | 8 +- libs/androidfw/tests/LoadedArsc_test.cpp | 6 +- startop/view_compiler/apk_layout_compiler.cc | 2 +- 14 files changed, 816 insertions(+), 652 deletions(-) create mode 100644 libs/androidfw/AssetsProvider.cpp create mode 100644 libs/androidfw/include/androidfw/AssetsProvider.h diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index 66753e4fc2eef..f1998a583a21a 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -47,6 +47,7 @@ static struct assetfiledescriptor_offsets_t { static struct assetsprovider_offsets_t { jclass classObject; jmethodID loadAssetFd; + jmethodID toString; } gAssetsProviderOffsets; static struct { @@ -72,9 +73,22 @@ enum : format_type_t { class LoaderAssetsProvider : public AssetsProvider { public: static std::unique_ptr Create(JNIEnv* env, jobject assets_provider) { - return (!assets_provider) ? nullptr + return (!assets_provider) ? EmptyAssetsProvider::Create() : std::unique_ptr(new LoaderAssetsProvider( - env->NewGlobalRef(assets_provider))); + env, assets_provider)); + } + + bool ForEachFile(const std::string& /* root_path */, + const std::function& /* f */) const { + return true; + } + + const std::string& GetDebugName() const override { + return debug_name_; + } + + bool IsUpToDate() const override { + return true; } ~LoaderAssetsProvider() override { @@ -142,20 +156,26 @@ class LoaderAssetsProvider : public AssetsProvider { *file_exists = true; } - return ApkAssets::CreateAssetFromFd(base::unique_fd(fd), - nullptr /* path */, - static_cast(mOffset), - static_cast(mLength)); + return AssetsProvider::CreateAssetFromFd(base::unique_fd(fd), + nullptr /* path */, + static_cast(mOffset), + static_cast(mLength)); } private: DISALLOW_COPY_AND_ASSIGN(LoaderAssetsProvider); - explicit LoaderAssetsProvider(jobject assets_provider) - : assets_provider_(assets_provider) { } + explicit LoaderAssetsProvider(JNIEnv* env, jobject assets_provider) { + assets_provider_ = env->NewGlobalRef(assets_provider); + auto string_result = static_cast(env->CallObjectMethod( + assets_provider_, gAssetsProviderOffsets.toString)); + ScopedUtfChars str(env, string_result); + debug_name_ = std::string(str.c_str(), str.size()); + } // The global reference to the AssetsProvider jobject assets_provider_; + std::string debug_name_; }; static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t format, @@ -170,18 +190,26 @@ static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, const format_type_t forma auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider); std::unique_ptr apk_assets; switch (format) { - case FORMAT_APK: - apk_assets = ApkAssets::Load(path.c_str(), property_flags, std::move(loader_assets)); + case FORMAT_APK: { + auto assets = MultiAssetsProvider::Create(std::move(loader_assets), + ZipAssetsProvider::Create(path.c_str())); + apk_assets = ApkAssets::Load(std::move(assets), property_flags); break; + } case FORMAT_IDMAP: apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags); break; case FORMAT_ARSC: - apk_assets = ApkAssets::LoadTable(path.c_str(), property_flags, std::move(loader_assets)); + apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()), + std::move(loader_assets), + property_flags); break; - case FORMAT_DIRECTORY: - apk_assets = ApkAssets::LoadFromDir(path.c_str(), property_flags, std::move(loader_assets)); + case FORMAT_DIRECTORY: { + auto assets = MultiAssetsProvider::Create(std::move(loader_assets), + DirectoryAssetsProvider::Create(path.c_str())); + apk_assets = ApkAssets::Load(std::move(assets), property_flags); break; + } default: const std::string error_msg = base::StringPrintf("Unsupported format type %d", format); jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str()); @@ -221,13 +249,17 @@ static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, const format_type_t auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider); std::unique_ptr apk_assets; switch (format) { - case FORMAT_APK: - apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(), - property_flags, std::move(loader_assets)); + case FORMAT_APK: { + auto assets = MultiAssetsProvider::Create( + std::move(loader_assets), + ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str())); + apk_assets = ApkAssets::Load(std::move(assets), property_flags); break; + } case FORMAT_ARSC: - apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(), - property_flags, std::move(loader_assets)); + apk_assets = ApkAssets::LoadTable( + AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */), + std::move(loader_assets), property_flags); break; default: const std::string error_msg = base::StringPrintf("Unsupported format type %d", format); @@ -282,17 +314,20 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_ auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider); std::unique_ptr apk_assets; switch (format) { - case FORMAT_APK: - apk_assets = ApkAssets::LoadFromFd(std::move(dup_fd), friendly_name_utf8.c_str(), - property_flags, std::move(loader_assets), - static_cast(offset), - static_cast(length)); + case FORMAT_APK: { + auto assets = MultiAssetsProvider::Create( + std::move(loader_assets), + ZipAssetsProvider::Create(std::move(dup_fd), friendly_name_utf8.c_str(), + static_cast(offset), static_cast(length))); + apk_assets = ApkAssets::Load(std::move(assets), property_flags); break; + } case FORMAT_ARSC: - apk_assets = ApkAssets::LoadTableFromFd(std::move(dup_fd), friendly_name_utf8.c_str(), - property_flags, std::move(loader_assets), - static_cast(offset), - static_cast(length)); + apk_assets = ApkAssets::LoadTable( + AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */, + static_cast(offset), + static_cast(length)), + std::move(loader_assets), property_flags); break; default: const std::string error_msg = base::StringPrintf("Unsupported format type %d", format); @@ -310,8 +345,7 @@ static jlong NativeLoadFromFdOffset(JNIEnv* env, jclass /*clazz*/, const format_ } static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) { - auto loader_assets = LoaderAssetsProvider::Create(env, assets_provider); - auto apk_assets = ApkAssets::LoadEmpty(flags, std::move(loader_assets)); + auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags); return reinterpret_cast(apk_assets.release()); } @@ -458,6 +492,8 @@ int register_android_content_res_ApkAssets(JNIEnv* env) { gAssetsProviderOffsets.loadAssetFd = GetMethodIDOrDie( env, gAssetsProviderOffsets.classObject, "loadAssetFd", "(Ljava/lang/String;I)Landroid/content/res/AssetFileDescriptor;"); + gAssetsProviderOffsets.toString = GetMethodIDOrDie( + env, gAssetsProviderOffsets.classObject, "toString", "()Ljava/lang/String;"); jclass parcelFd = FindClassOrDie(env, "android/os/ParcelFileDescriptor"); gParcelFileDescriptorOffsets.detachFd = GetMethodIDOrDie(env, parcelFd, "detachFd", "()I"); diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index b54f7d81274cd..4b4284a0c745b 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -41,6 +41,7 @@ cc_library { "AssetDir.cpp", "AssetManager.cpp", "AssetManager2.cpp", + "AssetsProvider.cpp", "AttributeResolution.cpp", "ChunkIterator.cpp", "ConfigDescription.cpp", diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index 011a0de8031f8..ca5981c0dd5cb 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -16,533 +16,145 @@ #include "androidfw/ApkAssets.h" -#include - #include "android-base/errors.h" -#include "android-base/file.h" #include "android-base/logging.h" -#include "android-base/stringprintf.h" -#include "android-base/unique_fd.h" -#include "android-base/utf8.h" -#include "utils/Compat.h" -#include "ziparchive/zip_archive.h" - -#include "androidfw/Asset.h" -#include "androidfw/Idmap.h" -#include "androidfw/misc.h" -#include "androidfw/Util.h" namespace android { using base::SystemErrorCodeToString; using base::unique_fd; -static const std::string kResourcesArsc("resources.arsc"); +constexpr const char* kResourcesArsc = "resources.arsc"; -ApkAssets::ApkAssets(std::unique_ptr assets_provider, - std::string path, - time_t last_mod_time, - package_property_t property_flags) - : assets_provider_(std::move(assets_provider)), - path_(std::move(path)), - last_mod_time_(last_mod_time), - property_flags_(property_flags) { +ApkAssets::ApkAssets(std::unique_ptr resources_asset, + std::unique_ptr loaded_arsc, + std::unique_ptr assets, + package_property_t property_flags, + std::unique_ptr idmap_asset, + std::unique_ptr loaded_idmap) + : resources_asset_(std::move(resources_asset)), + loaded_arsc_(std::move(loaded_arsc)), + assets_provider_(std::move(assets)), + property_flags_(property_flags), + idmap_asset_(std::move(idmap_asset)), + loaded_idmap_(std::move(loaded_idmap)) {} + +std::unique_ptr ApkAssets::Load(const std::string& path, package_property_t flags) { + return Load(ZipAssetsProvider::Create(path), flags); } -// Provides asset files from a zip file. -class ZipAssetsProvider : public AssetsProvider { - public: - ~ZipAssetsProvider() override = default; - - static std::unique_ptr Create(const std::string& path) { - ::ZipArchiveHandle unmanaged_handle; - const int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle); - if (result != 0) { - LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); - ::CloseArchive(unmanaged_handle); - return {}; - } - - return std::unique_ptr(new ZipAssetsProvider(path, path, unmanaged_handle)); - } - - static std::unique_ptr Create( - unique_fd fd, const std::string& friendly_name, const off64_t offset = 0, - const off64_t length = ApkAssets::kUnknownLength) { - - ::ZipArchiveHandle unmanaged_handle; - const int32_t result = (length == ApkAssets::kUnknownLength) - ? ::OpenArchiveFd(fd.release(), friendly_name.c_str(), &unmanaged_handle) - : ::OpenArchiveFdRange(fd.release(), friendly_name.c_str(), &unmanaged_handle, length, - offset); - - if (result != 0) { - LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset - << " and length " << length << ": " << ::ErrorCodeString(result); - ::CloseArchive(unmanaged_handle); - return {}; - } - - return std::unique_ptr(new ZipAssetsProvider({}, friendly_name, - unmanaged_handle)); - } - - // Iterate over all files and directories within the zip. The order of iteration is not - // guaranteed to be the same as the order of elements in the central directory but is stable for a - // given zip file. - bool ForEachFile(const std::string& root_path, - const std::function& f) const override { - // If this is a resource loader from an .arsc, there will be no zip handle - if (zip_handle_ == nullptr) { - return false; - } - - std::string root_path_full = root_path; - if (root_path_full.back() != '/') { - root_path_full += '/'; - } - - void* cookie; - if (::StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) { - return false; - } - - std::string name; - ::ZipEntry entry{}; - - // We need to hold back directories because many paths will contain them and we want to only - // surface one. - std::set dirs{}; - - int32_t result; - while ((result = ::Next(cookie, &entry, &name)) == 0) { - StringPiece full_file_path(name); - StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); - - if (!leaf_file_path.empty()) { - auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); - if (iter != leaf_file_path.end()) { - std::string dir = - leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); - dirs.insert(std::move(dir)); - } else { - f(leaf_file_path, kFileTypeRegular); - } - } - } - ::EndIteration(cookie); - - // Now present the unique directories. - for (const std::string& dir : dirs) { - f(dir, kFileTypeDirectory); - } - - // -1 is end of iteration, anything else is an error. - return result == -1; - } - - protected: - std::unique_ptr OpenInternal( - const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { - if (file_exists) { - *file_exists = false; - } - - ::ZipEntry entry; - int32_t result = ::FindEntry(zip_handle_.get(), path, &entry); - if (result != 0) { - return {}; - } - - if (file_exists) { - *file_exists = true; - } - - const int fd = ::GetFileDescriptor(zip_handle_.get()); - const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); - incfs::IncFsFileMap asset_map; - if (entry.method == kCompressDeflated) { - if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; - return {}; - } - - std::unique_ptr asset = - Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode); - if (asset == nullptr) { - LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'"; - return {}; - } - return asset; - } - - if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; - return {}; - } - - unique_fd ufd; - if (!GetPath()) { - // If the `path` is not set, create a new `fd` for the new Asset to own in order to create - // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used - // to create new file descriptors. - ufd = unique_fd(dup(fd)); - if (!ufd.ok()) { - LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'"; - return {}; - } - } - - auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd)); - if (asset == nullptr) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; - return {}; - } - return asset; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ZipAssetsProvider); - - explicit ZipAssetsProvider(std::string path, - std::string friendly_name, - ZipArchiveHandle unmanaged_handle) - : zip_handle_(unmanaged_handle, ::CloseArchive), - path_(std::move(path)), - friendly_name_(std::move(friendly_name)) { } - - const char* GetPath() const { - return path_.empty() ? nullptr : path_.c_str(); - } - - using ZipArchivePtr = std::unique_ptr; - ZipArchivePtr zip_handle_; - std::string path_; - std::string friendly_name_; -}; - -class DirectoryAssetsProvider : AssetsProvider { - public: - ~DirectoryAssetsProvider() override = default; - - static std::unique_ptr Create(const std::string& path) { - struct stat sb{}; - const int result = stat(path.c_str(), &sb); - if (result == -1) { - LOG(ERROR) << "Failed to find directory '" << path << "'."; - return nullptr; - } - - if (!S_ISDIR(sb.st_mode)) { - LOG(ERROR) << "Path '" << path << "' is not a directory."; - return nullptr; - } - - return std::unique_ptr(new DirectoryAssetsProvider(path)); - } - - protected: - std::unique_ptr OpenInternal( - const std::string& path, Asset::AccessMode /* mode */, bool* file_exists) const override { - const std::string resolved_path = ResolvePath(path); - if (file_exists) { - struct stat sb{}; - const int result = stat(resolved_path.c_str(), &sb); - *file_exists = result != -1 && S_ISREG(sb.st_mode); - } - - return ApkAssets::CreateAssetFromFile(resolved_path); - } - - private: - DISALLOW_COPY_AND_ASSIGN(DirectoryAssetsProvider); - - explicit DirectoryAssetsProvider(std::string path) : path_(std::move(path)) { } - - inline std::string ResolvePath(const std::string& path) const { - return base::StringPrintf("%s%c%s", path_.c_str(), OS_PATH_SEPARATOR, path.c_str()); - } - - const std::string path_; -}; - -// AssetProvider implementation that does not provide any assets. Used for ApkAssets::LoadEmpty. -class EmptyAssetsProvider : public AssetsProvider { - public: - EmptyAssetsProvider() = default; - ~EmptyAssetsProvider() override = default; - - protected: - std::unique_ptr OpenInternal(const std::string& /*path */, - Asset::AccessMode /* mode */, - bool* file_exists) const override { - if (file_exists) { - *file_exists = false; - } - return nullptr; - } - - private: - DISALLOW_COPY_AND_ASSIGN(EmptyAssetsProvider); -}; - -// AssetProvider implementation -class MultiAssetsProvider : public AssetsProvider { - public: - ~MultiAssetsProvider() override = default; - - static std::unique_ptr Create( - std::unique_ptr child, std::unique_ptr parent) { - CHECK(parent != nullptr) << "parent provider must not be null"; - return (!child) ? std::move(parent) - : std::unique_ptr(new MultiAssetsProvider( - std::move(child), std::move(parent))); - } - - bool ForEachFile(const std::string& root_path, - const std::function& f) const override { - // TODO: Only call the function once for files defined in the parent and child - return child_->ForEachFile(root_path, f) && parent_->ForEachFile(root_path, f); - } - - protected: - std::unique_ptr OpenInternal( - const std::string& path, Asset::AccessMode mode, bool* file_exists) const override { - auto asset = child_->Open(path, mode, file_exists); - return (asset) ? std::move(asset) : parent_->Open(path, mode, file_exists); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MultiAssetsProvider); - - MultiAssetsProvider(std::unique_ptr child, - std::unique_ptr parent) - : child_(std::move(child)), parent_(std::move(parent)) { } - - std::unique_ptr child_; - std::unique_ptr parent_; -}; - -// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the -// file. -std::unique_ptr ApkAssets::Load( - const std::string& path, const package_property_t flags, - std::unique_ptr override_asset) { - auto assets = ZipAssetsProvider::Create(path); - return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset)) - : nullptr; +std::unique_ptr ApkAssets::LoadFromFd(base::unique_fd fd, + const std::string& debug_name, + package_property_t flags, + off64_t offset, + off64_t len) { + return Load(ZipAssetsProvider::Create(std::move(fd), debug_name, offset, len), flags); } -// Opens the archive using the file file descriptor with the specified file offset and read length. -// If the `assume_ownership` parameter is 'true' calling CloseArchive will close the file. -std::unique_ptr ApkAssets::LoadFromFd( - unique_fd fd, const std::string& friendly_name, const package_property_t flags, - std::unique_ptr override_asset, const off64_t offset, - const off64_t length) { - CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; - CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " - << kUnknownLength; - - auto assets = ZipAssetsProvider::Create(std::move(fd), friendly_name, offset, length); - return (assets) ? LoadImpl(std::move(assets), friendly_name, flags, std::move(override_asset)) - : nullptr; +std::unique_ptr ApkAssets::Load(std::unique_ptr assets, + package_property_t flags) { + return LoadImpl(std::move(assets), flags, nullptr /* idmap_asset */, nullptr /* loaded_idmap */); } -std::unique_ptr ApkAssets::LoadTable( - const std::string& path, const package_property_t flags, - std::unique_ptr override_asset) { - - auto assets = CreateAssetFromFile(path); - return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset)) - : nullptr; +std::unique_ptr ApkAssets::LoadTable(std::unique_ptr resources_asset, + std::unique_ptr assets, + package_property_t flags) { + if (resources_asset == nullptr) { + return {}; + } + return LoadImpl(std::move(resources_asset), std::move(assets), flags, nullptr /* idmap_asset */, + nullptr /* loaded_idmap */); } -std::unique_ptr ApkAssets::LoadTableFromFd( - unique_fd fd, const std::string& friendly_name, const package_property_t flags, - std::unique_ptr override_asset, const off64_t offset, - const off64_t length) { - - auto assets = CreateAssetFromFd(std::move(fd), nullptr /* path */, offset, length); - return (assets) ? LoadTableImpl(std::move(assets), friendly_name, flags, - std::move(override_asset)) - : nullptr; -} - -std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path, - const package_property_t flags) { +std::unique_ptr ApkAssets::LoadOverlay(const std::string& idmap_path, + package_property_t flags) { CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders"; - std::unique_ptr idmap_asset = CreateAssetFromFile(idmap_path); + auto idmap_asset = AssetsProvider::CreateAssetFromFile(idmap_path); if (idmap_asset == nullptr) { + LOG(ERROR) << "failed to read IDMAP " << idmap_path; return {}; } - const StringPiece idmap_data( - reinterpret_cast(idmap_asset->getBuffer(true /*wordAligned*/)), - static_cast(idmap_asset->getLength())); - std::unique_ptr loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data); + StringPiece idmap_data(reinterpret_cast(idmap_asset->getBuffer(true /* aligned */)), + static_cast(idmap_asset->getLength())); + auto loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data); if (loaded_idmap == nullptr) { LOG(ERROR) << "failed to load IDMAP " << idmap_path; return {}; } - - auto overlay_path = std::string(loaded_idmap->OverlayApkPath()); - auto assets = ZipAssetsProvider::Create(overlay_path); - return (assets) ? LoadImpl(std::move(assets), overlay_path, flags | PROPERTY_OVERLAY, - nullptr /* override_asset */, std::move(idmap_asset), - std::move(loaded_idmap)) - : nullptr; -} -std::unique_ptr ApkAssets::LoadFromDir( - const std::string& path, const package_property_t flags, - std::unique_ptr override_asset) { - - auto assets = DirectoryAssetsProvider::Create(path); - return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset)) - : nullptr; -} - -std::unique_ptr ApkAssets::LoadEmpty( - const package_property_t flags, std::unique_ptr override_asset) { - - auto assets = (override_asset) ? std::move(override_asset) - : std::unique_ptr(new EmptyAssetsProvider()); - std::unique_ptr loaded_apk(new ApkAssets(std::move(assets), "empty" /* path */, - -1 /* last_mod-time */, flags)); - loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); - // Need to force a move for mingw32. - return std::move(loaded_apk); -} - -std::unique_ptr ApkAssets::CreateAssetFromFile(const std::string& path) { - unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_BINARY | O_CLOEXEC)); - if (!fd.ok()) { - LOG(ERROR) << "Failed to open file '" << path << "': " << SystemErrorCodeToString(errno); + const std::string overlay_path(loaded_idmap->OverlayApkPath()); + auto overlay_assets = ZipAssetsProvider::Create(overlay_path); + if (overlay_assets == nullptr) { return {}; } - return CreateAssetFromFd(std::move(fd), path.c_str()); + return LoadImpl(std::move(overlay_assets), flags | PROPERTY_OVERLAY, std::move(idmap_asset), + std::move(loaded_idmap)); } -std::unique_ptr ApkAssets::CreateAssetFromFd(base::unique_fd fd, - const char* path, - off64_t offset, - off64_t length) { - CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; - CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " - << kUnknownLength; - if (length == kUnknownLength) { - length = lseek64(fd, 0, SEEK_END); - if (length < 0) { - LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': " - << SystemErrorCodeToString(errno); - return {}; - } - } - - incfs::IncFsFileMap file_map; - if (!file_map.Create(fd, offset, static_cast(length), path)) { - LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': " - << SystemErrorCodeToString(errno); +std::unique_ptr ApkAssets::LoadImpl(std::unique_ptr assets, + package_property_t property_flags, + std::unique_ptr idmap_asset, + std::unique_ptr loaded_idmap) { + if (assets == nullptr) { return {}; } - // If `path` is set, do not pass ownership of the `fd` to the new Asset since - // Asset::openFileDescriptor can use `path` to create new file descriptors. - return Asset::createFromUncompressedMap(std::move(file_map), - Asset::AccessMode::ACCESS_RANDOM, - (path) ? base::unique_fd(-1) : std::move(fd)); -} - -std::unique_ptr ApkAssets::LoadImpl( - std::unique_ptr assets, const std::string& path, - package_property_t property_flags, std::unique_ptr override_assets, - std::unique_ptr idmap_asset, std::unique_ptr idmap) { - - const time_t last_mod_time = getFileModDate(path.c_str()); - // Open the resource table via mmap unless it is compressed. This logic is taken care of by Open. bool resources_asset_exists = false; - auto resources_asset_ = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, - &resources_asset_exists); - - assets = MultiAssetsProvider::Create(std::move(override_assets), std::move(assets)); - - // Wrap the handle in a unique_ptr so it gets automatically closed. - std::unique_ptr - loaded_apk(new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); - - if (!resources_asset_exists) { - loaded_apk->loaded_arsc_ = LoadedArsc::CreateEmpty(); - return std::move(loaded_apk); - } - - loaded_apk->resources_asset_ = std::move(resources_asset_); - if (!loaded_apk->resources_asset_) { - LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << path << "'."; + auto resources_asset = assets->Open(kResourcesArsc, Asset::AccessMode::ACCESS_BUFFER, + &resources_asset_exists); + if (resources_asset == nullptr && resources_asset_exists) { + LOG(ERROR) << "Failed to open '" << kResourcesArsc << "' in APK '" << assets->GetDebugName() + << "'."; return {}; } - // Must retain ownership of the IDMAP Asset so that all pointers to its mmapped data remain valid. - loaded_apk->idmap_asset_ = std::move(idmap_asset); - loaded_apk->loaded_idmap_ = std::move(idmap); - - const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */); - const size_t length = loaded_apk->resources_asset_->getLength(); - if (!data || length == 0) { - LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'."; - return {}; - } - - loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(), - property_flags); - if (!loaded_apk->loaded_arsc_) { - LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; - return {}; - } - - // Need to force a move for mingw32. - return std::move(loaded_apk); + return LoadImpl(std::move(resources_asset), std::move(assets), property_flags, + std::move(idmap_asset), std::move(loaded_idmap)); } -std::unique_ptr ApkAssets::LoadTableImpl( - std::unique_ptr resources_asset, const std::string& path, - package_property_t property_flags, std::unique_ptr override_assets) { - - const time_t last_mod_time = getFileModDate(path.c_str()); - - auto assets = (override_assets) ? std::move(override_assets) - : std::unique_ptr(new EmptyAssetsProvider()); - - std::unique_ptr loaded_apk( - new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); - loaded_apk->resources_asset_ = std::move(resources_asset); - - const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */); - const size_t length = loaded_apk->resources_asset_->getLength(); - if (!data || length == 0) { - LOG(ERROR) << "Failed to read resources table data in '" << path << "'."; +std::unique_ptr ApkAssets::LoadImpl(std::unique_ptr resources_asset, + std::unique_ptr assets, + package_property_t property_flags, + std::unique_ptr idmap_asset, + std::unique_ptr loaded_idmap) { + if (assets == nullptr ) { return {}; } - loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */, - property_flags); - if (loaded_apk->loaded_arsc_ == nullptr) { - LOG(ERROR) << "Failed to read resources table in '" << path << "'."; + std::unique_ptr loaded_arsc; + if (resources_asset != nullptr) { + const auto data = resources_asset->getIncFsBuffer(true /* aligned */); + const size_t length = resources_asset->getLength(); + if (!data || length == 0) { + LOG(ERROR) << "Failed to read resources table in APK '" << assets->GetDebugName() << "'."; + return {}; + } + loaded_arsc = LoadedArsc::Load(data, length, loaded_idmap.get(), property_flags); + } else { + loaded_arsc = LoadedArsc::CreateEmpty(); + } + + if (loaded_arsc == nullptr) { + LOG(ERROR) << "Failed to load resources table in APK '" << assets->GetDebugName() << "'."; return {}; } - // Need to force a move for mingw32. - return std::move(loaded_apk); + return std::unique_ptr(new ApkAssets(std::move(resources_asset), + std::move(loaded_arsc), std::move(assets), + property_flags, std::move(idmap_asset), + std::move(loaded_idmap))); +} + +const std::string& ApkAssets::GetPath() const { + return assets_provider_->GetDebugName(); } bool ApkAssets::IsUpToDate() const { - if (IsLoader()) { - // Loaders are invalidated by the app, not the system, so assume they are up to date. - return true; - } - return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) && - last_mod_time_ == getFileModDate(path_.c_str()); + // Loaders are invalidated by the app, not the system, so assume they are up to date. + return IsLoader() || ((!loaded_idmap_ || loaded_idmap_->IsUpToDate()) + && assets_provider_->IsUpToDate()); } - } // namespace android diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp new file mode 100644 index 0000000000000..23cacf88a6db5 --- /dev/null +++ b/libs/androidfw/AssetsProvider.cpp @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "androidfw/AssetsProvider.h" + +#include + +#include +#include +#include +#include + +namespace android { +namespace { +constexpr const char* kEmptyDebugString = ""; +} // namespace + +std::unique_ptr AssetsProvider::Open(const std::string& path, Asset::AccessMode mode, + bool* file_exists) const { + return OpenInternal(path, mode, file_exists); +} + +std::unique_ptr AssetsProvider::CreateAssetFromFile(const std::string& path) { + base::unique_fd fd(base::utf8::open(path.c_str(), O_RDONLY | O_CLOEXEC)); + if (!fd.ok()) { + LOG(ERROR) << "Failed to open file '" << path << "': " << base::SystemErrorCodeToString(errno); + return {}; + } + + return CreateAssetFromFd(std::move(fd), path.c_str()); +} + +std::unique_ptr AssetsProvider::CreateAssetFromFd(base::unique_fd fd, + const char* path, + off64_t offset, + off64_t length) { + CHECK(length >= kUnknownLength) << "length must be greater than or equal to " << kUnknownLength; + CHECK(length != kUnknownLength || offset == 0) << "offset must be 0 if length is " + << kUnknownLength; + if (length == kUnknownLength) { + length = lseek64(fd, 0, SEEK_END); + if (length < 0) { + LOG(ERROR) << "Failed to get size of file '" << ((path) ? path : "anon") << "': " + << base::SystemErrorCodeToString(errno); + return {}; + } + } + + incfs::IncFsFileMap file_map; + if (!file_map.Create(fd, offset, static_cast(length), path)) { + LOG(ERROR) << "Failed to mmap file '" << ((path != nullptr) ? path : "anon") << "': " + << base::SystemErrorCodeToString(errno); + return {}; + } + + // If `path` is set, do not pass ownership of the `fd` to the new Asset since + // Asset::openFileDescriptor can use `path` to create new file descriptors. + return Asset::createFromUncompressedMap(std::move(file_map), + Asset::AccessMode::ACCESS_RANDOM, + (path != nullptr) ? base::unique_fd(-1) : std::move(fd)); +} + +ZipAssetsProvider::PathOrDebugName::PathOrDebugName(std::string&& value, bool is_path) + : value_(std::forward(value)), is_path_(is_path) {} + +const std::string* ZipAssetsProvider::PathOrDebugName::GetPath() const { + return is_path_ ? &value_ : nullptr; +} + +const std::string& ZipAssetsProvider::PathOrDebugName::GetDebugName() const { + return value_; +} + +ZipAssetsProvider::ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, + time_t last_mod_time) + : zip_handle_(handle, ::CloseArchive), + name_(std::forward(path)), + last_mod_time_(last_mod_time) {} + +std::unique_ptr ZipAssetsProvider::Create(std::string path) { + ZipArchiveHandle handle; + if (int32_t result = OpenArchive(path.c_str(), &handle); result != 0) { + LOG(ERROR) << "Failed to open APK '" << path << "' " << ::ErrorCodeString(result); + CloseArchive(handle); + return {}; + } + + struct stat sb{.st_mtime = -1}; + if (stat(path.c_str(), &sb) < 0) { + // Stat requires execute permissions on all directories path to the file. If the process does + // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will + // always have to return true. + LOG(WARNING) << "Failed to stat file '" << path << "': " + << base::SystemErrorCodeToString(errno); + } + + return std::unique_ptr( + new ZipAssetsProvider(handle, PathOrDebugName{std::move(path), + true /* is_path */}, sb.st_mtime)); +} + +std::unique_ptr ZipAssetsProvider::Create(base::unique_fd fd, + std::string friendly_name, + off64_t offset, + off64_t len) { + ZipArchiveHandle handle; + const int released_fd = fd.release(); + const int32_t result = (len == AssetsProvider::kUnknownLength) + ? ::OpenArchiveFd(released_fd, friendly_name.c_str(), &handle) + : ::OpenArchiveFdRange(released_fd, friendly_name.c_str(), &handle, len, offset); + + if (result != 0) { + LOG(ERROR) << "Failed to open APK '" << friendly_name << "' through FD with offset " << offset + << " and length " << len << ": " << ::ErrorCodeString(result); + CloseArchive(handle); + return {}; + } + + struct stat sb{.st_mtime = -1}; + if (fstat(released_fd, &sb) < 0) { + // Stat requires execute permissions on all directories path to the file. If the process does + // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will + // always have to return true. + LOG(WARNING) << "Failed to fstat file '" << friendly_name << "': " + << base::SystemErrorCodeToString(errno); + } + + return std::unique_ptr( + new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name), + false /* is_path */}, sb.st_mtime)); +} + +std::unique_ptr ZipAssetsProvider::OpenInternal(const std::string& path, + Asset::AccessMode mode, + bool* file_exists) const { + if (file_exists != nullptr) { + *file_exists = false; + } + + ZipEntry entry; + if (FindEntry(zip_handle_.get(), path, &entry) != 0) { + return {}; + } + + if (file_exists != nullptr) { + *file_exists = true; + } + + const int fd = GetFileDescriptor(zip_handle_.get()); + const off64_t fd_offset = GetFileDescriptorOffset(zip_handle_.get()); + incfs::IncFsFileMap asset_map; + if (entry.method == kCompressDeflated) { + if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, + name_.GetDebugName().c_str())) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() + << "'"; + return {}; + } + + std::unique_ptr asset = + Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode); + if (asset == nullptr) { + LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << name_.GetDebugName() + << "'"; + return {}; + } + return asset; + } + + if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, + name_.GetDebugName().c_str())) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'"; + return {}; + } + + base::unique_fd ufd; + if (name_.GetPath() == nullptr) { + // If the zip name does not represent a path, create a new `fd` for the new Asset to own in + // order to create new file descriptors using Asset::openFileDescriptor. If the zip name is a + // path, it will be used to create new file descriptors. + ufd = base::unique_fd(dup(fd)); + if (!ufd.ok()) { + LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << name_.GetDebugName() << "'"; + return {}; + } + } + + auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd)); + if (asset == nullptr) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << name_.GetDebugName() << "'"; + return {}; + } + return asset; +} + +bool ZipAssetsProvider::ForEachFile(const std::string& root_path, + const std::function& f) + const { + std::string root_path_full = root_path; + if (root_path_full.back() != '/') { + root_path_full += '/'; + } + + void* cookie; + if (StartIteration(zip_handle_.get(), &cookie, root_path_full, "") != 0) { + return false; + } + + std::string name; + ::ZipEntry entry{}; + + // We need to hold back directories because many paths will contain them and we want to only + // surface one. + std::set dirs{}; + + int32_t result; + while ((result = Next(cookie, &entry, &name)) == 0) { + StringPiece full_file_path(name); + StringPiece leaf_file_path = full_file_path.substr(root_path_full.size()); + + if (!leaf_file_path.empty()) { + auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/'); + if (iter != leaf_file_path.end()) { + std::string dir = + leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string(); + dirs.insert(std::move(dir)); + } else { + f(leaf_file_path, kFileTypeRegular); + } + } + } + EndIteration(cookie); + + // Now present the unique directories. + for (const std::string& dir : dirs) { + f(dir, kFileTypeDirectory); + } + + // -1 is end of iteration, anything else is an error. + return result == -1; +} + +const std::string& ZipAssetsProvider::GetDebugName() const { + return name_.GetDebugName(); +} + +bool ZipAssetsProvider::IsUpToDate() const { + struct stat sb{}; + if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) { + // If fstat fails on the zip archive, return true so the zip archive the resource system does + // attempt to refresh the ApkAsset. + return true; + } + return last_mod_time_ == sb.st_mtime; +} + +DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time) + : dir_(std::forward(path)), last_mod_time_(last_mod_time) {} + +std::unique_ptr DirectoryAssetsProvider::Create(std::string path) { + struct stat sb{}; + const int result = stat(path.c_str(), &sb); + if (result == -1) { + LOG(ERROR) << "Failed to find directory '" << path << "'."; + return nullptr; + } + + if (!S_ISDIR(sb.st_mode)) { + LOG(ERROR) << "Path '" << path << "' is not a directory."; + return nullptr; + } + + if (path[path.size() - 1] != OS_PATH_SEPARATOR) { + path += OS_PATH_SEPARATOR; + } + + return std::unique_ptr(new DirectoryAssetsProvider(std::move(path), + sb.st_mtime)); +} + +std::unique_ptr DirectoryAssetsProvider::OpenInternal(const std::string& path, + Asset::AccessMode /* mode */, + bool* file_exists) const { + const std::string resolved_path = dir_ + path; + if (file_exists != nullptr) { + struct stat sb{}; + *file_exists = (stat(resolved_path.c_str(), &sb) != -1) && S_ISREG(sb.st_mode); + } + + return CreateAssetFromFile(resolved_path); +} + +bool DirectoryAssetsProvider::ForEachFile( + const std::string& /* root_path */, + const std::function& /* f */) + const { + return true; +} + +const std::string& DirectoryAssetsProvider::GetDebugName() const { + return dir_; +} + +bool DirectoryAssetsProvider::IsUpToDate() const { + struct stat sb{}; + if (stat(dir_.c_str(), &sb) < 0) { + // If stat fails on the zip archive, return true so the zip archive the resource system does + // attempt to refresh the ApkAsset. + return true; + } + return last_mod_time_ == sb.st_mtime; +} + +MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr&& primary, + std::unique_ptr&& secondary) + : primary_(std::forward>(primary)), + secondary_(std::forward>(secondary)) { + if (primary_->GetDebugName() == kEmptyDebugString) { + debug_name_ = secondary_->GetDebugName(); + } else if (secondary_->GetDebugName() == kEmptyDebugString) { + debug_name_ = primary_->GetDebugName(); + } else { + debug_name_ = primary_->GetDebugName() + " and " + secondary_->GetDebugName(); + } +} + +std::unique_ptr MultiAssetsProvider::Create( + std::unique_ptr&& primary, std::unique_ptr&& secondary) { + if (primary == nullptr || secondary == nullptr) { + return nullptr; + } + return std::unique_ptr(new MultiAssetsProvider(std::move(primary), + std::move(secondary))); +} + +std::unique_ptr MultiAssetsProvider::OpenInternal(const std::string& path, + Asset::AccessMode mode, + bool* file_exists) const { + auto asset = primary_->Open(path, mode, file_exists); + return (asset) ? std::move(asset) : secondary_->Open(path, mode, file_exists); +} + +bool MultiAssetsProvider::ForEachFile(const std::string& root_path, + const std::function& f) + const { + return primary_->ForEachFile(root_path, f) && secondary_->ForEachFile(root_path, f); +} + +const std::string& MultiAssetsProvider::GetDebugName() const { + return debug_name_; +} + +bool MultiAssetsProvider::IsUpToDate() const { + return primary_->IsUpToDate() && secondary_->IsUpToDate(); +} + +std::unique_ptr EmptyAssetsProvider::Create() { + return std::make_unique(); +} + +std::unique_ptr EmptyAssetsProvider::OpenInternal(const std::string& /* path */, + Asset::AccessMode /* mode */, + bool* file_exists) const { + if (file_exists) { + *file_exists = false; + } + return nullptr; +} + +bool EmptyAssetsProvider::ForEachFile( + const std::string& /* root_path */, + const std::function& /* f */) const { + return true; +} + +const std::string& EmptyAssetsProvider::GetDebugName() const { + const static std::string kEmpty = kEmptyDebugString; + return kEmpty; +} + +bool EmptyAssetsProvider::IsUpToDate() const { + return true; +} + +} // namespace android \ No newline at end of file diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index adb383f95d40b..f216f55771c21 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -257,8 +257,8 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path, target_apk_path_(target_apk_path), idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {} -std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_path, - const StringPiece& idmap_data) { +std::unique_ptr LoadedIdmap::Load(const StringPiece& idmap_path, + const StringPiece& idmap_data) { ATRACE_CALL(); size_t data_size = idmap_data.size(); auto data_ptr = reinterpret_cast(idmap_data.data()); diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 996b42426282f..2a70f0d6a8040 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -767,10 +767,10 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, return true; } -std::unique_ptr LoadedArsc::Load(incfs::map_ptr data, - const size_t length, - const LoadedIdmap* loaded_idmap, - const package_property_t property_flags) { +std::unique_ptr LoadedArsc::Load(incfs::map_ptr data, + const size_t length, + const LoadedIdmap* loaded_idmap, + const package_property_t property_flags) { ATRACE_NAME("LoadedArsc::Load"); // Not using make_unique because the constructor is private. @@ -799,11 +799,10 @@ std::unique_ptr LoadedArsc::Load(incfs::map_ptr data, } } - // Need to force a move for mingw32. - return std::move(loaded_arsc); + return loaded_arsc; } -std::unique_ptr LoadedArsc::CreateEmpty() { +std::unique_ptr LoadedArsc::CreateEmpty() { return std::unique_ptr(new LoadedArsc()); } diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index e57490aab2d8e..d0019ed6be303 100644 --- a/libs/androidfw/include/androidfw/ApkAssets.h +++ b/libs/androidfw/include/androidfw/ApkAssets.h @@ -24,104 +24,49 @@ #include "android-base/unique_fd.h" #include "androidfw/Asset.h" +#include "androidfw/AssetsProvider.h" #include "androidfw/Idmap.h" #include "androidfw/LoadedArsc.h" #include "androidfw/misc.h" -struct ZipArchive; -typedef ZipArchive* ZipArchiveHandle; - namespace android { -class LoadedIdmap; - -// Interface for retrieving assets provided by an ApkAssets. -class AssetsProvider { - public: - virtual ~AssetsProvider() = default; - - // Opens a file for reading. - std::unique_ptr Open(const std::string& path, - Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM, - bool* file_exists = nullptr) const { - return OpenInternal(path, mode, file_exists); - } - - // Iterate over all files and directories provided by the zip. The order of iteration is stable. - virtual bool ForEachFile(const std::string& /* path */, - const std::function& /* f */) const { - return true; - } - - protected: - AssetsProvider() = default; - - virtual std::unique_ptr OpenInternal(const std::string& path, - Asset::AccessMode mode, - bool* file_exists) const = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(AssetsProvider); -}; - -class ZipAssetsProvider; - // Holds an APK. class ApkAssets { public: - // This means the data extends to the end of the file. - static constexpr off64_t kUnknownLength = -1; - // Creates an ApkAssets. - // If `system` is true, the package is marked as a system package, and allows some functions to - // filter out this package when computing what configurations/resources are available. - static std::unique_ptr Load( - const std::string& path, package_property_t flags = 0U, - std::unique_ptr override_asset = nullptr); + // Creates an ApkAssets from a path on device. + static std::unique_ptr Load(const std::string& path, + package_property_t flags = 0U); - // Creates an ApkAssets from the given file descriptor, and takes ownership of the file - // descriptor. The `friendly_name` is some name that will be used to identify the source of - // this ApkAssets in log messages and other debug scenarios. - // If `length` equals kUnknownLength, offset must equal 0; otherwise, the apk data will be read - // using the `offset` into the file descriptor and will be `length` bytes long. - static std::unique_ptr LoadFromFd( - base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U, - std::unique_ptr override_asset = nullptr, off64_t offset = 0, - off64_t length = kUnknownLength); + // Creates an ApkAssets from an open file descriptor. + static std::unique_ptr LoadFromFd(base::unique_fd fd, + const std::string& debug_name, + package_property_t flags = 0U, + off64_t offset = 0, + off64_t len = AssetsProvider::kUnknownLength); - // Creates an ApkAssets from the given path which points to a resources.arsc. - static std::unique_ptr LoadTable( - const std::string& path, package_property_t flags = 0U, - std::unique_ptr override_asset = nullptr); + // Creates an ApkAssets from an AssetProvider. + // The ApkAssets will take care of destroying the AssetsProvider when it is destroyed. + static std::unique_ptr Load(std::unique_ptr assets, + package_property_t flags = 0U); - // Creates an ApkAssets from the given file descriptor which points to an resources.arsc, and - // takes ownership of the file descriptor. - // If `length` equals kUnknownLength, offset must equal 0; otherwise, the .arsc data will be read - // using the `offset` into the file descriptor and will be `length` bytes long. - static std::unique_ptr LoadTableFromFd( - base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U, - std::unique_ptr override_asset = nullptr, off64_t offset = 0, - off64_t length = kUnknownLength); + // Creates an ApkAssets from the given asset file representing a resources.arsc. + static std::unique_ptr LoadTable(std::unique_ptr resources_asset, + std::unique_ptr assets, + package_property_t flags = 0U); // Creates an ApkAssets from an IDMAP, which contains the original APK path, and the overlay // data. - static std::unique_ptr LoadOverlay(const std::string& idmap_path, - package_property_t flags = 0U); + static std::unique_ptr LoadOverlay(const std::string& idmap_path, + package_property_t flags = 0U); - // Creates an ApkAssets from the directory path. File-based resources are read within the - // directory as if the directory is an APK. - static std::unique_ptr LoadFromDir( - const std::string& path, package_property_t flags = 0U, - std::unique_ptr override_asset = nullptr); - - // Creates a totally empty ApkAssets with no resources table and no file entries. - static std::unique_ptr LoadEmpty( - package_property_t flags = 0U, - std::unique_ptr override_asset = nullptr); - - const std::string& GetPath() const { - return path_; - } + // TODO(177101983): Remove all uses of GetPath for checking whether two ApkAssets are the same. + // With the introduction of ResourcesProviders, not all ApkAssets have paths. This could cause + // bugs when path is used for comparison because multiple ApkAssets could have the same "firendly + // name". Use pointer equality instead. ResourceManager caches and reuses ApkAssets so the + // same asset should have the same pointer. + const std::string& GetPath() const; const AssetsProvider* GetAssetsProvider() const { return assets_provider_.get(); @@ -146,53 +91,40 @@ class ApkAssets { // Returns whether the resources.arsc is allocated in RAM (not mmapped). bool IsTableAllocated() const { - return resources_asset_ && resources_asset_->isAllocated(); + return resources_asset_ != nullptr && resources_asset_->isAllocated(); } bool IsUpToDate() const; - // Creates an Asset from a file on disk. - static std::unique_ptr CreateAssetFromFile(const std::string& path); - - // Creates an Asset from a file descriptor. - // - // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset - // must equal 0; otherwise, the asset data will be read using the `offset` into the file - // descriptor and will be `length` bytes long. - static std::unique_ptr CreateAssetFromFd(base::unique_fd fd, - const char* path, - off64_t offset = 0, - off64_t length = kUnknownLength); private: - DISALLOW_COPY_AND_ASSIGN(ApkAssets); + static std::unique_ptr LoadImpl(std::unique_ptr assets, + package_property_t property_flags, + std::unique_ptr idmap_asset, + std::unique_ptr loaded_idmap); - static std::unique_ptr LoadImpl( - std::unique_ptr assets, const std::string& path, - package_property_t property_flags, - std::unique_ptr override_assets = nullptr, - std::unique_ptr idmap_asset = nullptr, - std::unique_ptr idmap = nullptr); + static std::unique_ptr LoadImpl(std::unique_ptr resources_asset, + std::unique_ptr assets, + package_property_t property_flags, + std::unique_ptr idmap_asset, + std::unique_ptr loaded_idmap); - static std::unique_ptr LoadTableImpl( - std::unique_ptr resources_asset, const std::string& path, - package_property_t property_flags, - std::unique_ptr override_assets = nullptr); + ApkAssets(std::unique_ptr resources_asset, + std::unique_ptr loaded_arsc, + std::unique_ptr assets, + package_property_t property_flags, + std::unique_ptr idmap_asset, + std::unique_ptr loaded_idmap); - ApkAssets(std::unique_ptr assets_provider, - std::string path, - time_t last_mod_time, - package_property_t property_flags); - - std::unique_ptr assets_provider_; - const std::string path_; - time_t last_mod_time_; - package_property_t property_flags_ = 0U; std::unique_ptr resources_asset_; + std::unique_ptr loaded_arsc_; + + std::unique_ptr assets_provider_; + package_property_t property_flags_ = 0U; + std::unique_ptr idmap_asset_; - std::unique_ptr loaded_arsc_; - std::unique_ptr loaded_idmap_; + std::unique_ptr loaded_idmap_; }; -} // namespace android +} // namespace android -#endif /* APKASSETS_H_ */ +#endif // APKASSETS_H_ \ No newline at end of file diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 80bae20f34197..40c91a6fcbf53 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -167,8 +167,8 @@ protected: private: /* AssetManager needs access to our "create" functions */ friend class AssetManager; - friend class ApkAssets; - friend class ZipAssetsProvider; + friend struct ZipAssetsProvider; + friend struct AssetsProvider; /* * Create the asset from a named file on disk. diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h new file mode 100644 index 0000000000000..7b06947f45aac --- /dev/null +++ b/libs/androidfw/include/androidfw/AssetsProvider.h @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROIDFW_ASSETSPROVIDER_H +#define ANDROIDFW_ASSETSPROVIDER_H + +#include +#include + +#include "android-base/macros.h" +#include "android-base/unique_fd.h" + +#include "androidfw/Asset.h" +#include "androidfw/Idmap.h" +#include "androidfw/LoadedArsc.h" +#include "androidfw/misc.h" + +struct ZipArchive; + +namespace android { + +// Interface responsible for opening and iterating through asset files. +struct AssetsProvider { + static constexpr off64_t kUnknownLength = -1; + + // Opens a file for reading. If `file_exists` is not null, it will be set to `true` if the file + // exists. This is useful for determining if the file exists but was unable to be opened due to + // an I/O error. + std::unique_ptr Open(const std::string& path, + Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM, + bool* file_exists = nullptr) const; + + // Iterate over all files and directories provided by the interface. The order of iteration is + // stable. + virtual bool ForEachFile(const std::string& path, + const std::function& f) const = 0; + + // Retrieves a name that represents the interface. This may or may not be the path of the + // interface source. + WARN_UNUSED virtual const std::string& GetDebugName() const = 0; + + // Returns whether the interface provides the most recent version of its files. + WARN_UNUSED virtual bool IsUpToDate() const = 0; + + // Creates an Asset from a file on disk. + static std::unique_ptr CreateAssetFromFile(const std::string& path); + + // Creates an Asset from a file descriptor. + // + // The asset takes ownership of the file descriptor. If `length` equals kUnknownLength, offset + // must equal 0; otherwise, the asset data will be read using the `offset` into the file + // descriptor and will be `length` bytes long. + static std::unique_ptr CreateAssetFromFd(base::unique_fd fd, + const char* path, + off64_t offset = 0, + off64_t length = AssetsProvider::kUnknownLength); + + virtual ~AssetsProvider() = default; + protected: + virtual std::unique_ptr OpenInternal(const std::string& path, Asset::AccessMode mode, + bool* file_exists) const = 0; +}; + +// Supplies assets from a zip archive. +struct ZipAssetsProvider : public AssetsProvider { + static std::unique_ptr Create(std::string path); + static std::unique_ptr Create(base::unique_fd fd, + std::string friendly_name, + off64_t offset = 0, + off64_t len = kUnknownLength); + + bool ForEachFile(const std::string& root_path, + const std::function& f) const override; + + WARN_UNUSED const std::string& GetDebugName() const override; + WARN_UNUSED bool IsUpToDate() const override; + + ~ZipAssetsProvider() override = default; + protected: + std::unique_ptr OpenInternal(const std::string& path, Asset::AccessMode mode, + bool* file_exists) const override; + + private: + struct PathOrDebugName; + ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, time_t last_mod_time); + + struct PathOrDebugName { + PathOrDebugName(std::string&& value, bool is_path); + + // Retrieves the path or null if this class represents a debug name. + WARN_UNUSED const std::string* GetPath() const; + + // Retrieves a name that represents the interface. This may or may not represent a path. + WARN_UNUSED const std::string& GetDebugName() const; + + private: + std::string value_; + bool is_path_; + }; + + std::unique_ptr zip_handle_; + PathOrDebugName name_; + time_t last_mod_time_; +}; + +// Supplies assets from a root directory. +struct DirectoryAssetsProvider : public AssetsProvider { + static std::unique_ptr Create(std::string root_dir); + + bool ForEachFile(const std::string& path, + const std::function& f) const override; + + WARN_UNUSED const std::string& GetDebugName() const override; + WARN_UNUSED bool IsUpToDate() const override; + + ~DirectoryAssetsProvider() override = default; + protected: + std::unique_ptr OpenInternal(const std::string& path, + Asset::AccessMode mode, + bool* file_exists) const override; + + private: + explicit DirectoryAssetsProvider(std::string&& path, time_t last_mod_time); + std::string dir_; + time_t last_mod_time_; +}; + +// Supplies assets from a `primary` asset provider and falls back to supplying assets from the +// `secondary` asset provider if the asset cannot be found in the `primary`. +struct MultiAssetsProvider : public AssetsProvider { + static std::unique_ptr Create(std::unique_ptr&& primary, + std::unique_ptr&& secondary); + + bool ForEachFile(const std::string& root_path, + const std::function& f) const override; + + WARN_UNUSED const std::string& GetDebugName() const override; + WARN_UNUSED bool IsUpToDate() const override; + + ~MultiAssetsProvider() override = default; + protected: + std::unique_ptr OpenInternal( + const std::string& path, Asset::AccessMode mode, bool* file_exists) const override; + + private: + MultiAssetsProvider(std::unique_ptr&& primary, + std::unique_ptr&& secondary); + + std::unique_ptr primary_; + std::unique_ptr secondary_; + std::string debug_name_; +}; + +// Does not provide any assets. +struct EmptyAssetsProvider : public AssetsProvider { + static std::unique_ptr Create(); + + bool ForEachFile(const std::string& path, + const std::function& f) const override; + + WARN_UNUSED const std::string& GetDebugName() const override; + WARN_UNUSED bool IsUpToDate() const override; + + ~EmptyAssetsProvider() override = default; + protected: + std::unique_ptr OpenInternal(const std::string& path, Asset::AccessMode mode, + bool* file_exists) const override; +}; + +} // namespace android + +#endif /* ANDROIDFW_ASSETSPROVIDER_H */ diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index fd9a8d13e0c68..0ded79309bc10 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -149,8 +149,8 @@ class IdmapResMap { class LoadedIdmap { public: // Loads an IDMAP from a chunk of memory. Returns nullptr if the IDMAP data was malformed. - static std::unique_ptr Load(const StringPiece& idmap_path, - const StringPiece& idmap_data); + static std::unique_ptr Load(const StringPiece& idmap_path, + const StringPiece& idmap_data); // Returns the path to the IDMAP. std::string_view IdmapPath() const { diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 891fb90d8eeba..d9225cd812ef8 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -300,17 +300,14 @@ class LoadedArsc { public: // Load a resource table from memory pointed to by `data` of size `len`. // The lifetime of `data` must out-live the LoadedArsc returned from this method. - // If `system` is set to true, the LoadedArsc is considered as a system provided resource. - // If `load_as_shared_library` is set to true, the application package (0x7f) is treated - // as a shared library (0x00). When loaded into an AssetManager, the package will be assigned an - // ID. - static std::unique_ptr Load(incfs::map_ptr data, - size_t length, - const LoadedIdmap* loaded_idmap = nullptr, - package_property_t property_flags = 0U); + + static std::unique_ptr Load(incfs::map_ptr data, + size_t length, + const LoadedIdmap* loaded_idmap = nullptr, + package_property_t property_flags = 0U); // Create an empty LoadedArsc. This is used when an APK has no resources.arsc. - static std::unique_ptr CreateEmpty(); + static std::unique_ptr CreateEmpty(); // Returns the string pool where all string resource values // (Res_value::dataType == Res_value::TYPE_STRING) are indexed. diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 3f0c7cbc8ffc4..b43491548e2b3 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -27,6 +27,8 @@ #include "data/overlayable/R.h" #include "data/system/R.h" +using ::testing::NotNull; + namespace overlay = com::android::overlay; namespace overlayable = com::android::overlayable; @@ -195,7 +197,11 @@ TEST_F(IdmapTest, OverlaidResourceHasSameName) { } TEST_F(IdmapTest, OverlayLoaderInterop) { - auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER); + auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc"); + ASSERT_THAT(asset, NotNull()); + + auto loader_assets = ApkAssets::LoadTable(std::move(asset), EmptyAssetsProvider::Create(), + PROPERTY_LOADER); AssetManager2 asset_manager; asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(), overlay_assets_.get()}); diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 9aa363495131e..f356c8130080b 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -339,10 +339,8 @@ TEST(LoadedArscTest, GetOverlayableMap) { } TEST(LoadedArscTest, LoadCustomLoader) { - std::string contents; - - std::unique_ptr - asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc"); + auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc"); + ASSERT_THAT(asset, NotNull()); const StringPiece data( reinterpret_cast(asset->getBuffer(true /*wordAligned*/)), diff --git a/startop/view_compiler/apk_layout_compiler.cc b/startop/view_compiler/apk_layout_compiler.cc index eaa3e04cc8147..5cb0c171e06fd 100644 --- a/startop/view_compiler/apk_layout_compiler.cc +++ b/startop/view_compiler/apk_layout_compiler.cc @@ -80,7 +80,7 @@ bool CanCompileLayout(ResXMLParser* parser) { } namespace { -void CompileApkAssetsLayouts(const std::unique_ptr& assets, +void CompileApkAssetsLayouts(const std::unique_ptr& assets, CompilationTarget target, std::ostream& target_out) { android::AssetManager2 resources; resources.SetApkAssets({assets.get()});