diff options
author | Winson <chiuwinson@google.com> | 2019-01-11 11:28:34 -0800 |
---|---|---|
committer | Winson <chiuwinson@google.com> | 2019-01-14 14:52:00 -0800 |
commit | 2f3669b767129bc8739bb03e80abc65eb54a3471 (patch) | |
tree | ccb7294d22f43c64f863defb345a1383294f221b | |
parent | 0c891e8f4e5d7e1798e564cee80694264751aa9a (diff) |
Add function to return path for last resolved resource
After an AssetManager.FindEntry call is made, either directly or from any of the resource entry calls, a stack of the steps taken to resolve the resource is saved. Those steps can be retrieved as a log later on by calling AssetManager.GetLastResourceResolution, which returns a formatted string of the resource ID/name and path taken, including the configs and package names of each step.
Logging and the saving of the steps to memory can be enabled/disabled with the @hide .setResourceResolutionLoggingEnabled() method on AssetManager.
Bug: 122374289
Test: cases for single and multi ApkAssets loaded
Test: case for no resolution made
Test: made test app to display log on device
Test: added debugging call to source and ran through on-device apps
Change-Id: I6a32b8d4020c3f8510032ff7f431510089fff43f
-rw-r--r-- | core/java/android/content/res/AssetManager.java | 34 | ||||
-rw-r--r-- | core/java/android/content/res/Resources.java | 24 | ||||
-rw-r--r-- | core/java/android/content/res/ResourcesImpl.java | 7 | ||||
-rw-r--r-- | core/jni/android_util_AssetManager.cpp | 55 | ||||
-rw-r--r-- | libs/androidfw/AssetManager2.cpp | 207 | ||||
-rw-r--r-- | libs/androidfw/ResourceUtils.cpp | 61 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/AssetManager2.h | 50 | ||||
-rw-r--r-- | libs/androidfw/include/androidfw/ResourceUtils.h | 12 | ||||
-rw-r--r-- | libs/androidfw/tests/AssetManager2_test.cpp | 107 |
9 files changed, 471 insertions, 86 deletions
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index f1a4db25cd14..9e0a9ba0dc7b 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -732,6 +732,38 @@ public final class AssetManager implements AutoCloseable { } } + /** + * Enable resource resolution logging to track the steps taken to resolve the last resource + * entry retrieved. Stores the configuration and package names for each step. + * + * Default disabled. + * + * @param enabled Boolean indicating whether to enable or disable logging. + * + * @hide + */ + public void setResourceResolutionLoggingEnabled(boolean enabled) { + synchronized (this) { + ensureValidLocked(); + nativeSetResourceResolutionLoggingEnabled(mObject, enabled); + } + } + + /** + * Retrieve the last resource resolution path logged. + * + * @return Formatted string containing last resource ID/name and steps taken to resolve final + * entry, including configuration and package names. + * + * @hide + */ + public @Nullable String getLastResourceResolution() { + synchronized (this) { + ensureValidLocked(); + return nativeGetLastResourceResolution(mObject); + } + } + CharSequence getPooledStringForCookie(int cookie, int id) { // Cookies map to ApkAssets starting at 1. return getApkAssets()[cookie - 1].getStringFromPool(id); @@ -1383,6 +1415,8 @@ public final class AssetManager implements AutoCloseable { private static native @Nullable String nativeGetResourceEntryName(long ptr, @AnyRes int resid); private static native @Nullable String[] nativeGetLocales(long ptr, boolean excludeSystem); private static native @Nullable Configuration[] nativeGetSizeConfigurations(long ptr); + private static native void nativeSetResourceResolutionLoggingEnabled(long ptr, boolean enabled); + private static native @Nullable String nativeGetLastResourceResolution(long ptr); // Style attribute retrieval native methods. private static native void nativeApplyStyle(long ptr, long themePtr, @AttrRes int defStyleAttr, diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 365ceac68ee1..c4b315ec90c8 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -2010,22 +2010,36 @@ public class Resources { public String getResourceTypeName(@AnyRes int resid) throws NotFoundException { return mResourcesImpl.getResourceTypeName(resid); } - + /** * Return the entry name for a given resource identifier. - * + * * @param resid The resource identifier whose entry name is to be * retrieved. - * + * * @return A string holding the entry name of the resource. - * + * * @throws NotFoundException Throws NotFoundException if the given ID does not exist. - * + * * @see #getResourceName */ public String getResourceEntryName(@AnyRes int resid) throws NotFoundException { return mResourcesImpl.getResourceEntryName(resid); } + + /** + * Return formatted log of the last retrieved resource's resolution path. + * + * @return A string holding a formatted log of the steps taken to resolve the last resource. + * + * @throws NotFoundException Throws NotFoundException if there hasn't been a resource + * resolved yet. + * + * @hide + */ + public String getLastResourceResolution() throws NotFoundException { + return mResourcesImpl.getLastResourceResolution(); + } /** * Parse a series of {@link android.R.styleable#Extra <extra>} tags from diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 2ad4f625ef8c..77796d9ebdf5 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -299,6 +299,13 @@ public class ResourcesImpl { } @NonNull + String getLastResourceResolution() throws NotFoundException { + String str = mAssets.getLastResourceResolution(); + if (str != null) return str; + throw new NotFoundException("Associated AssetManager hasn't resolved a resource"); + } + + @NonNull CharSequence getQuantityText(@PluralsRes int id, int quantity) throws NotFoundException { PluralRules rule = getPluralRule(); CharSequence res = mAssets.getResourceBagText(id, diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 7b564ae162ce..4101c04162af 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -44,6 +44,8 @@ #include "androidfw/MutexGuard.h" #include "androidfw/PosixUtils.h" #include "androidfw/ResourceTypes.h" +#include "androidfw/ResourceUtils.h" + #include "core_jni_helpers.h" #include "jni.h" #include "nativehelper/JNIHelp.h" @@ -975,34 +977,7 @@ static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, j return nullptr; } - std::string result; - if (name.package != nullptr) { - result.append(name.package, name.package_len); - } - - if (name.type != nullptr || name.type16 != nullptr) { - if (!result.empty()) { - result += ":"; - } - - if (name.type != nullptr) { - result.append(name.type, name.type_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); - } - } - - if (name.entry != nullptr || name.entry16 != nullptr) { - if (!result.empty()) { - result += "/"; - } - - if (name.entry != nullptr) { - result.append(name.entry, name.entry_len); - } else { - result += util::Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); - } - } + std::string result = ToFormattedResourceString(&name); return env->NewStringUTF(result.c_str()); } @@ -1049,6 +1024,26 @@ static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong p return nullptr; } +static void NativeSetResourceResolutionLoggingEnabled(JNIEnv* /*env*/, + jclass /*clazz*/, + jlong ptr, + jboolean enabled) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + assetmanager->SetResourceResolutionLoggingEnabled(enabled); +} + +static jstring NativeGetLastResourceResolution(JNIEnv* env, + jclass /*clazz*/, + jlong ptr) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + std::string resolution = assetmanager->GetLastResourceResolution(); + if (resolution.empty()) { + return nullptr; + } else { + return env->NewStringUTF(resolution.c_str()); + } +} + static jobjectArray NativeGetLocales(JNIEnv* env, jclass /*class*/, jlong ptr, jboolean exclude_system) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); @@ -1452,6 +1447,10 @@ static const JNINativeMethod gAssetManagerMethods[] = { {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V", + (void*) NativeSetResourceResolutionLoggingEnabled}, + {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;", + (void*) NativeGetLastResourceResolution}, {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", (void*)NativeGetSizeConfigurations}, diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index ad9ec02648b1..3c35d9b33fc8 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -20,8 +20,9 @@ #include <algorithm> #include <iterator> -#include <set> #include <map> +#include <set> +#include <sstream> #include "android-base/logging.h" #include "android-base/stringprintf.h" @@ -372,6 +373,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri uint32_t best_offset = 0u; uint32_t type_flags = 0u; + Resolution::Step::Type resolution_type; + std::vector<Resolution::Step> resolution_steps; + // If desired_config is the same as the set configuration, then we can use our filtered list // and we don't need to match the configurations, since they already matched. const bool use_fast_path = desired_config == &configuration_; @@ -403,8 +407,8 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // If the package is an overlay, then even configurations that are the same MUST be chosen. const bool package_is_overlay = loaded_package->IsOverlay(); - const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; if (use_fast_path) { + const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations; const size_t type_count = candidate_configs.size(); for (uint32_t i = 0; i < type_count; i++) { @@ -412,21 +416,34 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // We can skip calling ResTable_config::match() because we know that all candidate // configurations that do NOT match have been filtered-out. - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const ResTable_type* type_chunk = filtered_group.types[i]; - const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } + if (best_config == nullptr) { + resolution_type = Resolution::Step::Type::INITIAL; + } else if (this_config.isBetterThan(*best_config, desired_config)) { + resolution_type = Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_overlay && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID; + } else { + continue; + } + + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const ResTable_type* type = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } - best_cookie = cookie; - best_package = loaded_package; - best_type = type_chunk; - best_config = &this_config; - best_offset = offset; + best_cookie = cookie; + best_package = loaded_package; + best_type = type; + best_config = &this_config; + best_offset = offset; + + if (resource_resolution_logging_enabled_) { + resolution_steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } else { @@ -440,23 +457,38 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri ResTable_config this_config; this_config.copyFromDtoH((*iter)->config); - if (this_config.match(*desired_config)) { - if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) || - (package_is_overlay && this_config.compare(*best_config) == 0)) { - // The configuration matches and is better than the previous selection. - // Find the entry value if it exists for this configuration. - const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } + if (!this_config.match(*desired_config)) { + continue; + } - best_cookie = cookie; - best_package = loaded_package; - best_type = *iter; - best_config_copy = this_config; - best_config = &best_config_copy; - best_offset = offset; - } + if (best_config == nullptr) { + resolution_type = Resolution::Step::Type::INITIAL; + } else if (this_config.isBetterThan(*best_config, desired_config)) { + resolution_type = Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_overlay && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID; + } else { + continue; + } + + // The configuration matches and is better than the previous selection. + // Find the entry value if it exists for this configuration. + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + continue; + } + + best_cookie = cookie; + best_package = loaded_package; + best_type = *iter; + best_config_copy = this_config; + best_config = &best_config_copy; + best_offset = offset; + + if (resource_resolution_logging_enabled_) { + resolution_steps.push_back(Resolution::Step{resolution_type, + this_config.toString(), + &loaded_package->GetPackageName()}); } } } @@ -478,9 +510,95 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri out_entry->entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); out_entry->dynamic_ref_table = &package_group.dynamic_ref_table; + + if (resource_resolution_logging_enabled_) { + last_resolution.resid = resid; + last_resolution.cookie = best_cookie; + last_resolution.steps = resolution_steps; + + // Cache only the type/entry refs since that's all that's needed to build name + last_resolution.type_string_ref = + StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + last_resolution.entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + } + return best_cookie; } +void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) { + resource_resolution_logging_enabled_ = enabled; + + if (!enabled) { + last_resolution.cookie = kInvalidCookie; + last_resolution.resid = 0; + last_resolution.steps.clear(); + last_resolution.type_string_ref = StringPoolRef(); + last_resolution.entry_string_ref = StringPoolRef(); + } +} + +std::string AssetManager2::GetLastResourceResolution() const { + if (!resource_resolution_logging_enabled_) { + LOG(ERROR) << "Must enable resource resolution logging before getting path."; + return std::string(); + } + + auto cookie = last_resolution.cookie; + if (cookie == kInvalidCookie) { + LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path."; + return std::string(); + } + + uint32_t resid = last_resolution.resid; + std::vector<Resolution::Step>& steps = last_resolution.steps; + + ResourceName resource_name; + std::string resource_name_string; + + const LoadedPackage* package = + apk_assets_[cookie]->GetLoadedArsc()->GetPackageById(get_package_id(resid)); + + if (package != nullptr) { + ToResourceName(last_resolution.type_string_ref, + last_resolution.entry_string_ref, + package, + &resource_name); + resource_name_string = ToFormattedResourceString(&resource_name); + } + + std::stringstream log_stream; + log_stream << base::StringPrintf("Resolution for 0x%08x ", resid) + << resource_name_string + << "\n\tFor config -" + << configuration_.toString(); + + std::string prefix; + for (Resolution::Step step : steps) { + switch (step.type) { + case Resolution::Step::Type::INITIAL: + prefix = "Found initial"; + break; + case Resolution::Step::Type::BETTER_MATCH: + prefix = "Found better"; + break; + case Resolution::Step::Type::OVERLAID: + prefix = "Overlaid"; + break; + } + + if (!prefix.empty()) { + log_stream << "\n\t" << prefix << ": " << *step.package_name; + + if (!step.config_name.isEmpty()) { + log_stream << " -" << step.config_name; + } + } + } + + return log_stream.str(); +} + bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { FindEntryResult entry; ApkAssetsCookie cookie = @@ -495,27 +613,10 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons return false; } - out_name->package = package->GetPackageName().data(); - out_name->package_len = package->GetPackageName().size(); - - out_name->type = entry.type_string_ref.string8(&out_name->type_len); - out_name->type16 = nullptr; - if (out_name->type == nullptr) { - out_name->type16 = entry.type_string_ref.string16(&out_name->type_len); - if (out_name->type16 == nullptr) { - return false; - } - } - - out_name->entry = entry.entry_string_ref.string8(&out_name->entry_len); - out_name->entry16 = nullptr; - if (out_name->entry == nullptr) { - out_name->entry16 = entry.entry_string_ref.string16(&out_name->entry_len); - if (out_name->entry16 == nullptr) { - return false; - } - } - return true; + return ToResourceName(entry.type_string_ref, + entry.entry_string_ref, + package, + out_name); } bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp index d63feb01ef83..645984d85c34 100644 --- a/libs/androidfw/ResourceUtils.cpp +++ b/libs/androidfw/ResourceUtils.cpp @@ -48,4 +48,65 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin !(has_type_separator && out_type->empty()); } +bool ToResourceName(StringPoolRef& type_string_ref, + StringPoolRef& entry_string_ref, + const LoadedPackage* package, + AssetManager2::ResourceName* out_name) { + out_name->package = package->GetPackageName().data(); + out_name->package_len = package->GetPackageName().size(); + + out_name->type = type_string_ref.string8(&out_name->type_len); + out_name->type16 = nullptr; + if (out_name->type == nullptr) { + out_name->type16 = type_string_ref.string16(&out_name->type_len); + if (out_name->type16 == nullptr) { + return false; + } + } + + out_name->entry = entry_string_ref.string8(&out_name->entry_len); + out_name->entry16 = nullptr; + if (out_name->entry == nullptr) { + out_name->entry16 = entry_string_ref.string16(&out_name->entry_len); + if (out_name->entry16 == nullptr) { + return false; + } + } + + return true; +} + +std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name) { + std::string result; + if (resource_name->package != nullptr) { + result.append(resource_name->package, resource_name->package_len); + } + + if (resource_name->type != nullptr || resource_name->type16 != nullptr) { + if (!result.empty()) { + result += ":"; + } + + if (resource_name->type != nullptr) { + result.append(resource_name->type, resource_name->type_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len)); + } + } + + if (resource_name->entry != nullptr || resource_name->entry16 != nullptr) { + if (!result.empty()) { + result += "/"; + } + + if (resource_name->entry != nullptr) { + result.append(resource_name->entry, resource_name->entry_len); + } else { + result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len)); + } + } + + return result; +} + } // namespace android diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index 0d492984d41d..f29769b834d1 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -229,6 +229,14 @@ class AssetManager2 { ResTable_config* in_out_selected_config, uint32_t* in_out_flags, uint32_t* out_last_reference) const; + // Enables or disables resource resolution logging. Clears stored steps when + // disabled. + void SetResourceResolutionLoggingEnabled(bool enabled); + + // Returns formatted log of last resource resolution path, or empty if no + // resource has been resolved yet. + std::string GetLastResourceResolution() const; + // 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: @@ -346,6 +354,48 @@ class AssetManager2 { // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; + + // Whether or not to save resource resolution steps + bool resource_resolution_logging_enabled_ = false; + + struct Resolution { + + struct Step { + + enum class Type { + INITIAL, + BETTER_MATCH, + OVERLAID + }; + + // Marks what kind of override this step was. + Type type; + + // Built name of configuration for this step. + String8 config_name; + + // Marks the package name of the better resource found in this step. + const std::string* package_name; + }; + + // Last resolved resource ID. + uint32_t resid; + + // Last resolved resource result cookie. + ApkAssetsCookie cookie = kInvalidCookie; + + // Last resolved resource type. + StringPoolRef type_string_ref; + + // Last resolved resource entry. + StringPoolRef entry_string_ref; + + // Steps taken to resolve last resource. + std::vector<Step> steps; + }; + + // Record of the last resolved resource's resolution path. + mutable Resolution last_resolution; }; class Theme { diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index d94779bf5225..eb6eb8e66175 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -17,6 +17,7 @@ #ifndef ANDROIDFW_RESOURCEUTILS_H #define ANDROIDFW_RESOURCEUTILS_H +#include "androidfw/AssetManager2.h" #include "androidfw/StringPiece.h" namespace android { @@ -27,6 +28,17 @@ namespace android { bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, StringPiece* out_type, StringPiece* out_entry); +// Convert a type_string_ref, entry_string_ref, and package +// to AssetManager2::ResourceName. Useful for getting +// resource name without re-running AssetManager2::FindEntry searches. +bool ToResourceName(StringPoolRef& type_string_ref, + StringPoolRef& entry_string_ref, + const LoadedPackage* package, + AssetManager2::ResourceName* out_name); + +// Formats a ResourceName to "package:type/entry_name". +std::string ToFormattedResourceString(AssetManager2::ResourceName* resource_name); + inline uint32_t fix_package_id(uint32_t resid, uint8_t package_id) { return (resid & 0x00ffffffu) | (static_cast<uint32_t>(package_id) << 24); } diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 5449a54d08de..105dcd209bf7 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -586,4 +586,111 @@ TEST_F(AssetManager2Test, OpenDirFromManyApks) { EXPECT_THAT(asset_dir->getFileType(2), Eq(FileType::kFileTypeDirectory)); } +TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) { + ResTable_config desired_config; + + AssetManager2 assetmanager; + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get()}); + assetmanager.SetResourceResolutionLoggingEnabled(false); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + auto result = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("", result); +} + +TEST_F(AssetManager2Test, GetLastPathWithoutResolutionReturnsEmpty) { + ResTable_config desired_config; + + AssetManager2 assetmanager; + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get()}); + + auto result = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("", result); +} + +TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) { + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + AssetManager2 assetmanager; + assetmanager.SetResourceResolutionLoggingEnabled(true); + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get()}); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + auto result = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic", result); +} + +TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + desired_config.language[0] = 'd'; + desired_config.language[1] = 'e'; + + AssetManager2 assetmanager; + assetmanager.SetResourceResolutionLoggingEnabled(true); + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()}); + + Res_value value = Res_value(); + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + auto result = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("Resolution for 0x7f030000 com.android.basic:string/test1\n\tFor config -de\n\tFound initial: com.android.basic\n\tFound better: com.android.basic -de", result); +} + +TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { + ResTable_config desired_config; + memset(&desired_config, 0, sizeof(desired_config)); + + AssetManager2 assetmanager; + assetmanager.SetResourceResolutionLoggingEnabled(true); + assetmanager.SetConfiguration(desired_config); + assetmanager.SetApkAssets({basic_assets_.get()}); + + Res_value value = Res_value(); + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::string::test1, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + + auto resultEnabled = assetmanager.GetLastResourceResolution(); + ASSERT_NE("", resultEnabled); + + assetmanager.SetResourceResolutionLoggingEnabled(false); + + auto resultDisabled = assetmanager.GetLastResourceResolution(); + EXPECT_EQ("", resultDisabled); +} + } // namespace android |