diff options
Diffstat (limited to 'libs')
-rw-r--r-- | libs/androidfw/ApkAssets.cpp | 89 | ||||
-rw-r--r-- | libs/androidfw/Asset.cpp | 32 | ||||
-rw-r--r-- | libs/androidfw/AssetManager2.cpp | 79 | ||||
-rw-r--r-- | libs/androidfw/ResourceTypes.cpp | 6 | ||||
-rw-r--r-- | libs/androidfw/Util.cpp | 26 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/ApkAssets.h | 5 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/Asset.h | 9 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/AssetDir.h | 1 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/AssetManager2.h | 18 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/ResourceTypes.h | 6 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/Util.h | 8 | ||||
-rw-r--r-- | libs/androidfw/tests/ApkAssets_test.cpp | 40 | ||||
-rw-r--r-- | libs/androidfw/tests/AssetManager2_test.cpp | 8 | ||||
-rw-r--r-- | libs/androidfw/tests/data/basic/assets/uncompressed.txt | 2 | ||||
-rw-r--r-- | libs/androidfw/tests/data/basic/basic.apk | bin | 3055 -> 3207 bytes | |||
-rwxr-xr-x | libs/androidfw/tests/data/basic/build | 9 |
16 files changed, 299 insertions, 39 deletions
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index fe68ec01e4b7..a5b1d29dbf91 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 @@ 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 diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 8e8c6a2e25a2..247458d3f4fd 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 @@ 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 diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index ef0c96736868..5667f9283241 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -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_) { diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 763a178ed43b..9f241a0537a9 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -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; diff --git a/libs/androidfw/Util.cpp b/libs/androidfw/Util.cpp index 202bc8e5743a..575cd18a36dd 100644 --- a/libs/androidfw/Util.cpp +++ b/libs/androidfw/Util.cpp @@ -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 diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h index 6d1578c2d50e..b7e66fb68be5 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 @@ 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_; diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 461e773e5818..9d12a35395c9 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 @@ 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. diff --git a/libs/androidfw/include/androidfw/AssetDir.h b/libs/androidfw/include/androidfw/AssetDir.h index bd89d7d34b97..7aef02dc4133 100644 --- a/libs/androidfw/include/androidfw/AssetDir.h +++ b/libs/androidfw/include/androidfw/AssetDir.h @@ -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. diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 81cdc46be0b4..d2bc6ee45576 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -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); diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 04a5d958c614..cfe2652a0d17 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -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); diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index 3950cf240a9e..e4cd6a8ab6b8 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 @@ 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 diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp index 6b4a7199eba4..c85b0b98ca5e 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 @@ 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 diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 78fbb0f85f68..d8e5abfe8ce9 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -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, 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 000000000000..c3d39c5200c3 --- /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 Binary files differindex 7ee6734e0d6a..0c17328e86b2 100644 --- a/libs/androidfw/tests/data/basic/basic.apk +++ b/libs/androidfw/tests/data/basic/basic.apk diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build index af0fd8787f48..d619800d6fc5 100755 --- a/libs/androidfw/tests/data/basic/build +++ b/libs/androidfw/tests/data/basic/build @@ -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 |