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
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index fe68ec0..a5b1d29 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -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 @@
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 @@
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
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 8e8c6a2..247458d 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -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 @@
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 @@
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
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index ef0c967..5667f92 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -138,6 +138,17 @@
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 @@
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 @@
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 @@
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::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 @@
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 @@
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_) {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 763a178..9f241a0 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -6532,6 +6532,8 @@
return NO_ERROR;
}
+DynamicRefTable::DynamicRefTable() : DynamicRefTable(0, false) {}
+
DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
: mAssignedPackageId(packageId)
, mAppAsLib(appAsLib)
@@ -6637,11 +6639,11 @@
// 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;
diff --git a/libs/androidfw/Util.cpp b/libs/androidfw/Util.cpp
index 202bc8e..575cd18 100644
--- a/libs/androidfw/Util.cpp
+++ b/libs/androidfw/Util.cpp
@@ -41,5 +41,31 @@
}
}
+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
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 6d1578c..b7e66fb 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -25,6 +25,7 @@
#include "androidfw/Asset.h"
#include "androidfw/LoadedArsc.h"
+#include "androidfw/misc.h"
namespace android {
@@ -38,6 +39,9 @@
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 @@
using ZipArchivePtr =
std::unique_ptr<typename std::remove_pointer<::ZipArchiveHandle>::type, ZipArchivePtrCloser>;
+
ZipArchivePtr zip_handle_;
std::string path_;
std::unique_ptr<Asset> resources_asset_;
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 461e773..9d12a35 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -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 @@
/* 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 @@
*/
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 @@
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.
diff --git a/libs/androidfw/include/androidfw/AssetDir.h b/libs/androidfw/include/androidfw/AssetDir.h
index bd89d7d..7aef02d 100644
--- a/libs/androidfw/include/androidfw/AssetDir.h
+++ b/libs/androidfw/include/androidfw/AssetDir.h
@@ -70,6 +70,7 @@
const AssetDir& operator=(const AssetDir& src);
friend class AssetManager;
+ friend class AssetManager2;
/*
* This holds information about files in the asset hierarchy.
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 81cdc46..d2bc6ee 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -107,6 +107,9 @@
// 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 @@
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 @@
// 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 @@
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 @@
// 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);
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 04a5d95..cfe2652 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1554,7 +1554,7 @@
{
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 @@
}
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);
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index 3950cf2..e4cd6a8 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -22,6 +22,8 @@
#include "android-base/macros.h"
+#include "androidfw/StringPiece.h"
+
namespace android {
namespace util {
@@ -108,6 +110,12 @@
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
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 6b4a719..c85b0b9 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -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 @@
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
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 78fbb0f..d8e5abf 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -312,12 +312,12 @@
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 @@
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,
diff --git a/libs/androidfw/tests/data/basic/assets/uncompressed.txt b/libs/androidfw/tests/data/basic/assets/uncompressed.txt
new file mode 100644
index 0000000..c3d39c5
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/assets/uncompressed.txt
@@ -0,0 +1,2 @@
+This should be uncompressed.
+
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 7ee6734..0c17328 100644
--- a/libs/androidfw/tests/data/basic/basic.apk
+++ b/libs/androidfw/tests/data/basic/basic.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build
index af0fd87..d619800 100755
--- a/libs/androidfw/tests/data/basic/build
+++ b/libs/androidfw/tests/data/basic/build
@@ -19,4 +19,11 @@
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