Merge "Dependency injection of AssetProviders into ApkAssets" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e10c0c05b1
@@ -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");
|
||||
|
||||
@@ -41,6 +41,7 @@ cc_library {
|
||||
"AssetDir.cpp",
|
||||
"AssetManager.cpp",
|
||||
"AssetManager2.cpp",
|
||||
"AssetsProvider.cpp",
|
||||
"AttributeResolution.cpp",
|
||||
"ChunkIterator.cpp",
|
||||
"ConfigDescription.cpp",
|
||||
|
||||
@@ -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
|
||||
|
||||
398
libs/androidfw/AssetsProvider.cpp
Normal file
398
libs/androidfw/AssetsProvider.cpp
Normal 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
|
||||
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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_
|
||||
@@ -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.
|
||||
|
||||
185
libs/androidfw/include/androidfw/AssetsProvider.h
Normal file
185
libs/androidfw/include/androidfw/AssetsProvider.h
Normal 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 */
|
||||
@@ -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 {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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()});
|
||||
|
||||
@@ -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*/)),
|
||||
|
||||
@@ -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()});
|
||||
|
||||
Reference in New Issue
Block a user