AssetManager2: Various fixes
- Use FileMaps to open Assets (prevents closing of ApkAssets underlying zip) - Implement OpenDir and List methods - Fix issue where DynamicRefTable wasn't properly constructed Test: make libandroidfw_tests Change-Id: Ib21a84e1114d028120744aa3bc1c6eb9d9399fa8
This commit is contained in:
@@ -18,7 +18,10 @@
|
||||
|
||||
#include "androidfw/ApkAssets.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "android-base/logging.h"
|
||||
#include "utils/FileMap.h"
|
||||
#include "utils/Trace.h"
|
||||
#include "ziparchive/zip_archive.h"
|
||||
|
||||
@@ -62,13 +65,13 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bo
|
||||
LOG(WARNING) << "resources.arsc is compressed.";
|
||||
}
|
||||
|
||||
loaded_apk->path_ = path;
|
||||
loaded_apk->resources_asset_ =
|
||||
loaded_apk->Open("resources.arsc", Asset::AccessMode::ACCESS_BUFFER);
|
||||
if (loaded_apk->resources_asset_ == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
loaded_apk->path_ = path;
|
||||
loaded_apk->loaded_arsc_ =
|
||||
LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
|
||||
loaded_apk->resources_asset_->getLength(), system, load_as_shared_library);
|
||||
@@ -80,37 +83,93 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bo
|
||||
return std::move(loaded_apk);
|
||||
}
|
||||
|
||||
std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode /*mode*/) const {
|
||||
ATRACE_NAME("ApkAssets::Open");
|
||||
std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode mode) const {
|
||||
ATRACE_CALL();
|
||||
CHECK(zip_handle_ != nullptr);
|
||||
|
||||
::ZipString name(path.c_str());
|
||||
::ZipEntry entry;
|
||||
int32_t result = ::FindEntry(zip_handle_.get(), name, &entry);
|
||||
if (result != 0) {
|
||||
LOG(ERROR) << "No entry '" << path << "' found in APK.";
|
||||
LOG(ERROR) << "No entry '" << path << "' found in APK '" << path_ << "'";
|
||||
return {};
|
||||
}
|
||||
|
||||
if (entry.method == kCompressDeflated) {
|
||||
auto compressed_asset = util::make_unique<_CompressedAsset>();
|
||||
if (compressed_asset->openChunk(::GetFileDescriptor(zip_handle_.get()), entry.offset,
|
||||
entry.method, entry.uncompressed_length,
|
||||
entry.compressed_length) != NO_ERROR) {
|
||||
std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
|
||||
if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
|
||||
entry.compressed_length, true /*readOnly*/)) {
|
||||
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<Asset> asset =
|
||||
Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode);
|
||||
if (asset == nullptr) {
|
||||
LOG(ERROR) << "Failed to decompress '" << path << "'.";
|
||||
return {};
|
||||
}
|
||||
return std::move(compressed_asset);
|
||||
return asset;
|
||||
} else {
|
||||
auto uncompressed_asset = util::make_unique<_FileAsset>();
|
||||
if (uncompressed_asset->openChunk(path.c_str(), ::GetFileDescriptor(zip_handle_.get()),
|
||||
entry.offset, entry.uncompressed_length) != NO_ERROR) {
|
||||
LOG(ERROR) << "Failed to mmap '" << path << "'.";
|
||||
std::unique_ptr<FileMap> map = util::make_unique<FileMap>();
|
||||
if (!map->create(path_.c_str(), ::GetFileDescriptor(zip_handle_.get()), entry.offset,
|
||||
entry.uncompressed_length, true /*readOnly*/)) {
|
||||
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
|
||||
return {};
|
||||
}
|
||||
return std::move(uncompressed_asset);
|
||||
|
||||
std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), mode);
|
||||
if (asset == nullptr) {
|
||||
LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << path_ << "'";
|
||||
return {};
|
||||
}
|
||||
return asset;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool ApkAssets::ForEachFile(const std::string& root_path,
|
||||
const std::function<void(const StringPiece&, FileType)>& f) const {
|
||||
CHECK(zip_handle_ != nullptr);
|
||||
|
||||
std::string root_path_full = root_path;
|
||||
if (root_path_full.back() != '/') {
|
||||
root_path_full += '/';
|
||||
}
|
||||
|
||||
::ZipString prefix(root_path_full.c_str());
|
||||
void* cookie;
|
||||
if (::StartIteration(zip_handle_.get(), &cookie, &prefix, nullptr) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
::ZipString 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(reinterpret_cast<const char*>(name.name), name.name_length);
|
||||
StringPiece leaf_file_path = full_file_path.substr(root_path_full.size());
|
||||
auto iter = std::find(leaf_file_path.begin(), leaf_file_path.end(), '/');
|
||||
if (iter != leaf_file_path.end()) {
|
||||
dirs.insert(
|
||||
leaf_file_path.substr(0, std::distance(leaf_file_path.begin(), iter)).to_string());
|
||||
} else if (!leaf_file_path.empty()) {
|
||||
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;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include <androidfw/Asset.h>
|
||||
#include <androidfw/StreamingZipInflater.h>
|
||||
#include <androidfw/Util.h>
|
||||
#include <androidfw/ZipFileRO.h>
|
||||
#include <androidfw/ZipUtils.h>
|
||||
#include <utils/Atomic.h>
|
||||
@@ -298,6 +299,22 @@ Asset::Asset(void)
|
||||
return pAsset;
|
||||
}
|
||||
|
||||
/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
|
||||
AccessMode mode)
|
||||
{
|
||||
std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>();
|
||||
|
||||
status_t result = pAsset->openChunk(dataMap.get());
|
||||
if (result != NO_ERROR) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// We succeeded, so relinquish control of dataMap
|
||||
(void) dataMap.release();
|
||||
pAsset->mAccessMode = mode;
|
||||
return std::move(pAsset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a new Asset from compressed data in a memory mapping.
|
||||
*/
|
||||
@@ -316,6 +333,21 @@ Asset::Asset(void)
|
||||
return pAsset;
|
||||
}
|
||||
|
||||
/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
|
||||
size_t uncompressedLen, AccessMode mode)
|
||||
{
|
||||
std::unique_ptr<_CompressedAsset> pAsset = util::make_unique<_CompressedAsset>();
|
||||
|
||||
status_t result = pAsset->openChunk(dataMap.get(), uncompressedLen);
|
||||
if (result != NO_ERROR) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// We succeeded, so relinquish control of dataMap
|
||||
(void) dataMap.release();
|
||||
pAsset->mAccessMode = mode;
|
||||
return std::move(pAsset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Do generic seek() housekeeping. Pass in the offset/whence values from
|
||||
|
||||
@@ -138,6 +138,17 @@ const DynamicRefTable* AssetManager2::GetDynamicRefTableForPackage(uint32_t pack
|
||||
return &package_groups_[idx].dynamic_ref_table;
|
||||
}
|
||||
|
||||
const DynamicRefTable* AssetManager2::GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const {
|
||||
for (const PackageGroup& package_group : package_groups_) {
|
||||
for (const ApkAssetsCookie& package_cookie : package_group.cookies_) {
|
||||
if (package_cookie == cookie) {
|
||||
return &package_group.dynamic_ref_table;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
|
||||
const int diff = configuration_.diff(configuration);
|
||||
configuration_ = configuration;
|
||||
@@ -188,6 +199,35 @@ std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, ApkAsset
|
||||
return OpenNonAsset(new_path, cookie, mode);
|
||||
}
|
||||
|
||||
std::unique_ptr<AssetDir> AssetManager2::OpenDir(const std::string& dirname) {
|
||||
ATRACE_CALL();
|
||||
|
||||
std::string full_path = "assets/" + dirname;
|
||||
std::unique_ptr<SortedVector<AssetDir::FileInfo>> files =
|
||||
util::make_unique<SortedVector<AssetDir::FileInfo>>();
|
||||
|
||||
// Start from the back.
|
||||
for (auto iter = apk_assets_.rbegin(); iter != apk_assets_.rend(); ++iter) {
|
||||
const ApkAssets* apk_assets = *iter;
|
||||
|
||||
auto func = [&](const StringPiece& name, FileType type) {
|
||||
AssetDir::FileInfo info;
|
||||
info.setFileName(String8(name.data(), name.size()));
|
||||
info.setFileType(type);
|
||||
info.setSourceName(String8(apk_assets->GetPath().c_str()));
|
||||
files->add(info);
|
||||
};
|
||||
|
||||
if (!apk_assets->ForEachFile(full_path, func)) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AssetDir> asset_dir = util::make_unique<AssetDir>();
|
||||
asset_dir->setFileList(files.release());
|
||||
return asset_dir;
|
||||
}
|
||||
|
||||
// Search in reverse because that's how we used to do it and we need to preserve behaviour.
|
||||
// This is unfortunate, because ClassLoaders delegate to the parent first, so the order
|
||||
// is inconsistent for split APKs.
|
||||
@@ -237,15 +277,15 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
desired_config = &density_override_config;
|
||||
}
|
||||
|
||||
const uint32_t package_id = get_package_id(resid);
|
||||
const uint8_t type_id = get_type_id(resid);
|
||||
const uint16_t entry_id = get_entry_id(resid);
|
||||
|
||||
if (type_id == 0) {
|
||||
if (!is_valid_resid(resid)) {
|
||||
LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
|
||||
return kInvalidCookie;
|
||||
}
|
||||
|
||||
const uint32_t package_id = get_package_id(resid);
|
||||
const uint8_t type_idx = get_type_id(resid) - 1;
|
||||
const uint16_t entry_id = get_entry_id(resid);
|
||||
|
||||
const uint8_t idx = package_ids_[package_id];
|
||||
if (idx == 0xff) {
|
||||
LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
|
||||
@@ -265,7 +305,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
|
||||
uint32_t current_flags = 0;
|
||||
|
||||
const LoadedPackage* loaded_package = package_group.packages_[i];
|
||||
if (!loaded_package->FindEntry(type_id - 1, entry_id, *desired_config, ¤t_entry,
|
||||
if (!loaded_package->FindEntry(type_idx, entry_id, *desired_config, ¤t_entry,
|
||||
¤t_config, ¤t_flags)) {
|
||||
continue;
|
||||
}
|
||||
@@ -385,16 +425,16 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
|
||||
ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
|
||||
ResTable_config* in_out_selected_config,
|
||||
uint32_t* in_out_flags,
|
||||
ResTable_ref* out_last_reference) {
|
||||
uint32_t* out_last_reference) {
|
||||
ATRACE_CALL();
|
||||
constexpr const int kMaxIterations = 20;
|
||||
|
||||
out_last_reference->ident = 0u;
|
||||
*out_last_reference = 0u;
|
||||
for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE &&
|
||||
in_out_value->data != 0u && iteration < kMaxIterations;
|
||||
iteration++) {
|
||||
if (out_last_reference != nullptr) {
|
||||
out_last_reference->ident = in_out_value->data;
|
||||
*out_last_reference = in_out_value->data;
|
||||
}
|
||||
uint32_t new_flags = 0u;
|
||||
cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
|
||||
@@ -405,7 +445,7 @@ ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_valu
|
||||
if (in_out_flags != nullptr) {
|
||||
*in_out_flags |= new_flags;
|
||||
}
|
||||
if (out_last_reference->ident == in_out_value->data) {
|
||||
if (*out_last_reference == in_out_value->data) {
|
||||
// This reference can't be resolved, so exit now and let the caller deal with it.
|
||||
return cookie;
|
||||
}
|
||||
@@ -832,6 +872,25 @@ ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value,
|
||||
return kInvalidCookie;
|
||||
}
|
||||
|
||||
ApkAssetsCookie Theme::ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
|
||||
ResTable_config* in_out_selected_config,
|
||||
uint32_t* in_out_type_spec_flags,
|
||||
uint32_t* out_last_ref) {
|
||||
if (in_out_value->dataType == Res_value::TYPE_ATTRIBUTE) {
|
||||
uint32_t new_flags;
|
||||
cookie = GetAttribute(in_out_value->data, in_out_value, &new_flags);
|
||||
if (cookie == kInvalidCookie) {
|
||||
return kInvalidCookie;
|
||||
}
|
||||
|
||||
if (in_out_type_spec_flags != nullptr) {
|
||||
*in_out_type_spec_flags |= new_flags;
|
||||
}
|
||||
}
|
||||
return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config,
|
||||
in_out_type_spec_flags, out_last_ref);
|
||||
}
|
||||
|
||||
void Theme::Clear() {
|
||||
type_spec_flags_ = 0u;
|
||||
for (std::unique_ptr<Package>& package : packages_) {
|
||||
|
||||
@@ -6532,6 +6532,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
DynamicRefTable::DynamicRefTable() : DynamicRefTable(0, false) {}
|
||||
|
||||
DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
|
||||
: mAssignedPackageId(packageId)
|
||||
, mAppAsLib(appAsLib)
|
||||
@@ -6637,11 +6639,11 @@ status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {
|
||||
// Do a proper lookup.
|
||||
uint8_t translatedId = mLookupTable[packageId];
|
||||
if (translatedId == 0) {
|
||||
ALOGV("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
|
||||
ALOGW("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",
|
||||
(uint8_t)mAssignedPackageId, (uint8_t)packageId);
|
||||
for (size_t i = 0; i < 256; i++) {
|
||||
if (mLookupTable[i] != 0) {
|
||||
ALOGV("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
|
||||
ALOGW("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);
|
||||
}
|
||||
}
|
||||
return UNKNOWN_ERROR;
|
||||
|
||||
@@ -41,5 +41,31 @@ void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out
|
||||
}
|
||||
}
|
||||
|
||||
std::u16string Utf8ToUtf16(const StringPiece& utf8) {
|
||||
ssize_t utf16_length =
|
||||
utf8_to_utf16_length(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
|
||||
if (utf16_length <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::u16string utf16;
|
||||
utf16.resize(utf16_length);
|
||||
utf8_to_utf16(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length(), &*utf16.begin(),
|
||||
utf16_length + 1);
|
||||
return utf16;
|
||||
}
|
||||
|
||||
std::string Utf16ToUtf8(const StringPiece16& utf16) {
|
||||
ssize_t utf8_length = utf16_to_utf8_length(utf16.data(), utf16.length());
|
||||
if (utf8_length <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string utf8;
|
||||
utf8.resize(utf8_length);
|
||||
utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8_length + 1);
|
||||
return utf8;
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
} // namespace android
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "androidfw/Asset.h"
|
||||
#include "androidfw/LoadedArsc.h"
|
||||
#include "androidfw/misc.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -38,6 +39,9 @@ class ApkAssets {
|
||||
std::unique_ptr<Asset> Open(const std::string& path,
|
||||
Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
|
||||
|
||||
bool ForEachFile(const std::string& path,
|
||||
const std::function<void(const StringPiece&, FileType)>& f) const;
|
||||
|
||||
inline const std::string& GetPath() const { return path_; }
|
||||
|
||||
inline const LoadedArsc* GetLoadedArsc() const { return loaded_arsc_.get(); }
|
||||
@@ -56,6 +60,7 @@ class ApkAssets {
|
||||
|
||||
using ZipArchivePtr =
|
||||
std::unique_ptr<typename std::remove_pointer<::ZipArchiveHandle>::type, ZipArchivePtrCloser>;
|
||||
|
||||
ZipArchivePtr zip_handle_;
|
||||
std::string path_;
|
||||
std::unique_ptr<Asset> resources_asset_;
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <utils/Compat.h>
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
@@ -150,6 +152,7 @@ private:
|
||||
|
||||
/* AssetManager needs access to our "create" functions */
|
||||
friend class AssetManager;
|
||||
friend class ApkAssets;
|
||||
|
||||
/*
|
||||
* Create the asset from a named file on disk.
|
||||
@@ -194,6 +197,9 @@ private:
|
||||
*/
|
||||
static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode);
|
||||
|
||||
static std::unique_ptr<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap,
|
||||
AccessMode mode);
|
||||
|
||||
/*
|
||||
* Create the asset from a memory-mapped file segment with compressed
|
||||
* data.
|
||||
@@ -203,6 +209,9 @@ private:
|
||||
static Asset* createFromCompressedMap(FileMap* dataMap,
|
||||
size_t uncompressedLen, AccessMode mode);
|
||||
|
||||
static std::unique_ptr<Asset> createFromCompressedMap(std::unique_ptr<FileMap> dataMap,
|
||||
size_t uncompressedLen, AccessMode mode);
|
||||
|
||||
|
||||
/*
|
||||
* Create from a reference-counted chunk of shared memory.
|
||||
|
||||
@@ -70,6 +70,7 @@ private:
|
||||
const AssetDir& operator=(const AssetDir& src);
|
||||
|
||||
friend class AssetManager;
|
||||
friend class AssetManager2;
|
||||
|
||||
/*
|
||||
* This holds information about files in the asset hierarchy.
|
||||
|
||||
@@ -107,6 +107,9 @@ class AssetManager2 : public ::AAssetManager {
|
||||
// Returns the DynamicRefTable for the given package ID.
|
||||
const DynamicRefTable* GetDynamicRefTableForPackage(uint32_t package_id) const;
|
||||
|
||||
// Returns the DynamicRefTable for the ApkAssets represented by the cookie.
|
||||
const DynamicRefTable* GetDynamicRefTableForCookie(ApkAssetsCookie cookie) const;
|
||||
|
||||
// Sets/resets the configuration for this AssetManager. This will cause all
|
||||
// caches that are related to the configuration change to be invalidated.
|
||||
void SetConfiguration(const ResTable_config& configuration);
|
||||
@@ -143,6 +146,11 @@ class AssetManager2 : public ::AAssetManager {
|
||||
std::unique_ptr<Asset> Open(const std::string& filename, ApkAssetsCookie cookie,
|
||||
Asset::AccessMode mode);
|
||||
|
||||
// Opens the directory specified by `dirname`. The result is an AssetDir that is the combination
|
||||
// of all directories matching `dirname` under the assets/ directory of every ApkAssets loaded.
|
||||
// The entries are sorted by their ASCII name.
|
||||
std::unique_ptr<AssetDir> OpenDir(const std::string& dirname);
|
||||
|
||||
// Searches the set of APKs loaded by this AssetManager and opens the first one found.
|
||||
// `mode` controls how the file is opened.
|
||||
// `out_cookie` is populated with the cookie of the APK this file was found in.
|
||||
@@ -203,7 +211,7 @@ class AssetManager2 : public ::AAssetManager {
|
||||
// it was not found.
|
||||
ApkAssetsCookie ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value,
|
||||
ResTable_config* in_out_selected_config, uint32_t* in_out_flags,
|
||||
ResTable_ref* out_last_reference);
|
||||
uint32_t* out_last_reference);
|
||||
|
||||
// Retrieves the best matching bag/map resource with ID `resid`.
|
||||
// This method will resolve all parent references for this bag and merge keys with the child.
|
||||
@@ -298,6 +306,8 @@ class Theme {
|
||||
|
||||
inline const AssetManager2* GetAssetManager() const { return asset_manager_; }
|
||||
|
||||
inline AssetManager2* GetAssetManager() { return asset_manager_; }
|
||||
|
||||
// Returns a bit mask of configuration changes that will impact this
|
||||
// theme (and thus require completely reloading it).
|
||||
inline uint32_t GetChangingConfigurations() const { return type_spec_flags_; }
|
||||
@@ -318,10 +328,10 @@ class Theme {
|
||||
|
||||
// This is like AssetManager2::ResolveReference(), but also takes
|
||||
// care of resolving attribute references to the theme.
|
||||
ApkAssetsCookie ResolveAttributeReference(Res_value* in_out_value, ApkAssetsCookie src_cookie,
|
||||
uint32_t* out_last_ref = nullptr,
|
||||
ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value,
|
||||
ResTable_config* in_out_selected_config = nullptr,
|
||||
uint32_t* in_out_type_spec_flags = nullptr,
|
||||
ResTable_config* out_selected_config = nullptr) const;
|
||||
uint32_t* out_last_ref = nullptr);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(Theme);
|
||||
|
||||
@@ -1554,7 +1554,7 @@ class DynamicRefTable
|
||||
{
|
||||
friend class AssetManager2;
|
||||
public:
|
||||
DynamicRefTable() = default;
|
||||
DynamicRefTable();
|
||||
DynamicRefTable(uint8_t packageId, bool appAsLib);
|
||||
|
||||
// Loads an unmapped reference table from the package.
|
||||
@@ -1577,10 +1577,10 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t mAssignedPackageId = 0;
|
||||
uint8_t mAssignedPackageId;
|
||||
uint8_t mLookupTable[256];
|
||||
KeyedVector<String16, uint8_t> mEntries;
|
||||
bool mAppAsLib = false;
|
||||
bool mAppAsLib;
|
||||
};
|
||||
|
||||
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
|
||||
#include "android-base/macros.h"
|
||||
|
||||
#include "androidfw/StringPiece.h"
|
||||
|
||||
namespace android {
|
||||
namespace util {
|
||||
|
||||
@@ -108,6 +110,12 @@ class unique_cptr {
|
||||
|
||||
void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out);
|
||||
|
||||
// Converts a UTF-8 string to a UTF-16 string.
|
||||
std::u16string Utf8ToUtf16(const StringPiece& utf8);
|
||||
|
||||
// Converts a UTF-16 string to a UTF-8 string.
|
||||
std::string Utf16ToUtf8(const StringPiece16& utf16);
|
||||
|
||||
} // namespace util
|
||||
} // namespace android
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
#include "androidfw/ApkAssets.h"
|
||||
|
||||
#include "android-base/file.h"
|
||||
#include "android-base/unique_fd.h"
|
||||
|
||||
#include "TestHelpers.h"
|
||||
#include "data/basic/R.h"
|
||||
|
||||
@@ -51,4 +54,41 @@ TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
|
||||
EXPECT_TRUE(loaded_arsc->GetPackages()[0]->IsDynamic());
|
||||
}
|
||||
|
||||
TEST(ApkAssetsTest, CreateAndDestroyAssetKeepsApkAssetsOpen) {
|
||||
std::unique_ptr<const ApkAssets> loaded_apk =
|
||||
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
|
||||
ASSERT_NE(nullptr, loaded_apk);
|
||||
|
||||
{
|
||||
std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER);
|
||||
ASSERT_NE(nullptr, assets);
|
||||
}
|
||||
|
||||
{
|
||||
std::unique_ptr<Asset> assets = loaded_apk->Open("res/layout/main.xml", Asset::ACCESS_BUFFER);
|
||||
ASSERT_NE(nullptr, assets);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ApkAssetsTest, OpenUncompressedAssetFd) {
|
||||
std::unique_ptr<const ApkAssets> loaded_apk =
|
||||
ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
|
||||
ASSERT_NE(nullptr, loaded_apk);
|
||||
|
||||
auto asset = loaded_apk->Open("assets/uncompressed.txt", Asset::ACCESS_UNKNOWN);
|
||||
ASSERT_NE(nullptr, asset);
|
||||
|
||||
off64_t start, length;
|
||||
base::unique_fd fd(asset->openFileDescriptor(&start, &length));
|
||||
EXPECT_GE(fd.get(), 0);
|
||||
|
||||
lseek64(fd.get(), start, SEEK_SET);
|
||||
|
||||
std::string buffer;
|
||||
buffer.resize(length);
|
||||
ASSERT_TRUE(base::ReadFully(fd.get(), &*buffer.begin(), length));
|
||||
|
||||
EXPECT_EQ("This should be uncompressed.\n\n", buffer);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -312,12 +312,12 @@ TEST_F(AssetManager2Test, ResolveReferenceToResource) {
|
||||
EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
|
||||
EXPECT_EQ(basic::R::integer::ref2, value.data);
|
||||
|
||||
ResTable_ref last_ref;
|
||||
uint32_t last_ref;
|
||||
cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
|
||||
ASSERT_NE(kInvalidCookie, cookie);
|
||||
EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType);
|
||||
EXPECT_EQ(12000u, value.data);
|
||||
EXPECT_EQ(basic::R::integer::ref2, last_ref.ident);
|
||||
EXPECT_EQ(basic::R::integer::ref2, last_ref);
|
||||
}
|
||||
|
||||
TEST_F(AssetManager2Test, ResolveReferenceToBag) {
|
||||
@@ -335,12 +335,12 @@ TEST_F(AssetManager2Test, ResolveReferenceToBag) {
|
||||
EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
|
||||
EXPECT_EQ(basic::R::array::integerArray1, value.data);
|
||||
|
||||
ResTable_ref last_ref;
|
||||
uint32_t last_ref;
|
||||
cookie = assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_ref);
|
||||
ASSERT_NE(kInvalidCookie, cookie);
|
||||
EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
|
||||
EXPECT_EQ(basic::R::array::integerArray1, value.data);
|
||||
EXPECT_EQ(basic::R::array::integerArray1, last_ref.ident);
|
||||
EXPECT_EQ(basic::R::array::integerArray1, last_ref);
|
||||
}
|
||||
|
||||
static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
|
||||
|
||||
2
libs/androidfw/tests/data/basic/assets/uncompressed.txt
Normal file
2
libs/androidfw/tests/data/basic/assets/uncompressed.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
This should be uncompressed.
|
||||
|
||||
Binary file not shown.
@@ -19,4 +19,11 @@ set -e
|
||||
|
||||
PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar
|
||||
|
||||
aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --split hdpi --split xhdpi --split xxhdpi --split fr,de -F basic.apk -f
|
||||
aapt package \
|
||||
-M AndroidManifest.xml \
|
||||
-S res \
|
||||
-A assets \
|
||||
-I $PATH_TO_FRAMEWORK_RES \
|
||||
--split hdpi --split xhdpi --split xxhdpi --split fr,de \
|
||||
-F basic.apk \
|
||||
-f
|
||||
|
||||
Reference in New Issue
Block a user