Merge "Dependency injection of AssetProviders into ApkAssets" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-02-03 21:13:11 +00:00
committed by Android (Google) Code Review
14 changed files with 816 additions and 652 deletions

View File

@@ -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<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
return (!assets_provider) ? nullptr
return (!assets_provider) ? EmptyAssetsProvider::Create()
: std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider(
env->NewGlobalRef(assets_provider)));
env, assets_provider));
}
bool ForEachFile(const std::string& /* root_path */,
const std::function<void(const StringPiece&, FileType)>& /* 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<off64_t>(mOffset),
static_cast<off64_t>(mLength));
return AssetsProvider::CreateAssetFromFd(base::unique_fd(fd),
nullptr /* path */,
static_cast<off64_t>(mOffset),
static_cast<off64_t>(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<jstring>(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<const ApkAssets> 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<const ApkAssets> 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<const ApkAssets> 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<off64_t>(offset),
static_cast<off64_t>(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<off64_t>(offset), static_cast<off64_t>(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<off64_t>(offset),
static_cast<off64_t>(length));
apk_assets = ApkAssets::LoadTable(
AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */,
static_cast<off64_t>(offset),
static_cast<off64_t>(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<jlong>(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");

View File

@@ -41,6 +41,7 @@ cc_library {
"AssetDir.cpp",
"AssetManager.cpp",
"AssetManager2.cpp",
"AssetsProvider.cpp",
"AttributeResolution.cpp",
"ChunkIterator.cpp",
"ConfigDescription.cpp",

View File

@@ -16,533 +16,145 @@
#include "androidfw/ApkAssets.h"
#include <algorithm>
#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<const AssetsProvider> 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<Asset> resources_asset,
std::unique_ptr<LoadedArsc> loaded_arsc,
std::unique_ptr<AssetsProvider> assets,
package_property_t property_flags,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<LoadedIdmap> 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> 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<const AssetsProvider> 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<AssetsProvider>(new ZipAssetsProvider(path, path, unmanaged_handle));
}
static std::unique_ptr<const AssetsProvider> 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<AssetsProvider>(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<void(const StringPiece&, FileType)>& 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<std::string> 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<Asset> 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 =
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<ZipArchive, void (*)(ZipArchiveHandle)>;
ZipArchivePtr zip_handle_;
std::string path_;
std::string friendly_name_;
};
class DirectoryAssetsProvider : AssetsProvider {
public:
~DirectoryAssetsProvider() override = default;
static std::unique_ptr<const AssetsProvider> 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<AssetsProvider>(new DirectoryAssetsProvider(path));
}
protected:
std::unique_ptr<Asset> 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<Asset> 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<const AssetsProvider> Create(
std::unique_ptr<const AssetsProvider> child, std::unique_ptr<const AssetsProvider> parent) {
CHECK(parent != nullptr) << "parent provider must not be null";
return (!child) ? std::move(parent)
: std::unique_ptr<const AssetsProvider>(new MultiAssetsProvider(
std::move(child), std::move(parent)));
}
bool ForEachFile(const std::string& root_path,
const std::function<void(const StringPiece&, FileType)>& 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<Asset> 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<const AssetsProvider> child,
std::unique_ptr<const AssetsProvider> parent)
: child_(std::move(child)), parent_(std::move(parent)) { }
std::unique_ptr<const AssetsProvider> child_;
std::unique_ptr<const AssetsProvider> parent_;
};
// Opens the archive using the file path. Calling CloseArchive on the zip handle will close the
// file.
std::unique_ptr<const ApkAssets> ApkAssets::Load(
const std::string& path, const package_property_t flags,
std::unique_ptr<const AssetsProvider> override_asset) {
auto assets = ZipAssetsProvider::Create(path);
return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
: nullptr;
std::unique_ptr<ApkAssets> 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<const ApkAssets> ApkAssets::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) {
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> ApkAssets::Load(std::unique_ptr<AssetsProvider> assets,
package_property_t flags) {
return LoadImpl(std::move(assets), flags, nullptr /* idmap_asset */, nullptr /* loaded_idmap */);
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadTable(
const std::string& path, const package_property_t flags,
std::unique_ptr<const AssetsProvider> override_asset) {
auto assets = CreateAssetFromFile(path);
return (assets) ? LoadTableImpl(std::move(assets), path, flags, std::move(override_asset))
: nullptr;
std::unique_ptr<ApkAssets> ApkAssets::LoadTable(std::unique_ptr<Asset> resources_asset,
std::unique_ptr<AssetsProvider> 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<const ApkAssets> ApkAssets::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) {
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<const ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
const package_property_t flags) {
std::unique_ptr<ApkAssets> ApkAssets::LoadOverlay(const std::string& idmap_path,
package_property_t flags) {
CHECK((flags & PROPERTY_LOADER) == 0U) << "Cannot load RROs through loaders";
std::unique_ptr<Asset> 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<const char*>(idmap_asset->getBuffer(true /*wordAligned*/)),
static_cast<size_t>(idmap_asset->getLength()));
std::unique_ptr<const LoadedIdmap> loaded_idmap = LoadedIdmap::Load(idmap_path, idmap_data);
StringPiece idmap_data(reinterpret_cast<const char*>(idmap_asset->getBuffer(true /* aligned */)),
static_cast<size_t>(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<const ApkAssets> ApkAssets::LoadFromDir(
const std::string& path, const package_property_t flags,
std::unique_ptr<const AssetsProvider> override_asset) {
auto assets = DirectoryAssetsProvider::Create(path);
return (assets) ? LoadImpl(std::move(assets), path, flags, std::move(override_asset))
: nullptr;
}
std::unique_ptr<const ApkAssets> ApkAssets::LoadEmpty(
const package_property_t flags, std::unique_ptr<const AssetsProvider> override_asset) {
auto assets = (override_asset) ? std::move(override_asset)
: std::unique_ptr<const AssetsProvider>(new EmptyAssetsProvider());
std::unique_ptr<ApkAssets> 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<Asset> 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<Asset> 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<size_t>(length), path)) {
LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': "
<< SystemErrorCodeToString(errno);
std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(std::unique_ptr<AssetsProvider> assets,
package_property_t property_flags,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<LoadedIdmap> 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<const ApkAssets> ApkAssets::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) {
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<ApkAssets>
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<const ApkAssets> ApkAssets::LoadTableImpl(
std::unique_ptr<Asset> resources_asset, const std::string& path,
package_property_t property_flags, std::unique_ptr<const AssetsProvider> override_assets) {
const time_t last_mod_time = getFileModDate(path.c_str());
auto assets = (override_assets) ? std::move(override_assets)
: std::unique_ptr<AssetsProvider>(new EmptyAssetsProvider());
std::unique_ptr<ApkAssets> 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> ApkAssets::LoadImpl(std::unique_ptr<Asset> resources_asset,
std::unique_ptr<AssetsProvider> assets,
package_property_t property_flags,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<LoadedIdmap> 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<LoadedArsc> 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<ApkAssets>(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

View File

@@ -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 <sys/stat.h>
#include <android-base/errors.h>
#include <android-base/stringprintf.h>
#include <android-base/utf8.h>
#include <ziparchive/zip_archive.h>
namespace android {
namespace {
constexpr const char* kEmptyDebugString = "<empty>";
} // namespace
std::unique_ptr<Asset> AssetsProvider::Open(const std::string& path, Asset::AccessMode mode,
bool* file_exists) const {
return OpenInternal(path, mode, file_exists);
}
std::unique_ptr<Asset> 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<Asset> 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<size_t>(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<std::string>(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<PathOrDebugName>(path)),
last_mod_time_(last_mod_time) {}
std::unique_ptr<ZipAssetsProvider> 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<ZipAssetsProvider>(
new ZipAssetsProvider(handle, PathOrDebugName{std::move(path),
true /* is_path */}, sb.st_mtime));
}
std::unique_ptr<ZipAssetsProvider> 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<ZipAssetsProvider>(
new ZipAssetsProvider(handle, PathOrDebugName{std::move(friendly_name),
false /* is_path */}, sb.st_mtime));
}
std::unique_ptr<Asset> 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 =
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<void(const StringPiece&, FileType)>& 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<std::string> 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<std::string>(path)), last_mod_time_(last_mod_time) {}
std::unique_ptr<DirectoryAssetsProvider> 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<DirectoryAssetsProvider>(new DirectoryAssetsProvider(std::move(path),
sb.st_mtime));
}
std::unique_ptr<Asset> 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<void(const StringPiece&, FileType)>& /* 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<AssetsProvider>&& primary,
std::unique_ptr<AssetsProvider>&& secondary)
: primary_(std::forward<std::unique_ptr<AssetsProvider>>(primary)),
secondary_(std::forward<std::unique_ptr<AssetsProvider>>(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<AssetsProvider> MultiAssetsProvider::Create(
std::unique_ptr<AssetsProvider>&& primary, std::unique_ptr<AssetsProvider>&& secondary) {
if (primary == nullptr || secondary == nullptr) {
return nullptr;
}
return std::unique_ptr<MultiAssetsProvider>(new MultiAssetsProvider(std::move(primary),
std::move(secondary)));
}
std::unique_ptr<Asset> 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<void(const StringPiece&, FileType)>& 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<AssetsProvider> EmptyAssetsProvider::Create() {
return std::make_unique<EmptyAssetsProvider>();
}
std::unique_ptr<Asset> 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<void(const StringPiece&, FileType)>& /* 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

View File

@@ -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<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
const StringPiece& idmap_data) {
std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
const StringPiece& idmap_data) {
ATRACE_CALL();
size_t data_size = idmap_data.size();
auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());

View File

@@ -767,10 +767,10 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap,
return true;
}
std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
const size_t length,
const LoadedIdmap* loaded_idmap,
const package_property_t property_flags) {
std::unique_ptr<LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> 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<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data,
}
}
// Need to force a move for mingw32.
return std::move(loaded_arsc);
return loaded_arsc;
}
std::unique_ptr<const LoadedArsc> LoadedArsc::CreateEmpty() {
std::unique_ptr<LoadedArsc> LoadedArsc::CreateEmpty() {
return std::unique_ptr<LoadedArsc>(new LoadedArsc());
}

View File

@@ -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<Asset> 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<void(const StringPiece&, FileType)>& /* f */) const {
return true;
}
protected:
AssetsProvider() = default;
virtual std::unique_ptr<Asset> 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<const ApkAssets> Load(
const std::string& path, package_property_t flags = 0U,
std::unique_ptr<const AssetsProvider> override_asset = nullptr);
// Creates an ApkAssets from a path on device.
static std::unique_ptr<ApkAssets> 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<const ApkAssets> LoadFromFd(
base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
std::unique_ptr<const AssetsProvider> override_asset = nullptr, off64_t offset = 0,
off64_t length = kUnknownLength);
// Creates an ApkAssets from an open file descriptor.
static std::unique_ptr<ApkAssets> 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<const ApkAssets> LoadTable(
const std::string& path, package_property_t flags = 0U,
std::unique_ptr<const AssetsProvider> 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<ApkAssets> Load(std::unique_ptr<AssetsProvider> 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<const ApkAssets> LoadTableFromFd(
base::unique_fd fd, const std::string& friendly_name, package_property_t flags = 0U,
std::unique_ptr<const AssetsProvider> 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<ApkAssets> LoadTable(std::unique_ptr<Asset> resources_asset,
std::unique_ptr<AssetsProvider> 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<const ApkAssets> LoadOverlay(const std::string& idmap_path,
package_property_t flags = 0U);
static std::unique_ptr<ApkAssets> 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<const ApkAssets> LoadFromDir(
const std::string& path, package_property_t flags = 0U,
std::unique_ptr<const AssetsProvider> override_asset = nullptr);
// Creates a totally empty ApkAssets with no resources table and no file entries.
static std::unique_ptr<const ApkAssets> LoadEmpty(
package_property_t flags = 0U,
std::unique_ptr<const AssetsProvider> 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<Asset> 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<Asset> 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<ApkAssets> LoadImpl(std::unique_ptr<AssetsProvider> assets,
package_property_t property_flags,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<LoadedIdmap> loaded_idmap);
static std::unique_ptr<const ApkAssets> LoadImpl(
std::unique_ptr<const AssetsProvider> assets, const std::string& path,
package_property_t property_flags,
std::unique_ptr<const AssetsProvider> override_assets = nullptr,
std::unique_ptr<Asset> idmap_asset = nullptr,
std::unique_ptr<const LoadedIdmap> idmap = nullptr);
static std::unique_ptr<ApkAssets> LoadImpl(std::unique_ptr<Asset> resources_asset,
std::unique_ptr<AssetsProvider> assets,
package_property_t property_flags,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<LoadedIdmap> loaded_idmap);
static std::unique_ptr<const ApkAssets> LoadTableImpl(
std::unique_ptr<Asset> resources_asset, const std::string& path,
package_property_t property_flags,
std::unique_ptr<const AssetsProvider> override_assets = nullptr);
ApkAssets(std::unique_ptr<Asset> resources_asset,
std::unique_ptr<LoadedArsc> loaded_arsc,
std::unique_ptr<AssetsProvider> assets,
package_property_t property_flags,
std::unique_ptr<Asset> idmap_asset,
std::unique_ptr<LoadedIdmap> loaded_idmap);
ApkAssets(std::unique_ptr<const AssetsProvider> assets_provider,
std::string path,
time_t last_mod_time,
package_property_t property_flags);
std::unique_ptr<const AssetsProvider> assets_provider_;
const std::string path_;
time_t last_mod_time_;
package_property_t property_flags_ = 0U;
std::unique_ptr<Asset> resources_asset_;
std::unique_ptr<LoadedArsc> loaded_arsc_;
std::unique_ptr<AssetsProvider> assets_provider_;
package_property_t property_flags_ = 0U;
std::unique_ptr<Asset> idmap_asset_;
std::unique_ptr<const LoadedArsc> loaded_arsc_;
std::unique_ptr<const LoadedIdmap> loaded_idmap_;
std::unique_ptr<LoadedIdmap> loaded_idmap_;
};
} // namespace android
} // namespace android
#endif /* APKASSETS_H_ */
#endif // APKASSETS_H_

View File

@@ -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.

View File

@@ -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 <memory>
#include <string>
#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<Asset> 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<void(const StringPiece&, FileType)>& 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<Asset> 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<Asset> 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<Asset> 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<ZipAssetsProvider> Create(std::string path);
static std::unique_ptr<ZipAssetsProvider> 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<void(const StringPiece&, FileType)>& f) const override;
WARN_UNUSED const std::string& GetDebugName() const override;
WARN_UNUSED bool IsUpToDate() const override;
~ZipAssetsProvider() override = default;
protected:
std::unique_ptr<Asset> 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<ZipArchive, void (*)(ZipArchive*)> zip_handle_;
PathOrDebugName name_;
time_t last_mod_time_;
};
// Supplies assets from a root directory.
struct DirectoryAssetsProvider : public AssetsProvider {
static std::unique_ptr<DirectoryAssetsProvider> Create(std::string root_dir);
bool ForEachFile(const std::string& path,
const std::function<void(const StringPiece&, FileType)>& f) const override;
WARN_UNUSED const std::string& GetDebugName() const override;
WARN_UNUSED bool IsUpToDate() const override;
~DirectoryAssetsProvider() override = default;
protected:
std::unique_ptr<Asset> 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<AssetsProvider> Create(std::unique_ptr<AssetsProvider>&& primary,
std::unique_ptr<AssetsProvider>&& secondary);
bool ForEachFile(const std::string& root_path,
const std::function<void(const StringPiece&, FileType)>& f) const override;
WARN_UNUSED const std::string& GetDebugName() const override;
WARN_UNUSED bool IsUpToDate() const override;
~MultiAssetsProvider() override = default;
protected:
std::unique_ptr<Asset> OpenInternal(
const std::string& path, Asset::AccessMode mode, bool* file_exists) const override;
private:
MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
std::unique_ptr<AssetsProvider>&& secondary);
std::unique_ptr<AssetsProvider> primary_;
std::unique_ptr<AssetsProvider> secondary_;
std::string debug_name_;
};
// Does not provide any assets.
struct EmptyAssetsProvider : public AssetsProvider {
static std::unique_ptr<AssetsProvider> Create();
bool ForEachFile(const std::string& path,
const std::function<void(const StringPiece&, FileType)>& f) const override;
WARN_UNUSED const std::string& GetDebugName() const override;
WARN_UNUSED bool IsUpToDate() const override;
~EmptyAssetsProvider() override = default;
protected:
std::unique_ptr<Asset> OpenInternal(const std::string& path, Asset::AccessMode mode,
bool* file_exists) const override;
};
} // namespace android
#endif /* ANDROIDFW_ASSETSPROVIDER_H */

View File

@@ -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<const LoadedIdmap> Load(const StringPiece& idmap_path,
const StringPiece& idmap_data);
static std::unique_ptr<LoadedIdmap> Load(const StringPiece& idmap_path,
const StringPiece& idmap_data);
// Returns the path to the IDMAP.
std::string_view IdmapPath() const {

View File

@@ -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<const LoadedArsc> Load(incfs::map_ptr<void> data,
size_t length,
const LoadedIdmap* loaded_idmap = nullptr,
package_property_t property_flags = 0U);
static std::unique_ptr<LoadedArsc> Load(incfs::map_ptr<void> 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<const LoadedArsc> CreateEmpty();
static std::unique_ptr<LoadedArsc> CreateEmpty();
// Returns the string pool where all string resource values
// (Res_value::dataType == Res_value::TYPE_STRING) are indexed.

View File

@@ -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()});

View File

@@ -339,10 +339,8 @@ TEST(LoadedArscTest, GetOverlayableMap) {
}
TEST(LoadedArscTest, LoadCustomLoader) {
std::string contents;
std::unique_ptr<Asset>
asset = ApkAssets::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
auto asset = AssetsProvider::CreateAssetFromFile(GetTestDataPath() + "/loader/resources.arsc");
ASSERT_THAT(asset, NotNull());
const StringPiece data(
reinterpret_cast<const char*>(asset->getBuffer(true /*wordAligned*/)),

View File

@@ -80,7 +80,7 @@ bool CanCompileLayout(ResXMLParser* parser) {
}
namespace {
void CompileApkAssetsLayouts(const std::unique_ptr<const android::ApkAssets>& assets,
void CompileApkAssetsLayouts(const std::unique_ptr<android::ApkAssets>& assets,
CompilationTarget target, std::ostream& target_out) {
android::AssetManager2 resources;
resources.SetApkAssets({assets.get()});