AssetManager2: Add other support methods
- Add GetResourceConfigurations()
- Add GetResourceLocales()
- Add ResolveReference()
- Add stub for GetResourceId()
- Change LoadedArsc and ApkAssets factory method to return const
Test: make libandroidfw_tests
Change-Id: Ia797dc9381a523b1a3e7029048a413e544730379
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index 9a08f63..fe68ec0 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -27,16 +27,17 @@
namespace android {
-std::unique_ptr<ApkAssets> ApkAssets::Load(const std::string& path) {
- return ApkAssets::LoadImpl(path, false /*load_as_shared_library*/);
+std::unique_ptr<const ApkAssets> ApkAssets::Load(const std::string& path, bool system) {
+ return ApkAssets::LoadImpl(path, system, false /*load_as_shared_library*/);
}
-std::unique_ptr<ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path) {
- return ApkAssets::LoadImpl(path, true /*load_as_shared_library*/);
+std::unique_ptr<const ApkAssets> ApkAssets::LoadAsSharedLibrary(const std::string& path,
+ bool system) {
+ return ApkAssets::LoadImpl(path, system, true /*load_as_shared_library*/);
}
-std::unique_ptr<ApkAssets> ApkAssets::LoadImpl(const std::string& path,
- bool load_as_shared_library) {
+std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl(const std::string& path, bool system,
+ bool load_as_shared_library) {
ATRACE_CALL();
::ZipArchiveHandle unmanaged_handle;
int32_t result = ::OpenArchive(path.c_str(), &unmanaged_handle);
@@ -70,11 +71,13 @@
loaded_apk->path_ = path;
loaded_apk->loaded_arsc_ =
LoadedArsc::Load(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/),
- loaded_apk->resources_asset_->getLength(), load_as_shared_library);
+ loaded_apk->resources_asset_->getLength(), system, load_as_shared_library);
if (loaded_apk->loaded_arsc_ == nullptr) {
return {};
}
- return loaded_apk;
+
+ // Need to force a move for mingw32.
+ return std::move(loaded_apk);
}
std::unique_ptr<Asset> ApkAssets::Open(const std::string& path, Asset::AccessMode /*mode*/) const {
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index d2eff65..542a125 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -18,6 +18,8 @@
#include "androidfw/AssetManager2.h"
+#include <set>
+
#include "android-base/logging.h"
#include "android-base/stringprintf.h"
#include "utils/ByteOrder.h"
@@ -143,6 +145,36 @@
}
}
+std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system,
+ bool exclude_mipmap) {
+ ATRACE_CALL();
+ std::set<ResTable_config> configurations;
+ for (const PackageGroup& package_group : package_groups_) {
+ for (const LoadedPackage* package : package_group.packages_) {
+ if (exclude_system && package->IsSystem()) {
+ continue;
+ }
+ package->CollectConfigurations(exclude_mipmap, &configurations);
+ }
+ }
+ return configurations;
+}
+
+std::set<std::string> AssetManager2::GetResourceLocales(bool exclude_system,
+ bool merge_equivalent_languages) {
+ ATRACE_CALL();
+ std::set<std::string> locales;
+ for (const PackageGroup& package_group : package_groups_) {
+ for (const LoadedPackage* package : package_group.packages_) {
+ if (exclude_system && package->IsSystem()) {
+ continue;
+ }
+ package->CollectLocales(merge_equivalent_languages, &locales);
+ }
+ }
+ return locales;
+}
+
std::unique_ptr<Asset> AssetManager2::Open(const std::string& filename, Asset::AccessMode mode) {
const std::string new_path = "assets/" + filename;
return OpenNonAsset(new_path, mode);
@@ -325,8 +357,15 @@
if (dtohl(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
+ return kInvalidCookie;
}
- return kInvalidCookie;
+
+ // Create a reference since we can't represent this complex type as a Res_value.
+ out_value->dataType = Res_value::TYPE_REFERENCE;
+ out_value->data = resid;
+ *out_selected_config = config;
+ *out_flags = flags;
+ return cookie;
}
const Res_value* device_value = reinterpret_cast<const Res_value*>(
@@ -341,6 +380,37 @@
return cookie;
}
+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) {
+ ATRACE_CALL();
+ constexpr const int kMaxIterations = 20;
+
+ out_last_reference->ident = 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;
+ }
+ uint32_t new_flags = 0u;
+ cookie = GetResource(in_out_value->data, true /*may_be_bag*/, 0u /*density_override*/,
+ in_out_value, in_out_selected_config, &new_flags);
+ if (cookie == kInvalidCookie) {
+ return kInvalidCookie;
+ }
+ if (in_out_flags != nullptr) {
+ *in_out_flags |= new_flags;
+ }
+ if (out_last_reference->ident == in_out_value->data) {
+ // This reference can't be resolved, so exit now and let the caller deal with it.
+ return cookie;
+ }
+ }
+ return cookie;
+}
+
const ResolvedBag* AssetManager2::GetBag(uint32_t resid) {
ATRACE_CALL();
@@ -501,6 +571,15 @@
return result;
}
+uint32_t AssetManager2::GetResourceId(const std::string& resource_name,
+ const std::string& fallback_type,
+ const std::string& fallback_package) {
+ (void)resource_name;
+ (void)fallback_type;
+ (void)fallback_package;
+ return 0u;
+}
+
void AssetManager2::InvalidateCaches(uint32_t diff) {
if (diff == 0xffffffffu) {
// Everything must go.
diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp
index c7d0fa5..cb589ec 100644
--- a/libs/androidfw/LoadedArsc.cpp
+++ b/libs/androidfw/LoadedArsc.cpp
@@ -321,6 +321,57 @@
return true;
}
+void LoadedPackage::CollectConfigurations(bool exclude_mipmap,
+ std::set<ResTable_config>* out_configs) const {
+ const static std::u16string kMipMap = u"mipmap";
+ const size_t type_count = type_specs_.size();
+ for (size_t i = 0; i < type_count; i++) {
+ const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
+ if (type_spec != nullptr) {
+ if (exclude_mipmap) {
+ const int type_idx = type_spec->type_spec->id - 1;
+ size_t type_name_len;
+ const char16_t* type_name16 = type_string_pool_.stringAt(type_idx, &type_name_len);
+ if (type_name16 != nullptr) {
+ if (kMipMap.compare(0, std::u16string::npos, type_name16, type_name_len) == 0) {
+ // This is a mipmap type, skip collection.
+ continue;
+ }
+ }
+ const char* type_name = type_string_pool_.string8At(type_idx, &type_name_len);
+ if (type_name != nullptr) {
+ if (strncmp(type_name, "mipmap", type_name_len) == 0) {
+ // This is a mipmap type, skip collection.
+ continue;
+ }
+ }
+ }
+
+ for (size_t j = 0; j < type_spec->type_count; j++) {
+ out_configs->insert(type_spec->types[j].configuration);
+ }
+ }
+ }
+}
+
+void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const {
+ char temp_locale[RESTABLE_MAX_LOCALE_LEN];
+ const size_t type_count = type_specs_.size();
+ for (size_t i = 0; i < type_count; i++) {
+ const util::unique_cptr<TypeSpec>& type_spec = type_specs_[i];
+ if (type_spec != nullptr) {
+ for (size_t j = 0; j < type_spec->type_count; j++) {
+ const ResTable_config& configuration = type_spec->types[j].configuration;
+ if (configuration.locale != 0) {
+ configuration.getBcp47Locale(temp_locale, canonicalize);
+ std::string locale(temp_locale);
+ out_locales->insert(std::move(locale));
+ }
+ }
+ }
+ }
+}
+
std::unique_ptr<LoadedPackage> LoadedPackage::Load(const Chunk& chunk) {
ATRACE_CALL();
std::unique_ptr<LoadedPackage> loaded_package{new LoadedPackage()};
@@ -574,6 +625,7 @@
if (loaded_package->package_id_ == kAppPackageId) {
loaded_package->dynamic_ = load_as_shared_library;
}
+ loaded_package->system_ = system_;
packages_.push_back(std::move(loaded_package));
} break;
@@ -590,12 +642,13 @@
return true;
}
-std::unique_ptr<LoadedArsc> LoadedArsc::Load(const void* data, size_t len,
- bool load_as_shared_library) {
+std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const void* data, size_t len, bool system,
+ bool load_as_shared_library) {
ATRACE_CALL();
// Not using make_unique because the constructor is private.
std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc());
+ loaded_arsc->system_ = system;
ChunkIterator iter(data, len);
while (iter.HasNext()) {
@@ -617,7 +670,9 @@
LOG(ERROR) << iter.GetLastError();
return {};
}
- return loaded_arsc;
+
+ // Need to force a move for mingw32.
+ return std::move(loaded_arsc);
}
} // namespace android
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 9d4fd29..6d1578c 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -31,8 +31,9 @@
// Holds an APK.
class ApkAssets {
public:
- static std::unique_ptr<ApkAssets> Load(const std::string& path);
- static std::unique_ptr<ApkAssets> LoadAsSharedLibrary(const std::string& path);
+ static std::unique_ptr<const ApkAssets> Load(const std::string& path, bool system = false);
+ static std::unique_ptr<const ApkAssets> LoadAsSharedLibrary(const std::string& path,
+ bool system = false);
std::unique_ptr<Asset> Open(const std::string& path,
Asset::AccessMode mode = Asset::AccessMode::ACCESS_RANDOM) const;
@@ -44,7 +45,8 @@
private:
DISALLOW_COPY_AND_ASSIGN(ApkAssets);
- static std::unique_ptr<ApkAssets> LoadImpl(const std::string& path, bool load_as_shared_library);
+ static std::unique_ptr<const ApkAssets> LoadImpl(const std::string& path, bool system,
+ bool load_as_shared_library);
ApkAssets() = default;
@@ -57,7 +59,7 @@
ZipArchivePtr zip_handle_;
std::string path_;
std::unique_ptr<Asset> resources_asset_;
- std::unique_ptr<LoadedArsc> loaded_arsc_;
+ std::unique_ptr<const LoadedArsc> loaded_arsc_;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index 8655339..81cdc46 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -21,6 +21,7 @@
#include <array>
#include <limits>
+#include <set>
#include <unordered_map>
#include "androidfw/ApkAssets.h"
@@ -112,6 +113,24 @@
inline const ResTable_config& GetConfiguration() const { return configuration_; }
+ // Returns all configurations for which there are resources defined. This includes resource
+ // configurations in all the ApkAssets set for this AssetManager.
+ // If `exclude_system` is set to true, resource configurations from system APKs
+ // ('android' package, other libraries) will be excluded from the list.
+ // If `exclude_mipmap` is set to true, resource configurations defined for resource type 'mipmap'
+ // will be excluded from the list.
+ std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false,
+ bool exclude_mipmap = false);
+
+ // Returns all the locales for which there are resources defined. This includes resource
+ // locales in all the ApkAssets set for this AssetManager.
+ // If `exclude_system` is set to true, resource locales from system APKs
+ // ('android' package, other libraries) will be excluded from the list.
+ // If `merge_equivalent_languages` is set to true, resource locales will be canonicalized
+ // and de-duped in the resulting list.
+ std::set<std::string> GetResourceLocales(bool exclude_system = false,
+ bool merge_equivalent_languages = false);
+
// Searches the set of APKs loaded by this AssetManager and opens the first one found located
// in the assets/ directory.
// `mode` controls how the file is opened.
@@ -149,6 +168,14 @@
// Returns false if the resource was not found.
bool GetResourceFlags(uint32_t resid, uint32_t* out_flags);
+ // Finds the resource ID assigned to `resource_name`.
+ // `resource_name` must be of the form '[package:][type/]entry'.
+ // If no package is specified in `resource_name`, then `fallback_package` is used as the package.
+ // If no type is specified in `resource_name`, then `fallback_type` is used as the type.
+ // Returns 0x0 if no resource by that name was found.
+ uint32_t GetResourceId(const std::string& resource_name, const std::string& fallback_type = {},
+ const std::string& fallback_package = {});
+
// Retrieves the best matching resource with ID `resid`. The resource value is filled into
// `out_value` and the configuration for the selected value is populated in `out_selected_config`.
// `out_flags` holds the same flags as retrieved with GetResourceFlags().
@@ -162,6 +189,22 @@
Res_value* out_value, ResTable_config* out_selected_config,
uint32_t* out_flags);
+ // Resolves the resource reference in `in_out_value` if the data type is
+ // Res_value::TYPE_REFERENCE.
+ // `cookie` is the ApkAssetsCookie of the reference in `in_out_value`.
+ // `in_out_value` is the reference to resolve. The result is placed back into this object.
+ // `in_out_flags` is the type spec flags returned from calls to GetResource() or
+ // GetResourceFlags(). Configuration flags of the values pointed to by the reference
+ // are OR'd together with `in_out_flags`.
+ // `in_out_config` is populated with the configuration for which the resolved value was defined.
+ // `out_last_reference` is populated with the last reference ID before resolving to an actual
+ // value.
+ // Returns the cookie of the APK the resolved resource was defined in, or kInvalidCookie if
+ // 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);
+
// 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.
// To iterate over the keys, use the following idiom:
diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h
index e8cb164..91a7cb7 100644
--- a/libs/androidfw/include/androidfw/LoadedArsc.h
+++ b/libs/androidfw/include/androidfw/LoadedArsc.h
@@ -18,6 +18,7 @@
#define LOADEDARSC_H_
#include <memory>
+#include <set>
#include <vector>
#include "android-base/macros.h"
@@ -68,20 +69,38 @@
LoadedArscEntry* out_entry, ResTable_config* out_selected_config,
uint32_t* out_flags) const;
+ // Returns the string pool where type names are stored.
inline const ResStringPool* GetTypeStringPool() const { return &type_string_pool_; }
+ // Returns the string pool where the names of resource entries are stored.
inline const ResStringPool* GetKeyStringPool() const { return &key_string_pool_; }
inline const std::string& GetPackageName() const { return package_name_; }
inline int GetPackageId() const { return package_id_; }
+ // Returns true if this package is dynamic (shared library) and needs to have an ID assigned.
inline bool IsDynamic() const { return dynamic_; }
+ // Returns true if this package originates from a system provided resource.
+ inline bool IsSystem() const { return system_; }
+
+ // Returns the map of package name to package ID used in this LoadedPackage. At runtime, a
+ // package could have been assigned a different package ID than what this LoadedPackage was
+ // compiled with. AssetManager rewrites the package IDs so that they are compatible at runtime.
inline const std::vector<DynamicPackageEntry>& GetDynamicPackageMap() const {
return dynamic_package_map_;
}
+ // Populates a set of ResTable_config structs, possibly excluding configurations defined for
+ // the mipmap type.
+ void CollectConfigurations(bool exclude_mipmap, std::set<ResTable_config>* out_configs) const;
+
+ // Populates a set of strings representing locales.
+ // If `canonicalize` is set to true, each locale is transformed into its canonical format
+ // before being inserted into the set. This may cause some equivalent locales to de-dupe.
+ void CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const;
+
private:
DISALLOW_COPY_AND_ASSIGN(LoadedPackage);
@@ -93,8 +112,9 @@
ResStringPool key_string_pool_;
std::string package_name_;
int package_id_ = -1;
- bool dynamic_ = false;
int type_id_offset_ = 0;
+ bool dynamic_ = false;
+ bool system_ = false;
ByteBucketArray<util::unique_cptr<TypeSpec>> type_specs_;
std::vector<DynamicPackageEntry> dynamic_package_map_;
@@ -104,10 +124,14 @@
// when loading, including offsets and lengths.
class LoadedArsc {
public:
- // Load the resource table from memory. The data's lifetime must out-live the
- // object returned from this method.
- static std::unique_ptr<LoadedArsc> Load(const void* data, size_t len,
- bool load_as_shared_library = false);
+ // 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(const void* data, size_t len, bool system = false,
+ bool load_as_shared_library = false);
~LoadedArsc();
@@ -125,6 +149,10 @@
// Gets a pointer to the name of the package in `resid`, or nullptr if the package doesn't exist.
const LoadedPackage* GetPackageForId(uint32_t resid) const;
+ // Returns true if this is a system provided resource.
+ inline bool IsSystem() const { return system_; }
+
+ // Returns a vector of LoadedPackage pointers, representing the packages in this LoadedArsc.
inline const std::vector<std::unique_ptr<const LoadedPackage>>& GetPackages() const {
return packages_;
}
@@ -137,6 +165,7 @@
ResStringPool global_string_pool_;
std::vector<std::unique_ptr<const LoadedPackage>> packages_;
+ bool system_ = false;
};
} // namespace android
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 56c22e6..04a5d95 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1188,6 +1188,8 @@
int compare(const ResTable_config& o) const;
int compareLogical(const ResTable_config& o) const;
+ inline bool operator<(const ResTable_config& o) const { return compare(o) < 0; }
+
// Flags indicating a set of config values. These flag constants must
// match the corresponding ones in android.content.pm.ActivityInfo and
// attrs_manifest.xml.
diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h
index fd96730..96b42bf 100644
--- a/libs/androidfw/include/androidfw/Util.h
+++ b/libs/androidfw/include/androidfw/Util.h
@@ -94,8 +94,12 @@
inline bool operator==(const unique_cptr& o) const { return ptr_ == o.ptr_; }
+ inline bool operator!=(const unique_cptr& o) const { return ptr_ != o.ptr_; }
+
inline bool operator==(std::nullptr_t) const { return ptr_ == nullptr; }
+ inline bool operator!=(std::nullptr_t) const { return ptr_ != nullptr; }
+
private:
DISALLOW_COPY_AND_ASSIGN(unique_cptr);
diff --git a/libs/androidfw/tests/ApkAssets_test.cpp b/libs/androidfw/tests/ApkAssets_test.cpp
index 0203712..6b4a719 100644
--- a/libs/androidfw/tests/ApkAssets_test.cpp
+++ b/libs/androidfw/tests/ApkAssets_test.cpp
@@ -24,7 +24,8 @@
namespace android {
TEST(ApkAssetsTest, LoadApk) {
- std::unique_ptr<ApkAssets> loaded_apk = ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
+ std::unique_ptr<const ApkAssets> loaded_apk =
+ ApkAssets::Load(GetTestDataPath() + "/basic/basic.apk");
ASSERT_NE(nullptr, loaded_apk);
EXPECT_NE(nullptr, loaded_apk->GetLoadedArsc());
@@ -33,7 +34,7 @@
}
TEST(ApkAssetsTest, LoadApkAsSharedLibrary) {
- std::unique_ptr<ApkAssets> loaded_apk =
+ std::unique_ptr<const ApkAssets> loaded_apk =
ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
ASSERT_NE(nullptr, loaded_apk);
const LoadedArsc* loaded_arsc = loaded_apk->GetLoadedArsc();
diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp
index b3c2dc3..273290a 100644
--- a/libs/androidfw/tests/AssetManager2_bench.cpp
+++ b/libs/androidfw/tests/AssetManager2_bench.cpp
@@ -38,7 +38,7 @@
static void BM_AssetManagerLoadAssets(benchmark::State& state) {
std::string path = GetTestDataPath() + "/basic/basic.apk";
while (state.KeepRunning()) {
- std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+ std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path);
AssetManager2 assets;
assets.SetApkAssets({apk.get()});
}
@@ -61,7 +61,7 @@
static void BM_AssetManagerLoadFrameworkAssets(benchmark::State& state) {
std::string path = kFrameworkPath;
while (state.KeepRunning()) {
- std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+ std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path);
AssetManager2 assets;
assets.SetApkAssets({apk.get()});
}
@@ -84,10 +84,10 @@
static void GetResourceBenchmark(const std::vector<std::string>& paths,
const ResTable_config* config, uint32_t resid,
benchmark::State& state) {
- std::vector<std::unique_ptr<ApkAssets>> apk_assets;
+ std::vector<std::unique_ptr<const ApkAssets>> apk_assets;
std::vector<const ApkAssets*> apk_assets_ptrs;
for (const std::string& path : paths) {
- std::unique_ptr<ApkAssets> apk = ApkAssets::Load(path);
+ std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path);
if (apk == nullptr) {
state.SkipWithError(base::StringPrintf("Failed to load assets %s", path.c_str()).c_str());
return;
@@ -187,7 +187,7 @@
BENCHMARK(BM_AssetManagerGetResourceFrameworkLocaleOld);
static void BM_AssetManagerGetBag(benchmark::State& state) {
- std::unique_ptr<ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
+ std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(GetTestDataPath() + "/styles/styles.apk");
if (apk == nullptr) {
state.SkipWithError("Failed to load assets");
return;
@@ -234,4 +234,40 @@
}
BENCHMARK(BM_AssetManagerGetBagOld);
+static void BM_AssetManagerGetResourceLocales(benchmark::State& state) {
+ std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+ if (apk == nullptr) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ AssetManager2 assets;
+ assets.SetApkAssets({apk.get()});
+
+ while (state.KeepRunning()) {
+ std::set<std::string> locales =
+ assets.GetResourceLocales(false /*exclude_system*/, true /*merge_equivalent_languages*/);
+ benchmark::DoNotOptimize(locales);
+ }
+}
+BENCHMARK(BM_AssetManagerGetResourceLocales);
+
+static void BM_AssetManagerGetResourceLocalesOld(benchmark::State& state) {
+ AssetManager assets;
+ if (!assets.addAssetPath(String8(kFrameworkPath), nullptr /*cookie*/, false /*appAsLib*/,
+ false /*isSystemAssets*/)) {
+ state.SkipWithError("Failed to load assets");
+ return;
+ }
+
+ const ResTable& table = assets.getResources(true);
+
+ while (state.KeepRunning()) {
+ Vector<String8> locales;
+ table.getLocales(&locales, true /*includeSystemLocales*/, true /*mergeEquivalentLangs*/);
+ benchmark::DoNotOptimize(locales);
+ }
+}
+BENCHMARK(BM_AssetManagerGetResourceLocalesOld);
+
} // namespace android
diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp
index 543456a..557d8d4 100644
--- a/libs/androidfw/tests/AssetManager2_test.cpp
+++ b/libs/androidfw/tests/AssetManager2_test.cpp
@@ -26,6 +26,7 @@
#include "data/lib_two/R.h"
#include "data/libclient/R.h"
#include "data/styles/R.h"
+#include "data/system/R.h"
namespace app = com::android::app;
namespace appaslib = com::android::appaslib::app;
@@ -59,16 +60,20 @@
appaslib_assets_ = ApkAssets::Load(GetTestDataPath() + "/appaslib/appaslib.apk");
ASSERT_NE(nullptr, appaslib_assets_);
+
+ system_assets_ = ApkAssets::Load(GetTestDataPath() + "/system/system.apk", true /*system*/);
+ ASSERT_NE(nullptr, system_assets_);
}
protected:
- std::unique_ptr<ApkAssets> basic_assets_;
- std::unique_ptr<ApkAssets> basic_de_fr_assets_;
- std::unique_ptr<ApkAssets> style_assets_;
- std::unique_ptr<ApkAssets> lib_one_assets_;
- std::unique_ptr<ApkAssets> lib_two_assets_;
- std::unique_ptr<ApkAssets> libclient_assets_;
- std::unique_ptr<ApkAssets> appaslib_assets_;
+ std::unique_ptr<const ApkAssets> basic_assets_;
+ std::unique_ptr<const ApkAssets> basic_de_fr_assets_;
+ std::unique_ptr<const ApkAssets> style_assets_;
+ std::unique_ptr<const ApkAssets> lib_one_assets_;
+ std::unique_ptr<const ApkAssets> lib_two_assets_;
+ std::unique_ptr<const ApkAssets> libclient_assets_;
+ std::unique_ptr<const ApkAssets> appaslib_assets_;
+ std::unique_ptr<const ApkAssets> system_assets_;
};
TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
@@ -291,6 +296,131 @@
EXPECT_EQ(0, bag_two->entries[4].cookie);
}
+TEST_F(AssetManager2Test, ResolveReferenceToResource) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
+
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ ApkAssetsCookie cookie =
+ assetmanager.GetResource(basic::R::integer::ref1, false /*may_be_bag*/,
+ 0u /*density_override*/, &value, &selected_config, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ EXPECT_EQ(basic::R::integer::ref2, value.data);
+
+ ResTable_ref 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);
+}
+
+TEST_F(AssetManager2Test, ResolveReferenceToBag) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
+
+ Res_value value;
+ ResTable_config selected_config;
+ uint32_t flags;
+ ApkAssetsCookie cookie =
+ assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/,
+ 0u /*density_override*/, &value, &selected_config, &flags);
+ ASSERT_NE(kInvalidCookie, cookie);
+
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType);
+ EXPECT_EQ(basic::R::array::integerArray1, value.data);
+
+ ResTable_ref 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);
+}
+
+static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations,
+ const ResTable_config& configuration) {
+ return configurations.count(configuration) > 0;
+}
+
+TEST_F(AssetManager2Test, GetResourceConfigurations) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()});
+
+ std::set<ResTable_config> configurations = assetmanager.GetResourceConfigurations();
+
+ // We expect the locale sv from the system assets, and de and fr from basic_de_fr assets.
+ // And one extra for the default configuration.
+ EXPECT_EQ(4u, configurations.size());
+
+ ResTable_config expected_config;
+ memset(&expected_config, 0, sizeof(expected_config));
+ expected_config.language[0] = 's';
+ expected_config.language[1] = 'v';
+ EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+
+ expected_config.language[0] = 'd';
+ expected_config.language[1] = 'e';
+ EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+
+ expected_config.language[0] = 'f';
+ expected_config.language[1] = 'r';
+ EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+
+ // Take out the system assets.
+ configurations = assetmanager.GetResourceConfigurations(true /* exclude_system */);
+
+ // We expect de and fr from basic_de_fr assets.
+ EXPECT_EQ(2u, configurations.size());
+
+ expected_config.language[0] = 's';
+ expected_config.language[1] = 'v';
+ EXPECT_FALSE(IsConfigurationPresent(configurations, expected_config));
+
+ expected_config.language[0] = 'd';
+ expected_config.language[1] = 'e';
+ EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+
+ expected_config.language[0] = 'f';
+ expected_config.language[1] = 'r';
+ EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config));
+}
+
+TEST_F(AssetManager2Test, GetResourceLocales) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()});
+
+ std::set<std::string> locales = assetmanager.GetResourceLocales();
+
+ // We expect the locale sv from the system assets, and de and fr from basic_de_fr assets.
+ EXPECT_EQ(3u, locales.size());
+ EXPECT_GT(locales.count("sv"), 0u);
+ EXPECT_GT(locales.count("de"), 0u);
+ EXPECT_GT(locales.count("fr"), 0u);
+
+ locales = assetmanager.GetResourceLocales(true /*exclude_system*/);
+ // We expect the de and fr locales from basic_de_fr assets.
+ EXPECT_EQ(2u, locales.size());
+ EXPECT_GT(locales.count("de"), 0u);
+ EXPECT_GT(locales.count("fr"), 0u);
+}
+
+TEST_F(AssetManager2Test, GetResourceId) {
+ AssetManager2 assetmanager;
+ assetmanager.SetApkAssets({basic_assets_.get()});
+
+ EXPECT_EQ(basic::R::layout::main,
+ assetmanager.GetResourceId("com.android.basic:layout/main", "", ""));
+ EXPECT_EQ(basic::R::layout::main,
+ assetmanager.GetResourceId("layout/main", "", "com.android.basic"));
+ EXPECT_EQ(basic::R::layout::main,
+ assetmanager.GetResourceId("main", "layout", "com.android.basic"));
+}
+
TEST_F(AssetManager2Test, OpensFileFromSingleApkAssets) {}
TEST_F(AssetManager2Test, OpensFileFromMultipleApkAssets) {}
diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp
index f8aa61a..756869f 100644
--- a/libs/androidfw/tests/LoadedArsc_test.cpp
+++ b/libs/androidfw/tests/LoadedArsc_test.cpp
@@ -32,7 +32,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc",
&contents));
- std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(contents.data(), contents.size());
ASSERT_NE(nullptr, loaded_arsc);
const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages();
@@ -58,7 +59,8 @@
ASSERT_TRUE(
ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents));
- std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(contents.data(), contents.size());
ASSERT_NE(nullptr, loaded_arsc);
ResTable_config desired_config;
@@ -80,7 +82,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc",
&contents));
- std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(contents.data(), contents.size());
ASSERT_NE(nullptr, loaded_arsc);
const auto& packages = loaded_arsc->GetPackages();
@@ -101,7 +104,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk",
"resources.arsc", &contents));
- std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(contents.data(), contents.size());
ASSERT_NE(nullptr, loaded_arsc);
const auto& packages = loaded_arsc->GetPackages();
@@ -128,8 +132,8 @@
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk",
"resources.arsc", &contents));
- std::unique_ptr<LoadedArsc> loaded_arsc =
- LoadedArsc::Load(contents.data(), contents.size(), true /*load_as_shared_library*/);
+ std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(
+ contents.data(), contents.size(), false /*system*/, true /*load_as_shared_library*/);
ASSERT_NE(nullptr, loaded_arsc);
const auto& packages = loaded_arsc->GetPackages();
@@ -143,7 +147,8 @@
std::string contents;
ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc",
&contents));
- std::unique_ptr<LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), contents.size());
+ std::unique_ptr<const LoadedArsc> loaded_arsc =
+ LoadedArsc::Load(contents.data(), contents.size());
ASSERT_NE(nullptr, loaded_arsc);
ResTable_config desired_config;
diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp
index c471be6..594c39e 100644
--- a/libs/androidfw/tests/Theme_bench.cpp
+++ b/libs/androidfw/tests/Theme_bench.cpp
@@ -28,7 +28,7 @@
constexpr const static uint32_t kAttrId = 0x01010030u; // android:attr/colorForeground
static void BM_ThemeApplyStyleFramework(benchmark::State& state) {
- std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+ std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
if (apk == nullptr) {
state.SkipWithError("Failed to load assets");
return;
@@ -62,7 +62,7 @@
BENCHMARK(BM_ThemeApplyStyleFrameworkOld);
static void BM_ThemeGetAttribute(benchmark::State& state) {
- std::unique_ptr<ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
+ std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(kFrameworkPath);
AssetManager2 assets;
assets.SetApkAssets({apk.get()});
diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp
index 59cb18a..daed28b 100644
--- a/libs/androidfw/tests/Theme_test.cpp
+++ b/libs/androidfw/tests/Theme_test.cpp
@@ -46,10 +46,10 @@
}
protected:
- std::unique_ptr<ApkAssets> style_assets_;
- std::unique_ptr<ApkAssets> libclient_assets_;
- std::unique_ptr<ApkAssets> lib_one_assets_;
- std::unique_ptr<ApkAssets> lib_two_assets_;
+ std::unique_ptr<const ApkAssets> style_assets_;
+ std::unique_ptr<const ApkAssets> libclient_assets_;
+ std::unique_ptr<const ApkAssets> lib_one_assets_;
+ std::unique_ptr<const ApkAssets> lib_two_assets_;
};
TEST_F(ThemeTest, EmptyTheme) {
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index 9352b5c..8e9741e 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -53,6 +53,8 @@
enum : uint32_t {
number1 = 0x7f040000,
number2 = 0x7f040001,
+ ref1 = 0x7f040002,
+ ref2 = 0x7f040003,
// From feature
number3 = 0x7f090000,
diff --git a/libs/androidfw/tests/data/basic/basic.apk b/libs/androidfw/tests/data/basic/basic.apk
index 2c9771b..7ee6734 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/basic_de_fr.apk b/libs/androidfw/tests/data/basic/basic_de_fr.apk
index 0481444..e45258c 100644
--- a/libs/androidfw/tests/data/basic/basic_de_fr.apk
+++ b/libs/androidfw/tests/data/basic/basic_de_fr.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
index a8d06e7..4ae1a7c 100644
--- a/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
+++ b/libs/androidfw/tests/data/basic/basic_hdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
index d1dfb14..a240d4c 100644
--- a/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
+++ b/libs/androidfw/tests/data/basic/basic_xhdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
index dca6f2f..fd3d9b2 100644
--- a/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
+++ b/libs/androidfw/tests/data/basic/basic_xxhdpi-v4.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index 11f6b8a..638c983 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -37,6 +37,12 @@
<public type="integer" name="number2" id="0x7f040001" />
<integer name="number2">@array/integerArray1</integer>
+ <public type="integer" name="ref1" id="0x7f040002" />
+ <integer name="ref1">@integer/ref2</integer>
+
+ <public type="integer" name="ref2" id="0x7f040003" />
+ <integer name="ref2">12000</integer>
+
<public type="style" name="Theme1" id="0x7f050000" />
<style name="Theme1">
<item name="com.android.basic:attr1">100</item>