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
This commit is contained in:
Winson
2019-01-11 11:28:34 -08:00
parent 0c891e8f4e
commit 2f3669b767
9 changed files with 471 additions and 86 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -298,6 +298,13 @@ public class ResourcesImpl {
+ Integer.toHexString(resid));
}
@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();

View File

@@ -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},

View File

@@ -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;
}
best_cookie = cookie;
best_package = loaded_package;
best_type = type_chunk;
best_config = &this_config;
best_offset = offset;
// 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;
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 {

View File

@@ -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

View File

@@ -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 {

View File

@@ -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);
}

View File

@@ -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