diff options
63 files changed, 2401 insertions, 2671 deletions
diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index 8323d0ba2415..c44170928992 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -45,8 +45,11 @@ using android::ApkAssets; using android::ApkAssetsCookie; using android::AssetManager2; using android::ConfigDescription; +using android::is_valid_resid; +using android::kInvalidCookie; using android::Res_value; using android::ResStringPool; +using android::ResTable_config; using android::StringPiece16; using android::base::StringPrintf; using android::idmap2::CommandLineOptions; @@ -56,6 +59,7 @@ using android::idmap2::ResourceId; using android::idmap2::Result; using android::idmap2::Unit; using android::idmap2::utils::ExtractOverlayManifestInfo; +using android::util::Utf16ToUtf8; namespace { @@ -65,23 +69,25 @@ Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const // first, try to parse as a hex number char* endptr = nullptr; - const ResourceId parsed_resid = strtol(res.c_str(), &endptr, kBaseHex); + ResourceId resid; + resid = strtol(res.c_str(), &endptr, kBaseHex); if (*endptr == '\0') { - return parsed_resid; + return resid; } // next, try to parse as a package:type/name string - if (auto resid = am.GetResourceId(res, "", fallback_package)) { - return *resid; + resid = am.GetResourceId(res, "", fallback_package); + if (is_valid_resid(resid)) { + return resid; } // end of the road: res could not be parsed return Error("failed to obtain resource id for %s", res.c_str()); } -void PrintValue(AssetManager2* const am, const AssetManager2::SelectedValue& value, +void PrintValue(AssetManager2* const am, const Res_value& value, const ApkAssetsCookie& cookie, std::string* const out) { - switch (value.type) { + switch (value.dataType) { case Res_value::TYPE_INT_DEC: out->append(StringPrintf("%d", value.data)); break; @@ -92,21 +98,30 @@ void PrintValue(AssetManager2* const am, const AssetManager2::SelectedValue& val out->append(value.data != 0 ? "true" : "false"); break; case Res_value::TYPE_STRING: { - const ResStringPool* pool = am->GetStringPoolForCookie(value.cookie); + const ResStringPool* pool = am->GetStringPoolForCookie(cookie); out->append("\""); - if (auto str = pool->string8ObjectAt(value.data)) { - out->append(*str); + size_t len; + if (pool->isUTF8()) { + const char* str = pool->string8At(value.data, &len); + out->append(str, len); + } else { + const char16_t* str16 = pool->stringAt(value.data, &len); + out->append(Utf16ToUtf8(StringPiece16(str16, len))); } + out->append("\""); } break; default: - out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.type, value.data)); + out->append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data)); break; } } Result<std::string> WARN_UNUSED GetValue(AssetManager2* const am, ResourceId resid) { - auto value = am->GetResource(resid); - if (!value.has_value()) { + Res_value value; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = am->GetResource(resid, true, 0, &value, &config, &flags); + if (cookie == kInvalidCookie) { return Error("no resource 0x%08x in asset manager", resid); } @@ -114,35 +129,41 @@ Result<std::string> WARN_UNUSED GetValue(AssetManager2* const am, ResourceId res // TODO(martenkongstad): use optional parameter GetResource(..., std::string* // stacktrace = NULL) instead - out.append(StringPrintf("cookie=%d ", value->cookie)); + out.append(StringPrintf("cookie=%d ", cookie)); out.append("config='"); - out.append(value->config.toString().c_str()); + out.append(config.toString().c_str()); out.append("' value="); - if (value->type == Res_value::TYPE_REFERENCE) { - auto bag_result = am->GetBag(static_cast<uint32_t>(value->data)); - if (!bag_result.has_value()) { - out.append(StringPrintf("dataType=0x%02x data=0x%08x", value->type, value->data)); + if (value.dataType == Res_value::TYPE_REFERENCE) { + const android::ResolvedBag* bag = am->GetBag(static_cast<uint32_t>(value.data)); + if (bag == nullptr) { + out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data)); return out; } - out.append("["); - const android::ResolvedBag* bag = bag_result.value(); + Res_value bag_val; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie bag_cookie; for (size_t i = 0; i < bag->entry_count; ++i) { - AssetManager2::SelectedValue entry(bag, bag->entries[i]); - if (am->ResolveReference(entry).has_value()) { - out.append(StringPrintf("Error: dataType=0x%02x data=0x%08x", entry.type, entry.data)); + const android::ResolvedBag::Entry& entry = bag->entries[i]; + bag_val = entry.value; + bag_cookie = am->ResolveReference(entry.cookie, &bag_val, &selected_config, &flags, &ref); + if (bag_cookie == kInvalidCookie) { + out.append( + StringPrintf("Error: dataType=0x%02x data=0x%08x", bag_val.dataType, bag_val.data)); continue; } - PrintValue(am, entry, &out); + PrintValue(am, bag_val, bag_cookie, &out); if (i != bag->entry_count - 1) { out.append(", "); } } out.append("]"); } else { - PrintValue(am, *value, &out); + PrintValue(am, value, cookie, &out); } return out; diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index 0127e874b444..1d2090c431ae 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -17,6 +17,8 @@ #ifndef IDMAP2_IDMAP2D_IDMAP2SERVICE_H_ #define IDMAP2_IDMAP2D_IDMAP2SERVICE_H_ +#include <string> + #include <android-base/unique_fd.h> #include <binder/BinderService.h> diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h index f66916ccb78c..0a58ec43d8ff 100644 --- a/cmds/idmap2/include/idmap2/ResourceMapping.h +++ b/cmds/idmap2/include/idmap2/ResourceMapping.h @@ -117,8 +117,7 @@ class ResourceMapping { static Result<ResourceMapping> CreateResourceMappingLegacy(const AssetManager2* target_am, const AssetManager2* overlay_am, const LoadedPackage* target_package, - const LoadedPackage* overlay_package, - LogInfo& log_info); + const LoadedPackage* overlay_package); // Removes resources that do not pass policy or overlayable checks of the target package. void FilterOverlayableResources(const AssetManager2* target_am, diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp index 3037a791328e..a93202a64d31 100644 --- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp @@ -100,9 +100,10 @@ void PrettyPrintVisitor::visit(const IdmapData& data) { stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id) << utils::DataTypeToString(target_entry.value.data_type); + size_t unused; if (target_entry.value.data_type == Res_value::TYPE_STRING) { - auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset); - stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\""; + auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset, &unused); + stream_ << " \"" << StringPiece16(str) << "\""; } else { stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value); } diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp index d777cbfa9a14..31f1c16ba5a6 100644 --- a/cmds/idmap2/libidmap2/ResourceMapping.cpp +++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp @@ -71,10 +71,9 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package, if (!target_package.DefinesOverlayable()) { return (sDefaultPolicies & fulfilled_policies) != 0 ? Result<Unit>({}) - : Error( - "overlay must be preinstalled, signed with the same signature as the target," - " or signed with the same signature as the package referenced through" - " <overlay-config-signature>."); + : Error("overlay must be preinstalled, signed with the same signature as the target," + " or signed with the same signature as the package referenced through" + " <overlay-config-signature>."); } const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource); @@ -120,25 +119,32 @@ const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { Result<std::unique_ptr<Asset>> OpenNonAssetFromResource(const ResourceId& resource_id, const AssetManager2& asset_manager) { - auto value = asset_manager.GetResource(resource_id); - if (!value.has_value()) { + Res_value value{}; + ResTable_config selected_config{}; + uint32_t flags; + auto cookie = + asset_manager.GetResource(resource_id, /* may_be_bag */ false, + /* density_override */ 0U, &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { return Error("failed to find resource for id 0x%08x", resource_id); } - if (value->type != Res_value::TYPE_STRING) { + if (value.dataType != Res_value::TYPE_STRING) { return Error("resource for is 0x%08x is not a file", resource_id); } - auto string_pool = asset_manager.GetStringPoolForCookie(value->cookie); - auto file = string_pool->string8ObjectAt(value->data); - if (!file.has_value()) { - return Error("failed to find string for index %d", value->data); + auto string_pool = asset_manager.GetStringPoolForCookie(cookie); + size_t len; + auto file_path16 = string_pool->stringAt(value.data, &len); + if (file_path16 == nullptr) { + return Error("failed to find string for index %d", value.data); } // Load the overlay resource mappings from the file specified using android:resourcesMap. - auto asset = asset_manager.OpenNonAsset(file->c_str(), Asset::AccessMode::ACCESS_BUFFER); + auto file_path = String8(String16(file_path16)); + auto asset = asset_manager.OpenNonAsset(file_path.c_str(), Asset::AccessMode::ACCESS_BUFFER); if (asset == nullptr) { - return Error("file \"%s\" not found", file->c_str()); + return Error("file \"%s\" not found", file_path.c_str()); } return asset; @@ -184,16 +190,16 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManage return Error(R"(<item> tag missing expected attribute "value")"); } - auto target_id_result = + ResourceId target_id = target_am->GetResourceId(*target_resource, "", target_package->GetPackageName()); - if (!target_id_result.has_value()) { + if (target_id == 0U) { log_info.Warning(LogMessage() << "failed to find resource \"" << *target_resource << "\" in target resources"); continue; } // Retrieve the compile-time resource id of the target resource. - uint32_t target_id = REWRITE_PACKAGE(*target_id_result, target_package_id); + target_id = REWRITE_PACKAGE(target_id, target_package_id); if (overlay_resource->dataType == Res_value::TYPE_STRING) { overlay_resource->data += string_pool_offset; @@ -214,7 +220,7 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManage Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy( const AssetManager2* target_am, const AssetManager2* overlay_am, - const LoadedPackage* target_package, const LoadedPackage* overlay_package, LogInfo& log_info) { + const LoadedPackage* target_package, const LoadedPackage* overlay_package) { ResourceMapping resource_mapping; const uint8_t target_package_id = target_package->GetPackageId(); const auto end = overlay_package->end(); @@ -228,15 +234,13 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy( // Find the resource with the same type and entry name within the target package. const std::string full_name = base::StringPrintf("%s:%s", target_package->GetPackageName().c_str(), name->c_str()); - auto target_resource_result = target_am->GetResourceId(full_name); - if (!target_resource_result.has_value()) { - log_info.Warning(LogMessage() << "failed to find resource \"" << full_name - << "\" in target resources"); + ResourceId target_resource = target_am->GetResourceId(full_name); + if (target_resource == 0U) { continue; } // Retrieve the compile-time resource id of the target resource. - ResourceId target_resource = REWRITE_PACKAGE(*target_resource_result, target_package_id); + target_resource = REWRITE_PACKAGE(target_resource, target_package_id); resource_mapping.AddMapping(target_resource, overlay_resid, false /* rewrite_overlay_reference */); } @@ -343,9 +347,7 @@ Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_a auto& string_pool = (*parser)->get_strings(); string_pool_data_length = string_pool.bytes(); string_pool_data.reset(new uint8_t[string_pool_data_length]); - - // Overlays should not be incrementally installed, so calling unsafe_ptr is fine here. - memcpy(string_pool_data.get(), string_pool.data().unsafe_ptr(), string_pool_data_length); + memcpy(string_pool_data.get(), string_pool.data(), string_pool_data_length); // Offset string indices by the size of the overlay resource table string pool. string_pool_offset = overlay_arsc->GetStringPool()->size(); @@ -356,7 +358,7 @@ Result<ResourceMapping> ResourceMapping::FromApkAssets(const ApkAssets& target_a // If no file is specified using android:resourcesMap, it is assumed that the overlay only // defines resources intended to override target resources of the same type and name. resource_mapping = CreateResourceMappingLegacy(&target_asset_manager, &overlay_asset_manager, - target_pkg, overlay_pkg, log_info); + target_pkg, overlay_pkg); } if (!resource_mapping) { diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp index e817140238ae..98d026bc70dc 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -72,21 +72,21 @@ StringPiece DataTypeToString(uint8_t data_type) { } Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) { - const auto name = am.GetResourceName(resid); - if (!name.has_value()) { + AssetManager2::ResourceName name; + if (!am.GetResourceName(resid, &name)) { return Error("no resource 0x%08x in asset manager", resid); } std::string out; - if (name->type != nullptr) { - out.append(name->type, name->type_len); + if (name.type != nullptr) { + out.append(name.type, name.type_len); } else { - out += Utf16ToUtf8(StringPiece16(name->type16, name->type_len)); + out += Utf16ToUtf8(StringPiece16(name.type16, name.type_len)); } out.append("/"); - if (name->entry != nullptr) { - out.append(name->entry, name->entry_len); + if (name.entry != nullptr) { + out.append(name.entry, name.entry_len); } else { - out += Utf16ToUtf8(StringPiece16(name->entry16, name->entry_len)); + out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } return out; } diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp index 7c55b64566f2..526a560907aa 100644 --- a/cmds/idmap2/libidmap2/XmlParser.cpp +++ b/cmds/idmap2/libidmap2/XmlParser.cpp @@ -98,19 +98,18 @@ Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& switch ((*value).dataType) { case Res_value::TYPE_STRING: { - if (auto str = parser_.getStrings().string8ObjectAt((*value).data)) { - return std::string(str->string()); - } - break; + size_t len; + const String16 value16(parser_.getStrings().stringAt((*value).data, &len)); + return std::string(String8(value16).c_str()); } case Res_value::TYPE_INT_DEC: case Res_value::TYPE_INT_HEX: case Res_value::TYPE_INT_BOOLEAN: { return std::to_string((*value).data); } + default: + return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str()); } - - return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str()); } Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const { diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp index 444bb668dc57..fbdd4060d7f2 100644 --- a/core/jni/android_content_res_ApkAssets.cpp +++ b/core/jni/android_content_res_ApkAssets.cpp @@ -24,7 +24,6 @@ #include "utils/misc.h" #include "utils/Trace.h" -#include "android_util_AssetManager_private.h" #include "core_jni_helpers.h" #include "jni.h" #include "nativehelper/ScopedUtfChars.h" @@ -348,17 +347,12 @@ static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring fil return 0; } - const auto buffer = asset->getIncFsBuffer(true /* aligned */); - const size_t length = asset->getLength(); - if (!buffer.convert<uint8_t>().verify(length)) { - jniThrowException(env, kResourcesNotFound, kIOErrorMessage); - return 0; - } - // DynamicRefTable is only needed when looking up resource references. Opening an XML file // directly from an ApkAssets has no notion of proper resource references. - auto xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/); - status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true); + std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); + if (err != NO_ERROR) { jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); return 0; diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index b2c69a0aeafd..efca33aaf520 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -42,7 +42,6 @@ #include "androidfw/ResourceTypes.h" #include "androidfw/ResourceUtils.h" -#include "android_util_AssetManager_private.h" #include "core_jni_helpers.h" #include "jni.h" #include "nativehelper/JNIPlatformHelp.h" @@ -113,17 +112,19 @@ constexpr inline static ApkAssetsCookie JavaCookieToApkAssetsCookie(jint cookie) return cookie > 0 ? static_cast<ApkAssetsCookie>(cookie - 1) : kInvalidCookie; } -static jint CopyValue(JNIEnv* env, const AssetManager2::SelectedValue& value, - jobject out_typed_value) { - env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.type); +static jint CopyValue(JNIEnv* env, ApkAssetsCookie cookie, const Res_value& value, uint32_t ref, + uint32_t type_spec_flags, ResTable_config* config, jobject out_typed_value) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mType, value.dataType); env->SetIntField(out_typed_value, gTypedValueOffsets.mAssetCookie, - ApkAssetsCookieToJavaCookie(value.cookie)); + ApkAssetsCookieToJavaCookie(cookie)); env->SetIntField(out_typed_value, gTypedValueOffsets.mData, value.data); env->SetObjectField(out_typed_value, gTypedValueOffsets.mString, nullptr); - env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, value.resid); - env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, value.flags); - env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, value.config.density); - return static_cast<jint>(ApkAssetsCookieToJavaCookie(value.cookie)); + env->SetIntField(out_typed_value, gTypedValueOffsets.mResourceId, ref); + env->SetIntField(out_typed_value, gTypedValueOffsets.mChangingConfigurations, type_spec_flags); + if (config != nullptr) { + env->SetIntField(out_typed_value, gTypedValueOffsets.mDensity, config->density); + } + return static_cast<jint>(ApkAssetsCookieToJavaCookie(cookie)); } // ---------------------------------------------------------------------------- @@ -568,15 +569,15 @@ static jlong NativeOpenXmlAsset(JNIEnv* env, jobject /*clazz*/, jlong ptr, jint return 0; } - const incfs::map_ptr<void> buffer = asset->getIncFsBuffer(true /* aligned */); - const size_t length = asset->getLength(); - if (!buffer.convert<uint8_t>().verify(length)) { - jniThrowException(env, kResourcesNotFound, kIOErrorMessage); - return 0; - } + // May be nullptr. + std::shared_ptr<const DynamicRefTable> dynamic_ref_table = + assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>( + std::move(dynamic_ref_table)); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); - auto xml_tree = util::make_unique<ResXMLTree>(assetmanager->GetDynamicRefTableForCookie(cookie)); - status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true); if (err != NO_ERROR) { jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); return 0; @@ -605,15 +606,15 @@ static jlong NativeOpenXmlAssetFd(JNIEnv* env, jobject /*clazz*/, jlong ptr, int ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); ApkAssetsCookie cookie = JavaCookieToApkAssetsCookie(jcookie); - const incfs::map_ptr<void> buffer = asset->getIncFsBuffer(true /* aligned */); - const size_t length = asset->getLength(); - if (!buffer.convert<uint8_t>().verify(length)) { - jniThrowException(env, kResourcesNotFound, kIOErrorMessage); - return 0; - } + // May be nullptr. + std::shared_ptr<const DynamicRefTable> dynamic_ref_table = + assetmanager->GetDynamicRefTableForCookie(cookie); + + std::unique_ptr<ResXMLTree> xml_tree = util::make_unique<ResXMLTree>( + std::move(dynamic_ref_table)); + status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), true); + asset.reset(); - auto xml_tree = util::make_unique<ResXMLTree>(assetmanager->GetDynamicRefTableForCookie(cookie)); - status_t err = xml_tree->setTo(buffer.unsafe_ptr(), length, true); if (err != NO_ERROR) { jniThrowException(env, "java/io/FileNotFoundException", "Corrupt XML binary file"); return 0; @@ -625,62 +626,67 @@ static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin jshort density, jobject typed_value, jboolean resolve_references) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto value = assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/, - static_cast<uint16_t>(density)); - if (!value.has_value()) { - ThrowIfIOError(env, value); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = + assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/, + static_cast<uint16_t>(density), &value, &selected_config, &flags); + if (cookie == kInvalidCookie) { return ApkAssetsCookieToJavaCookie(kInvalidCookie); } + uint32_t ref = static_cast<uint32_t>(resid); if (resolve_references) { - auto result = assetmanager->ResolveReference(value.value()); - if (!result.has_value()) { - ThrowIfIOError(env, result); + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { return ApkAssetsCookieToJavaCookie(kInvalidCookie); } } - return CopyValue(env, *value, typed_value); + return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value); } static jint NativeGetResourceBagValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, jint bag_entry_id, jobject typed_value) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (!bag.has_value()) { - ThrowIfIOError(env, bag); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { return ApkAssetsCookieToJavaCookie(kInvalidCookie); } - // The legacy would find the last entry with the target bag entry id - using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>; - const auto rbegin = reverse_bag_iterator(end(*bag)); - const auto rend = reverse_bag_iterator(begin(*bag)); - auto entry = std::find_if(rbegin, rend, [bag_entry_id](auto&& e) { - return e.key == static_cast<uint32_t>(bag_entry_id); - }); + uint32_t type_spec_flags = bag->type_spec_flags; + ApkAssetsCookie cookie = kInvalidCookie; + const Res_value* bag_value = nullptr; + for (const ResolvedBag::Entry& entry : bag) { + if (entry.key == static_cast<uint32_t>(bag_entry_id)) { + cookie = entry.cookie; + bag_value = &entry.value; + + // Keep searching (the old implementation did that). + } + } - if (entry == rend) { + if (cookie == kInvalidCookie) { return ApkAssetsCookieToJavaCookie(kInvalidCookie); } - AssetManager2::SelectedValue attr_value(*bag, *entry); - auto result = assetmanager->ResolveReference(attr_value); - if (!result.has_value()) { - ThrowIfIOError(env, result); + Res_value value = *bag_value; + uint32_t ref = static_cast<uint32_t>(resid); + ResTable_config selected_config; + cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &type_spec_flags, &ref); + if (cookie == kInvalidCookie) { return ApkAssetsCookieToJavaCookie(kInvalidCookie); } - return CopyValue(env, attr_value, typed_value); + return CopyValue(env, cookie, value, ref, type_spec_flags, nullptr, typed_value); } static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (!bag_result.has_value()) { - ThrowIfIOError(env, bag_result); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { return nullptr; } - const ResolvedBag* bag = *bag_result; jintArray array = env->NewIntArray(bag->entry_count); if (env->ExceptionCheck()) { return nullptr; @@ -696,47 +702,42 @@ static jintArray NativeGetStyleAttributes(JNIEnv* env, jclass /*clazz*/, jlong p static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (!bag_result.has_value()) { - ThrowIfIOError(env, bag_result); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { return nullptr; } - const ResolvedBag* bag = *bag_result; jobjectArray array = env->NewObjectArray(bag->entry_count, g_stringClass, nullptr); if (array == nullptr) { return nullptr; } for (uint32_t i = 0; i < bag->entry_count; i++) { + const ResolvedBag::Entry& entry = bag->entries[i]; + // Resolve any references to their final value. - AssetManager2::SelectedValue attr_value(bag, bag->entries[i]); - auto result = assetmanager->ResolveReference(attr_value); - if (!result.has_value()) { - ThrowIfIOError(env, result); + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { return nullptr; } - if (attr_value.type == Res_value::TYPE_STRING) { - const ApkAssets* apk_assets = assetmanager->GetApkAssets()[attr_value.cookie]; + if (value.dataType == Res_value::TYPE_STRING) { + const ApkAssets* apk_assets = assetmanager->GetApkAssets()[cookie]; const ResStringPool* pool = apk_assets->GetLoadedArsc()->GetStringPool(); jstring java_string = nullptr; - auto str_utf8 = pool->string8At(attr_value.data); - if (UNLIKELY(ThrowIfIOError(env, str_utf8))) { - return nullptr; - } - - if (str_utf8.has_value()) { - java_string = env->NewStringUTF(str_utf8->data()); + size_t str_len; + const char* str_utf8 = pool->string8At(value.data, &str_len); + if (str_utf8 != nullptr) { + java_string = env->NewStringUTF(str_utf8); } else { - auto str_utf16 = pool->stringAt(attr_value.data); - if (!str_utf16.has_value()) { - ThrowIfIOError(env, str_utf16); - return nullptr; - } - java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16->data()), - str_utf16->size()); + const char16_t* str_utf16 = pool->stringAt(value.data, &str_len); + java_string = env->NewString(reinterpret_cast<const jchar*>(str_utf16), str_len); } // Check for errors creating the strings (if malformed or no memory). @@ -757,13 +758,11 @@ static jobjectArray NativeGetResourceStringArray(JNIEnv* env, jclass /*clazz*/, static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (!bag_result.has_value()) { - ThrowIfIOError(env, bag_result); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { return nullptr; } - const ResolvedBag* bag = *bag_result; jintArray array = env->NewIntArray(bag->entry_count * 2); if (array == nullptr) { return nullptr; @@ -775,20 +774,24 @@ static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, } for (size_t i = 0; i < bag->entry_count; i++) { - AssetManager2::SelectedValue attr_value(bag, bag->entries[i]); - auto result = assetmanager->ResolveReference(attr_value); - if (!result.has_value()) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); - ThrowIfIOError(env, result); return nullptr; } jint string_index = -1; - if (attr_value.type == Res_value::TYPE_STRING) { - string_index = static_cast<jint>(attr_value.data); + if (value.dataType == Res_value::TYPE_STRING) { + string_index = static_cast<jint>(value.data); } - buffer[i * 2] = ApkAssetsCookieToJavaCookie(attr_value.cookie); + buffer[i * 2] = ApkAssetsCookieToJavaCookie(cookie); buffer[(i * 2) + 1] = string_index; } env->ReleasePrimitiveArrayCritical(array, buffer, 0); @@ -797,13 +800,11 @@ static jintArray NativeGetResourceStringArrayInfo(JNIEnv* env, jclass /*clazz*/, static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (!bag_result.has_value()) { - ThrowIfIOError(env, bag_result); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { return nullptr; } - const ResolvedBag* bag = *bag_result; jintArray array = env->NewIntArray(bag->entry_count); if (array == nullptr) { return nullptr; @@ -815,39 +816,40 @@ static jintArray NativeGetResourceIntArray(JNIEnv* env, jclass /*clazz*/, jlong } for (size_t i = 0; i < bag->entry_count; i++) { - AssetManager2::SelectedValue attr_value(bag, bag->entries[i]); - auto result = assetmanager->ResolveReference(attr_value); - if (!result.has_value()) { - env->ReleasePrimitiveArrayCritical(array, buffer, 0); - ThrowIfIOError(env, result); + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + uint32_t flags; + uint32_t ref; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + env->ReleasePrimitiveArrayCritical(array, buffer, JNI_ABORT); return nullptr; } - if (attr_value.type >= Res_value::TYPE_FIRST_INT && - attr_value.type <= Res_value::TYPE_LAST_INT) { - buffer[i] = static_cast<jint>(attr_value.data); + if (value.dataType >= Res_value::TYPE_FIRST_INT && value.dataType <= Res_value::TYPE_LAST_INT) { + buffer[i] = static_cast<jint>(value.data); } } env->ReleasePrimitiveArrayCritical(array, buffer, 0); return array; } -static jint NativeGetResourceArraySize(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (!bag.has_value()) { - ThrowIfIOError(env, bag); - return -1; - } - return static_cast<jint>((*bag)->entry_count); +static jint NativeGetResourceArraySize(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jint resid) { + ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { + return -1; + } + return static_cast<jint>(bag->entry_count); } static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid, jintArray out_data) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto bag_result = assetmanager->GetBag(static_cast<uint32_t>(resid)); - if (!bag_result.has_value()) { - ThrowIfIOError(env, bag_result); + const ResolvedBag* bag = assetmanager->GetBag(static_cast<uint32_t>(resid)); + if (bag == nullptr) { return -1; } @@ -856,10 +858,8 @@ static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin return -1; } - const ResolvedBag* bag = *bag_result; if (static_cast<jsize>(bag->entry_count) > out_data_length * STYLE_NUM_ENTRIES) { - jniThrowException(env, "java/lang/IllegalArgumentException", - "Input array is not large enough"); + jniThrowException(env, "java/lang/IllegalArgumentException", "Input array is not large enough"); return -1; } @@ -870,26 +870,31 @@ static jint NativeGetResourceArray(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin jint* cursor = buffer; for (size_t i = 0; i < bag->entry_count; i++) { - AssetManager2::SelectedValue attr_value(bag, bag->entries[i]); - auto result = assetmanager->ResolveReference(attr_value); - if (!result.has_value()) { + const ResolvedBag::Entry& entry = bag->entries[i]; + Res_value value = entry.value; + ResTable_config selected_config; + selected_config.density = 0; + uint32_t flags = bag->type_spec_flags; + uint32_t ref = 0; + ApkAssetsCookie cookie = + assetmanager->ResolveReference(entry.cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { env->ReleasePrimitiveArrayCritical(out_data, buffer, JNI_ABORT); - ThrowIfIOError(env, bag_result); return -1; } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (attr_value.type == Res_value::TYPE_REFERENCE && attr_value.data == 0) { - attr_value.type = Res_value::TYPE_NULL; - attr_value.data = Res_value::DATA_NULL_UNDEFINED; + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; } - cursor[STYLE_TYPE] = static_cast<jint>(attr_value.type); - cursor[STYLE_DATA] = static_cast<jint>(attr_value.data); - cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(attr_value.cookie); - cursor[STYLE_RESOURCE_ID] = static_cast<jint>(attr_value.resid); - cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(attr_value.flags); - cursor[STYLE_DENSITY] = static_cast<jint>(attr_value.config.density); + cursor[STYLE_TYPE] = static_cast<jint>(value.dataType); + cursor[STYLE_DATA] = static_cast<jint>(value.data); + cursor[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + cursor[STYLE_RESOURCE_ID] = static_cast<jint>(ref); + cursor[STYLE_CHANGING_CONFIGURATIONS] = static_cast<jint>(flags); + cursor[STYLE_DENSITY] = static_cast<jint>(selected_config.density); cursor += STYLE_NUM_ENTRIES; } env->ReleasePrimitiveArrayCritical(out_data, buffer, 0); @@ -917,71 +922,60 @@ static jint NativeGetResourceIdentifier(JNIEnv* env, jclass /*clazz*/, jlong ptr CHECK(package_utf8.c_str() != nullptr); package = package_utf8.c_str(); } - ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto resid = assetmanager->GetResourceId(name_utf8.c_str(), type, package); - if (!resid.has_value()) { - ThrowIfIOError(env, resid); - return 0; - } - - return static_cast<jint>(*resid); + return static_cast<jint>(assetmanager->GetResourceId(name_utf8.c_str(), type, package)); } static jstring NativeGetResourceName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid)); - if (!name.has_value()) { - ThrowIfIOError(env, name); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { return nullptr; } - const std::string result = ToFormattedResourceString(name.value()); + std::string result = ToFormattedResourceString(&name); return env->NewStringUTF(result.c_str()); } static jstring NativeGetResourcePackageName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid)); - if (!name.has_value()) { - ThrowIfIOError(env, name); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { return nullptr; } - if (name->package != nullptr) { - return env->NewStringUTF(name->package); + if (name.package != nullptr) { + return env->NewStringUTF(name.package); } return nullptr; } static jstring NativeGetResourceTypeName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid)); - if (!name.has_value()) { - ThrowIfIOError(env, name); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { return nullptr; } - if (name->type != nullptr) { - return env->NewStringUTF(name->type); - } else if (name->type16 != nullptr) { - return env->NewString(reinterpret_cast<const jchar*>(name->type16), name->type_len); + if (name.type != nullptr) { + return env->NewStringUTF(name.type); + } else if (name.type16 != nullptr) { + return env->NewString(reinterpret_cast<const jchar*>(name.type16), name.type_len); } return nullptr; } static jstring NativeGetResourceEntryName(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto name = assetmanager->GetResourceName(static_cast<uint32_t>(resid)); - if (!name.has_value()) { - ThrowIfIOError(env, name); + AssetManager2::ResourceName name; + if (!assetmanager->GetResourceName(static_cast<uint32_t>(resid), &name)) { return nullptr; } - if (name->entry != nullptr) { - return env->NewStringUTF(name->entry); - } else if (name->entry16 != nullptr) { - return env->NewString(reinterpret_cast<const jchar*>(name->entry16), name->entry_len); + if (name.entry != nullptr) { + return env->NewStringUTF(name.entry); + } else if (name.entry16 != nullptr) { + return env->NewString(reinterpret_cast<const jchar*>(name.entry16), name.entry_len); } return nullptr; } @@ -1045,21 +1039,17 @@ static jobject ConstructConfigurationObject(JNIEnv* env, const ResTable_config& static jobjectArray NativeGetSizeConfigurations(JNIEnv* env, jclass /*clazz*/, jlong ptr) { ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); - auto configurations = assetmanager->GetResourceConfigurations(true /*exclude_system*/, - false /*exclude_mipmap*/); - if (!configurations.has_value()) { - ThrowIfIOError(env, configurations); - return nullptr; - } + std::set<ResTable_config> configurations = + assetmanager->GetResourceConfigurations(true /*exclude_system*/, false /*exclude_mipmap*/); jobjectArray array = - env->NewObjectArray(configurations->size(), gConfigurationOffsets.classObject, nullptr); + env->NewObjectArray(configurations.size(), gConfigurationOffsets.classObject, nullptr); if (array == nullptr) { return nullptr; } size_t idx = 0; - for (const ResTable_config& configuration : *configurations) { + for (const ResTable_config& configuration : configurations) { jobject java_configuration = ConstructConfigurationObject(env, configuration); if (java_configuration == nullptr) { return nullptr; @@ -1082,10 +1072,13 @@ static jintArray NativeAttributeResolutionStack( (void) assetmanager; // Load default style from attribute, if specified... + uint32_t def_style_flags = 0u; if (def_style_attr != 0) { - auto value = theme->GetAttribute(def_style_attr); - if (value.has_value() && value->type == Res_value::TYPE_REFERENCE) { - def_style_resid = value->data; + Res_value value; + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (value.dataType == Res_value::TYPE_REFERENCE) { + def_style_resid = value.data; + } } } @@ -1126,11 +1119,10 @@ static void NativeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong the return; } - auto result = ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr), - static_cast<uint32_t>(def_style_resid), - reinterpret_cast<uint32_t*>(attrs), attrs_len, out_values, out_indices); + ApplyStyle(theme, xml_parser, static_cast<uint32_t>(def_style_attr), + static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(attrs), attrs_len, + out_values, out_indices); env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - ThrowIfIOError(env, result); } static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong theme_ptr, @@ -1191,12 +1183,11 @@ static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlo Theme* theme = reinterpret_cast<Theme*>(theme_ptr); CHECK(theme->GetAssetManager() == &(*assetmanager)); (void) assetmanager; - auto result = - ResolveAttrs(theme, static_cast<uint32_t>(def_style_attr), - static_cast<uint32_t>(def_style_resid), reinterpret_cast<uint32_t*>(values), - values_len, reinterpret_cast<uint32_t*>(attrs), attrs_len, - reinterpret_cast<uint32_t*>(out_values), - reinterpret_cast<uint32_t*>(out_indices)); + + bool result = ResolveAttrs( + theme, static_cast<uint32_t>(def_style_attr), static_cast<uint32_t>(def_style_resid), + reinterpret_cast<uint32_t*>(values), values_len, reinterpret_cast<uint32_t*>(attrs), + attrs_len, reinterpret_cast<uint32_t*>(out_values), reinterpret_cast<uint32_t*>(out_indices)); if (out_indices != nullptr) { env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); } @@ -1205,13 +1196,8 @@ static jboolean NativeResolveAttrs(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlo if (values != nullptr) { env->ReleasePrimitiveArrayCritical(java_values, values, JNI_ABORT); } - env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (!result.has_value()) { - ThrowIfIOError(env, result); - return JNI_FALSE; - } - return JNI_TRUE; + return result ? JNI_TRUE : JNI_FALSE; } static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong ptr, @@ -1252,22 +1238,18 @@ static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong pt ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr)); ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr); - auto result = - RetrieveAttributes(assetmanager.get(), xml_parser, reinterpret_cast<uint32_t*>(attrs), - attrs_len, reinterpret_cast<uint32_t*>(out_values), - reinterpret_cast<uint32_t*>(out_indices)); + + bool result = RetrieveAttributes(assetmanager.get(), xml_parser, + reinterpret_cast<uint32_t*>(attrs), attrs_len, + reinterpret_cast<uint32_t*>(out_values), + reinterpret_cast<uint32_t*>(out_indices)); if (out_indices != nullptr) { env->ReleasePrimitiveArrayCritical(out_java_indices, out_indices, 0); } - env->ReleasePrimitiveArrayCritical(out_java_values, out_values, 0); env->ReleasePrimitiveArrayCritical(java_attrs, attrs, JNI_ABORT); - if (!result.has_value()) { - ThrowIfIOError(env, result); - return JNI_FALSE; - } - return JNI_TRUE; + return result ? JNI_TRUE : JNI_FALSE; } static jlong NativeThemeCreate(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr) { @@ -1286,9 +1268,7 @@ static void NativeThemeApplyStyle(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlon Theme* theme = reinterpret_cast<Theme*>(theme_ptr); CHECK(theme->GetAssetManager() == &(*assetmanager)); (void) assetmanager; - - auto result = theme->ApplyStyle(static_cast<uint32_t>(resid), force); - ThrowIfIOError(env, result); + theme->ApplyStyle(static_cast<uint32_t>(resid), force); // TODO(adamlesinski): Consider surfacing exception when result is failure. // CTS currently expects no exceptions from this method. @@ -1301,22 +1281,19 @@ static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manag Theme* dst_theme = reinterpret_cast<Theme*>(dst_theme_ptr); Theme* src_theme = reinterpret_cast<Theme*>(src_theme_ptr); - ScopedLock<AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr)); - CHECK(src_theme->GetAssetManager() == &(*src_assetmanager)); - (void) src_assetmanager; - if (dst_asset_manager_ptr != src_asset_manager_ptr) { ScopedLock<AssetManager2> dst_assetmanager(AssetManagerFromLong(dst_asset_manager_ptr)); CHECK(dst_theme->GetAssetManager() == &(*dst_assetmanager)); (void) dst_assetmanager; - auto result = dst_theme->SetTo(*src_theme); - ThrowIfIOError(env, result); - return; - } + ScopedLock <AssetManager2> src_assetmanager(AssetManagerFromLong(src_asset_manager_ptr)); + CHECK(src_theme->GetAssetManager() == &(*src_assetmanager)); + (void) src_assetmanager; - auto result = dst_theme->SetTo(*src_theme); - ThrowIfIOError(env, result); + dst_theme->SetTo(*src_theme); + } else { + dst_theme->SetTo(*src_theme); + } } static void NativeThemeClear(JNIEnv* /*env*/, jclass /*clazz*/, jlong theme_ptr) { @@ -1331,21 +1308,23 @@ static jint NativeThemeGetAttributeValue(JNIEnv* env, jclass /*clazz*/, jlong pt CHECK(theme->GetAssetManager() == &(*assetmanager)); (void) assetmanager; - auto value = theme->GetAttribute(static_cast<uint32_t>(resid)); - if (!value.has_value()) { + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie = theme->GetAttribute(static_cast<uint32_t>(resid), &value, &flags); + if (cookie == kInvalidCookie) { return ApkAssetsCookieToJavaCookie(kInvalidCookie); } - if (!resolve_references) { - return CopyValue(env, *value, typed_value); - } - - auto result = theme->GetAssetManager()->ResolveReference(*value); - if (!result.has_value()) { - ThrowIfIOError(env, result); - return ApkAssetsCookieToJavaCookie(kInvalidCookie); + uint32_t ref = 0u; + if (resolve_references) { + ResTable_config selected_config; + cookie = + theme->GetAssetManager()->ResolveReference(cookie, &value, &selected_config, &flags, &ref); + if (cookie == kInvalidCookie) { + return ApkAssetsCookieToJavaCookie(kInvalidCookie); + } } - return CopyValue(env, *value, typed_value); + return CopyValue(env, cookie, value, ref, flags, nullptr, typed_value); } static void NativeThemeDump(JNIEnv* /*env*/, jclass /*clazz*/, jlong ptr, jlong theme_ptr, diff --git a/core/jni/android_util_AssetManager_private.h b/core/jni/android_util_AssetManager_private.h deleted file mode 100644 index 153509b98db5..000000000000 --- a/core/jni/android_util_AssetManager_private.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_UTIL_ASSETMANAGER_PRIVATE_H -#define ANDROID_UTIL_ASSETMANAGER_PRIVATE_H - -#include <optional> - -#include <androidfw/Errors.h> -#include <android-base/expected.h> - -#include "core_jni_helpers.h" -#include "jni.h" -#include "nativehelper/JNIHelp.h" - -namespace android { - -constexpr const char* kResourcesNotFound = "android/content/res/Resources$NotFoundException"; -constexpr const static char* kIOErrorMessage = "failed to read resources.arsc data"; - -template <typename T, typename E> -static bool ThrowIfIOError(JNIEnv* env, const base::expected<T, E>& result) { - if constexpr (std::is_same<NullOrIOError, E>::value) { - if (IsIOError(result)) { - jniThrowException(env, kResourcesNotFound, kIOErrorMessage); - return true; - } - return false; - } else { - if (!result.has_value()) { - static_assert(std::is_same<IOError, E>::value, "Unknown result error type"); - jniThrowException(env, kResourcesNotFound, kIOErrorMessage); - return true; - } - return false; - } -} - -} // namespace android - -#endif //ANDROID_UTIL_ASSETMANAGER_PRIVATE_H diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp index 45f6b72b3727..760f9e36ed3d 100644 --- a/core/jni/android_util_StringBlock.cpp +++ b/core/jni/android_util_StringBlock.cpp @@ -17,7 +17,6 @@ #define LOG_TAG "StringBlock" -#include "android_util_AssetManager_private.h" #include "jni.h" #include <nativehelper/JNIHelp.h> #include <utils/misc.h> @@ -32,8 +31,10 @@ namespace android { // ---------------------------------------------------------------------------- -static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz, jbyteArray bArray, - jint off, jint len) { +static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz, + jbyteArray bArray, + jint off, jint len) +{ if (bArray == NULL) { jniThrowNullPointerException(env, NULL); return 0; @@ -58,7 +59,9 @@ static jlong android_content_StringBlock_nativeCreate(JNIEnv* env, jobject clazz return reinterpret_cast<jlong>(osb); } -static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz, jlong token) { +static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz, + jlong token) +{ ResStringPool* osb = reinterpret_cast<ResStringPool*>(token); if (osb == NULL) { jniThrowNullPointerException(env, NULL); @@ -68,84 +71,76 @@ static jint android_content_StringBlock_nativeGetSize(JNIEnv* env, jobject clazz return osb->size(); } -static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject clazz, jlong token, - jint idx) { +static jstring android_content_StringBlock_nativeGetString(JNIEnv* env, jobject clazz, + jlong token, jint idx) +{ ResStringPool* osb = reinterpret_cast<ResStringPool*>(token); if (osb == NULL) { jniThrowNullPointerException(env, NULL); return 0; } - auto str8 = osb->string8At(idx); - if (UNLIKELY(ThrowIfIOError(env, str8))) { - return 0; - } else if (str8.has_value()) { - return env->NewStringUTF(str8->data()); + size_t len; + const char* str8 = osb->string8At(idx, &len); + if (str8 != NULL) { + return env->NewStringUTF(str8); } - auto str = osb->stringAt(idx); - if (UNLIKELY(ThrowIfIOError(env, str))) { - return 0; - } else if (UNLIKELY(!str.has_value())) { + const char16_t* str = osb->stringAt(idx, &len); + if (str == NULL) { jniThrowException(env, "java/lang/IndexOutOfBoundsException", NULL); return 0; } - return env->NewString((const jchar*)str->data(), str->size()); + return env->NewString((const jchar*)str, len); } -static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject clazz, jlong token, - jint idx) { +static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject clazz, + jlong token, jint idx) +{ ResStringPool* osb = reinterpret_cast<ResStringPool*>(token); if (osb == NULL) { jniThrowNullPointerException(env, NULL); return NULL; } - auto spans = osb->styleAt(idx); - if (!spans.has_value()) { - ThrowIfIOError(env, spans); + const ResStringPool_span* spans = osb->styleAt(idx); + if (spans == NULL) { return NULL; } - jintArray array; - { - int num = 0; - auto pos = *spans; - while (true) { - if (UNLIKELY(!pos)) { - jniThrowException(env, kResourcesNotFound, kIOErrorMessage); - return NULL; - } - if (pos->name.index == ResStringPool_span::END) { - break; - } - num++; - pos++; - } - - if (num == 0) { - return NULL; - } - - array = env->NewIntArray((num * sizeof(ResStringPool_span)) / sizeof(jint)); - if (array == NULL) { // NewIntArray already threw OutOfMemoryError. - return NULL; - } + const ResStringPool_span* pos = spans; + int num = 0; + while (pos->name.index != ResStringPool_span::END) { + num++; + pos++; } - { - int num = 0; - static const int numInts = sizeof(ResStringPool_span) / sizeof(jint); - while ((*spans)->name.index != ResStringPool_span::END) { - env->SetIntArrayRegion(array, num * numInts, numInts, (jint*)spans->unsafe_ptr()); - (*spans)++; - num++; - } + + if (num == 0) { + return NULL; } + + jintArray array = env->NewIntArray((num*sizeof(ResStringPool_span))/sizeof(jint)); + if (array == NULL) { // NewIntArray already threw OutOfMemoryError. + return NULL; + } + + num = 0; + static const int numInts = sizeof(ResStringPool_span)/sizeof(jint); + while (spans->name.index != ResStringPool_span::END) { + env->SetIntArrayRegion(array, + num*numInts, numInts, + (jint*)spans); + spans++; + num++; + } + return array; } -static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz, jlong token) { +static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz, + jlong token) +{ ResStringPool* osb = reinterpret_cast<ResStringPool*>(token); if (osb == NULL) { jniThrowNullPointerException(env, NULL); diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 8330363f3c84..a3fcf90b2d46 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -61,9 +61,6 @@ cc_library { ], export_include_dirs: ["include"], export_shared_lib_headers: ["libz"], - static_libs: ["libincfs-utils"], - whole_static_libs: ["libincfs-utils"], - export_static_lib_headers: ["libincfs-utils"], target: { android: { srcs: [ @@ -72,14 +69,13 @@ cc_library { "CursorWindow.cpp", ], shared_libs: [ + "libziparchive", "libbase", "libbinder", "liblog", "libcutils", - "libincfs", "libutils", "libz", - "libziparchive", ], static: { enabled: false, @@ -90,11 +86,11 @@ cc_library { enabled: false, }, static_libs: [ + "libziparchive", "libbase", - "libcutils", "liblog", + "libcutils", "libutils", - "libziparchive", ], shared_libs: [ "libz", diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp index cb56a5172a45..e15b42d46f53 100755 --- a/libs/androidfw/ApkAssets.cpp +++ b/libs/androidfw/ApkAssets.cpp @@ -25,11 +25,13 @@ #include "android-base/unique_fd.h" #include "android-base/utf8.h" #include "utils/Compat.h" +#include "utils/FileMap.h" #include "ziparchive/zip_archive.h" #include "androidfw/Asset.h" #include "androidfw/Idmap.h" #include "androidfw/misc.h" +#include "androidfw/ResourceTypes.h" #include "androidfw/Util.h" namespace android { @@ -159,46 +161,50 @@ class ZipAssetsProvider : public AssetsProvider { } const int fd = ::GetFileDescriptor(zip_handle_.get()); - const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); - incfs::IncFsFileMap asset_map; + const off64_t fd_offset = ::GetFileDescriptorOffset(zip_handle_.get()); if (entry.method == kCompressDeflated) { - if (!asset_map.Create(fd, entry.offset + fd_offset, entry.compressed_length, GetPath())) { + std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); + if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.compressed_length, + true /*readOnly*/)) { LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; return {}; } std::unique_ptr<Asset> asset = - Asset::createFromCompressedMap(std::move(asset_map), entry.uncompressed_length, mode); + Asset::createFromCompressedMap(std::move(map), entry.uncompressed_length, mode); if (asset == nullptr) { LOG(ERROR) << "Failed to decompress '" << path << "' in APK '" << friendly_name_ << "'"; return {}; } return asset; - } + } else { + std::unique_ptr<FileMap> map = util::make_unique<FileMap>(); + if (!map->create(GetPath(), fd, entry.offset + fd_offset, entry.uncompressed_length, + true /*readOnly*/)) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } - if (!asset_map.Create(fd, entry.offset + fd_offset, entry.uncompressed_length, GetPath())) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; - return {}; - } + unique_fd ufd; + if (!GetPath()) { + // If the `path` is not set, create a new `fd` for the new Asset to own in order to create + // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used + // to create new file descriptors. + ufd = unique_fd(dup(fd)); + if (!ufd.ok()) { + LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'"; + return {}; + } + } - unique_fd ufd; - if (!GetPath()) { - // If the `path` is not set, create a new `fd` for the new Asset to own in order to create - // new file descriptors using Asset::openFileDescriptor. If the path is set, it will be used - // to create new file descriptors. - ufd = unique_fd(dup(fd)); - if (!ufd.ok()) { - LOG(ERROR) << "Unable to dup fd '" << path << "' in APK '" << friendly_name_ << "'"; + std::unique_ptr<Asset> asset = Asset::createFromUncompressedMap(std::move(map), + std::move(ufd), mode); + if (asset == nullptr) { + LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; return {}; } + return asset; } - - auto asset = Asset::createFromUncompressedMap(std::move(asset_map), mode, std::move(ufd)); - if (asset == nullptr) { - LOG(ERROR) << "Failed to mmap file '" << path << "' in APK '" << friendly_name_ << "'"; - return {}; - } - return asset; } private: @@ -440,8 +446,8 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd, } } - incfs::IncFsFileMap file_map; - if (!file_map.Create(fd, offset, static_cast<size_t>(length), path)) { + std::unique_ptr<FileMap> file_map = util::make_unique<FileMap>(); + if (!file_map->create(path, fd, offset, static_cast<size_t>(length), true /*readOnly*/)) { LOG(ERROR) << "Failed to mmap file '" << ((path) ? path : "anon") << "': " << SystemErrorCodeToString(errno); return {}; @@ -450,8 +456,8 @@ std::unique_ptr<Asset> ApkAssets::CreateAssetFromFd(base::unique_fd fd, // If `path` is set, do not pass ownership of the `fd` to the new Asset since // Asset::openFileDescriptor can use `path` to create new file descriptors. return Asset::createFromUncompressedMap(std::move(file_map), - Asset::AccessMode::ACCESS_RANDOM, - (path) ? base::unique_fd(-1) : std::move(fd)); + (path) ? base::unique_fd(-1) : std::move(fd), + Asset::AccessMode::ACCESS_RANDOM); } std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( @@ -487,14 +493,15 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadImpl( loaded_apk->idmap_asset_ = std::move(idmap_asset); loaded_apk->loaded_idmap_ = std::move(idmap); - const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */); - const size_t length = loaded_apk->resources_asset_->getLength(); - if (!data || length == 0) { + const StringPiece data( + reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), + loaded_apk->resources_asset_->getLength()); + if (data.data() == nullptr || data.empty()) { LOG(ERROR) << "Failed to read '" << kResourcesArsc << "' data in APK '" << path << "'."; return {}; } - loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, loaded_apk->loaded_idmap_.get(), + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, loaded_apk->loaded_idmap_.get(), property_flags); if (!loaded_apk->loaded_arsc_) { LOG(ERROR) << "Failed to load '" << kResourcesArsc << "' in APK '" << path << "'."; @@ -518,15 +525,15 @@ std::unique_ptr<const ApkAssets> ApkAssets::LoadTableImpl( new ApkAssets(std::move(assets), path, last_mod_time, property_flags)); loaded_apk->resources_asset_ = std::move(resources_asset); - const auto data = loaded_apk->resources_asset_->getIncFsBuffer(true /* aligned */); - const size_t length = loaded_apk->resources_asset_->getLength(); - if (!data || length == 0) { + const StringPiece data( + reinterpret_cast<const char*>(loaded_apk->resources_asset_->getBuffer(true /*wordAligned*/)), + loaded_apk->resources_asset_->getLength()); + if (data.data() == nullptr || data.empty()) { LOG(ERROR) << "Failed to read resources table data in '" << path << "'."; return {}; } - loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, length, nullptr /* loaded_idmap */, - property_flags); + loaded_apk->loaded_arsc_ = LoadedArsc::Load(data, nullptr, property_flags); if (loaded_apk->loaded_arsc_ == nullptr) { LOG(ERROR) << "Failed to read resources table in '" << path << "'."; return {}; @@ -543,6 +550,7 @@ bool ApkAssets::IsUpToDate() const { } return (!loaded_idmap_ || loaded_idmap_->IsUpToDate()) && last_mod_time_ == getFileModDate(path_.c_str()); + } } // namespace android diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 4fbe4a3efbdd..cd30c184d5a4 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -298,18 +298,34 @@ Asset::Asset(void) /* * Create a new Asset from a memory mapping. */ -/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(incfs::IncFsFileMap&& dataMap, - AccessMode mode, - base::unique_fd fd) +/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, AccessMode mode) { - auto pAsset = util::make_unique<_FileAsset>(); + _FileAsset* pAsset; + status_t result; + + pAsset = new _FileAsset; + result = pAsset->openChunk(dataMap, base::unique_fd(-1)); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } + + pAsset->mAccessMode = mode; + return pAsset; +} - status_t result = pAsset->openChunk(std::move(dataMap), std::move(fd)); +/*static*/ std::unique_ptr<Asset> Asset::createFromUncompressedMap(std::unique_ptr<FileMap> dataMap, + base::unique_fd fd, AccessMode mode) +{ + std::unique_ptr<_FileAsset> pAsset = util::make_unique<_FileAsset>(); + + status_t result = pAsset->openChunk(dataMap.get(), std::move(fd)); if (result != NO_ERROR) { return NULL; } // We succeeded, so relinquish control of dataMap + (void) dataMap.release(); pAsset->mAccessMode = mode; return std::move(pAsset); } @@ -317,18 +333,35 @@ Asset::Asset(void) /* * Create a new Asset from compressed data in a memory mapping. */ -/*static*/ std::unique_ptr<Asset> Asset::createFromCompressedMap(incfs::IncFsFileMap&& dataMap, - size_t uncompressedLen, - AccessMode mode) +/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, + size_t uncompressedLen, AccessMode mode) { - auto pAsset = util::make_unique<_CompressedAsset>(); + _CompressedAsset* pAsset; + status_t result; + + pAsset = new _CompressedAsset; + result = pAsset->openChunk(dataMap, uncompressedLen); + if (result != NO_ERROR) { + delete pAsset; + return NULL; + } - status_t result = pAsset->openChunk(std::move(dataMap), uncompressedLen); + pAsset->mAccessMode = mode; + 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); } @@ -381,7 +414,7 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m * Constructor. */ _FileAsset::_FileAsset(void) - : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mBuf(NULL) + : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mFd(-1), mMap(NULL), mBuf(NULL) { // Register the Asset with the global list here after it is fully constructed and its // vtable pointer points to this concrete type. b/31113965 @@ -408,7 +441,7 @@ _FileAsset::~_FileAsset(void) status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length) { assert(mFp == NULL); // no reopen - assert(!mMap.has_value()); + assert(mMap == NULL); assert(fd >= 0); assert(offset >= 0); @@ -451,15 +484,15 @@ status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, siz /* * Create the chunk from the map. */ -status_t _FileAsset::openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd) +status_t _FileAsset::openChunk(FileMap* dataMap, base::unique_fd fd) { assert(mFp == NULL); // no reopen - assert(!mMap.has_value()); + assert(mMap == NULL); assert(dataMap != NULL); - mMap = std::move(dataMap); + mMap = dataMap; mStart = -1; // not used - mLength = mMap->length(); + mLength = dataMap->getDataLength(); mFd = std::move(fd); assert(mOffset == 0); @@ -495,15 +528,10 @@ ssize_t _FileAsset::read(void* buf, size_t count) if (!count) return 0; - if (mMap.has_value()) { + if (mMap != NULL) { /* copy from mapped area */ //printf("map read\n"); - const auto readPos = mMap->data().offset(mOffset).convert<char>(); - if (!readPos.verify(count)) { - return -1; - } - - memcpy(buf, readPos.unsafe_ptr(), count); + memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); actual = count; } else if (mBuf != NULL) { /* copy from buffer */ @@ -566,6 +594,10 @@ off64_t _FileAsset::seek(off64_t offset, int whence) */ void _FileAsset::close(void) { + if (mMap != NULL) { + delete mMap; + mMap = NULL; + } if (mBuf != NULL) { delete[] mBuf; mBuf = NULL; @@ -592,21 +624,16 @@ void _FileAsset::close(void) * level and we'd be using a different object, but we didn't, so we * deal with it here. */ -const void* _FileAsset::getBuffer(bool aligned) -{ - return getIncFsBuffer(aligned).unsafe_ptr(); -} - -incfs::map_ptr<void> _FileAsset::getIncFsBuffer(bool aligned) +const void* _FileAsset::getBuffer(bool wordAligned) { /* subsequent requests just use what we did previously */ if (mBuf != NULL) return mBuf; - if (mMap.has_value()) { - if (!aligned) { - return mMap->data(); + if (mMap != NULL) { + if (!wordAligned) { + return mMap->getDataPtr(); } - return ensureAlignment(*mMap); + return ensureAlignment(mMap); } assert(mFp != NULL); @@ -644,44 +671,47 @@ incfs::map_ptr<void> _FileAsset::getIncFsBuffer(bool aligned) mBuf = buf; return mBuf; } else { - incfs::IncFsFileMap map; - if (!map.Create(fileno(mFp), mStart, mLength, NULL /* file_name */ )) { + FileMap* map; + + map = new FileMap; + if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { + delete map; return NULL; } ALOGV(" getBuffer: mapped\n"); - mMap = std::move(map); - if (!aligned) { - return mMap->data(); + mMap = map; + if (!wordAligned) { + return mMap->getDataPtr(); } - return ensureAlignment(*mMap); + return ensureAlignment(mMap); } } int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const { - if (mMap.has_value()) { + if (mMap != NULL) { if (mFd.ok()) { - *outStart = mMap->offset(); - *outLength = mMap->length(); - const int fd = dup(mFd); - if (fd < 0) { - ALOGE("Unable to dup fd (%d).", mFd.get()); - return -1; - } - lseek64(fd, 0, SEEK_SET); - return fd; + *outStart = mMap->getDataOffset(); + *outLength = mMap->getDataLength(); + const int fd = dup(mFd); + if (fd < 0) { + ALOGE("Unable to dup fd (%d).", mFd.get()); + return -1; + } + lseek64(fd, 0, SEEK_SET); + return fd; } - const char* fname = mMap->file_name(); + const char* fname = mMap->getFileName(); if (fname == NULL) { fname = mFileName; } if (fname == NULL) { return -1; } - *outStart = mMap->offset(); - *outLength = mMap->length(); + *outStart = mMap->getDataOffset(); + *outLength = mMap->getDataLength(); return open(fname, O_RDONLY | O_BINARY); } if (mFileName == NULL) { @@ -692,21 +722,16 @@ int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const return open(mFileName, O_RDONLY | O_BINARY); } -incfs::map_ptr<void> _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map) +const void* _FileAsset::ensureAlignment(FileMap* map) { - const auto data = map.data(); - if (util::IsFourByteAligned(data)) { + void* data = map->getDataPtr(); + if ((((size_t)data)&0x3) == 0) { // We can return this directly if it is aligned on a word // boundary. ALOGV("Returning aligned FileAsset %p (%s).", this, getAssetSource()); return data; } - - if (!data.convert<uint8_t>().verify(mLength)) { - return NULL; - } - // If not aligned on a word boundary, then we need to copy it into // our own buffer. ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this, @@ -716,8 +741,7 @@ incfs::map_ptr<void> _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map) ALOGE("alloc of %ld bytes failed\n", (long) mLength); return NULL; } - - memcpy(buf, data.unsafe_ptr(), mLength); + memcpy(buf, data, mLength); mBuf = buf; return buf; } @@ -733,7 +757,7 @@ incfs::map_ptr<void> _FileAsset::ensureAlignment(const incfs::IncFsFileMap& map) */ _CompressedAsset::_CompressedAsset(void) : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), - mFd(-1), mZipInflater(NULL), mBuf(NULL) + mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL) { // Register the Asset with the global list here after it is fully constructed and its // vtable pointer points to this concrete type. b/31113965 @@ -762,7 +786,7 @@ status_t _CompressedAsset::openChunk(int fd, off64_t offset, int compressionMethod, size_t uncompressedLen, size_t compressedLen) { assert(mFd < 0); // no re-open - assert(!mMap.has_value()); + assert(mMap == NULL); assert(fd >= 0); assert(offset >= 0); assert(compressedLen > 0); @@ -791,20 +815,20 @@ status_t _CompressedAsset::openChunk(int fd, off64_t offset, * * Nothing is expanded until the first read call. */ -status_t _CompressedAsset::openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen) +status_t _CompressedAsset::openChunk(FileMap* dataMap, size_t uncompressedLen) { assert(mFd < 0); // no re-open - assert(!mMap.has_value()); + assert(mMap == NULL); assert(dataMap != NULL); - mMap = std::move(dataMap); + mMap = dataMap; mStart = -1; // not used - mCompressedLen = mMap->length(); + mCompressedLen = dataMap->getDataLength(); mUncompressedLen = uncompressedLen; assert(mOffset == 0); if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { - mZipInflater = new StreamingZipInflater(&(*mMap), uncompressedLen); + mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen); } return NO_ERROR; } @@ -877,6 +901,11 @@ off64_t _CompressedAsset::seek(off64_t offset, int whence) */ void _CompressedAsset::close(void) { + if (mMap != NULL) { + delete mMap; + mMap = NULL; + } + delete[] mBuf; mBuf = NULL; @@ -911,8 +940,8 @@ const void* _CompressedAsset::getBuffer(bool) goto bail; } - if (mMap.has_value()) { - if (!ZipUtils::inflateToBuffer(mMap->data(), buf, + if (mMap != NULL) { + if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf, mUncompressedLen, mCompressedLen)) goto bail; } else { @@ -947,6 +976,3 @@ bail: return mBuf; } -incfs::map_ptr<void> _CompressedAsset::getIncFsBuffer(bool aligned) { - return incfs::map_ptr<void>(getBuffer(aligned)); -} diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index fb2b57193b83..f7c83371f79c 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -917,7 +917,7 @@ Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, const ZipEntryRO entry, AccessMode mode, const String8& entryName) { - std::unique_ptr<Asset> pAsset; + Asset* pAsset = NULL; // TODO: look for previously-created shared memory slice? uint16_t method; @@ -932,28 +932,28 @@ Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, return NULL; } - std::optional<incfs::IncFsFileMap> dataMap = pZipFile->createEntryIncFsFileMap(entry); - if (!dataMap.has_value()) { + FileMap* dataMap = pZipFile->createEntryFileMap(entry); + if (dataMap == NULL) { ALOGW("create map from entry failed\n"); return NULL; } if (method == ZipFileRO::kCompressStored) { - pAsset = Asset::createFromUncompressedMap(std::move(*dataMap), mode); + pAsset = Asset::createFromUncompressedMap(dataMap, mode); ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->file_name(), mode, pAsset.get()); + dataMap->getFileName(), mode, pAsset); } else { - pAsset = Asset::createFromCompressedMap(std::move(*dataMap), + pAsset = Asset::createFromCompressedMap(dataMap, static_cast<size_t>(uncompressedLen), mode); ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->file_name(), mode, pAsset.get()); + dataMap->getFileName(), mode, pAsset); } if (pAsset == NULL) { /* unexpected */ ALOGW("create from segment failed\n"); } - return pAsset.release(); + return pAsset; } /* diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index c49dc28124b2..99dd3134ff8a 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -38,43 +38,9 @@ namespace android { -namespace { - -using EntryValue = std::variant<Res_value, incfs::verified_map_ptr<ResTable_map_entry>>; - -base::expected<EntryValue, IOError> GetEntryValue( - incfs::verified_map_ptr<ResTable_entry> table_entry) { - const uint16_t entry_size = dtohs(table_entry->size); - - // Check if the entry represents a bag value. - if (entry_size >= sizeof(ResTable_map_entry) && - (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX)) { - const auto map_entry = table_entry.convert<ResTable_map_entry>(); - if (!map_entry) { - return base::unexpected(IOError::PAGES_MISSING); - } - return map_entry.verified(); - } - - // The entry represents a non-bag value. - const auto entry_value = table_entry.offset(entry_size).convert<Res_value>(); - if (!entry_value) { - return base::unexpected(IOError::PAGES_MISSING); - } - Res_value value; - value.copyFrom_dtoh(entry_value.value()); - return value; -} - -} // namespace - struct FindEntryResult { - // The cookie representing the ApkAssets in which the value resides. - ApkAssetsCookie cookie; - - // The value of the resource table entry. Either an android::Res_value for non-bag types or an - // incfs::verified_map_ptr<ResTable_map_entry> for bag types. - EntryValue entry; + // A pointer to the value of the resource table entry. + std::variant<Res_value, const ResTable_map_entry*> entry; // The configuration for which the resulting entry was defined. This is already swapped to host // endianness. @@ -299,7 +265,7 @@ const std::unordered_map<std::string, std::string>* } const PackageGroup& package_group = package_groups_[idx]; - if (package_group.packages_.empty()) { + if (package_group.packages_.size() == 0) { return nullptr; } @@ -344,14 +310,14 @@ bool AssetManager2::GetOverlayablesToString(const android::StringPiece& package_ for (auto it = loaded_package->begin(); it != loaded_package->end(); it++) { const OverlayableInfo* info = loaded_package->GetOverlayableInfo(*it); if (info != nullptr) { - auto res_name = GetResourceName(*it); - if (!res_name.has_value()) { + ResourceName res_name; + if (!GetResourceName(*it, &res_name)) { ANDROID_LOG(ERROR) << base::StringPrintf( "Unable to retrieve name of overlayable resource 0x%08x", *it); return false; } - const std::string name = ToFormattedResourceString(*res_name); + const std::string name = ToFormattedResourceString(&res_name); output.append(base::StringPrintf( "resource='%s' overlayable='%s' actor='%s' policy='0x%08x'\n", name.c_str(), info->name.c_str(), info->actor.c_str(), info->policy_flags)); @@ -399,8 +365,8 @@ std::set<std::string> AssetManager2::GetNonSystemOverlayPaths() const { return non_system_overlays; } -base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceConfigurations( - bool exclude_system, bool exclude_mipmap) const { +std::set<ResTable_config> AssetManager2::GetResourceConfigurations(bool exclude_system, + bool exclude_mipmap) const { ATRACE_NAME("AssetManager::GetResourceConfigurations"); const auto non_system_overlays = (exclude_system) ? GetNonSystemOverlayPaths() : std::set<std::string>(); @@ -420,10 +386,7 @@ base::expected<std::set<ResTable_config>, IOError> AssetManager2::GetResourceCon continue; } - auto result = package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); - if (UNLIKELY(!result.has_value())) { - return base::unexpected(result.error()); - } + package.loaded_package_->CollectConfigurations(exclude_mipmap, &configurations); } } return configurations; @@ -538,11 +501,11 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, return apk_assets_[cookie]->GetAssetsProvider()->Open(filename, mode); } -base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( - uint32_t resid, uint16_t density_override, bool stop_at_first_match, - bool ignore_configuration) const { - const bool logging_enabled = resource_resolution_logging_enabled_; - if (UNLIKELY(logging_enabled)) { +ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, + bool /*stop_at_first_match*/, + bool ignore_configuration, + FindEntryResult* out_entry) const { + if (resource_resolution_logging_enabled_) { // Clear the last logged resource resolution. ResetResourceResolution(); last_resolution_.resid = resid; @@ -560,96 +523,94 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( } // Retrieve the package group from the package id of the resource id. - if (UNLIKELY(!is_valid_resid(resid))) { + if (!is_valid_resid(resid)) { LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid); - return base::unexpected(std::nullopt); + 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_idx = get_entry_id(resid); uint8_t package_idx = package_ids_[package_id]; - if (UNLIKELY(package_idx == 0xff)) { + if (package_idx == 0xff) { ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); - return base::unexpected(std::nullopt); + return kInvalidCookie; } const PackageGroup& package_group = package_groups_[package_idx]; - auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, - stop_at_first_match, ignore_configuration); - if (UNLIKELY(!result.has_value())) { - return base::unexpected(result.error()); + ApkAssetsCookie cookie = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, + false /* stop_at_first_match */, + ignore_configuration, out_entry); + if (UNLIKELY(cookie == kInvalidCookie)) { + return kInvalidCookie; } - if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) { + if (!apk_assets_[cookie]->IsLoader()) { for (const auto& id_map : package_group.overlays_) { auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid); if (!overlay_entry) { // No id map entry exists for this target resource. continue; - } - if (overlay_entry.IsInlineValue()) { + } else if (overlay_entry.IsInlineValue()) { // The target resource is overlaid by an inline value not represented by a resource. - result->entry = overlay_entry.GetInlineValue(); - result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); - result->cookie = id_map.cookie; + out_entry->entry = overlay_entry.GetInlineValue(); + out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + cookie = id_map.cookie; continue; } - auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override, - false /* stop_at_first_match */, - false /* ignore_configuration */); - if (UNLIKELY(IsIOError(overlay_result))) { - return base::unexpected(overlay_result.error()); - } - if (!overlay_result.has_value()) { + FindEntryResult overlay_result; + ApkAssetsCookie overlay_cookie = FindEntry(overlay_entry.GetResourceId(), density_override, + false /* stop_at_first_match */, + ignore_configuration, &overlay_result); + if (UNLIKELY(overlay_cookie == kInvalidCookie)) { continue; } - if (!overlay_result->config.isBetterThan(result->config, desired_config) - && overlay_result->config.compare(result->config) != 0) { + if (!overlay_result.config.isBetterThan(out_entry->config, desired_config) + && overlay_result.config.compare(out_entry->config) != 0) { // The configuration of the entry for the overlay must be equal to or better than the target // configuration to be chosen as the better value. continue; } - result->cookie = overlay_result->cookie; - result->entry = overlay_result->entry; - result->config = overlay_result->config; - result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); - - if (UNLIKELY(logging_enabled)) { + cookie = overlay_cookie; + out_entry->entry = overlay_result.entry; + out_entry->config = overlay_result.config; + out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + if (resource_resolution_logging_enabled_) { last_resolution_.steps.push_back( - Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->config.toString(), - overlay_result->package_name}); + Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result.config.toString(), + overlay_result.package_name}); } } } - if (UNLIKELY(logging_enabled)) { - last_resolution_.cookie = result->cookie; - last_resolution_.type_string_ref = result->type_string_ref; - last_resolution_.entry_string_ref = result->entry_string_ref; + if (resource_resolution_logging_enabled_) { + last_resolution_.cookie = cookie; + last_resolution_.type_string_ref = out_entry->type_string_ref; + last_resolution_.entry_string_ref = out_entry->entry_string_ref; } - return result; + return cookie; } -base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( - const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx, - const ResTable_config& desired_config, bool stop_at_first_match, - bool ignore_configuration) const { - const bool logging_enabled = resource_resolution_logging_enabled_; +ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_group, + uint8_t type_idx, uint16_t entry_idx, + const ResTable_config& desired_config, + bool /*stop_at_first_match*/, + bool ignore_configuration, + FindEntryResult* out_entry) const { ApkAssetsCookie best_cookie = kInvalidCookie; const LoadedPackage* best_package = nullptr; - incfs::verified_map_ptr<ResTable_type> best_type; + const ResTable_type* best_type = nullptr; const ResTable_config* best_config = nullptr; ResTable_config best_config_copy; - uint32_t best_offset = 0U; - uint32_t type_flags = 0U; + uint32_t best_offset = 0u; + uint32_t type_flags = 0u; - auto resolution_type = Resolution::Step::Type::NO_ENTRY; + Resolution::Step::Type resolution_type = Resolution::Step::Type::NO_ENTRY; std::vector<Resolution::Step> resolution_steps; // If desired_config is the same as the set configuration, then we can use our filtered list @@ -669,20 +630,17 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( continue; } - auto entry_flags = type_spec->GetFlagsForEntryIndex(entry_idx); - if (UNLIKELY(!entry_flags)) { - return base::unexpected(entry_flags.error()); - } - type_flags |= entry_flags.value(); - // If the package is an overlay or custom loader, // then even configurations that are the same MUST be chosen. const bool package_is_loader = loaded_package->IsCustomLoader(); + type_flags |= type_spec->GetFlagsForEntryIndex(entry_idx); if (use_fast_path) { const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx]; - for (const auto& type_config : filtered_group.type_configs) { - const ResTable_config& this_config = type_config.config; + 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++) { + const ResTable_config& this_config = candidate_configs[i]; // We can skip calling ResTable_config::match() because we know that all candidate // configurations that do NOT match have been filtered-out. @@ -694,7 +652,7 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( } else if (package_is_loader && this_config.compare(*best_config) == 0) { resolution_type = Resolution::Step::Type::OVERLAID_LOADER; } else { - if (UNLIKELY(logging_enabled)) { + if (resource_resolution_logging_enabled_) { resolution_type = (package_is_loader) ? Resolution::Step::Type::SKIPPED_LOADER : Resolution::Step::Type::SKIPPED; resolution_steps.push_back(Resolution::Step{resolution_type, @@ -706,13 +664,10 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. - const auto& type = type_config.type; - const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx); - if (UNLIKELY(IsIOError(offset))) { - return base::unexpected(offset.error()); - } - if (!offset.has_value()) { - if (UNLIKELY(logging_enabled)) { + const ResTable_type* type = filtered_group.types[i]; + const uint32_t offset = LoadedPackage::GetEntryOffset(type, entry_idx); + if (offset == ResTable_type::NO_ENTRY) { + if (resource_resolution_logging_enabled_) { if (package_is_loader) { resolution_type = Resolution::Step::Type::NO_ENTRY_LOADER; } else { @@ -729,9 +684,9 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( best_package = loaded_package; best_type = type; best_config = &this_config; - best_offset = offset.value(); + best_offset = offset; - if (UNLIKELY(logging_enabled)) { + if (resource_resolution_logging_enabled_) { last_resolution_.steps.push_back(Resolution::Step{resolution_type, this_config.toString(), &loaded_package->GetPackageName()}); @@ -745,11 +700,10 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( // ResTable_config, we must copy it. const auto iter_end = type_spec->types + type_spec->type_count; for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const incfs::verified_map_ptr<ResTable_type>& type = *iter; - ResTable_config this_config{}; + if (!ignore_configuration) { - this_config.copyFromDtoH(type->config); + this_config.copyFromDtoH((*iter)->config); if (!this_config.match(desired_config)) { continue; } @@ -768,27 +722,24 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. - const auto offset = LoadedPackage::GetEntryOffset(type, entry_idx); - if (UNLIKELY(IsIOError(offset))) { - return base::unexpected(offset.error()); - } - if (!offset.has_value()) { + const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, entry_idx); + if (offset == ResTable_type::NO_ENTRY) { continue; } best_cookie = cookie; best_package = loaded_package; - best_type = type; + best_type = *iter; best_config_copy = this_config; best_config = &best_config_copy; - best_offset = offset.value(); + best_offset = offset; - if (stop_at_first_match) { + if (ignore_configuration) { // Any configuration will suffice, so break. break; } - if (UNLIKELY(logging_enabled)) { + if (resource_resolution_logging_enabled_) { last_resolution_.steps.push_back(Resolution::Step{resolution_type, this_config.toString(), &loaded_package->GetPackageName()}); @@ -798,35 +749,36 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( } if (UNLIKELY(best_cookie == kInvalidCookie)) { - return base::unexpected(std::nullopt); + return kInvalidCookie; } - auto best_entry_result = LoadedPackage::GetEntryFromOffset(best_type, best_offset); - if (!best_entry_result.has_value()) { - return base::unexpected(best_entry_result.error()); + const ResTable_entry* best_entry = LoadedPackage::GetEntryFromOffset(best_type, best_offset); + if (UNLIKELY(best_entry == nullptr)) { + return kInvalidCookie; } - const incfs::map_ptr<ResTable_entry> best_entry = *best_entry_result; - if (!best_entry) { - return base::unexpected(IOError::PAGES_MISSING); - } - - const auto entry = GetEntryValue(best_entry.verified()); - if (!entry.has_value()) { - return base::unexpected(entry.error()); - } - - return FindEntryResult{ - .cookie = best_cookie, - .entry = *entry, - .config = *best_config, - .type_flags = type_flags, - .package_name = &best_package->GetPackageName(), - .type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1), - .entry_string_ref = StringPoolRef(best_package->GetKeyStringPool(), - best_entry->key.index), - .dynamic_ref_table = package_group.dynamic_ref_table.get(), - }; + const uint16_t entry_size = dtohs(best_entry->size); + if (entry_size >= sizeof(ResTable_map_entry) && + (dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) { + // The entry represents a bag/map. + out_entry->entry = reinterpret_cast<const ResTable_map_entry*>(best_entry); + } else { + // The entry represents a value. + Res_value value; + value.copyFrom_dtoh(*reinterpret_cast<const Res_value*>( + reinterpret_cast<const uint8_t*>(best_entry) + entry_size)); + out_entry->entry = value; + } + + out_entry->config = *best_config; + out_entry->type_flags = type_flags; + out_entry->package_name = &best_package->GetPackageName(); + out_entry->type_string_ref = StringPoolRef(best_package->GetTypeStringPool(), best_type->id - 1); + out_entry->entry_string_ref = + StringPoolRef(best_package->GetKeyStringPool(), best_entry->key.index); + out_entry->dynamic_ref_table = package_group.dynamic_ref_table.get(); + + return best_cookie; } void AssetManager2::ResetResourceResolution() const { @@ -847,28 +799,30 @@ void AssetManager2::SetResourceResolutionLoggingEnabled(bool enabled) { std::string AssetManager2::GetLastResourceResolution() const { if (!resource_resolution_logging_enabled_) { LOG(ERROR) << "Must enable resource resolution logging before getting path."; - return {}; + return std::string(); } auto cookie = last_resolution_.cookie; if (cookie == kInvalidCookie) { LOG(ERROR) << "AssetManager hasn't resolved a resource to read resolution path."; - return {}; + 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) { - auto resource_name = ToResourceName(last_resolution_.type_string_ref, - last_resolution_.entry_string_ref, - package->GetPackageName()); - resource_name_string = resource_name.has_value() ? - ToFormattedResourceString(resource_name.value()) : "<unknown>"; + ToResourceName(last_resolution_.type_string_ref, + last_resolution_.entry_string_ref, + package->GetPackageName(), + &resource_name); + resource_name_string = ToFormattedResourceString(&resource_name); } std::stringstream log_stream; @@ -921,201 +875,200 @@ std::string AssetManager2::GetLastResourceResolution() const { return log_stream.str(); } -base::expected<AssetManager2::ResourceName, NullOrIOError> AssetManager2::GetResourceName( - uint32_t resid) const { - auto result = FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, - true /* ignore_configuration */); - if (!result.has_value()) { - return base::unexpected(result.error()); +bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { + FindEntryResult entry; + ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, + true /* stop_at_first_match */, + true /* ignore_configuration */, &entry); + if (cookie == kInvalidCookie) { + return false; } - return ToResourceName(result->type_string_ref, - result->entry_string_ref, - *result->package_name); + return ToResourceName(entry.type_string_ref, + entry.entry_string_ref, + *entry.package_name, + out_name); } -base::expected<AssetManager2::SelectedValue, NullOrIOError> AssetManager2::GetResource( - uint32_t resid, bool may_be_bag, uint16_t density_override) const { - auto result = FindEntry(resid, density_override, false /* stop_at_first_match */, - false /* ignore_configuration */); - if (!result.has_value()) { - return base::unexpected(result.error()); +bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { + FindEntryResult entry; + ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, + false /* stop_at_first_match */, + true /* ignore_configuration */, &entry); + if (cookie != kInvalidCookie) { + *out_flags = entry.type_flags; + return true; } + return false; +} - auto result_map_entry = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&result->entry); +ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, + uint16_t density_override, Res_value* out_value, + ResTable_config* out_selected_config, + uint32_t* out_flags) const { + FindEntryResult entry; + ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */, + false /* ignore_configuration */, &entry); + if (cookie == kInvalidCookie) { + return kInvalidCookie; + } + + auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry); if (result_map_entry != nullptr) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); - return base::unexpected(std::nullopt); + return kInvalidCookie; } // Create a reference since we can't represent this complex type as a Res_value. - return SelectedValue(Res_value::TYPE_REFERENCE, resid, result->cookie, result->type_flags, - resid, result->config); + out_value->dataType = Res_value::TYPE_REFERENCE; + out_value->data = resid; + *out_selected_config = entry.config; + *out_flags = entry.type_flags; + return cookie; } // Convert the package ID to the runtime assigned package ID. - Res_value value = std::get<Res_value>(result->entry); - result->dynamic_ref_table->lookupResourceValue(&value); + *out_value = std::get<Res_value>(entry.entry); + entry.dynamic_ref_table->lookupResourceValue(out_value); - return SelectedValue(value.dataType, value.data, result->cookie, result->type_flags, - resid, result->config); + *out_selected_config = entry.config; + *out_flags = entry.type_flags; + return cookie; } -base::expected<std::monostate, NullOrIOError> AssetManager2::ResolveReference( - AssetManager2::SelectedValue& value, bool cache_value) const { - if (value.type != Res_value::TYPE_REFERENCE || value.data == 0U) { - // Not a reference. Nothing to do. - return {}; - } - - const uint32_t original_flags = value.flags; - const uint32_t original_resid = value.data; - if (cache_value) { - auto cached_value = cached_resolved_values_.find(value.data); - if (cached_value != cached_resolved_values_.end()) { - value = cached_value->second; - value.flags |= original_flags; - return {}; +ApkAssetsCookie AssetManager2::ResolveReference(ApkAssetsCookie cookie, Res_value* in_out_value, + ResTable_config* in_out_selected_config, + uint32_t* in_out_flags, + uint32_t* out_last_reference) const { + constexpr const int kMaxIterations = 20; + + for (size_t iteration = 0u; in_out_value->dataType == Res_value::TYPE_REFERENCE && + in_out_value->data != 0u && iteration < kMaxIterations; + iteration++) { + *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*/, + in_out_value, in_out_selected_config, &new_flags); + if (cookie == kInvalidCookie) { + return kInvalidCookie; } - } - - uint32_t combined_flags = 0U; - uint32_t resolve_resid = original_resid; - constexpr const uint32_t kMaxIterations = 20; - for (uint32_t i = 0U;; i++) { - auto result = GetResource(resolve_resid, true /*may_be_bag*/); - if (!result.has_value()) { - return base::unexpected(result.error()); + if (in_out_flags != nullptr) { + *in_out_flags |= new_flags; } - - if (result->type != Res_value::TYPE_REFERENCE || - result->data == Res_value::DATA_NULL_UNDEFINED || - result->data == resolve_resid || i == kMaxIterations) { - result->flags |= combined_flags; - if (cache_value) { - cached_resolved_values_[original_resid] = *result; - } - - // Add the original flags after caching the result so queries with a different set of original - // flags do not include these original flags. - value = *result; - value.flags |= original_flags; - return {}; + 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; } - - combined_flags |= result->flags; - resolve_resid = result->data; } + return cookie; } -const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) const { +const std::vector<uint32_t> AssetManager2::GetBagResIdStack(uint32_t resid) { auto cached_iter = cached_bag_resid_stacks_.find(resid); if (cached_iter != cached_bag_resid_stacks_.end()) { return cached_iter->second; + } else { + auto found_resids = std::vector<uint32_t>(); + GetBag(resid, found_resids); + // Cache style stacks if they are not already cached. + cached_bag_resid_stacks_[resid] = found_resids; + return found_resids; } - - std::vector<uint32_t> found_resids; - GetBag(resid, found_resids); - cached_bag_resid_stacks_.emplace(resid, found_resids); - return found_resids; } -base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::ResolveBag( - AssetManager2::SelectedValue& value) const { - if (UNLIKELY(value.type != Res_value::TYPE_REFERENCE)) { - return base::unexpected(std::nullopt); - } +const ResolvedBag* AssetManager2::GetBag(uint32_t resid) { + auto found_resids = std::vector<uint32_t>(); + auto bag = GetBag(resid, found_resids); - auto bag = GetBag(value.data); - if (bag.has_value()) { - value.flags |= (*bag)->type_spec_flags; + // Cache style stacks if they are not already cached. + auto cached_iter = cached_bag_resid_stacks_.find(resid); + if (cached_iter == cached_bag_resid_stacks_.end()) { + cached_bag_resid_stacks_[resid] = found_resids; } return bag; } -base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag(uint32_t resid) const { - std::vector<uint32_t> found_resids; - return GetBag(resid, found_resids); +static bool compare_bag_entries(const ResolvedBag::Entry& entry1, + const ResolvedBag::Entry& entry2) { + return entry1.key < entry2.key; } -base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag( - uint32_t resid, std::vector<uint32_t>& child_resids) const { - if (auto cached_iter = cached_bags_.find(resid); cached_iter != cached_bags_.end()) { +const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& child_resids) { + auto cached_iter = cached_bags_.find(resid); + if (cached_iter != cached_bags_.end()) { return cached_iter->second.get(); } - auto entry = FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, - false /* ignore_configuration */); - if (!entry.has_value()) { - return base::unexpected(entry.error()); + FindEntryResult entry; + ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, + false /* stop_at_first_match */, + false /* ignore_configuration */, + &entry); + if (cookie == kInvalidCookie) { + return nullptr; } - auto entry_map = std::get_if<incfs::verified_map_ptr<ResTable_map_entry>>(&entry->entry); - if (entry_map == nullptr) { + auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry); + if (result_map_entry == nullptr) { // Not a bag, nothing to do. - return base::unexpected(std::nullopt); + return nullptr; } - auto map = *entry_map; - auto map_entry = map.offset(dtohs(map->size)).convert<ResTable_map>(); - const auto map_entry_end = map_entry + dtohl(map->count); + auto map = reinterpret_cast<const ResTable_map_entry*>(*result_map_entry); + auto map_entry = reinterpret_cast<const ResTable_map*>( + reinterpret_cast<const uint8_t*>(map) + map->size); + const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); // Keep track of ids that have already been seen to prevent infinite loops caused by circular - // dependencies between bags. + // dependencies between bags child_resids.push_back(resid); uint32_t parent_resid = dtohl(map->parent.ident); - if (parent_resid == 0U || - std::find(child_resids.begin(), child_resids.end(), parent_resid) != child_resids.end()) { - // There is no parent or a circular parental dependency exist, meaning there is nothing to - // inherit and we can do a simple copy of the entries in the map. + if (parent_resid == 0U || std::find(child_resids.begin(), child_resids.end(), parent_resid) + != child_resids.end()) { + // There is no parent or a circular dependency exist, meaning there is nothing to inherit and + // we can do a simple copy of the entries in the map. const size_t entry_count = map_entry_end - map_entry; util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>( malloc(sizeof(ResolvedBag) + (entry_count * sizeof(ResolvedBag::Entry))))}; bool sort_entries = false; - for (auto new_entry = new_bag->entries; map_entry != map_entry_end; ++map_entry) { - if (UNLIKELY(!map_entry)) { - return base::unexpected(IOError::PAGES_MISSING); - } - + ResolvedBag::Entry* new_entry = new_bag->entries; + for (; map_entry != map_entry_end; ++map_entry) { uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { // Attributes, arrays, etc don't have a resource id as the name. They specify // other data, which would be wrong to change via a lookup. - if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) { + if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); - return base::unexpected(std::nullopt); + return nullptr; } } - - new_entry->cookie = entry->cookie; + new_entry->cookie = cookie; new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; new_entry->style = resid; new_entry->value.copyFrom_dtoh(map_entry->value); - status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value); - if (UNLIKELY(err != NO_ERROR)) { + status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (err != NO_ERROR) { LOG(ERROR) << base::StringPrintf( "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, new_entry->value.data, new_key); - return base::unexpected(std::nullopt); + return nullptr; } - sort_entries = sort_entries || (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); ++new_entry; } if (sort_entries) { - std::sort(new_bag->entries, new_bag->entries + entry_count, - [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; }); + std::sort(new_bag->entries, new_bag->entries + entry_count, compare_bag_entries); } - new_bag->type_spec_flags = entry->type_flags; + new_bag->type_spec_flags = entry.type_flags; new_bag->entry_count = static_cast<uint32_t>(entry_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -1123,58 +1076,54 @@ base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag( } // In case the parent is a dynamic reference, resolve it. - entry->dynamic_ref_table->lookupResourceId(&parent_resid); + entry.dynamic_ref_table->lookupResourceId(&parent_resid); // Get the parent and do a merge of the keys. - const auto parent_bag = GetBag(parent_resid, child_resids); - if (UNLIKELY(!parent_bag.has_value())) { + const ResolvedBag* parent_bag = GetBag(parent_resid, child_resids); + if (parent_bag == nullptr) { // Failed to get the parent that should exist. LOG(ERROR) << base::StringPrintf("Failed to find parent 0x%08x of bag 0x%08x.", parent_resid, resid); - return base::unexpected(parent_bag.error()); + return nullptr; } // Create the max possible entries we can make. Once we construct the bag, // we will realloc to fit to size. - const size_t max_count = (*parent_bag)->entry_count + dtohl(map->count); + const size_t max_count = parent_bag->entry_count + dtohl(map->count); util::unique_cptr<ResolvedBag> new_bag{reinterpret_cast<ResolvedBag*>( malloc(sizeof(ResolvedBag) + (max_count * sizeof(ResolvedBag::Entry))))}; ResolvedBag::Entry* new_entry = new_bag->entries; - const ResolvedBag::Entry* parent_entry = (*parent_bag)->entries; - const ResolvedBag::Entry* const parent_entry_end = parent_entry + (*parent_bag)->entry_count; + const ResolvedBag::Entry* parent_entry = parent_bag->entries; + const ResolvedBag::Entry* const parent_entry_end = parent_entry + parent_bag->entry_count; // The keys are expected to be in sorted order. Merge the two bags. bool sort_entries = false; while (map_entry != map_entry_end && parent_entry != parent_entry_end) { - if (UNLIKELY(!map_entry)) { - return base::unexpected(IOError::PAGES_MISSING); - } - uint32_t child_key = dtohl(map_entry->name.ident); if (!is_internal_resid(child_key)) { - if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR)) { + if (entry.dynamic_ref_table->lookupResourceId(&child_key) != NO_ERROR) { LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", child_key, resid); - return base::unexpected(std::nullopt); + return nullptr; } } if (child_key <= parent_entry->key) { // Use the child key if it comes before the parent // or is equal to the parent (overrides). - new_entry->cookie = entry->cookie; + new_entry->cookie = cookie; new_entry->key = child_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->style = resid; - status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value); - if (UNLIKELY(err != NO_ERROR)) { + status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (err != NO_ERROR) { LOG(ERROR) << base::StringPrintf( "Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, new_entry->value.data, child_key); - return base::unexpected(std::nullopt); + return nullptr; } ++map_entry; } else { @@ -1194,29 +1143,25 @@ base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag( // Finish the child entries if they exist. while (map_entry != map_entry_end) { - if (UNLIKELY(!map_entry)) { - return base::unexpected(IOError::PAGES_MISSING); - } - uint32_t new_key = dtohl(map_entry->name.ident); if (!is_internal_resid(new_key)) { - if (UNLIKELY(entry->dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR)) { + if (entry.dynamic_ref_table->lookupResourceId(&new_key) != NO_ERROR) { LOG(ERROR) << base::StringPrintf("Failed to resolve key 0x%08x in bag 0x%08x.", new_key, resid); - return base::unexpected(std::nullopt); + return nullptr; } } - new_entry->cookie = entry->cookie; + new_entry->cookie = cookie; new_entry->key = new_key; new_entry->key_pool = nullptr; new_entry->type_pool = nullptr; new_entry->value.copyFrom_dtoh(map_entry->value); new_entry->style = resid; - status_t err = entry->dynamic_ref_table->lookupResourceValue(&new_entry->value); - if (UNLIKELY(err != NO_ERROR)) { + status_t err = entry.dynamic_ref_table->lookupResourceValue(&new_entry->value); + if (err != NO_ERROR) { LOG(ERROR) << base::StringPrintf("Failed to resolve value t=0x%02x d=0x%08x for key 0x%08x.", new_entry->value.dataType, new_entry->value.data, new_key); - return base::unexpected(std::nullopt); + return nullptr; } sort_entries = sort_entries || (new_entry != new_bag->entries && (new_entry->key < (new_entry - 1U)->key)); @@ -1240,12 +1185,11 @@ base::expected<const ResolvedBag*, NullOrIOError> AssetManager2::GetBag( } if (sort_entries) { - std::sort(new_bag->entries, new_bag->entries + actual_count, - [](auto&& lhs, auto&& rhs) { return lhs.key < rhs.key; }); + std::sort(new_bag->entries, new_bag->entries + actual_count, compare_bag_entries); } // Combine flags from the parent and our own bag. - new_bag->type_spec_flags = entry->type_flags | (*parent_bag)->type_spec_flags; + new_bag->type_spec_flags = entry.type_flags | parent_bag->type_spec_flags; new_bag->entry_count = static_cast<uint32_t>(actual_count); ResolvedBag* result = new_bag.get(); cached_bags_[resid] = std::move(new_bag); @@ -1264,16 +1208,16 @@ static bool Utf8ToUtf16(const StringPiece& str, std::u16string* out) { return true; } -base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId( - const std::string& resource_name, const std::string& fallback_type, - const std::string& fallback_package) const { +uint32_t AssetManager2::GetResourceId(const std::string& resource_name, + const std::string& fallback_type, + const std::string& fallback_package) const { StringPiece package_name, type, entry; if (!ExtractResourceName(resource_name, &package_name, &type, &entry)) { - return base::unexpected(std::nullopt); + return 0u; } if (entry.empty()) { - return base::unexpected(std::nullopt); + return 0u; } if (package_name.empty()) { @@ -1286,12 +1230,12 @@ base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId( std::u16string type16; if (!Utf8ToUtf16(type, &type16)) { - return base::unexpected(std::nullopt); + return 0u; } std::u16string entry16; if (!Utf8ToUtf16(entry, &entry16)) { - return base::unexpected(std::nullopt); + return 0u; } const StringPiece16 kAttr16 = u"attr"; @@ -1305,24 +1249,20 @@ base::expected<uint32_t, NullOrIOError> AssetManager2::GetResourceId( break; } - base::expected<uint32_t, NullOrIOError> resid = package->FindEntryByName(type16, entry16); - if (UNLIKELY(IsIOError(resid))) { - return base::unexpected(resid.error()); - } - - if (!resid.has_value() && kAttr16 == type16) { + uint32_t resid = package->FindEntryByName(type16, entry16); + if (resid == 0u && kAttr16 == type16) { // Private attributes in libraries (such as the framework) are sometimes encoded // under the type '^attr-private' in order to leave the ID space of public 'attr' // free for future additions. Check '^attr-private' for the same name. resid = package->FindEntryByName(kAttrPrivate16, entry16); } - if (resid.has_value()) { - return fix_package_id(*resid, package_group.dynamic_ref_table->mAssignedPackageId); + if (resid != 0u) { + return fix_package_id(resid, package_group.dynamic_ref_table->mAssignedPackageId); } } } - return base::unexpected(std::nullopt); + return 0u; } void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) { @@ -1342,7 +1282,8 @@ void AssetManager2::RebuildFilterList(bool filter_incompatible_configs) { ResTable_config this_config; this_config.copyFromDtoH((*iter)->config); if (!filter_incompatible_configs || this_config.match(configuration_)) { - group.type_configs.push_back(TypeConfig{*iter, this_config}); + group.configurations.push_back(this_config); + group.types.push_back(*iter); } } }); @@ -1368,8 +1309,6 @@ void AssetManager2::InvalidateCaches(uint32_t diff) { ++iter; } } - - cached_resolved_values_.clear(); } uint8_t AssetManager2::GetAssignedPackageId(const LoadedPackage* package) const { @@ -1415,16 +1354,16 @@ struct Theme::Package { std::array<util::unique_cptr<ThemeType>, kTypeCount> types; }; -base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, bool force) { +bool Theme::ApplyStyle(uint32_t resid, bool force) { ATRACE_NAME("Theme::ApplyStyle"); - auto bag = asset_manager_->GetBag(resid); - if (!bag.has_value()) { - return base::unexpected(bag.error()); + const ResolvedBag* bag = asset_manager_->GetBag(resid); + if (bag == nullptr) { + return false; } // Merge the flags from this style. - type_spec_flags_ |= (*bag)->type_spec_flags; + type_spec_flags_ |= bag->type_spec_flags; int last_type_idx = -1; int last_package_idx = -1; @@ -1434,14 +1373,14 @@ base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, // Iterate backwards, because each bag is sorted in ascending key ID order, meaning we will only // need to perform one resize per type. using reverse_bag_iterator = std::reverse_iterator<const ResolvedBag::Entry*>; - const auto rbegin = reverse_bag_iterator(begin(*bag)); - for (auto it = reverse_bag_iterator(end(*bag)); it != rbegin; ++it) { - const uint32_t attr_resid = it->key; + const auto bag_iter_end = reverse_bag_iterator(begin(bag)); + for (auto bag_iter = reverse_bag_iterator(end(bag)); bag_iter != bag_iter_end; ++bag_iter) { + const uint32_t attr_resid = bag_iter->key; // If the resource ID passed in is not a style, the key can be some other identifier that is not // a resource ID. We should fail fast instead of operating with strange resource IDs. if (!is_valid_resid(attr_resid)) { - return base::unexpected(std::nullopt); + return false; } // We don't use the 0-based index for the type so that we can avoid doing ID validation @@ -1489,18 +1428,20 @@ base::expected<std::monostate, NullOrIOError> Theme::ApplyStyle(uint32_t resid, ThemeEntry& entry = last_type->entries[entry_idx]; if (force || (entry.value.dataType == Res_value::TYPE_NULL && entry.value.data != Res_value::DATA_NULL_EMPTY)) { - entry.cookie = it->cookie; - entry.type_spec_flags |= (*bag)->type_spec_flags; - entry.value = it->value; + entry.cookie = bag_iter->cookie; + entry.type_spec_flags |= bag->type_spec_flags; + entry.value = bag_iter->value; } } - return {}; + return true; } -std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) const { - +ApkAssetsCookie Theme::GetAttribute(uint32_t resid, Res_value* out_value, + uint32_t* out_flags) const { int cnt = 20; + uint32_t type_spec_flags = 0u; + do { const int package_idx = get_package_id(resid); const Package* package = packages_[package_idx].get(); @@ -1520,42 +1461,43 @@ std::optional<AssetManager2::SelectedValue> Theme::GetAttribute(uint32_t resid) resid = entry.value.data; continue; } - return std::nullopt; + return kInvalidCookie; } // @null is different than @empty. if (entry.value.dataType == Res_value::TYPE_NULL && entry.value.data != Res_value::DATA_NULL_EMPTY) { - return std::nullopt; + return kInvalidCookie; } - return AssetManager2::SelectedValue(entry.value.dataType, entry.value.data, entry.cookie, - type_spec_flags, 0U /* resid */, {} /* config */); + *out_value = entry.value; + *out_flags = type_spec_flags; + return entry.cookie; } } } break; } while (true); - return std::nullopt; + return kInvalidCookie; } -base::expected<std::monostate, NullOrIOError> Theme::ResolveAttributeReference( - AssetManager2::SelectedValue& value) const { - if (value.type != Res_value::TYPE_ATTRIBUTE) { - return asset_manager_->ResolveReference(value); - } - - std::optional<AssetManager2::SelectedValue> result = GetAttribute(value.data); - if (!result.has_value()) { - return base::unexpected(std::nullopt); - } +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) const { + 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; + } - auto resolve_result = asset_manager_->ResolveReference(*result, true /* cache_value */); - if (resolve_result.has_value()) { - result->flags |= value.flags; - value = *result; + if (in_out_type_spec_flags != nullptr) { + *in_out_type_spec_flags |= new_flags; + } } - return resolve_result; + return asset_manager_->ResolveReference(cookie, in_out_value, in_out_selected_config, + in_out_type_spec_flags, out_last_ref); } void Theme::Clear() { @@ -1565,9 +1507,9 @@ void Theme::Clear() { } } -base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { +void Theme::SetTo(const Theme& o) { if (this == &o) { - return {}; + return; } type_spec_flags_ = o.type_spec_flags_; @@ -1618,8 +1560,10 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { // Map the runtime package of the source apk asset to the destination apk asset. if (src_asset->GetPath() == dest_asset->GetPath()) { - const auto& src_packages = src_asset->GetLoadedArsc()->GetPackages(); - const auto& dest_packages = dest_asset->GetLoadedArsc()->GetPackages(); + const std::vector<std::unique_ptr<const LoadedPackage>>& src_packages = + src_asset->GetLoadedArsc()->GetPackages(); + const std::vector<std::unique_ptr<const LoadedPackage>>& dest_packages = + dest_asset->GetLoadedArsc()->GetPackages(); SourceToDestinationRuntimePackageMap package_map; @@ -1716,20 +1660,15 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { int attribute_dest_package_id = p; if (attribute_dest_package_id != 0x01) { // Find the cookie of the attribute resource id in the source AssetManager - base::expected<FindEntryResult, NullOrIOError> attribute_entry_result = + FindEntryResult attribute_entry_result; + ApkAssetsCookie attribute_cookie = o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , true /* stop_at_first_match */, - true /* ignore_configuration */); - if (UNLIKELY(IsIOError(attribute_entry_result))) { - return base::unexpected(GetIOError(attribute_entry_result.error())); - } - if (!attribute_entry_result.has_value()) { - continue; - } + true /* ignore_configuration */, + &attribute_entry_result); // Determine the package id of the attribute in the destination AssetManager. - auto attribute_package_map = src_asset_cookie_id_map.find( - attribute_entry_result->cookie); + auto attribute_package_map = src_asset_cookie_id_map.find(attribute_cookie); if (attribute_package_map == src_asset_cookie_id_map.end()) { continue; } @@ -1773,7 +1712,6 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { } } } - return {}; } void Theme::Dump() const { diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp index 347b4ec8346c..e62fb614e195 100644 --- a/libs/androidfw/AttributeResolution.cpp +++ b/libs/androidfw/AttributeResolution.cpp @@ -24,12 +24,9 @@ #include "androidfw/AttributeFinder.h" constexpr bool kDebugStyles = false; -#define DEBUG_LOG(...) do { if (kDebugStyles) { ALOGI(__VA_ARGS__); } } while(0) namespace android { -namespace { - // Java asset cookies have 0 as an invalid cookie, but TypedArray expects < 0. static uint32_t ApkAssetsCookieToJavaCookie(ApkAssetsCookie cookie) { return cookie != kInvalidCookie ? static_cast<uint32_t>(cookie + 1) : static_cast<uint32_t>(-1); @@ -39,7 +36,8 @@ class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> { public: explicit XmlAttributeFinder(const ResXMLParser* parser) - : BackTrackingAttributeFinder(0, parser != nullptr ? parser->getAttributeCount() : 0), + : BackTrackingAttributeFinder( + 0, parser != nullptr ? parser->getAttributeCount() : 0), parser_(parser) {} inline uint32_t GetAttribute(size_t index) const { @@ -63,149 +61,136 @@ class BagAttributeFinder } }; -base::expected<const ResolvedBag*, NullOrIOError> GetStyleBag(Theme* theme, - uint32_t theme_attribute_resid, - uint32_t fallback_resid, - uint32_t* out_theme_flags) { - // Load the style from the attribute if specified. - if (theme_attribute_resid != 0U) { - std::optional<AssetManager2::SelectedValue> value = theme->GetAttribute(theme_attribute_resid); - if (value.has_value()) { - *out_theme_flags |= value->flags; - auto result = theme->GetAssetManager()->ResolveBag(*value); - if (result.has_value() || IsIOError(result)) { - return result; - } - } - } - - // Fallback to loading the style from the resource id if specified. - if (fallback_resid != 0U) { - return theme->GetAssetManager()->GetBag(fallback_resid); +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_res, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { + if (kDebugStyles) { + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, + def_style_attr, def_style_res); } - return base::unexpected(std::nullopt); -} - -base::expected<const ResolvedBag*, NullOrIOError> GetXmlStyleBag(Theme* theme, - ResXMLParser* xml_parser, - uint32_t* out_theme_flags) { - if (xml_parser == nullptr) { - return base::unexpected(std::nullopt); - } - - // Retrieve the style resource ID associated with the current XML tag's style attribute. + AssetManager2* assetmanager = theme->GetAssetManager(); + ResTable_config config; Res_value value; - const ssize_t idx = xml_parser->indexOfStyle(); - if (idx < 0 || xml_parser->getAttributeValue(idx, &value) < 0) { - return base::unexpected(std::nullopt); - } - if (value.dataType == Res_value::TYPE_ATTRIBUTE) { - // Resolve the attribute with out theme. - if (std::optional<AssetManager2::SelectedValue> result = theme->GetAttribute(value.data)) { - *out_theme_flags |= result->flags; - return theme->GetAssetManager()->ResolveBag(*result); - } - } + int indices_idx = 0; - if (value.dataType == Res_value::TYPE_REFERENCE) { - return theme->GetAssetManager()->GetBag(value.data); + // Load default style from attribute, if specified... + uint32_t def_style_flags = 0u; + if (def_style_attr != 0) { + Res_value value; + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (value.dataType == Res_value::TYPE_REFERENCE) { + def_style_res = value.data; + } + } } - return base::unexpected(std::nullopt); -} - -} // namespace - -base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_style_attr, - uint32_t def_style_res, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices) { - DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr, - def_style_res); - - int indices_idx = 0; - const AssetManager2* assetmanager = theme->GetAssetManager(); - - // Load default style from attribute or resource id, if specified... - uint32_t def_style_theme_flags = 0U; - const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_res, - &def_style_theme_flags); - if (UNLIKELY(IsIOError(default_style_bag))) { - return base::unexpected(GetIOError(default_style_bag.error())); + // Retrieve the default style bag, if requested. + const ResolvedBag* default_style_bag = nullptr; + if (def_style_res != 0) { + default_style_bag = assetmanager->GetBag(def_style_res); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } } - BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr)); + BagAttributeFinder def_style_attr_finder(default_style_bag); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident); + + if (kDebugStyles) { + ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); + } + + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0; + + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + config.density = 0; // Try to find a value for this attribute... we prioritize values // coming from, first XML attributes, then XML style, then default // style, and finally the theme. // Retrieve the current input value if available. - AssetManager2::SelectedValue value{}; if (src_values_length > 0 && src_values[ii] != 0) { - value.type = Res_value::TYPE_ATTRIBUTE; + value.dataType = Res_value::TYPE_ATTRIBUTE; value.data = src_values[ii]; - DEBUG_LOG("-> From values: type=0x%x, data=0x%08x", value.type, value.data); + if (kDebugStyles) { + ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data); + } } else { const ResolvedBag::Entry* const entry = def_style_attr_finder.Find(cur_ident); if (entry != def_style_attr_finder.end()) { - value = AssetManager2::SelectedValue(*default_style_bag, *entry); - value.flags |= def_style_theme_flags; - DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x", value.type, value.data); + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; + if (kDebugStyles) { + ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data); + } } } - if (value.type != Res_value::TYPE_NULL) { + uint32_t resid = 0; + if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - const auto result = theme->ResolveAttributeReference(value); - if (UNLIKELY(IsIOError(result))) { - return base::unexpected(GetIOError(result.error())); + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } + if (kDebugStyles) { + ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } - DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data); } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - if (auto attr_value = theme->GetAttribute(cur_ident)) { - value = *attr_value; - DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); - - const auto result = assetmanager->ResolveReference(value, true /* cache_value */); - if (UNLIKELY(IsIOError(result))) { - return base::unexpected(GetIOError(result.error())); + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + if (new_cookie != kInvalidCookie) { + if (kDebugStyles) { + ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); + } + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } + if (kDebugStyles) { + ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data); } } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.type == Res_value::TYPE_REFERENCE && value.data == 0) { - DEBUG_LOG("-> Setting to @null!"); - value.type = Res_value::TYPE_NULL; + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + if (kDebugStyles) { + ALOGI("-> Setting to @null!"); + } + value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - value.cookie = kInvalidCookie; + cookie = kInvalidCookie; } - DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data); + if (kDebugStyles) { + ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data); + } // Write the final value back to Java. - out_values[STYLE_TYPE] = value.type; + out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); - out_values[STYLE_RESOURCE_ID] = value.resid; - out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; - out_values[STYLE_DENSITY] = value.config.density; + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_RESOURCE_ID] = resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; + out_values[STYLE_DENSITY] = config.density; if (out_indices != nullptr && - (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { - out_indices[++indices_idx] = ii; + (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { + indices_idx++; + out_indices[indices_idx] = ii; } out_values += STYLE_NUM_ENTRIES; @@ -214,46 +199,93 @@ base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_ if (out_indices != nullptr) { out_indices[0] = indices_idx; } - return {}; + return true; } -base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* xml_parser, - uint32_t def_style_attr, - uint32_t def_style_resid, - const uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices) { - DEBUG_LOG("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, - def_style_attr, def_style_resid, xml_parser); +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices) { + if (kDebugStyles) { + ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme, + def_style_attr, def_style_resid, xml_parser); + } + + AssetManager2* assetmanager = theme->GetAssetManager(); + ResTable_config config; + Res_value value; int indices_idx = 0; - const AssetManager2* assetmanager = theme->GetAssetManager(); // Load default style from attribute, if specified... - uint32_t def_style_theme_flags = 0U; - const auto default_style_bag = GetStyleBag(theme, def_style_attr, def_style_resid, - &def_style_theme_flags); - if (IsIOError(default_style_bag)) { - return base::unexpected(GetIOError(default_style_bag.error())); + uint32_t def_style_flags = 0u; + if (def_style_attr != 0) { + Res_value value; + if (theme->GetAttribute(def_style_attr, &value, &def_style_flags) != kInvalidCookie) { + if (value.dataType == Res_value::TYPE_REFERENCE) { + def_style_resid = value.data; + } + } } // Retrieve the style resource ID associated with the current XML tag's style attribute. - uint32_t xml_style_theme_flags = 0U; - const auto xml_style_bag = GetXmlStyleBag(theme, xml_parser, &def_style_theme_flags); - if (IsIOError(xml_style_bag)) { - return base::unexpected(GetIOError(xml_style_bag.error())); + uint32_t style_resid = 0u; + uint32_t style_flags = 0u; + if (xml_parser != nullptr) { + ssize_t idx = xml_parser->indexOfStyle(); + if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { + if (value.dataType == value.TYPE_ATTRIBUTE) { + // Resolve the attribute with out theme. + if (theme->GetAttribute(value.data, &value, &style_flags) == kInvalidCookie) { + value.dataType = Res_value::TYPE_NULL; + } + } + + if (value.dataType == value.TYPE_REFERENCE) { + style_resid = value.data; + } + } + } + + // Retrieve the default style bag, if requested. + const ResolvedBag* default_style_bag = nullptr; + if (def_style_resid != 0) { + default_style_bag = assetmanager->GetBag(def_style_resid); + if (default_style_bag != nullptr) { + def_style_flags |= default_style_bag->type_spec_flags; + } } - BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr)); - BagAttributeFinder xml_style_attr_finder(xml_style_bag.value_or(nullptr)); + BagAttributeFinder def_style_attr_finder(default_style_bag); + + // Retrieve the style class bag, if requested. + const ResolvedBag* xml_style_bag = nullptr; + if (style_resid != 0) { + xml_style_bag = assetmanager->GetBag(style_resid); + if (xml_style_bag != nullptr) { + style_flags |= xml_style_bag->type_spec_flags; + } + } + + BagAttributeFinder xml_style_attr_finder(xml_style_bag); + + // Retrieve the XML attributes, if requested. XmlAttributeFinder xml_attr_finder(xml_parser); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - DEBUG_LOG("RETRIEVING ATTR 0x%08x...", cur_ident); - AssetManager2::SelectedValue value{}; + if (kDebugStyles) { + ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); + } + + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; + + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + config.density = 0; uint32_t value_source_resid = 0; // Try to find a value for this attribute... we prioritize values @@ -264,152 +296,178 @@ base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* x const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); if (xml_attr_idx != xml_attr_finder.end()) { // We found the attribute we were looking for. - Res_value attribute_value; - xml_parser->getAttributeValue(xml_attr_idx, &attribute_value); - value.type = attribute_value.dataType; - value.data = attribute_value.data; + xml_parser->getAttributeValue(xml_attr_idx, &value); + if (kDebugStyles) { + ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data); + } value_source_resid = xml_parser->getSourceResourceId(); - DEBUG_LOG("-> From XML: type=0x%x, data=0x%08x", value.type, value.data); } - if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { + if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the style class values looking for the requested attribute. const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident); if (entry != xml_style_attr_finder.end()) { - value = AssetManager2::SelectedValue(*xml_style_bag, *entry); - value.flags |= xml_style_theme_flags; + // We found the attribute we were looking for. + cookie = entry->cookie; + type_set_flags = style_flags; + value = entry->value; value_source_resid = entry->style; - DEBUG_LOG("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data, - value_source_resid); + if (kDebugStyles) { + ALOGI("-> From style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data, + entry->style); + } } } - if (value.type == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { + if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) { // Walk through the default style values looking for the requested attribute. const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident); if (entry != def_style_attr_finder.end()) { - value = AssetManager2::SelectedValue(*default_style_bag, *entry); - value.flags |= def_style_theme_flags; + // We found the attribute we were looking for. + cookie = entry->cookie; + type_set_flags = def_style_flags; + value = entry->value; + if (kDebugStyles) { + ALOGI("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.dataType, value.data, + entry->style); + } value_source_resid = entry->style; - DEBUG_LOG("-> From def style: type=0x%x, data=0x%08x, style=0x%08x", value.type, value.data, - entry->style); } } - if (value.type != Res_value::TYPE_NULL) { + uint32_t resid = 0u; + if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - auto result = theme->ResolveAttributeReference(value); - if (UNLIKELY(IsIOError(result))) { - return base::unexpected(GetIOError(result.error())); + ApkAssetsCookie new_cookie = + theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } + + if (kDebugStyles) { + ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data); } - DEBUG_LOG("-> Resolved attr: type=0x%x, data=0x%08x", value.type, value.data); } else if (value.data != Res_value::DATA_NULL_EMPTY) { // If we still don't have a value for this attribute, try to find it in the theme! - if (auto attr_value = theme->GetAttribute(cur_ident)) { - value = *attr_value; - DEBUG_LOG("-> From theme: type=0x%x, data=0x%08x", value.type, value.data); + ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags); + // TODO: set value_source_resid for the style in the theme that was used. + if (new_cookie != kInvalidCookie) { + if (kDebugStyles) { + ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data); + } + new_cookie = + assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; + } - auto result = assetmanager->ResolveReference(value, true /* cache_value */); - if (UNLIKELY(IsIOError(result))) { - return base::unexpected(GetIOError(result.error())); + if (kDebugStyles) { + ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data); } - DEBUG_LOG("-> Resolved theme: type=0x%x, data=0x%08x", value.type, value.data); - // TODO: set value_source_resid for the style in the theme that was used. } } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) { - DEBUG_LOG("-> Setting to @null!"); - value.type = Res_value::TYPE_NULL; + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + if (kDebugStyles) { + ALOGI("-> Setting to @null!"); + } + value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - value.cookie = kInvalidCookie; + cookie = kInvalidCookie; } - DEBUG_LOG("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.type, value.data); + if (kDebugStyles) { + ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data); + } // Write the final value back to Java. - out_values[STYLE_TYPE] = value.type; + out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); - out_values[STYLE_RESOURCE_ID] = value.resid; - out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; - out_values[STYLE_DENSITY] = value.config.density; + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_RESOURCE_ID] = resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; + out_values[STYLE_DENSITY] = config.density; out_values[STYLE_SOURCE_RESOURCE_ID] = value_source_resid; - if (value.type != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) { + if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) { + indices_idx++; + // out_indices must NOT be nullptr. - out_indices[++indices_idx] = ii; + out_indices[indices_idx] = ii; } out_values += STYLE_NUM_ENTRIES; } // out_indices must NOT be nullptr. out_indices[0] = indices_idx; - return {}; } -base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetmanager, - ResXMLParser* xml_parser, - uint32_t* attrs, - size_t attrs_length, - uint32_t* out_values, - uint32_t* out_indices) { +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) { + ResTable_config config; + Res_value value; + int indices_idx = 0; // Retrieve the XML attributes, if requested. - size_t ix = 0; const size_t xml_attr_count = xml_parser->getAttributeCount(); + size_t ix = 0; uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); // Now iterate through all of the attributes that the client has requested, // filling in each with whatever data we can find. for (size_t ii = 0; ii < attrs_length; ii++) { const uint32_t cur_ident = attrs[ii]; - AssetManager2::SelectedValue value{}; + ApkAssetsCookie cookie = kInvalidCookie; + uint32_t type_set_flags = 0u; + + value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; + config.density = 0; // Try to find a value for this attribute... // Skip through XML attributes until the end or the next possible match. while (ix < xml_attr_count && cur_ident > cur_xml_attr) { - cur_xml_attr = xml_parser->getAttributeNameResID(++ix); + ix++; + cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - // Retrieve the current XML attribute if it matches, and step to next. if (ix < xml_attr_count && cur_ident == cur_xml_attr) { - Res_value attribute_value; - xml_parser->getAttributeValue(ix, &attribute_value); - value.type = attribute_value.dataType; - value.data = attribute_value.data; - cur_xml_attr = xml_parser->getAttributeNameResID(++ix); + xml_parser->getAttributeValue(ix, &value); + ix++; + cur_xml_attr = xml_parser->getAttributeNameResID(ix); } - if (value.type != Res_value::TYPE_NULL) { + uint32_t resid = 0u; + if (value.dataType != Res_value::TYPE_NULL) { // Take care of resolving the found resource to its final value. - auto result = assetmanager->ResolveReference(value); - if (UNLIKELY(IsIOError(result))) { - return base::unexpected(GetIOError(result.error())); + ApkAssetsCookie new_cookie = + assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid); + if (new_cookie != kInvalidCookie) { + cookie = new_cookie; } } // Deal with the special @null value -- it turns back to TYPE_NULL. - if (value.type == Res_value::TYPE_REFERENCE && value.data == 0U) { - value.type = Res_value::TYPE_NULL; + if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { + value.dataType = Res_value::TYPE_NULL; value.data = Res_value::DATA_NULL_UNDEFINED; - value.cookie = kInvalidCookie; + cookie = kInvalidCookie; } // Write the final value back to Java. - out_values[STYLE_TYPE] = value.type; + out_values[STYLE_TYPE] = value.dataType; out_values[STYLE_DATA] = value.data; - out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(value.cookie); - out_values[STYLE_RESOURCE_ID] = value.resid; - out_values[STYLE_CHANGING_CONFIGURATIONS] = value.flags; - out_values[STYLE_DENSITY] = value.config.density; + out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie); + out_values[STYLE_RESOURCE_ID] = resid; + out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; + out_values[STYLE_DENSITY] = config.density; if (out_indices != nullptr && - (value.type != Res_value::TYPE_NULL || - value.data == Res_value::DATA_NULL_EMPTY)) { - out_indices[++indices_idx] = ii; + (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) { + indices_idx++; + out_indices[indices_idx] = ii; } out_values += STYLE_NUM_ENTRIES; @@ -418,7 +476,7 @@ base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetm if (out_indices != nullptr) { out_indices[0] = indices_idx; } - return {}; + return true; } } // namespace android diff --git a/libs/androidfw/ChunkIterator.cpp b/libs/androidfw/ChunkIterator.cpp index 25c8aa64e492..8fc321968055 100644 --- a/libs/androidfw/ChunkIterator.cpp +++ b/libs/androidfw/ChunkIterator.cpp @@ -15,7 +15,6 @@ */ #include "androidfw/Chunk.h" -#include "androidfw/Util.h" #include "android-base/logging.h" @@ -24,11 +23,11 @@ namespace android { Chunk ChunkIterator::Next() { CHECK(len_ != 0) << "called Next() after last chunk"; - const incfs::map_ptr<ResChunk_header> this_chunk = next_chunk_; - CHECK((bool) this_chunk) << "Next() called without verifying next chunk"; + const ResChunk_header* this_chunk = next_chunk_; // We've already checked the values of this_chunk, so safely increment. - next_chunk_ = this_chunk.offset(dtohl(this_chunk->size)).convert<ResChunk_header>(); + next_chunk_ = reinterpret_cast<const ResChunk_header*>( + reinterpret_cast<const uint8_t*>(this_chunk) + dtohl(this_chunk->size)); len_ -= dtohl(this_chunk->size); if (len_ != 0) { @@ -37,7 +36,7 @@ Chunk ChunkIterator::Next() { VerifyNextChunk(); } } - return Chunk(this_chunk.verified()); + return Chunk(this_chunk); } // TODO(b/111401637) remove this and have full resource file verification @@ -48,13 +47,6 @@ bool ChunkIterator::VerifyNextChunkNonFatal() { last_error_was_fatal_ = false; return false; } - - if (!next_chunk_) { - last_error_ = "failed to read chunk from data"; - last_error_was_fatal_ = false; - return false; - } - const size_t size = dtohl(next_chunk_->size); if (size > len_) { last_error_ = "chunk size is bigger than given data"; @@ -66,10 +58,12 @@ bool ChunkIterator::VerifyNextChunkNonFatal() { // Returns false if there was an error. bool ChunkIterator::VerifyNextChunk() { + const uintptr_t header_start = reinterpret_cast<uintptr_t>(next_chunk_); + // This data must be 4-byte aligned, since we directly // access 32-bit words, which must be aligned on // certain architectures. - if (!util::IsFourByteAligned(next_chunk_)) { + if (header_start & 0x03) { last_error_ = "header not aligned on 4-byte boundary"; return false; } @@ -79,11 +73,6 @@ bool ChunkIterator::VerifyNextChunk() { return false; } - if (!next_chunk_) { - last_error_ = "failed to read chunk from data"; - return false; - } - const size_t header_size = dtohs(next_chunk_->headerSize); const size_t size = dtohl(next_chunk_->size); if (header_size < sizeof(ResChunk_header)) { @@ -101,7 +90,7 @@ bool ChunkIterator::VerifyNextChunk() { return false; } - if ((size | header_size) & 0x03U) { + if ((size | header_size) & 0x03) { last_error_ = "header sizes are not aligned on 4-byte boundary"; return false; } diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index a61309514143..4e03ce5d9584 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -52,22 +52,22 @@ OverlayStringPool::~OverlayStringPool() { uninit(); } -base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const { +const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const { const size_t offset = dtohl(data_header_->string_pool_index_offset); if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { - return idmap_string_pool_->stringAt(idx - offset); + return idmap_string_pool_->stringAt(idx - offset, outLen); } - return ResStringPool::stringAt(idx); + return ResStringPool::stringAt(idx, outLen); } -base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const { +const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const { const size_t offset = dtohl(data_header_->string_pool_index_offset); if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) { - return idmap_string_pool_->string8At(idx - offset); + return idmap_string_pool_->string8At(idx - offset, outLen); } - return ResStringPool::string8At(idx); + return ResStringPool::string8At(idx, outLen); } size_t OverlayStringPool::size() const { diff --git a/libs/androidfw/LoadedArsc.cpp b/libs/androidfw/LoadedArsc.cpp index 2fc3b05011c2..70bb441f94cb 100644 --- a/libs/androidfw/LoadedArsc.cpp +++ b/libs/androidfw/LoadedArsc.cpp @@ -38,7 +38,7 @@ #include "androidfw/ResourceUtils.h" #include "androidfw/Util.h" -using android::base::StringPrintf; +using ::android::base::StringPrintf; namespace android { @@ -51,17 +51,17 @@ namespace { // the Type structs. class TypeSpecPtrBuilder { public: - explicit TypeSpecPtrBuilder(incfs::verified_map_ptr<ResTable_typeSpec> header) + explicit TypeSpecPtrBuilder(const ResTable_typeSpec* header) : header_(header) { } - void AddType(incfs::verified_map_ptr<ResTable_type> type) { + void AddType(const ResTable_type* type) { types_.push_back(type); } TypeSpecPtr Build() { // Check for overflow. - using ElementType = incfs::verified_map_ptr<ResTable_type>; + using ElementType = const ResTable_type*; if ((std::numeric_limits<size_t>::max() - sizeof(TypeSpec)) / sizeof(ElementType) < types_.size()) { return {}; @@ -77,8 +77,8 @@ class TypeSpecPtrBuilder { private: DISALLOW_COPY_AND_ASSIGN(TypeSpecPtrBuilder); - incfs::verified_map_ptr<ResTable_typeSpec> header_; - std::vector<incfs::verified_map_ptr<ResTable_type>> types_; + const ResTable_typeSpec* header_; + std::vector<const ResTable_type*> types_; }; } // namespace @@ -88,7 +88,7 @@ LoadedPackage::~LoadedPackage() = default; // Precondition: The header passed in has already been verified, so reading any fields and trusting // the ResChunk_header is safe. -static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) { +static bool VerifyResTableType(const ResTable_type* header) { if (header->id == 0) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE has invalid ID 0."; return false; @@ -115,99 +115,89 @@ static bool VerifyResTableType(incfs::map_ptr<ResTable_type> header) { return false; } - if (entries_offset & 0x03U) { + if (entries_offset & 0x03) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE entries start at unaligned address."; return false; } return true; } -static base::expected<std::monostate, NullOrIOError> VerifyResTableEntry( - incfs::verified_map_ptr<ResTable_type> type, uint32_t entry_offset) { +static bool VerifyResTableEntry(const ResTable_type* type, uint32_t entry_offset) { // Check that the offset is aligned. - if (UNLIKELY(entry_offset & 0x03U)) { + if (entry_offset & 0x03) { LOG(ERROR) << "Entry at offset " << entry_offset << " is not 4-byte aligned."; - return base::unexpected(std::nullopt); + return false; } // Check that the offset doesn't overflow. - if (UNLIKELY(entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart))) { + if (entry_offset > std::numeric_limits<uint32_t>::max() - dtohl(type->entriesStart)) { // Overflow in offset. LOG(ERROR) << "Entry at offset " << entry_offset << " is too large."; - return base::unexpected(std::nullopt); + return false; } const size_t chunk_size = dtohl(type->header.size); entry_offset += dtohl(type->entriesStart); - if (UNLIKELY(entry_offset > chunk_size - sizeof(ResTable_entry))) { + if (entry_offset > chunk_size - sizeof(ResTable_entry)) { LOG(ERROR) << "Entry at offset " << entry_offset << " is too large. No room for ResTable_entry."; - return base::unexpected(std::nullopt); + return false; } - auto entry = type.offset(entry_offset).convert<ResTable_entry>(); - if (UNLIKELY(!entry)) { - return base::unexpected(IOError::PAGES_MISSING); - } + const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<const uint8_t*>(type) + entry_offset); const size_t entry_size = dtohs(entry->size); - if (UNLIKELY(entry_size < sizeof(entry.value()))) { + if (entry_size < sizeof(*entry)) { LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too small."; - return base::unexpected(std::nullopt); + return false; } - if (UNLIKELY(entry_size > chunk_size || entry_offset > chunk_size - entry_size)) { + if (entry_size > chunk_size || entry_offset > chunk_size - entry_size) { LOG(ERROR) << "ResTable_entry size " << entry_size << " at offset " << entry_offset << " is too large."; - return base::unexpected(std::nullopt); + return false; } if (entry_size < sizeof(ResTable_map_entry)) { // There needs to be room for one Res_value struct. - if (UNLIKELY(entry_offset + entry_size > chunk_size - sizeof(Res_value))) { + if (entry_offset + entry_size > chunk_size - sizeof(Res_value)) { LOG(ERROR) << "No room for Res_value after ResTable_entry at offset " << entry_offset << " for type " << (int)type->id << "."; - return base::unexpected(std::nullopt); - } - - auto value = entry.offset(entry_size).convert<Res_value>(); - if (UNLIKELY(!value)) { - return base::unexpected(IOError::PAGES_MISSING); + return false; } + const Res_value* value = + reinterpret_cast<const Res_value*>(reinterpret_cast<const uint8_t*>(entry) + entry_size); const size_t value_size = dtohs(value->size); - if (UNLIKELY(value_size < sizeof(Res_value))) { + if (value_size < sizeof(Res_value)) { LOG(ERROR) << "Res_value at offset " << entry_offset << " is too small."; - return base::unexpected(std::nullopt); + return false; } - if (UNLIKELY(value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size)) { + if (value_size > chunk_size || entry_offset + entry_size > chunk_size - value_size) { LOG(ERROR) << "Res_value size " << value_size << " at offset " << entry_offset << " is too large."; - return base::unexpected(std::nullopt); + return false; } } else { - auto map = entry.convert<ResTable_map_entry>(); - if (UNLIKELY(!map)) { - return base::unexpected(IOError::PAGES_MISSING); - } - + const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(entry); const size_t map_entry_count = dtohl(map->count); size_t map_entries_start = entry_offset + entry_size; - if (UNLIKELY(map_entries_start & 0x03U)) { + if (map_entries_start & 0x03) { LOG(ERROR) << "Map entries at offset " << entry_offset << " start at unaligned offset."; - return base::unexpected(std::nullopt); + return false; } // Each entry is sizeof(ResTable_map) big. - if (UNLIKELY(map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map)))) { + if (map_entry_count > ((chunk_size - map_entries_start) / sizeof(ResTable_map))) { LOG(ERROR) << "Too many map entries in ResTable_map_entry at offset " << entry_offset << "."; - return base::unexpected(std::nullopt); + return false; } } - return {}; + return true; } LoadedPackage::iterator::iterator(const LoadedPackage* lp, size_t ti, size_t ei) @@ -243,125 +233,99 @@ uint32_t LoadedPackage::iterator::operator*() const { entryIndex_); } -base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntry( - incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) { - base::expected<uint32_t, NullOrIOError> entry_offset = GetEntryOffset(type_chunk, entry_index); - if (UNLIKELY(!entry_offset.has_value())) { - return base::unexpected(entry_offset.error()); +const ResTable_entry* LoadedPackage::GetEntry(const ResTable_type* type_chunk, + uint16_t entry_index) { + uint32_t entry_offset = GetEntryOffset(type_chunk, entry_index); + if (entry_offset == ResTable_type::NO_ENTRY) { + return nullptr; } - return GetEntryFromOffset(type_chunk, entry_offset.value()); + return GetEntryFromOffset(type_chunk, entry_offset); } -base::expected<uint32_t, NullOrIOError> LoadedPackage::GetEntryOffset( - incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index) { +uint32_t LoadedPackage::GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index) { // The configuration matches and is better than the previous selection. // Find the entry value if it exists for this configuration. const size_t entry_count = dtohl(type_chunk->entryCount); const size_t offsets_offset = dtohs(type_chunk->header.headerSize); // Check if there is the desired entry in this type. + if (type_chunk->flags & ResTable_type::FLAG_SPARSE) { // This is encoded as a sparse map, so perform a binary search. - bool error = false; - auto sparse_indices = type_chunk.offset(offsets_offset) - .convert<ResTable_sparseTypeEntry>().iterator(); - auto sparse_indices_end = sparse_indices + entry_count; - auto result = std::lower_bound(sparse_indices, sparse_indices_end, entry_index, - [&error](const incfs::map_ptr<ResTable_sparseTypeEntry>& entry, - uint16_t entry_idx) { - if (UNLIKELY(!entry)) { - return error = true; - } - return dtohs(entry->idx) < entry_idx; - }); - - if (result == sparse_indices_end) { + const ResTable_sparseTypeEntry* sparse_indices = + reinterpret_cast<const ResTable_sparseTypeEntry*>( + reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); + const ResTable_sparseTypeEntry* sparse_indices_end = sparse_indices + entry_count; + const ResTable_sparseTypeEntry* result = + std::lower_bound(sparse_indices, sparse_indices_end, entry_index, + [](const ResTable_sparseTypeEntry& entry, uint16_t entry_idx) { + return dtohs(entry.idx) < entry_idx; + }); + + if (result == sparse_indices_end || dtohs(result->idx) != entry_index) { // No entry found. - return base::unexpected(std::nullopt); - } - - const incfs::verified_map_ptr<ResTable_sparseTypeEntry> entry = (*result).verified(); - if (dtohs(entry->idx) != entry_index) { - if (error) { - return base::unexpected(IOError::PAGES_MISSING); - } - return base::unexpected(std::nullopt); + return ResTable_type::NO_ENTRY; } // Extract the offset from the entry. Each offset must be a multiple of 4 so we store it as // the real offset divided by 4. - return uint32_t{dtohs(entry->offset)} * 4u; + return uint32_t{dtohs(result->offset)} * 4u; } // This type is encoded as a dense array. if (entry_index >= entry_count) { // This entry cannot be here. - return base::unexpected(std::nullopt); + return ResTable_type::NO_ENTRY; } - const auto entry_offset_ptr = type_chunk.offset(offsets_offset).convert<uint32_t>() + entry_index; - if (UNLIKELY(!entry_offset_ptr)) { - return base::unexpected(IOError::PAGES_MISSING); - } - - const uint32_t value = dtohl(entry_offset_ptr.value()); - if (value == ResTable_type::NO_ENTRY) { - return base::unexpected(std::nullopt); - } - - return value; + const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( + reinterpret_cast<const uint8_t*>(type_chunk) + offsets_offset); + return dtohl(entry_offsets[entry_index]); } -base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> LoadedPackage::GetEntryFromOffset( - incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset) { - auto valid = VerifyResTableEntry(type_chunk, offset); - if (UNLIKELY(!valid.has_value())) { - return base::unexpected(valid.error()); +const ResTable_entry* LoadedPackage::GetEntryFromOffset(const ResTable_type* type_chunk, + uint32_t offset) { + if (UNLIKELY(!VerifyResTableEntry(type_chunk, offset))) { + return nullptr; } - return type_chunk.offset(offset + dtohl(type_chunk->entriesStart)).convert<ResTable_entry>(); + return reinterpret_cast<const ResTable_entry*>(reinterpret_cast<const uint8_t*>(type_chunk) + + offset + dtohl(type_chunk->entriesStart)); } -base::expected<std::monostate, IOError> LoadedPackage::CollectConfigurations( - bool exclude_mipmap, std::set<ResTable_config>* out_configs) const { +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 TypeSpecPtr& type_spec = type_specs_[i]; - if (type_spec == nullptr) { - continue; - } - if (exclude_mipmap) { - const int type_idx = type_spec->type_spec->id - 1; - const auto type_name16 = type_string_pool_.stringAt(type_idx); - if (UNLIKELY(IsIOError(type_name16))) { - return base::unexpected(GetIOError(type_name16.error())); - } - if (type_name16.has_value()) { - if (strncmp16(type_name16->data(), u"mipmap", type_name16->size()) == 0) { - // This is a mipmap type, skip collection. - continue; + 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 auto type_name = type_string_pool_.string8At(type_idx); - if (UNLIKELY(IsIOError(type_name))) { - return base::unexpected(GetIOError(type_name.error())); - } - if (type_name.has_value()) { - if (strncmp(type_name->data(), "mipmap", type_name->size()) == 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; + } } } - } - const auto iter_end = type_spec->types + type_spec->type_count; - for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config config; - config.copyFromDtoH((*iter)->config); - out_configs->insert(config); + const auto iter_end = type_spec->types + type_spec->type_count; + for (auto iter = type_spec->types; iter != iter_end; ++iter) { + ResTable_config config; + config.copyFromDtoH((*iter)->config); + out_configs->insert(config); + } } } - return {}; } void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out_locales) const { @@ -384,53 +348,43 @@ void LoadedPackage::CollectLocales(bool canonicalize, std::set<std::string>* out } } -base::expected<uint32_t, NullOrIOError> LoadedPackage::FindEntryByName( - const std::u16string& type_name, const std::u16string& entry_name) const { - const base::expected<size_t, NullOrIOError> type_idx = type_string_pool_.indexOfString( - type_name.data(), type_name.size()); - if (!type_idx.has_value()) { - return base::unexpected(type_idx.error()); +uint32_t LoadedPackage::FindEntryByName(const std::u16string& type_name, + const std::u16string& entry_name) const { + ssize_t type_idx = type_string_pool_.indexOfString(type_name.data(), type_name.size()); + if (type_idx < 0) { + return 0u; } - const base::expected<size_t, NullOrIOError> key_idx = key_string_pool_.indexOfString( - entry_name.data(), entry_name.size()); - if (!key_idx.has_value()) { - return base::unexpected(key_idx.error()); + ssize_t key_idx = key_string_pool_.indexOfString(entry_name.data(), entry_name.size()); + if (key_idx < 0) { + return 0u; } - const TypeSpec* type_spec = type_specs_[*type_idx].get(); + const TypeSpec* type_spec = type_specs_[type_idx].get(); if (type_spec == nullptr) { - return base::unexpected(std::nullopt); + return 0u; } const auto iter_end = type_spec->types + type_spec->type_count; for (auto iter = type_spec->types; iter != iter_end; ++iter) { - const incfs::verified_map_ptr<ResTable_type>& type = *iter; - + const ResTable_type* type = *iter; size_t entry_count = dtohl(type->entryCount); for (size_t entry_idx = 0; entry_idx < entry_count; entry_idx++) { - auto entry_offset_ptr = type.offset(dtohs(type->header.headerSize)).convert<uint32_t>() + - entry_idx; - if (!entry_offset_ptr) { - return base::unexpected(IOError::PAGES_MISSING); - } - - auto offset = dtohl(entry_offset_ptr.value()); + const uint32_t* entry_offsets = reinterpret_cast<const uint32_t*>( + reinterpret_cast<const uint8_t*>(type) + dtohs(type->header.headerSize)); + const uint32_t offset = dtohl(entry_offsets[entry_idx]); if (offset != ResTable_type::NO_ENTRY) { - auto entry = type.offset(dtohl(type->entriesStart) + offset).convert<ResTable_entry>(); - if (!entry) { - return base::unexpected(IOError::PAGES_MISSING); - } - - if (dtohl(entry->key.index) == static_cast<uint32_t>(*key_idx)) { + const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>( + reinterpret_cast<const uint8_t*>(type) + dtohl(type->entriesStart) + offset); + if (dtohl(entry->key.index) == static_cast<uint32_t>(key_idx)) { // The package ID will be overridden by the caller (due to runtime assignment of package // IDs for shared libraries). - return make_resid(0x00, *type_idx + type_id_offset_ + 1, entry_idx); + return make_resid(0x00, type_idx + type_id_offset_ + 1, entry_idx); } } } } - return base::unexpected(std::nullopt); + return 0u; } const LoadedPackage* LoadedArsc::GetPackageById(uint8_t package_id) const { @@ -451,8 +405,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, // was added. constexpr size_t kMinPackageSize = sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset); - const incfs::map_ptr<ResTable_package> header = chunk.header<ResTable_package, kMinPackageSize>(); - if (!header) { + const ResTable_package* header = chunk.header<ResTable_package, kMinPackageSize>(); + if (header == nullptr) { LOG(ERROR) << "RES_TABLE_PACKAGE_TYPE too small."; return {}; } @@ -499,13 +453,10 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, const Chunk child_chunk = iter.Next(); switch (child_chunk.type()) { case RES_STRING_POOL_TYPE: { - const auto pool_address = child_chunk.header<ResChunk_header>(); - if (!pool_address) { - LOG(ERROR) << "RES_STRING_POOL_TYPE is incomplete due to incremental installation."; - return {}; - } - - if (pool_address == header.offset(dtohl(header->typeStrings)).convert<ResChunk_header>()) { + const uintptr_t pool_address = + reinterpret_cast<uintptr_t>(child_chunk.header<ResChunk_header>()); + const uintptr_t header_address = reinterpret_cast<uintptr_t>(header); + if (pool_address == header_address + dtohl(header->typeStrings)) { // This string pool is the type string pool. status_t err = loaded_package->type_string_pool_.setTo( child_chunk.header<ResStringPool_header>(), child_chunk.size()); @@ -513,8 +464,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, LOG(ERROR) << "RES_STRING_POOL_TYPE for types corrupt."; return {}; } - } else if (pool_address == header.offset(dtohl(header->keyStrings)) - .convert<ResChunk_header>()) { + } else if (pool_address == header_address + dtohl(header->keyStrings)) { // This string pool is the key string pool. status_t err = loaded_package->key_string_pool_.setTo( child_chunk.header<ResStringPool_header>(), child_chunk.size()); @@ -528,8 +478,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_SPEC_TYPE: { - const auto type_spec = child_chunk.header<ResTable_typeSpec>(); - if (!type_spec) { + const ResTable_typeSpec* type_spec = child_chunk.header<ResTable_typeSpec>(); + if (type_spec == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE_SPEC_TYPE too small."; return {}; } @@ -564,7 +514,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type_spec->id - 1]; if (builder_ptr == nullptr) { - builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec.verified()); + builder_ptr = util::make_unique<TypeSpecPtrBuilder>(type_spec); loaded_package->resource_ids_.set(type_spec->id, entry_count); } else { LOG(WARNING) << StringPrintf("RES_TABLE_TYPE_SPEC_TYPE already defined for ID %02x", @@ -573,8 +523,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_TYPE_TYPE: { - const auto type = child_chunk.header<ResTable_type, kResTableTypeMinSize>(); - if (!type) { + const ResTable_type* type = child_chunk.header<ResTable_type, kResTableTypeMinSize>(); + if (type == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE_TYPE too small."; return {}; } @@ -586,7 +536,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, // Type chunks must be preceded by their TypeSpec chunks. std::unique_ptr<TypeSpecPtrBuilder>& builder_ptr = type_builder_map[type->id - 1]; if (builder_ptr != nullptr) { - builder_ptr->AddType(type.verified()); + builder_ptr->AddType(type); } else { LOG(ERROR) << StringPrintf( "RES_TABLE_TYPE_TYPE with ID %02x found without preceding RES_TABLE_TYPE_SPEC_TYPE.", @@ -596,8 +546,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_LIBRARY_TYPE: { - const auto lib = child_chunk.header<ResTable_lib_header>(); - if (!lib) { + const ResTable_lib_header* lib = child_chunk.header<ResTable_lib_header>(); + if (lib == nullptr) { LOG(ERROR) << "RES_TABLE_LIBRARY_TYPE too small."; return {}; } @@ -609,13 +559,10 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, loaded_package->dynamic_package_map_.reserve(dtohl(lib->count)); - const auto entry_begin = child_chunk.data_ptr().convert<ResTable_lib_entry>(); - const auto entry_end = entry_begin + dtohl(lib->count); + const ResTable_lib_entry* const entry_begin = + reinterpret_cast<const ResTable_lib_entry*>(child_chunk.data_ptr()); + const ResTable_lib_entry* const entry_end = entry_begin + dtohl(lib->count); for (auto entry_iter = entry_begin; entry_iter != entry_end; ++entry_iter) { - if (!entry_iter) { - return {}; - } - std::string package_name; util::ReadUtf16StringFromDevice(entry_iter->packageName, arraysize(entry_iter->packageName), &package_name); @@ -633,16 +580,17 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, } break; case RES_TABLE_OVERLAYABLE_TYPE: { - const auto overlayable = child_chunk.header<ResTable_overlayable_header>(); - if (!overlayable) { + const ResTable_overlayable_header* header = + child_chunk.header<ResTable_overlayable_header>(); + if (header == nullptr) { LOG(ERROR) << "RES_TABLE_OVERLAYABLE_TYPE too small."; return {}; } std::string name; - util::ReadUtf16StringFromDevice(overlayable->name, arraysize(overlayable->name), &name); + util::ReadUtf16StringFromDevice(header->name, arraysize(header->name), &name); std::string actor; - util::ReadUtf16StringFromDevice(overlayable->actor, arraysize(overlayable->actor), &actor); + util::ReadUtf16StringFromDevice(header->actor, arraysize(header->actor), &actor); if (loaded_package->overlayable_map_.find(name) != loaded_package->overlayable_map_.end()) { @@ -658,9 +606,9 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, switch (overlayable_child_chunk.type()) { case RES_TABLE_OVERLAYABLE_POLICY_TYPE: { - const auto policy_header = + const ResTable_overlayable_policy_header* policy_header = overlayable_child_chunk.header<ResTable_overlayable_policy_header>(); - if (!policy_header) { + if (policy_header == nullptr) { LOG(ERROR) << "RES_TABLE_OVERLAYABLE_POLICY_TYPE too small."; return {}; } @@ -673,12 +621,10 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, // Retrieve all the resource ids belonging to this policy chunk std::unordered_set<uint32_t> ids; - const auto ids_begin = overlayable_child_chunk.data_ptr().convert<ResTable_ref>(); + const auto ids_begin = + reinterpret_cast<const ResTable_ref*>(overlayable_child_chunk.data_ptr()); const auto ids_end = ids_begin + dtohl(policy_header->entry_count); for (auto id_iter = ids_begin; id_iter != ids_end; ++id_iter) { - if (!id_iter) { - return {}; - } ids.insert(dtohl(id_iter->ident)); } @@ -687,7 +633,7 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, overlayable_info.name = name; overlayable_info.actor = actor; overlayable_info.policy_flags = policy_header->policy_flags; - loaded_package->overlayable_infos_.emplace_back(overlayable_info, ids); + loaded_package->overlayable_infos_.push_back(std::make_pair(overlayable_info, ids)); loaded_package->defines_overlayable_ = true; break; } @@ -737,8 +683,8 @@ std::unique_ptr<const LoadedPackage> LoadedPackage::Load(const Chunk& chunk, bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, package_property_t property_flags) { - incfs::map_ptr<ResTable_header> header = chunk.header<ResTable_header>(); - if (!header) { + const ResTable_header* header = chunk.header<ResTable_header>(); + if (header == nullptr) { LOG(ERROR) << "RES_TABLE_TYPE too small."; return false; } @@ -801,8 +747,7 @@ bool LoadedArsc::LoadTable(const Chunk& chunk, const LoadedIdmap* loaded_idmap, return true; } -std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data, - const size_t length, +std::unique_ptr<const LoadedArsc> LoadedArsc::Load(const StringPiece& data, const LoadedIdmap* loaded_idmap, const package_property_t property_flags) { ATRACE_NAME("LoadedArsc::Load"); @@ -810,7 +755,7 @@ std::unique_ptr<const LoadedArsc> LoadedArsc::Load(incfs::map_ptr<void> data, // Not using make_unique because the constructor is private. std::unique_ptr<LoadedArsc> loaded_arsc(new LoadedArsc()); - ChunkIterator iter(data, length); + ChunkIterator iter(data.data(), data.size()); while (iter.HasNext()) { const Chunk chunk = iter.Next(); switch (chunk.type()) { diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 4010e4e10317..dfb4009b07e2 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -104,26 +104,22 @@ static void strcpy16_dtoh(char16_t* dst, const uint16_t* src, size_t avail) *dst = 0; } -static status_t validate_chunk(const incfs::map_ptr<ResChunk_header>& chunk, +static status_t validate_chunk(const ResChunk_header* chunk, size_t minSize, - const incfs::map_ptr<uint8_t> dataEnd, + const uint8_t* dataEnd, const char* name) { - if (!chunk) { - return BAD_TYPE; - } - const uint16_t headerSize = dtohs(chunk->headerSize); const uint32_t size = dtohl(chunk->size); if (headerSize >= minSize) { if (headerSize <= size) { if (((headerSize|size)&0x3) == 0) { - if ((size_t)size <= (size_t)(dataEnd-chunk.convert<uint8_t>())) { + if ((size_t)size <= (size_t)(dataEnd-((const uint8_t*)chunk))) { return NO_ERROR; } ALOGW("%s data size 0x%x extends beyond resource end %p.", - name, size, (void*)(dataEnd-chunk.convert<uint8_t>())); + name, size, (void*)(dataEnd-((const uint8_t*)chunk))); return BAD_TYPE; } ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", @@ -454,7 +450,7 @@ void ResStringPool::setToEmpty() mHeader = (const ResStringPool_header*) header; } -status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyData) +status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) { if (!data || !size) { return (mError=BAD_TYPE); @@ -471,8 +467,8 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD // The data is at least as big as a ResChunk_header, so we can safely validate the other // header fields. // `data + size` is safe because the source of `size` comes from the kernel/filesystem. - const auto chunk_header = data.convert<ResChunk_header>(); - if (validate_chunk(chunk_header, sizeof(ResStringPool_header), data.convert<uint8_t>() + size, + if (validate_chunk(reinterpret_cast<const ResChunk_header*>(data), sizeof(ResStringPool_header), + reinterpret_cast<const uint8_t*>(data) + size, "ResStringPool_header") != NO_ERROR) { ALOGW("Bad string block: malformed block dimensions"); return (mError=BAD_TYPE); @@ -485,25 +481,16 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD if (mOwnedData == NULL) { return (mError=NO_MEMORY); } - - if (!data.convert<uint8_t>().verify(size)) { - return (mError=NO_MEMORY); - } - - memcpy(mOwnedData, data.unsafe_ptr(), size); + memcpy(mOwnedData, data, size); data = mOwnedData; } // The size has been checked, so it is safe to read the data in the ResStringPool_header // data structure. - const auto header = data.convert<ResStringPool_header>(); - if (!header) { - return (mError=BAD_TYPE); - } + mHeader = (const ResStringPool_header*)data; - mHeader = header.verified(); if (notDeviceEndian) { - ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader.unsafe_ptr()); + ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader); h->header.headerSize = dtohs(mHeader->header.headerSize); h->header.type = dtohs(mHeader->header.type); h->header.size = dtohl(mHeader->header.size); @@ -521,7 +508,8 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD return (mError=BAD_TYPE); } mSize = mHeader->header.size; - mEntries = data.offset(mHeader->header.headerSize).convert<uint32_t>(); + mEntries = (const uint32_t*) + (((const uint8_t*)data)+mHeader->header.headerSize); if (mHeader->stringCount > 0) { if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? @@ -548,7 +536,9 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD return (mError=BAD_TYPE); } - mStrings = data.offset(mHeader->stringsStart).convert<void>(); + mStrings = (const void*) + (((const uint8_t*)data) + mHeader->stringsStart); + if (mHeader->styleCount == 0) { mStringPoolSize = (mSize - mHeader->stringsStart) / charSize; } else { @@ -570,37 +560,31 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD // check invariant: stringCount > 0 requires a string pool to exist if (mStringPoolSize == 0) { - ALOGW("Bad string block: stringCount is %d but pool size is 0\n", - (int)mHeader->stringCount); + ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); return (mError=BAD_TYPE); } if (notDeviceEndian) { size_t i; - auto e = const_cast<uint32_t*>(mEntries.unsafe_ptr()); + uint32_t* e = const_cast<uint32_t*>(mEntries); for (i=0; i<mHeader->stringCount; i++) { - e[i] = dtohl(e[i]); + e[i] = dtohl(mEntries[i]); } if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) { - uint16_t* s = const_cast<uint16_t*>(mStrings.convert<uint16_t>().unsafe_ptr()); + const uint16_t* strings = (const uint16_t*)mStrings; + uint16_t* s = const_cast<uint16_t*>(strings); for (i=0; i<mStringPoolSize; i++) { - s[i] = dtohs(s[i]); + s[i] = dtohs(strings[i]); } } } - if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { - auto end = mStrings.convert<uint8_t>() + (mStringPoolSize-1); - if (!end || end.value() != 0) { - ALOGW("Bad string block: last string is not 0-terminated\n"); - return (mError=BAD_TYPE); - } - } else { - auto end = mStrings.convert<uint16_t>() + (mStringPoolSize-1); - if (!end || end.value() != 0) { - ALOGW("Bad string block: last string is not 0-terminated\n"); - return (mError=BAD_TYPE); - } + if ((mHeader->flags&ResStringPool_header::UTF8_FLAG && + ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) || + (!(mHeader->flags&ResStringPool_header::UTF8_FLAG) && + ((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) { + ALOGW("Bad string block: last string is not 0-terminated\n"); + return (mError=BAD_TYPE); } } else { mStrings = NULL; @@ -615,13 +599,14 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD return (mError=BAD_TYPE); } - if ((mEntryStyles.convert<uint8_t>() - mHeader.convert<uint8_t>()) > (int)size) { + if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { ALOGW("Bad string block: entry of %d styles extends past data size %d\n", - (int)(mEntryStyles.convert<uint8_t>()-mHeader.convert<uint8_t>()), + (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), (int)size); return (mError=BAD_TYPE); } - mStyles = data.offset(mHeader->stylesStart).convert<uint32_t>(); + mStyles = (const uint32_t*) + (((const uint8_t*)data)+mHeader->stylesStart); if (mHeader->stylesStart >= mHeader->header.size) { ALOGW("Bad string block: style pool starts %d, after total size %d\n", (int)mHeader->stylesStart, (int)mHeader->header.size); @@ -632,13 +617,13 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD if (notDeviceEndian) { size_t i; - uint32_t* e = const_cast<uint32_t*>(mEntryStyles.unsafe_ptr()); + uint32_t* e = const_cast<uint32_t*>(mEntryStyles); for (i=0; i<mHeader->styleCount; i++) { - e[i] = dtohl(e[i]); + e[i] = dtohl(mEntryStyles[i]); } - uint32_t* s = const_cast<uint32_t*>(mStyles.unsafe_ptr()); + uint32_t* s = const_cast<uint32_t*>(mStyles); for (i=0; i<mStylePoolSize; i++) { - s[i] = dtohl(s[i]); + s[i] = dtohl(mStyles[i]); } } @@ -646,9 +631,8 @@ status_t ResStringPool::setTo(incfs::map_ptr<void> data, size_t size, bool copyD { htodl(ResStringPool_span::END) }, htodl(ResStringPool_span::END), htodl(ResStringPool_span::END) }; - - auto stylesEnd = mStyles + (mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))); - if (!stylesEnd || memcmp(stylesEnd.unsafe_ptr(), &endSpan, sizeof(endSpan)) != 0) { + if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))], + &endSpan, sizeof(endSpan)) != 0) { ALOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n"); return (mError=BAD_TYPE); } @@ -669,7 +653,7 @@ status_t ResStringPool::getError() const void ResStringPool::uninit() { mError = NO_INIT; - if (mHeader && mCache != NULL) { + if (mHeader != NULL && mCache != NULL) { for (size_t x = 0; x < mHeader->stringCount; x++) { if (mCache[x] != NULL) { free(mCache[x]); @@ -695,21 +679,15 @@ void ResStringPool::uninit() * data encoded. In that case, drop the high bit of the first character and * add it together with the next character. */ -static inline base::expected<size_t, IOError> decodeLength(incfs::map_ptr<uint16_t>* str) +static inline size_t +decodeLength(const uint16_t** str) { - if (UNLIKELY(!*str)) { - return base::unexpected(IOError::PAGES_MISSING); - } - - size_t len = str->value(); - if ((len & 0x8000U) != 0) { - ++(*str); - if (UNLIKELY(!*str)) { - return base::unexpected(IOError::PAGES_MISSING); - } - len = ((len & 0x7FFFU) << 16U) | str->value(); + size_t len = **str; + if ((len & 0x8000) != 0) { + (*str)++; + len = ((len & 0x7FFF) << 16) | **str; } - ++(*str); + (*str)++; return len; } @@ -723,119 +701,82 @@ static inline base::expected<size_t, IOError> decodeLength(incfs::map_ptr<uint16 * data encoded. In that case, drop the high bit of the first character and * add it together with the next character. */ -static inline base::expected<size_t, IOError> decodeLength(incfs::map_ptr<uint8_t>* str) +static inline size_t +decodeLength(const uint8_t** str) { - if (UNLIKELY(!*str)) { - return base::unexpected(IOError::PAGES_MISSING); + size_t len = **str; + if ((len & 0x80) != 0) { + (*str)++; + len = ((len & 0x7F) << 8) | **str; } - - size_t len = str->value(); - if ((len & 0x80U) != 0) { - ++(*str); - if (UNLIKELY(!*str)) { - return base::unexpected(IOError::PAGES_MISSING); - } - len = ((len & 0x7FU) << 8U) | str->value(); - } - ++(*str); + (*str)++; return len; } -base::expected<StringPiece16, NullOrIOError> ResStringPool::stringAt(size_t idx) const +const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; - auto offPtr = mEntries + idx; - if (UNLIKELY(!offPtr)) { - return base::unexpected(IOError::PAGES_MISSING); - } - - const uint32_t off = (offPtr.value())/(isUTF8?sizeof(uint8_t):sizeof(uint16_t)); + const uint32_t off = mEntries[idx]/(isUTF8?sizeof(uint8_t):sizeof(uint16_t)); if (off < (mStringPoolSize-1)) { if (!isUTF8) { - auto strings = mStrings.convert<uint16_t>(); - auto str = strings+off; - - const base::expected<size_t, IOError> u16len = decodeLength(&str); - if (UNLIKELY(!u16len.has_value())) { - return base::unexpected(u16len.error()); - } + const uint16_t* strings = (uint16_t*)mStrings; + const uint16_t* str = strings+off; + *u16len = decodeLength(&str); if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { // Reject malformed (non null-terminated) strings - const auto nullAddress = str + (*u16len); - if (UNLIKELY(!nullAddress)) { - return base::unexpected(IOError::PAGES_MISSING); - } - - if (nullAddress.value() != 0x0000) { - ALOGW("Bad string block: string #%d is not null-terminated", (int)idx); - return base::unexpected(std::nullopt); - } - - if (UNLIKELY(!str.verify(*u16len + 1U))) { - return base::unexpected(IOError::PAGES_MISSING); + if (str[*u16len] != 0x0000) { + ALOGW("Bad string block: string #%d is not null-terminated", + (int)idx); + return NULL; } - - return StringPiece16(reinterpret_cast<const char16_t*>(str.unsafe_ptr()), - *u16len); + return reinterpret_cast<const char16_t*>(str); } else { ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); } } else { - auto strings = mStrings.convert<uint8_t>(); - auto u8str = strings+off; + const uint8_t* strings = (uint8_t*)mStrings; + const uint8_t* u8str = strings+off; - base::expected<size_t, IOError> u16len = decodeLength(&u8str); - if (UNLIKELY(!u16len.has_value())) { - return base::unexpected(u16len.error()); - } - - const base::expected<size_t, IOError> u8len = decodeLength(&u8str); - if (UNLIKELY(!u8len.has_value())) { - return base::unexpected(u8len.error()); - } + *u16len = decodeLength(&u8str); + size_t u8len = decodeLength(&u8str); // encLen must be less than 0x7FFF due to encoding. - if ((uint32_t)(u8str+*u8len-strings) < mStringPoolSize) { + if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { AutoMutex lock(mDecodeLock); if (mCache != NULL && mCache[idx] != NULL) { - return StringPiece16(mCache[idx], *u16len); + return mCache[idx]; } // Retrieve the actual length of the utf8 string if the // encoded length was truncated - auto decodedString = stringDecodeAt(idx, u8str, *u8len); - if (!decodedString.has_value()) { - return base::unexpected(decodedString.error()); + if (stringDecodeAt(idx, u8str, u8len, &u8len) == NULL) { + return NULL; } // Since AAPT truncated lengths longer than 0x7FFF, check // that the bits that remain after truncation at least match // the bits of the actual length - ssize_t actualLen = utf8_to_utf16_length( - reinterpret_cast<const uint8_t*>(decodedString->data()), - decodedString->size()); - - if (actualLen < 0 || ((size_t)actualLen & 0x7FFFU) != *u16len) { + ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); + if (actualLen < 0 || ((size_t)actualLen & 0x7FFF) != *u16len) { ALOGW("Bad string block: string #%lld decoded length is not correct " "%lld vs %llu\n", (long long)idx, (long long)actualLen, (long long)*u16len); - return base::unexpected(std::nullopt); + return NULL; } - u16len = (size_t) actualLen; - auto u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); + *u16len = (size_t) actualLen; + char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); if (!u16str) { ALOGW("No memory when trying to allocate decode cache for string #%d\n", (int)idx); - return base::unexpected(std::nullopt); + return NULL; } - utf8_to_utf16(reinterpret_cast<const uint8_t*>(decodedString->data()), - decodedString->size(), u16str, *u16len + 1); + utf8_to_utf16(u8str, u8len, u16str, *u16len + 1); if (mCache == NULL) { #ifndef __ANDROID__ @@ -852,19 +793,19 @@ base::expected<StringPiece16, NullOrIOError> ResStringPool::stringAt(size_t idx) if (mCache == NULL) { ALOGW("No memory trying to allocate decode cache table of %d bytes\n", (int)(mHeader->stringCount*sizeof(char16_t**))); - return base::unexpected(std::nullopt); + return NULL; } } if (kDebugStringPoolNoisy) { - ALOGI("Caching UTF8 string: %s", u8str.unsafe_ptr()); + ALOGI("Caching UTF8 string: %s", u8str); } mCache[idx] = u16str; - return StringPiece16(u16str, *u16len); + return u16str; } else { ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", - (long long)idx, (long long)(u8str+*u8len-strings), + (long long)idx, (long long)(u8str+u8len-strings), (long long)mStringPoolSize); } } @@ -874,43 +815,33 @@ base::expected<StringPiece16, NullOrIOError> ResStringPool::stringAt(size_t idx) (int)(mStringPoolSize*sizeof(uint16_t))); } } - return base::unexpected(std::nullopt); + return NULL; } -base::expected<StringPiece, NullOrIOError> ResStringPool::string8At(size_t idx) const +const char* ResStringPool::string8At(size_t idx, size_t* outLen) const { if (mError == NO_ERROR && idx < mHeader->stringCount) { if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) { - return base::unexpected(std::nullopt); + return NULL; } - - auto offPtr = mEntries + idx; - if (UNLIKELY(!offPtr)) { - return base::unexpected(IOError::PAGES_MISSING); - } - - const uint32_t off = (offPtr.value())/sizeof(char); + const uint32_t off = mEntries[idx]/sizeof(char); if (off < (mStringPoolSize-1)) { - auto strings = mStrings.convert<uint8_t>(); - auto str = strings+off; + const uint8_t* strings = (uint8_t*)mStrings; + const uint8_t* str = strings+off; // Decode the UTF-16 length. This is not used if we're not // converting to UTF-16 from UTF-8. - const base::expected<size_t, IOError> u16len = decodeLength(&str); - if (UNLIKELY(!u16len)) { - return base::unexpected(u16len.error()); - } + decodeLength(&str); - const base::expected<size_t, IOError> u8len = decodeLength(&str); - if (UNLIKELY(!u8len)) { - return base::unexpected(u8len.error()); - } + const size_t encLen = decodeLength(&str); + *outLen = encLen; + + if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { + return stringDecodeAt(idx, str, encLen, outLen); - if ((uint32_t)(str+*u8len-strings) < mStringPoolSize) { - return stringDecodeAt(idx, str, *u8len); } else { ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+*u8len-strings), (int)mStringPoolSize); + (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); } } else { ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", @@ -918,7 +849,7 @@ base::expected<StringPiece, NullOrIOError> ResStringPool::string8At(size_t idx) (int)(mStringPoolSize*sizeof(uint16_t))); } } - return base::unexpected(std::nullopt); + return NULL; } /** @@ -928,93 +859,74 @@ base::expected<StringPiece, NullOrIOError> ResStringPool::string8At(size_t idx) * bits. Strings that exceed the maximum encode length are not placed into * StringPools in AAPT2. **/ -base::expected<StringPiece, NullOrIOError> ResStringPool::stringDecodeAt( - size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const -{ - const auto strings = mStrings.convert<uint8_t>(); +const char* ResStringPool::stringDecodeAt(size_t idx, const uint8_t* str, + const size_t encLen, size_t* outLen) const { + const uint8_t* strings = (uint8_t*)mStrings; + size_t i = 0, end = encLen; while ((uint32_t)(str+end-strings) < mStringPoolSize) { - const auto nullAddress = str + end; - if (UNLIKELY(!nullAddress)) { - return base::unexpected(IOError::PAGES_MISSING); - } - - if (nullAddress.value() == 0x00) { + if (str[end] == 0x00) { if (i != 0) { ALOGW("Bad string block: string #%d is truncated (actual length is %d)", (int)idx, (int)end); } - if (UNLIKELY(!str.verify(end + 1U))) { - return base::unexpected(IOError::PAGES_MISSING); - } - - return StringPiece((const char*) str.unsafe_ptr(), end); + *outLen = end; + return (const char*)str; } end = (++i << (sizeof(uint8_t) * 8 * 2 - 1)) | encLen; } // Reject malformed (non null-terminated) strings - ALOGW("Bad string block: string #%d is not null-terminated", (int)idx); - return base::unexpected(std::nullopt); + ALOGW("Bad string block: string #%d is not null-terminated", + (int)idx); + return NULL; } -base::expected<String8, IOError> ResStringPool::string8ObjectAt(size_t idx) const +const String8 ResStringPool::string8ObjectAt(size_t idx) const { - const base::expected<StringPiece, NullOrIOError> str = string8At(idx); - if (UNLIKELY(IsIOError(str))) { - return base::unexpected(GetIOError(str.error())); - } - if (str.has_value()) { - return String8(str->data(), str->size()); + size_t len; + const char *str = string8At(idx, &len); + if (str != NULL) { + return String8(str, len); } - const base::expected<StringPiece16, NullOrIOError> str16 = stringAt(idx); - if (UNLIKELY(IsIOError(str16))) { - return base::unexpected(GetIOError(str16.error())); + const char16_t *str16 = stringAt(idx, &len); + if (str16 != NULL) { + return String8(str16, len); } - if (str16.has_value()) { - return String8(str16->data(), str16->size()); - } - return String8(); } -base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool::styleAt( - const ResStringPool_ref& ref) const +const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const { return styleAt(ref.index); } -base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> ResStringPool::styleAt( - size_t idx) const +const ResStringPool_span* ResStringPool::styleAt(size_t idx) const { if (mError == NO_ERROR && idx < mHeader->styleCount) { - auto offPtr = mEntryStyles + idx; - if (UNLIKELY(!offPtr)) { - return base::unexpected(IOError::PAGES_MISSING); - } - - const uint32_t off = ((offPtr.value())/sizeof(uint32_t)); + const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); if (off < mStylePoolSize) { - return (mStyles+off).convert<ResStringPool_span>(); + return (const ResStringPool_span*)(mStyles+off); } else { ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n", (int)idx, (int)(off*sizeof(uint32_t)), (int)(mStylePoolSize*sizeof(uint32_t))); } } - return base::unexpected(std::nullopt); + return NULL; } -base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_t* str, - size_t strLen) const +ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const { if (mError != NO_ERROR) { - return base::unexpected(std::nullopt); + return mError; } + size_t len; + if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) { if (kDebugStringPoolNoisy) { ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string()); @@ -1036,19 +948,17 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ ssize_t mid; while (l <= h) { mid = l + (h - l)/2; - int c = -1; - const base::expected<StringPiece, NullOrIOError> s = string8At(mid); - if (UNLIKELY(IsIOError(s))) { - return base::unexpected(s.error()); - } - if (s.has_value()) { - char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()), - s->size(), convBuffer, convBufferLen); + const uint8_t* s = (const uint8_t*)string8At(mid, &len); + int c; + if (s != NULL) { + char16_t* end = utf8_to_utf16(s, len, convBuffer, convBufferLen); c = strzcmp16(convBuffer, end-convBuffer, str, strLen); + } else { + c = -1; } if (kDebugStringPoolNoisy) { ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - s->data(), c, (int)l, (int)mid, (int)h); + (const char*)s, c, (int)l, (int)mid, (int)h); } if (c == 0) { if (kDebugStringPoolNoisy) { @@ -1071,21 +981,15 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ String8 str8(str, strLen); const size_t str8Len = str8.size(); for (int i=mHeader->stringCount-1; i>=0; i--) { - const base::expected<StringPiece, NullOrIOError> s = string8At(i); - if (UNLIKELY(IsIOError(s))) { - return base::unexpected(s.error()); + const char* s = string8At(i, &len); + if (kDebugStringPoolNoisy) { + ALOGI("Looking at %s, i=%d\n", String8(s).string(), i); } - if (s.has_value()) { + if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) { if (kDebugStringPoolNoisy) { - ALOGI("Looking at %s, i=%d\n", s->data(), i); - } - if (str8Len == s->size() - && memcmp(s->data(), str8.string(), str8Len) == 0) { - if (kDebugStringPoolNoisy) { - ALOGI("MATCH!"); - } - return i; + ALOGI("MATCH!"); } + return i; } } } @@ -1103,14 +1007,11 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ ssize_t mid; while (l <= h) { mid = l + (h - l)/2; - const base::expected<StringPiece16, NullOrIOError> s = stringAt(mid); - if (UNLIKELY(IsIOError(s))) { - return base::unexpected(s.error()); - } - int c = s.has_value() ? strzcmp16(s->data(), s->size(), str, strLen) : -1; + const char16_t* s = stringAt(mid, &len); + int c = s ? strzcmp16(s, len, str, strLen) : -1; if (kDebugStringPoolNoisy) { ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - String8(s->data(), s->size()).string(), c, (int)l, (int)mid, (int)h); + String8(s).string(), c, (int)l, (int)mid, (int)h); } if (c == 0) { if (kDebugStringPoolNoisy) { @@ -1129,15 +1030,11 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ // span tags; since those always appear at the end of the string // block, start searching at the back. for (int i=mHeader->stringCount-1; i>=0; i--) { - const base::expected<StringPiece16, NullOrIOError> s = stringAt(i); - if (UNLIKELY(IsIOError(s))) { - return base::unexpected(s.error()); - } + const char16_t* s = stringAt(i, &len); if (kDebugStringPoolNoisy) { - ALOGI("Looking at %s, i=%d\n", String8(s->data(), s->size()).string(), i); + ALOGI("Looking at %s, i=%d\n", String8(s).string(), i); } - if (s.has_value() && strLen == s->size() && - strzcmp16(s->data(), s->size(), str, strLen) == 0) { + if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) { if (kDebugStringPoolNoisy) { ALOGI("MATCH!"); } @@ -1146,7 +1043,8 @@ base::expected<size_t, NullOrIOError> ResStringPool::indexOfString(const char16_ } } } - return base::unexpected(std::nullopt); + + return NAME_NOT_FOUND; } size_t ResStringPool::size() const @@ -1164,10 +1062,9 @@ size_t ResStringPool::bytes() const return (mError == NO_ERROR) ? mHeader->header.size : 0; } -incfs::map_ptr<void> ResStringPool::data() const +const void* ResStringPool::data() const { - - return mHeader.unsafe_ptr(); + return mHeader; } bool ResStringPool::isSorted() const @@ -1224,7 +1121,7 @@ int32_t ResXMLParser::getCommentID() const const char16_t* ResXMLParser::getComment(size_t* outLen) const { int32_t id = getCommentID(); - return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } uint32_t ResXMLParser::getLineNumber() const @@ -1243,7 +1140,7 @@ int32_t ResXMLParser::getTextID() const const char16_t* ResXMLParser::getText(size_t* outLen) const { int32_t id = getTextID(); - return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } ssize_t ResXMLParser::getTextValue(Res_value* outValue) const @@ -1267,7 +1164,7 @@ const char16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const { int32_t id = getNamespacePrefixID(); //printf("prefix=%d event=%p\n", id, mEventCode); - return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } int32_t ResXMLParser::getNamespaceUriID() const @@ -1282,7 +1179,7 @@ const char16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const { int32_t id = getNamespaceUriID(); //printf("uri=%d event=%p\n", id, mEventCode); - return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } int32_t ResXMLParser::getElementNamespaceID() const @@ -1299,7 +1196,7 @@ int32_t ResXMLParser::getElementNamespaceID() const const char16_t* ResXMLParser::getElementNamespace(size_t* outLen) const { int32_t id = getElementNamespaceID(); - return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } int32_t ResXMLParser::getElementNameID() const @@ -1316,7 +1213,7 @@ int32_t ResXMLParser::getElementNameID() const const char16_t* ResXMLParser::getElementName(size_t* outLen) const { int32_t id = getElementNameID(); - return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } size_t ResXMLParser::getAttributeCount() const @@ -1349,7 +1246,7 @@ const char16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) if (kDebugXMLNoisy) { printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const @@ -1359,7 +1256,7 @@ const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) con if (kDebugXMLNoisy) { printf("getAttributeNamespace 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; } int32_t ResXMLParser::getAttributeNameID(size_t idx) const @@ -1384,7 +1281,7 @@ const char16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const if (kDebugXMLNoisy) { printf("getAttributeName 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const @@ -1394,7 +1291,7 @@ const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const if (kDebugXMLNoisy) { printf("getAttributeName 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? UnpackOptionalString(mTree.mStrings.string8At(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; } uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const @@ -1431,7 +1328,7 @@ const char16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen if (kDebugXMLNoisy) { printf("getAttributeValue 0x%zx=0x%x\n", idx, id); } - return id >= 0 ? UnpackOptionalString(mTree.mStrings.stringAt(id), outLen) : NULL; + return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; } int32_t ResXMLParser::getAttributeDataType(size_t idx) const @@ -3699,10 +3596,9 @@ struct ResTable::PackageGroup ssize_t findType16(const char16_t* type, size_t len) const { const size_t N = packages.size(); for (size_t i = 0; i < N; i++) { - const base::expected<size_t, NullOrIOError> index = - packages[i]->typeStrings.indexOfString(type, len); - if (index.has_value()) { - return *index + packages[i]->typeIdOffset; + ssize_t index = packages[i]->typeStrings.indexOfString(type, len); + if (index >= 0) { + return index + packages[i]->typeIdOffset; } } return -1; @@ -4408,21 +4304,21 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou outName->package = grp->name.string(); outName->packageLen = grp->name.size(); if (allowUtf8) { - outName->type8 = UnpackOptionalString(entry.typeStr.string8(), &outName->typeLen); - outName->name8 = UnpackOptionalString(entry.keyStr.string8(), &outName->nameLen); + outName->type8 = entry.typeStr.string8(&outName->typeLen); + outName->name8 = entry.keyStr.string8(&outName->nameLen); } else { outName->type8 = NULL; outName->name8 = NULL; } if (outName->type8 == NULL) { - outName->type = UnpackOptionalString(entry.typeStr.string16(), &outName->typeLen); + outName->type = entry.typeStr.string16(&outName->typeLen); // If we have a bad index for some reason, we should abort. if (outName->type == NULL) { return false; } } if (outName->name8 == NULL) { - outName->name = UnpackOptionalString(entry.keyStr.string16(), &outName->nameLen); + outName->name = entry.keyStr.string16(&outName->nameLen); // If we have a bad index for some reason, we should abort. if (outName->name == NULL) { return false; @@ -4510,8 +4406,7 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag entry.package->header->index, outValue->dataType, outValue->dataType == Res_value::TYPE_STRING ? - String8(UnpackOptionalString( - entry.package->header->values.stringAt(outValue->data), &len)).string() : + String8(entry.package->header->values.stringAt(outValue->data, &len)).string() : "", outValue->data); } @@ -4567,8 +4462,7 @@ const char16_t* ResTable::valueToString( return NULL; } if (value->dataType == value->TYPE_STRING) { - return UnpackOptionalString(getTableStringBlock(stringBlock)->stringAt(value->data), - outLen); + return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); } // XXX do int to string conversions. return NULL; @@ -5084,13 +4978,15 @@ nope: size_t targetTypeLen = typeLen; do { - auto ti = group->packages[pi]->typeStrings.indexOfString(targetType, targetTypeLen); - if (!ti.has_value()) { + ssize_t ti = group->packages[pi]->typeStrings.indexOfString( + targetType, targetTypeLen); + if (ti < 0) { continue; } - *ti += group->packages[pi]->typeIdOffset; - const uint32_t identifier = findEntry(group, *ti, name, nameLen, + ti += group->packages[pi]->typeIdOffset; + + const uint32_t identifier = findEntry(group, ti, name, nameLen, outTypeSpecFlags); if (identifier != 0) { if (fakePublic && outTypeSpecFlags) { @@ -5113,9 +5009,8 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const const size_t typeCount = typeList.size(); for (size_t i = 0; i < typeCount; i++) { const Type* t = typeList[i]; - const base::expected<size_t, NullOrIOError> ei = - t->package->keyStrings.indexOfString(name, nameLen); - if (!ei.has_value()) { + const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen); + if (ei < 0) { continue; } @@ -5130,7 +5025,7 @@ uint32_t ResTable::findEntry(const PackageGroup* group, ssize_t typeIndex, const continue; } - if (dtohl(entry->key.index) == (size_t) *ei) { + if (dtohl(entry->key.index) == (size_t) ei) { uint32_t resId = Res_MAKEID(group->id - 1, typeIndex, iter.index()); if (outTypeSpecFlags) { Entry result; @@ -6292,9 +6187,8 @@ void ResTable::forEachConfiguration(bool ignoreMipmap, bool ignoreAndroidPackage for (size_t k = 0; k < numTypes; k++) { const Type* type = typeList[k]; const ResStringPool& typeStrings = type->package->typeStrings; - const base::expected<String8, NullOrIOError> typeStr = typeStrings.string8ObjectAt( - type->typeSpec->id - 1); - if (ignoreMipmap && typeStr.has_value() && *typeStr == "mipmap") { + if (ignoreMipmap && typeStrings.string8ObjectAt( + type->typeSpec->id - 1) == "mipmap") { continue; } @@ -6350,18 +6244,24 @@ void ResTable::getLocales(Vector<String8>* locales, bool includeSystemLocales, StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index) : mPool(pool), mIndex(index) {} -base::expected<StringPiece, NullOrIOError> StringPoolRef::string8() const { - if (LIKELY(mPool != NULL)) { - return mPool->string8At(mIndex); +const char* StringPoolRef::string8(size_t* outLen) const { + if (mPool != NULL) { + return mPool->string8At(mIndex, outLen); } - return base::unexpected(std::nullopt); + if (outLen != NULL) { + *outLen = 0; + } + return NULL; } -base::expected<StringPiece16, NullOrIOError> StringPoolRef::string16() const { - if (LIKELY(mPool != NULL)) { - return mPool->stringAt(mIndex); +const char16_t* StringPoolRef::string16(size_t* outLen) const { + if (mPool != NULL) { + return mPool->stringAt(mIndex, outLen); } - return base::unexpected(std::nullopt); + if (outLen != NULL) { + *outLen = 0; + } + return NULL; } bool ResTable::getResourceFlags(uint32_t resID, uint32_t* outFlags) const { @@ -7480,13 +7380,13 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const printf("(dynamic attribute) 0x%08x\n", value.data); } else if (value.dataType == Res_value::TYPE_STRING) { size_t len; - const char* str8 = UnpackOptionalString(pkg->header->values.string8At( - value.data), &len); + const char* str8 = pkg->header->values.string8At( + value.data, &len); if (str8 != NULL) { printf("(string8) \"%s\"\n", normalizeForOutput(str8).string()); } else { - const char16_t* str16 = UnpackOptionalString(pkg->header->values.stringAt( - value.data), &len); + const char16_t* str16 = pkg->header->values.stringAt( + value.data, &len); if (str16 != NULL) { printf("(string16) \"%s\"\n", normalizeForOutput(String8(str16, len).string()).string()); diff --git a/libs/androidfw/ResourceUtils.cpp b/libs/androidfw/ResourceUtils.cpp index a34aa7239250..c63dff8f9104 100644 --- a/libs/androidfw/ResourceUtils.cpp +++ b/libs/androidfw/ResourceUtils.cpp @@ -48,76 +48,61 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin !(has_type_separator && out_type->empty()); } -base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName( - const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref, - const StringPiece& package_name) { - AssetManager2::ResourceName name{ - .package = package_name.data(), - .package_len = package_name.size(), - }; - - if (base::expected<StringPiece, NullOrIOError> type_str = type_string_ref.string8()) { - name.type = type_str->data(); - name.type_len = type_str->size(); - } else if (UNLIKELY(IsIOError(type_str))) { - return base::unexpected(type_str.error()); - } - - if (name.type == nullptr) { - if (base::expected<StringPiece16, NullOrIOError> type16_str = type_string_ref.string16()) { - name.type16 = type16_str->data(); - name.type_len = type16_str->size(); - } else if (!type16_str.has_value()) { - return base::unexpected(type16_str.error()); +bool ToResourceName(const StringPoolRef& type_string_ref, + const StringPoolRef& entry_string_ref, + const StringPiece& package_name, + AssetManager2::ResourceName* out_name) { + out_name->package = package_name.data(); + out_name->package_len = package_name.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; } } - if (base::expected<StringPiece, NullOrIOError> entry_str = entry_string_ref.string8()) { - name.entry = entry_str->data(); - name.entry_len = entry_str->size(); - } else if (UNLIKELY(IsIOError(entry_str))) { - return base::unexpected(entry_str.error()); - } - - if (name.entry == nullptr) { - if (base::expected<StringPiece16, NullOrIOError> entry16_str = entry_string_ref.string16()) { - name.entry16 = entry16_str->data(); - name.entry_len = entry16_str->size(); - } else if (!entry16_str.has_value()) { - return base::unexpected(entry16_str.error()); + 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 name; + return true; } -std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_name) { +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->package != nullptr) { + result.append(resource_name->package, resource_name->package_len); } - if (resource_name.type != nullptr || resource_name.type16 != nullptr) { + 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); + 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)); + result += util::Utf16ToUtf8(StringPiece16(resource_name->type16, resource_name->type_len)); } } - if (resource_name.entry != nullptr || resource_name.entry16 != nullptr) { + 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); + 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)); + result += util::Utf16ToUtf8(StringPiece16(resource_name->entry16, resource_name->entry_len)); } } diff --git a/libs/androidfw/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp index 1c5e5d44c845..b39b5f0b8b36 100644 --- a/libs/androidfw/StreamingZipInflater.cpp +++ b/libs/androidfw/StreamingZipInflater.cpp @@ -70,13 +70,13 @@ StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart, /* * Streaming access to compressed data held in an mmapped region of memory */ -StreamingZipInflater::StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize) { +StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) { mFd = -1; mDataMap = dataMap; mOutTotalSize = uncompSize; - mInTotalSize = dataMap->length(); + mInTotalSize = dataMap->getDataLength(); - mInBuf = (uint8_t*) dataMap->unsafe_data(); // IncFs safety handled in zlib. + mInBuf = (uint8_t*) dataMap->getDataPtr(); mInBufSize = mInTotalSize; mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index 52e7a70521a1..e77ac3df474c 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -233,29 +233,6 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const } /* - * Create a new incfs::IncFsFileMap object that spans the data in "entry". - */ -std::optional<incfs::IncFsFileMap> ZipFileRO::createEntryIncFsFileMap(ZipEntryRO entry) const -{ - const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); - const ZipEntry& ze = zipEntry->entry; - int fd = GetFileDescriptor(mHandle); - size_t actualLen = 0; - - if (ze.method == kCompressStored) { - actualLen = ze.uncompressed_length; - } else { - actualLen = ze.compressed_length; - } - - incfs::IncFsFileMap newMap; - if (!newMap.Create(fd, ze.offset, actualLen, mFileName)) { - return std::nullopt; - } - return std::move(newMap); -} - -/* * Uncompress an entry, in its entirety, into the provided output buffer. * * This doesn't verify the data's CRC, which might be useful for diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp index 58fc5bbbab5e..568e3b63d67f 100644 --- a/libs/androidfw/ZipUtils.cpp +++ b/libs/androidfw/ZipUtils.cpp @@ -40,7 +40,7 @@ class FileReader : public zip_archive::Reader { explicit FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) { } - bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { // Data is usually requested sequentially, so this helps avoid pointless // fseeks every time we perform a read. There's an impedence mismatch // here because the original API was designed around pread and pwrite. @@ -71,7 +71,7 @@ class FdReader : public zip_archive::Reader { explicit FdReader(int fd) : mFd(fd) { } - bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { return android::base::ReadFullyAtOffset(mFd, buf, len, offset); } @@ -81,27 +81,22 @@ class FdReader : public zip_archive::Reader { class BufferReader : public zip_archive::Reader { public: - BufferReader(incfs::map_ptr<void> input, size_t inputSize) : Reader(), - mInput(input.convert<uint8_t>()), + BufferReader(const void* input, size_t inputSize) : Reader(), + mInput(reinterpret_cast<const uint8_t*>(input)), mInputSize(inputSize) { } - bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const override { + bool ReadAtOffset(uint8_t* buf, size_t len, off64_t offset) const { if (mInputSize < len || offset > mInputSize - len) { return false; } - const incfs::map_ptr<uint8_t> pos = mInput.offset(offset); - if (!pos.verify(len)) { - return false; - } - - memcpy(buf, pos.unsafe_ptr(), len); + memcpy(buf, mInput + offset, len); return true; } private: - const incfs::map_ptr<uint8_t> mInput; + const uint8_t* mInput; const size_t mInputSize; }; @@ -143,7 +138,7 @@ class BufferWriter : public zip_archive::Writer { return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0); } -/*static*/ bool ZipUtils::inflateToBuffer(incfs::map_ptr<void> in, void* buf, +/*static*/ bool ZipUtils::inflateToBuffer(const void* in, void* buf, long uncompressedLen, long compressedLen) { BufferReader reader(in, compressedLen); diff --git a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp index 5309ab2b6e20..96d44ab8e45c 100644 --- a/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp +++ b/libs/androidfw/fuzz/resourcefile_fuzzer/resourcefile_fuzzer.cpp @@ -31,6 +31,9 @@ using android::LoadedArsc; using android::StringPiece; extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(data, size); + + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(StringPiece(reinterpret_cast<const char*>(data), size)); + return 0; }
\ No newline at end of file diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h index 80bae20f3419..298509eb37a1 100644 --- a/libs/androidfw/include/androidfw/Asset.h +++ b/libs/androidfw/include/androidfw/Asset.h @@ -23,18 +23,18 @@ #include <stdio.h> #include <sys/types.h> + #include <memory> -#include <optional> #include <android-base/unique_fd.h> -#include <util/map_ptr.h> - #include <utils/Compat.h> #include <utils/Errors.h> #include <utils/String8.h> namespace android { +class FileMap; + /* * Instances of this class provide read-only operations on a byte stream. * @@ -49,8 +49,6 @@ namespace android { class Asset { public: virtual ~Asset(void) = default; - Asset(const Asset& src) = delete; - Asset& operator=(const Asset& src) = delete; static int32_t getGlobalCount(); static String8 getAssetAllocations(); @@ -89,19 +87,8 @@ public: /* * Get a pointer to a buffer with the entire contents of the file. - * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary. - * - * Use this function if the asset can never reside on IncFs. */ - virtual const void* getBuffer(bool aligned) = 0; - - /* - * Get a incfs::map_ptr<void> to a buffer with the entire contents of the file. - * If `aligned` is true, the buffer data will be aligned to a 4-byte boundary. - * - * Use this function if the asset can potentially reside on IncFs. - */ - virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned) = 0; + virtual const void* getBuffer(bool wordAligned) = 0; /* * Get the total amount of data that can be read. @@ -165,6 +152,10 @@ protected: AccessMode getAccessMode(void) const { return mAccessMode; } private: + /* these operations are not implemented */ + Asset(const Asset& src); + Asset& operator=(const Asset& src); + /* AssetManager needs access to our "create" functions */ friend class AssetManager; friend class ApkAssets; @@ -178,7 +169,8 @@ private: /* * Create the asset from a named, compressed file on disk (e.g. ".gz"). */ - static Asset* createFromCompressedFile(const char* fileName, AccessMode mode); + static Asset* createFromCompressedFile(const char* fileName, + AccessMode mode); #if 0 /* @@ -208,21 +200,31 @@ private: /* * Create the asset from a memory-mapped file segment. * - * The asset takes ownership of the incfs::IncFsFileMap and the file descriptor "fd". The - * file descriptor is used to request new file descriptors using "openFileDescriptor". + * The asset takes ownership of the FileMap. + */ + static Asset* createFromUncompressedMap(FileMap* dataMap, AccessMode mode); + + /* + * Create the asset from a memory-mapped file segment. + * + * The asset takes ownership of the FileMap and the file descriptor "fd". The file descriptor is + * used to request new file descriptors using "openFileDescriptor". */ - static std::unique_ptr<Asset> createFromUncompressedMap(incfs::IncFsFileMap&& dataMap, - AccessMode mode, - base::unique_fd fd = {}); + static std::unique_ptr<Asset> createFromUncompressedMap(std::unique_ptr<FileMap> dataMap, + base::unique_fd fd, AccessMode mode); /* * Create the asset from a memory-mapped file segment with compressed * data. * - * The asset takes ownership of the incfs::IncFsFileMap. + * The asset takes ownership of the FileMap. */ - static std::unique_ptr<Asset> createFromCompressedMap(incfs::IncFsFileMap&& dataMap, - size_t uncompressedLen, AccessMode mode); + 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. @@ -250,7 +252,7 @@ private: class _FileAsset : public Asset { public: _FileAsset(void); - ~_FileAsset(void) override; + virtual ~_FileAsset(void); /* * Use a piece of an already-open file. @@ -264,24 +266,21 @@ public: * * On success, the object takes ownership of "dataMap" and "fd". */ - status_t openChunk(incfs::IncFsFileMap&& dataMap, base::unique_fd fd); + status_t openChunk(FileMap* dataMap, base::unique_fd fd); /* * Standard Asset interfaces. */ - ssize_t read(void* buf, size_t count) override; - off64_t seek(off64_t offset, int whence) override; - void close(void) override; - const void* getBuffer(bool aligned) override; - incfs::map_ptr<void> getIncFsBuffer(bool aligned) override; - off64_t getLength(void) const override { return mLength; } - off64_t getRemainingLength(void) const override { return mLength-mOffset; } - int openFileDescriptor(off64_t* outStart, off64_t* outLength) const override; - bool isAllocated(void) const override { return mBuf != NULL; } + virtual ssize_t read(void* buf, size_t count); + virtual off64_t seek(off64_t offset, int whence); + virtual void close(void); + virtual const void* getBuffer(bool wordAligned); + virtual off64_t getLength(void) const { return mLength; } + virtual off64_t getRemainingLength(void) const { return mLength-mOffset; } + virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const; + virtual bool isAllocated(void) const { return mBuf != NULL; } private: - incfs::map_ptr<void> ensureAlignment(const incfs::IncFsFileMap& map); - off64_t mStart; // absolute file offset of start of chunk off64_t mLength; // length of the chunk off64_t mOffset; // current local offset, 0 == mStart @@ -296,8 +295,10 @@ private: */ enum { kReadVsMapThreshold = 4096 }; - unsigned char* mBuf; // for read - std::optional<incfs::IncFsFileMap> mMap; // for memory map + FileMap* mMap; // for memory map + unsigned char* mBuf; // for read + + const void* ensureAlignment(FileMap* map); }; @@ -322,7 +323,7 @@ public: * * On success, the object takes ownership of "fd". */ - status_t openChunk(incfs::IncFsFileMap&& dataMap, size_t uncompressedLen); + status_t openChunk(FileMap* dataMap, size_t uncompressedLen); /* * Standard Asset interfaces. @@ -330,23 +331,24 @@ public: virtual ssize_t read(void* buf, size_t count); virtual off64_t seek(off64_t offset, int whence); virtual void close(void); - virtual const void* getBuffer(bool aligned); - virtual incfs::map_ptr<void> getIncFsBuffer(bool aligned); + virtual const void* getBuffer(bool wordAligned); virtual off64_t getLength(void) const { return mUncompressedLen; } virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; } virtual int openFileDescriptor(off64_t* /* outStart */, off64_t* /* outLength */) const { return -1; } virtual bool isAllocated(void) const { return mBuf != NULL; } private: - off64_t mStart; // offset to start of compressed data - off64_t mCompressedLen; // length of the compressed data - off64_t mUncompressedLen; // length of the uncompressed data - off64_t mOffset; // current offset, 0 == start of uncomp data - int mFd; // for file input - - class StreamingZipInflater* mZipInflater; // for streaming large compressed assets - unsigned char* mBuf; // for getBuffer() - std::optional<incfs::IncFsFileMap> mMap; // for memory-mapped input + off64_t mStart; // offset to start of compressed data + off64_t mCompressedLen; // length of the compressed data + off64_t mUncompressedLen; // length of the uncompressed data + off64_t mOffset; // current offset, 0 == start of uncomp data + + FileMap* mMap; // for memory-mapped input + int mFd; // for file input + + class StreamingZipInflater* mZipInflater; // for streaming large compressed assets + + unsigned char* mBuf; // for getBuffer() }; // need: shared mmap version? diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index a92694c94b9f..30ef25c6a516 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -131,8 +131,8 @@ class AssetManager2 { bool GetOverlayablesToString(const android::StringPiece& package_name, std::string* out) const; - const std::unordered_map<std::string, std::string>* GetOverlayableMapForPackage( - uint32_t package_id) const; + const std::unordered_map<std::string, std::string>* + GetOverlayableMapForPackage(uint32_t package_id) const; // Returns whether the resources.arsc of any loaded apk assets is allocated in RAM (not mmapped). bool ContainsAllocatedTable() const; @@ -145,16 +145,14 @@ class AssetManager2 { return configuration_; } - // Returns all configurations for which there are resources defined, or an I/O error if reading - // resource data failed. - // - // This includes resource configurations in all the ApkAssets set for this AssetManager. + // 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. - base::expected<std::set<ResTable_config>, IOError> GetResourceConfigurations( - bool exclude_system = false, bool exclude_mipmap = false) const; + std::set<ResTable_config> GetResourceConfigurations(bool exclude_system = false, + bool exclude_mipmap = false) const; // Returns all the locales for which there are resources defined. This includes resource // locales in all the ApkAssets set for this AssetManager. @@ -196,119 +194,77 @@ class AssetManager2 { std::unique_ptr<Asset> OpenNonAsset(const std::string& filename, ApkAssetsCookie cookie, Asset::AccessMode mode) const; - // Returns the resource name of the specified resource ID. - // - // Utf8 strings are preferred, and only if they are unavailable are the Utf16 variants populated. - // - // Returns a null error if the name is missing/corrupt, or an I/O error if reading resource data - // failed. - base::expected<ResourceName, NullOrIOError> GetResourceName(uint32_t resid) const; + // Populates the `out_name` parameter with resource name information. + // Utf8 strings are preferred, and only if they are unavailable are + // the Utf16 variants populated. + // Returns false if the resource was not found or the name was missing/corrupt. + bool GetResourceName(uint32_t resid, ResourceName* out_name) const; + + // Populates `out_flags` with the bitmask of configuration axis that this resource varies with. + // See ResTable_config for the list of configuration axis. + // Returns false if the resource was not found. + bool GetResourceFlags(uint32_t resid, uint32_t* out_flags) const; // 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 = {}) const; + + // 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(). + // If `density_override` is non-zero, the configuration to match against is overridden with that + // density. // - // Returns a null error if no resource by that name was found, or an I/O error if reading resource - // data failed. - base::expected<uint32_t, NullOrIOError> GetResourceId( - const std::string& resource_name, const std::string& fallback_type = {}, - const std::string& fallback_package = {}) const; - - struct SelectedValue { - friend AssetManager2; - friend Theme; - SelectedValue() = default; - SelectedValue(const ResolvedBag* bag, const ResolvedBag::Entry& entry) : - cookie(entry.cookie), data(entry.value.data), type(entry.value.dataType), - flags(bag->type_spec_flags), resid(0U), config({}) {}; - - // The cookie representing the ApkAssets in which the value resides. - ApkAssetsCookie cookie = kInvalidCookie; - - // The data for this value, as interpreted according to `type`. - Res_value::data_type data; - - // Type of the data value. - uint8_t type; + // Returns a valid cookie if the resource was found. If the resource was not found, or if the + // resource was a map/bag type, then kInvalidCookie is returned. If `may_be_bag` is false, + // this function logs if the resource was a map/bag type before returning kInvalidCookie. + ApkAssetsCookie GetResource(uint32_t resid, bool may_be_bag, uint16_t density_override, + Res_value* out_value, ResTable_config* out_selected_config, + uint32_t* out_flags) const; + + // 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. This is only initialized if the passed in `in_out_value` is a reference. + // 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, + uint32_t* out_last_reference) const; - // The bitmask of configuration axis that this resource varies with. - // See ResTable_config::CONFIG_*. - uint32_t flags; - - // The resource ID from which this value was resolved. - uint32_t resid; - - // The configuration for which the resolved value was defined. - ResTable_config config; + // Resets the resource resolution structures in preparation for the next resource retrieval. + void ResetResourceResolution() const; - private: - SelectedValue(uint8_t value_type, Res_value::data_type value_data, ApkAssetsCookie cookie, - uint32_t type_flags, uint32_t resid, const ResTable_config& config) : - cookie(cookie), data(value_data), type(value_type), flags(type_flags), - resid(resid), config(config) {}; - }; + // Enables or disables resource resolution logging. Clears stored steps when disabled. + void SetResourceResolutionLoggingEnabled(bool enabled); - // Retrieves the best matching resource value with ID `resid`. - // - // If `may_be_bag` is false, this function logs if the resource was a map/bag type and returns a - // null result. If `density_override` is non-zero, the configuration to match against is - // overridden with that density. - // - // Returns a null error if a best match could not be found, or an I/O error if reading resource - // data failed. - base::expected<SelectedValue, NullOrIOError> GetResource(uint32_t resid, bool may_be_bag = false, - uint16_t density_override = 0U) const; + // Returns formatted log of last resource resolution path, or empty if no resource has been + // resolved yet. + std::string GetLastResourceResolution() const; - // Resolves the resource referenced in `value` if the type is Res_value::TYPE_REFERENCE. - // - // If the data type is not Res_value::TYPE_REFERENCE, no work is done. Configuration flags of the - // values pointed to by the reference are OR'd into `value.flags`. If `cache_value` is true, then - // the resolved value will be cached and used when attempting to resolve the resource id specified - // in `value`. - // - // Returns a null error if the resource could not be resolved, or an I/O error if reading - // resource data failed. - base::expected<std::monostate, NullOrIOError> ResolveReference(SelectedValue& value, - bool cache_value = false) const; + const std::vector<uint32_t> GetBagResIdStack(uint32_t resid); // 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: // - // base::expected<const ResolvedBag*, NullOrIOError> bag = asset_manager->GetBag(id); - // if (bag.has_value()) { - // for (auto iter = begin(*bag); iter != end(*bag); ++iter) { + // const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id); + // if (bag != nullptr) { + // for (auto iter = begin(bag); iter != end(bag); ++iter) { // ... // } // } - // - // Returns a null error if a best match could not be found, or an I/O error if reading resource - // data failed. - base::expected<const ResolvedBag*, NullOrIOError> GetBag(uint32_t resid) const; - - // Retrieves the best matching bag/map resource of the resource referenced in `value`. - // - // If `value.type` is not Res_value::TYPE_REFERENCE, a null result is returned. - // Configuration flags of the bag pointed to by the reference are OR'd into `value.flags`. - // - // Returns a null error if a best match could not be found, or an I/O error if reading resource - // data failed. - base::expected<const ResolvedBag*, NullOrIOError> ResolveBag(SelectedValue& value) const; - - const std::vector<uint32_t> GetBagResIdStack(uint32_t resid) const; - - // Resets the resource resolution structures in preparation for the next resource retrieval. - void ResetResourceResolution() 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; + const ResolvedBag* GetBag(uint32_t resid); // Creates a new Theme from this AssetManager. std::unique_ptr<Theme> NewTheme(); @@ -330,15 +286,11 @@ class AssetManager2 { private: DISALLOW_COPY_AND_ASSIGN(AssetManager2); - struct TypeConfig { - incfs::verified_map_ptr<ResTable_type> type; - ResTable_config config; - }; - // A collection of configurations and their associated ResTable_type that match the current // AssetManager configuration. struct FilteredConfigGroup { - std::vector<TypeConfig> type_configs; + std::vector<ResTable_config> configurations; + std::vector<const ResTable_type*> types; }; // Represents an single package. @@ -379,7 +331,9 @@ class AssetManager2 { }; // Finds the best entry for `resid` from the set of ApkAssets. The entry can be a simple - // Res_value, or a complex map/bag type. Returns a null result if a best entry cannot be found. + // Res_value, or a complex map/bag type. If successful, it is available in `out_entry`. + // Returns kInvalidCookie on failure. Otherwise, the return value is the cookie associated with + // the ApkAssets in which the entry was found. // // `density_override` overrides the density of the current configuration when doing a search. // @@ -393,15 +347,13 @@ class AssetManager2 { // // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. - base::expected<FindEntryResult, NullOrIOError> FindEntry(uint32_t resid, - uint16_t density_override, - bool stop_at_first_match, - bool ignore_configuration) const; + ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, + bool ignore_configuration, FindEntryResult* out_entry) const; - base::expected<FindEntryResult, NullOrIOError> FindEntryInternal( - const PackageGroup& package_group, uint8_t type_idx, uint16_t entry_idx, - const ResTable_config& desired_config, bool stop_at_first_match, - bool ignore_configuration) const; + ApkAssetsCookie FindEntryInternal(const PackageGroup& package_group, uint8_t type_idx, + uint16_t entry_idx, const ResTable_config& desired_config, + bool /*stop_at_first_match*/, + bool ignore_configuration, FindEntryResult* out_entry) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. @@ -420,8 +372,7 @@ class AssetManager2 { // AssetManager2::GetBag(resid) wraps this function to track which resource ids have already // been seen while traversing bag parents. - base::expected<const ResolvedBag*, NullOrIOError> GetBag( - uint32_t resid, std::vector<uint32_t>& child_resids) const; + const ResolvedBag* GetBag(uint32_t resid, std::vector<uint32_t>& child_resids); // The ordered list of ApkAssets to search. These are not owned by the AssetManager, and must // have a longer lifetime. @@ -443,20 +394,19 @@ class AssetManager2 { // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. - mutable std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; + std::unordered_map<uint32_t, util::unique_cptr<ResolvedBag>> cached_bags_; // Cached set of bag resid stacks for each bag. These are cached because they might be requested // a number of times for each view during View inspection. - mutable std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_; - - // Cached set of resolved resource values. - mutable std::unordered_map<uint32_t, SelectedValue> cached_resolved_values_; + std::unordered_map<uint32_t, std::vector<uint32_t>> cached_bag_resid_stacks_; // Whether or not to save resource resolution steps bool resource_resolution_logging_enabled_ = false; struct Resolution { + struct Step { + enum class Type { INITIAL, BETTER_MATCH, @@ -505,53 +455,55 @@ class Theme { public: ~Theme(); - // Applies the style identified by `resid` to this theme. - // - // This can be called multiple times with different styles. By default, any theme attributes that - // are already defined before this call are not overridden. If `force` is set to true, this - // behavior is changed and all theme attributes from the style at `resid` are applied. - // - // Returns a null error if the style could not be applied, or an I/O error if reading resource - // data failed. - base::expected<std::monostate, NullOrIOError> ApplyStyle(uint32_t resid, bool force = false); + // Applies the style identified by `resid` to this theme. This can be called + // multiple times with different styles. By default, any theme attributes that + // are already defined before this call are not overridden. If `force` is set + // to true, this behavior is changed and all theme attributes from the style at + // `resid` are applied. + // Returns false if the style failed to apply. + bool ApplyStyle(uint32_t resid, bool force = false); - // Sets this Theme to be a copy of `other` if `other` has the same AssetManager as this Theme. - // - // If `other` does not have the same AssetManager as this theme, only attributes from ApkAssets - // loaded into both AssetManagers will be copied to this theme. - // - // Returns an I/O error if reading resource data failed. - base::expected<std::monostate, IOError> SetTo(const Theme& other); + // Sets this Theme to be a copy of `o` if `o` has the same AssetManager as this Theme. + // If `o` does not have the same AssetManager as this theme, only attributes from ApkAssets loaded + // into both AssetManagers will be copied to this theme. + void SetTo(const Theme& o); void Clear(); - // Retrieves the value of attribute ID `resid` in the theme. - // - // NOTE: This function does not do reference traversal. If you want to follow references to other - // resources to get the "real" value to use, you need to call ResolveReference() after this - // function. - std::optional<AssetManager2::SelectedValue> GetAttribute(uint32_t resid) const; - - // This is like AssetManager2::ResolveReference(), but also takes care of resolving attribute - // references to the theme. - base::expected<std::monostate, NullOrIOError> ResolveAttributeReference( - AssetManager2::SelectedValue& value) const; + void Dump() const; - AssetManager2* GetAssetManager() { + inline const AssetManager2* GetAssetManager() const { return asset_manager_; } - const AssetManager2* GetAssetManager() const { + inline AssetManager2* GetAssetManager() { return asset_manager_; } // Returns a bit mask of configuration changes that will impact this // theme (and thus require completely reloading it). - uint32_t GetChangingConfigurations() const { + inline uint32_t GetChangingConfigurations() const { return type_spec_flags_; } - void Dump() const; + // Retrieve a value in the theme. If the theme defines this value, returns an asset cookie + // indicating which ApkAssets it came from and populates `out_value` with the value. + // `out_flags` is populated with a bitmask of the configuration axis with which the resource + // varies. + // + // If the attribute is not found, returns kInvalidCookie. + // + // NOTE: This function does not do reference traversal. If you want to follow references to other + // resources to get the "real" value to use, you need to call ResolveReference() after this + // function. + ApkAssetsCookie GetAttribute(uint32_t resid, Res_value* out_value, uint32_t* out_flags) const; + + // This is like AssetManager2::ResolveReference(), but also takes + // care of resolving attribute references to the theme. + ApkAssetsCookie ResolveAttributeReference(ApkAssetsCookie cookie, Res_value* in_out_value, + ResTable_config* in_out_selected_config = nullptr, + uint32_t* in_out_type_spec_flags = nullptr, + uint32_t* out_last_ref = nullptr) const; private: DISALLOW_COPY_AND_ASSIGN(Theme); diff --git a/libs/androidfw/include/androidfw/AttributeResolution.h b/libs/androidfw/include/androidfw/AttributeResolution.h index 1a69a309d365..d71aad29d917 100644 --- a/libs/androidfw/include/androidfw/AttributeResolution.h +++ b/libs/androidfw/include/androidfw/AttributeResolution.h @@ -45,28 +45,20 @@ enum { // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -base::expected<std::monostate, IOError> ResolveAttrs(Theme* theme, uint32_t def_style_attr, - uint32_t def_style_resid, uint32_t* src_values, - size_t src_values_length, uint32_t* attrs, - size_t attrs_length, uint32_t* out_values, - uint32_t* out_indices); +bool ResolveAttrs(Theme* theme, uint32_t def_style_attr, uint32_t def_style_resid, + uint32_t* src_values, size_t src_values_length, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` is NOT optional and must NOT be nullptr. -base::expected<std::monostate, IOError> ApplyStyle(Theme* theme, ResXMLParser* xml_parser, - uint32_t def_style_attr, - uint32_t def_style_resid, - const uint32_t* attrs, size_t attrs_length, - uint32_t* out_values, uint32_t* out_indices); +void ApplyStyle(Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, + uint32_t def_style_resid, const uint32_t* attrs, size_t attrs_length, + uint32_t* out_values, uint32_t* out_indices); // `out_values` must NOT be nullptr. // `out_indices` may be nullptr. -base::expected<std::monostate, IOError> RetrieveAttributes(AssetManager2* assetmanager, - ResXMLParser* xml_parser, - uint32_t* attrs, - size_t attrs_length, - uint32_t* out_values, - uint32_t* out_indices); +bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs, + size_t attrs_length, uint32_t* out_values, uint32_t* out_indices); } // namespace android diff --git a/libs/androidfw/include/androidfw/Chunk.h b/libs/androidfw/include/androidfw/Chunk.h index f1c43b298e53..a0f23433c676 100644 --- a/libs/androidfw/include/androidfw/Chunk.h +++ b/libs/androidfw/include/androidfw/Chunk.h @@ -36,7 +36,7 @@ namespace android { // of the chunk. class Chunk { public: - explicit Chunk(incfs::verified_map_ptr<ResChunk_header> chunk) : device_chunk_(chunk) {} + explicit Chunk(const ResChunk_header* chunk) : device_chunk_(chunk) {} // Returns the type of the chunk. Caller need not worry about endianness. inline int type() const { return dtohs(device_chunk_->type); } @@ -49,18 +49,21 @@ class Chunk { inline size_t header_size() const { return dtohs(device_chunk_->headerSize); } template <typename T, size_t MinSize = sizeof(T)> - inline incfs::map_ptr<T> header() const { - return (header_size() >= MinSize) ? device_chunk_.convert<T>() : nullptr; + inline const T* header() const { + if (header_size() >= MinSize) { + return reinterpret_cast<const T*>(device_chunk_); + } + return nullptr; } - inline incfs::map_ptr<void> data_ptr() const { - return device_chunk_.offset(header_size()); + inline const void* data_ptr() const { + return reinterpret_cast<const uint8_t*>(device_chunk_) + header_size(); } inline size_t data_size() const { return size() - header_size(); } private: - const incfs::verified_map_ptr<ResChunk_header> device_chunk_; + const ResChunk_header* device_chunk_; }; // Provides a Java style iterator over an array of ResChunk_header's. @@ -81,11 +84,11 @@ class Chunk { // class ChunkIterator { public: - ChunkIterator(incfs::map_ptr<void> data, size_t len) - : next_chunk_(data.convert<ResChunk_header>()), + ChunkIterator(const void* data, size_t len) + : next_chunk_(reinterpret_cast<const ResChunk_header*>(data)), len_(len), last_error_(nullptr) { - CHECK((bool) next_chunk_) << "data can't be null"; + CHECK(next_chunk_ != nullptr) << "data can't be nullptr"; if (len_ != 0) { VerifyNextChunk(); } @@ -110,7 +113,7 @@ class ChunkIterator { // Returns false if there was an error. For legacy purposes. bool VerifyNextChunkNonFatal(); - incfs::map_ptr<ResChunk_header> next_chunk_; + const ResChunk_header* next_chunk_; size_t len_; const char* last_error_; bool last_error_was_fatal_ = true; diff --git a/libs/androidfw/include/androidfw/Errors.h b/libs/androidfw/include/androidfw/Errors.h deleted file mode 100644 index 948162d10480..000000000000 --- a/libs/androidfw/include/androidfw/Errors.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROIDFW_ERRORS_H_ -#define ANDROIDFW_ERRORS_H_ - -#include <optional> -#include <variant> - -#include <android-base/result.h> - -namespace android { - -enum class IOError { - // Used when reading a file residing on an IncFs file-system times out. - PAGES_MISSING = -1, -}; - -// Represents an absent result or an I/O error. -using NullOrIOError = std::variant<std::nullopt_t, IOError>; - -// Checks whether the result holds an unexpected I/O error. -template <typename T> -static inline bool IsIOError(const base::expected<T, NullOrIOError> result) { - return !result.has_value() && std::holds_alternative<IOError>(result.error()); -} - -static inline IOError GetIOError(const NullOrIOError& error) { - return std::get<IOError>(error); -} - -} // namespace android - -#endif //ANDROIDFW_ERRORS_H_ diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index fdab03ba2de4..ab0f47f025d2 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -40,8 +40,8 @@ class IdmapResMap; class OverlayStringPool : public ResStringPool { public: virtual ~OverlayStringPool(); - base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const override; - base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const override; + const char16_t* stringAt(size_t idx, size_t* outLen) const override; + const char* string8At(size_t idx, size_t* outLen) const override; size_t size() const override; explicit OverlayStringPool(const LoadedIdmap* loaded_idmap); diff --git a/libs/androidfw/include/androidfw/LoadedArsc.h b/libs/androidfw/include/androidfw/LoadedArsc.h index 17d97a2a2e73..89ff9f52125d 100644 --- a/libs/androidfw/include/androidfw/LoadedArsc.h +++ b/libs/androidfw/include/androidfw/LoadedArsc.h @@ -23,8 +23,7 @@ #include <unordered_map> #include <unordered_set> -#include <android-base/macros.h> -#include <android-base/result.h> +#include "android-base/macros.h" #include "androidfw/ByteBucketArray.h" #include "androidfw/Chunk.h" @@ -50,7 +49,7 @@ struct TypeSpec { // Pointer to the mmapped data where flags are kept. // Flags denote whether the resource entry is public // and under which configurations it varies. - incfs::verified_map_ptr<ResTable_typeSpec> type_spec; + const ResTable_typeSpec* type_spec; // The number of types that follow this struct. // There is a type for each configuration that entries are defined for. @@ -58,17 +57,15 @@ struct TypeSpec { // Trick to easily access a variable number of Type structs // proceeding this struct, and to ensure their alignment. - incfs::verified_map_ptr<ResTable_type> types[0]; + const ResTable_type* types[0]; - base::expected<uint32_t, NullOrIOError> GetFlagsForEntryIndex(uint16_t entry_index) const { + inline uint32_t GetFlagsForEntryIndex(uint16_t entry_index) const { if (entry_index >= dtohl(type_spec->entryCount)) { - return 0U; + return 0u; } - const auto entry_flags_ptr = ((type_spec + 1).convert<uint32_t>() + entry_index); - if (!entry_flags_ptr) { - return base::unexpected(IOError::PAGES_MISSING); - } - return entry_flags_ptr.value(); + + const uint32_t* flags = reinterpret_cast<const uint32_t*>(type_spec + 1); + return flags[entry_index]; } }; @@ -164,17 +161,13 @@ class LoadedPackage { // the default policy in AAPT2 is to build UTF-8 string pools, this needs to change. // Returns a partial resource ID, with the package ID left as 0x00. The caller is responsible // for patching the correct package ID to the resource ID. - base::expected<uint32_t, NullOrIOError> FindEntryByName(const std::u16string& type_name, - const std::u16string& entry_name) const; + uint32_t FindEntryByName(const std::u16string& type_name, const std::u16string& entry_name) const; - static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntry( - incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index); + static const ResTable_entry* GetEntry(const ResTable_type* type_chunk, uint16_t entry_index); - static base::expected<uint32_t, NullOrIOError> GetEntryOffset( - incfs::verified_map_ptr<ResTable_type> type_chunk, uint16_t entry_index); + static uint32_t GetEntryOffset(const ResTable_type* type_chunk, uint16_t entry_index); - static base::expected<incfs::map_ptr<ResTable_entry>, NullOrIOError> GetEntryFromOffset( - incfs::verified_map_ptr<ResTable_type> type_chunk, uint32_t offset); + static const ResTable_entry* GetEntryFromOffset(const ResTable_type* type_chunk, uint32_t offset); // Returns the string pool where type names are stored. inline const ResStringPool* GetTypeStringPool() const { @@ -227,8 +220,7 @@ class LoadedPackage { // Populates a set of ResTable_config structs, possibly excluding configurations defined for // the mipmap type. - base::expected<std::monostate, IOError> CollectConfigurations( - bool exclude_mipmap, std::set<ResTable_config>* out_configs) const; + 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 @@ -308,8 +300,7 @@ class LoadedArsc { // 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(incfs::map_ptr<void> data, - size_t length, + static std::unique_ptr<const LoadedArsc> Load(const StringPiece& data, const LoadedIdmap* loaded_idmap = nullptr, package_property_t property_flags = 0U); diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index fb5f86473189..04ba78b6705d 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -20,10 +20,7 @@ #ifndef _LIBS_UTILS_RESOURCE_TYPES_H #define _LIBS_UTILS_RESOURCE_TYPES_H -#include <android-base/expected.h> - #include <androidfw/Asset.h> -#include <androidfw/Errors.h> #include <androidfw/LocaleData.h> #include <androidfw/StringPiece.h> #include <utils/Errors.h> @@ -500,7 +497,7 @@ public: virtual ~ResStringPool(); void setToEmpty(); - status_t setTo(incfs::map_ptr<void> data, size_t size, bool copyData=false); + status_t setTo(const void* data, size_t size, bool copyData=false); status_t getError() const; @@ -508,49 +505,48 @@ public: // Return string entry as UTF16; if the pool is UTF8, the string will // be converted before returning. - inline base::expected<StringPiece16, NullOrIOError> stringAt( - const ResStringPool_ref& ref) const { - return stringAt(ref.index); + inline const char16_t* stringAt(const ResStringPool_ref& ref, size_t* outLen) const { + return stringAt(ref.index, outLen); } - virtual base::expected<StringPiece16, NullOrIOError> stringAt(size_t idx) const; + virtual const char16_t* stringAt(size_t idx, size_t* outLen) const; // Note: returns null if the string pool is not UTF8. - virtual base::expected<StringPiece, NullOrIOError> string8At(size_t idx) const; + virtual const char* string8At(size_t idx, size_t* outLen) const; // Return string whether the pool is UTF8 or UTF16. Does not allow you // to distinguish null. - base::expected<String8, IOError> string8ObjectAt(size_t idx) const; + const String8 string8ObjectAt(size_t idx) const; - base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt( - const ResStringPool_ref& ref) const; - base::expected<incfs::map_ptr<ResStringPool_span>, NullOrIOError> styleAt(size_t idx) const; + const ResStringPool_span* styleAt(const ResStringPool_ref& ref) const; + const ResStringPool_span* styleAt(size_t idx) const; - base::expected<size_t, NullOrIOError> indexOfString(const char16_t* str, size_t strLen) const; + ssize_t indexOfString(const char16_t* str, size_t strLen) const; virtual size_t size() const; size_t styleCount() const; size_t bytes() const; - incfs::map_ptr<void> data() const; + const void* data() const; + bool isSorted() const; bool isUTF8() const; private: - status_t mError; - void* mOwnedData; - incfs::verified_map_ptr<ResStringPool_header> mHeader; - size_t mSize; - mutable Mutex mDecodeLock; - incfs::map_ptr<uint32_t> mEntries; - incfs::map_ptr<uint32_t> mEntryStyles; - incfs::map_ptr<void> mStrings; - char16_t mutable** mCache; - uint32_t mStringPoolSize; // number of uint16_t - incfs::map_ptr<uint32_t> mStyles; - uint32_t mStylePoolSize; // number of uint32_t - - base::expected<StringPiece, NullOrIOError> stringDecodeAt( - size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const; + status_t mError; + void* mOwnedData; + const ResStringPool_header* mHeader; + size_t mSize; + mutable Mutex mDecodeLock; + const uint32_t* mEntries; + const uint32_t* mEntryStyles; + const void* mStrings; + char16_t mutable** mCache; + uint32_t mStringPoolSize; // number of uint16_t + const uint32_t* mStyles; + uint32_t mStylePoolSize; // number of uint32_t + + const char* stringDecodeAt(size_t idx, const uint8_t* str, const size_t encLen, + size_t* outLen) const; }; /** @@ -562,8 +558,8 @@ public: StringPoolRef() = default; StringPoolRef(const ResStringPool* pool, uint32_t index); - base::expected<StringPiece, NullOrIOError> string8() const; - base::expected<StringPiece16, NullOrIOError> string16() const; + const char* string8(size_t* outLen) const; + const char16_t* string16(size_t* outLen) const; private: const ResStringPool* mPool = nullptr; @@ -1801,16 +1797,6 @@ private: bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue); -template<typename TChar, typename E> -static const TChar* UnpackOptionalString(base::expected<BasicStringPiece<TChar>, E>&& result, - size_t* outLen) { - if (result.has_value()) { - *outLen = result->size(); - return result->data(); - } - return NULL; -} - /** * Convenience class for accessing data in a ResTable resource. */ diff --git a/libs/androidfw/include/androidfw/ResourceUtils.h b/libs/androidfw/include/androidfw/ResourceUtils.h index bd1c44033b88..e649940cdde1 100644 --- a/libs/androidfw/include/androidfw/ResourceUtils.h +++ b/libs/androidfw/include/androidfw/ResourceUtils.h @@ -30,12 +30,13 @@ bool ExtractResourceName(const StringPiece& str, StringPiece* out_package, Strin // Convert a type_string_ref, entry_string_ref, and package to AssetManager2::ResourceName. // Useful for getting resource name without re-running AssetManager2::FindEntry searches. -base::expected<AssetManager2::ResourceName, NullOrIOError> ToResourceName( - const StringPoolRef& type_string_ref, const StringPoolRef& entry_string_ref, - const StringPiece& package_name); +bool ToResourceName(const StringPoolRef& type_string_ref, + const StringPoolRef& entry_string_ref, + const StringPiece& package_name, + AssetManager2::ResourceName* out_name); // Formats a ResourceName to "package:type/entry_name". -std::string ToFormattedResourceString(const AssetManager2::ResourceName& resource_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/include/androidfw/StreamingZipInflater.h b/libs/androidfw/include/androidfw/StreamingZipInflater.h index 472b794b911c..3ace5d5a83cf 100644 --- a/libs/androidfw/include/androidfw/StreamingZipInflater.h +++ b/libs/androidfw/include/androidfw/StreamingZipInflater.h @@ -19,8 +19,6 @@ #include <unistd.h> #include <inttypes.h> - -#include <util/map_ptr.h> #include <zlib.h> #include <utils/Compat.h> @@ -36,7 +34,7 @@ public: StreamingZipInflater(int fd, off64_t compDataStart, size_t uncompSize, size_t compSize); // Flavor that gets the compressed data from an in-memory buffer - StreamingZipInflater(const incfs::IncFsFileMap* dataMap, size_t uncompSize); + StreamingZipInflater(class FileMap* dataMap, size_t uncompSize); ~StreamingZipInflater(); @@ -56,7 +54,7 @@ private: // where to find the uncompressed data int mFd; off64_t mInFileStart; // where the compressed data lives in the file - const incfs::IncFsFileMap* mDataMap; + class FileMap* mDataMap; z_stream mInflateState; bool mStreamNeedsInit; diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index aceeeccccb61..9a3646b49db8 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -22,8 +22,7 @@ #include <sstream> #include <vector> -#include <android-base/macros.h> -#include <util/map_ptr.h> +#include "android-base/macros.h" #include "androidfw/StringPiece.h" @@ -127,11 +126,6 @@ std::string Utf16ToUtf8(const StringPiece16& utf16); std::vector<std::string> SplitAndLowercase(const android::StringPiece& str, char sep); -template <typename T> -bool IsFourByteAligned(const incfs::map_ptr<T>& data) { - return ((size_t)data.unsafe_ptr() & 0x3U) == 0; -} - } // namespace util } // namespace android diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h index 10f6d0655bf4..c221e3b7aeae 100644 --- a/libs/androidfw/include/androidfw/ZipFileRO.h +++ b/libs/androidfw/include/androidfw/ZipFileRO.h @@ -30,20 +30,17 @@ #ifndef __LIBS_ZIPFILERO_H #define __LIBS_ZIPFILERO_H -#include <optional> +#include <utils/Compat.h> +#include <utils/Errors.h> +#include <utils/FileMap.h> +#include <utils/threads.h> + #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h> -#include <util/map_ptr.h> - -#include <utils/Compat.h> -#include <utils/Errors.h> -#include <utils/FileMap.h> -#include <utils/threads.h> - struct ZipArchive; typedef ZipArchive* ZipArchiveHandle; @@ -139,26 +136,14 @@ public: uint32_t* pCrc32) const; /* - * Create a new FileMap object that maps a subset of the archive. For + * Create a new FileMap object that maps a subset of the archive. For * an uncompressed entry this effectively provides a pointer to the * actual data, for a compressed entry this provides the input buffer * for inflate(). - * - * Use this function if the archive can never reside on IncFs. */ FileMap* createEntryFileMap(ZipEntryRO entry) const; /* - * Create a new incfs::IncFsFileMap object that maps a subset of the archive. For - * an uncompressed entry this effectively provides a pointer to the - * actual data, for a compressed entry this provides the input buffer - * for inflate(). - * - * Use this function if the archive can potentially reside on IncFs. - */ - std::optional<incfs::IncFsFileMap> createEntryIncFsFileMap(ZipEntryRO entry) const; - - /* * Uncompress the data into a buffer. Depending on the compression * format, this is either an "inflate" operation or a memcpy. * diff --git a/libs/androidfw/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h index dbfec34fda89..4d35e992cc89 100644 --- a/libs/androidfw/include/androidfw/ZipUtils.h +++ b/libs/androidfw/include/androidfw/ZipUtils.h @@ -25,8 +25,6 @@ #include <stdio.h> #include <time.h> -#include "util/map_ptr.h" - namespace android { /* @@ -42,8 +40,8 @@ public: long compressedLen); static bool inflateToBuffer(int fd, void* buf, long uncompressedLen, long compressedLen); - static bool inflateToBuffer(incfs::map_ptr<void> in, void* buf, - long uncompressedLen, long compressedLen); + static bool inflateToBuffer(const void *in, void* buf, long uncompressedLen, + long compressedLen); /* * Someday we might want to make this generic and handle bzip2 ".bz2" diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index c7ae618991b9..437e14772964 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -139,13 +139,9 @@ static void BM_AssetManagerGetBag(benchmark::State& state) { assets.SetApkAssets({apk.get()}); while (state.KeepRunning()) { - auto bag = assets.GetBag(app::R::style::StyleTwo); - if (!bag.has_value()) { - state.SkipWithError("Failed to load get bag"); - return; - } - const auto bag_end = end(*bag); - for (auto iter = begin(*bag); iter != bag_end; ++iter) { + const ResolvedBag* bag = assets.GetBag(app::R::style::StyleTwo); + const auto bag_end = end(bag); + for (auto iter = begin(bag); iter != bag_end; ++iter) { uint32_t key = iter->key; Res_value value = iter->value; benchmark::DoNotOptimize(key); diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index 3638ce1f92d7..8c255d16fe1f 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -108,18 +108,24 @@ TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get()}); - auto value = assetmanager.GetResource(basic::R::string::test1); - ASSERT_TRUE(value.has_value()); + 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); // Came from our ApkAssets. - EXPECT_EQ(0, value->cookie); + EXPECT_EQ(0, cookie); // It is the default config. - EXPECT_EQ(0, value->config.language[0]); - EXPECT_EQ(0, value->config.language[1]); + EXPECT_EQ(0, selected_config.language[0]); + EXPECT_EQ(0, selected_config.language[1]); // It is a string. - EXPECT_EQ(Res_value::TYPE_STRING, value->type); + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); } TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) { @@ -132,18 +138,24 @@ TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()}); - auto value = assetmanager.GetResource(basic::R::string::test1); - ASSERT_TRUE(value.has_value()); + 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); // Came from our de_fr ApkAssets. - EXPECT_EQ(1, value->cookie); + EXPECT_EQ(1, cookie); // The configuration is German. - EXPECT_EQ('d', value->config.language[0]); - EXPECT_EQ('e', value->config.language[1]); + EXPECT_EQ('d', selected_config.language[0]); + EXPECT_EQ('e', selected_config.language[1]); // It is a string. - EXPECT_EQ(Res_value::TYPE_STRING, value->type); + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); } TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) { @@ -154,35 +166,44 @@ TEST_F(AssetManager2Test, FindsResourceFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - auto value = assetmanager.GetResource(libclient::R::string::foo_one); - ASSERT_TRUE(value.has_value()); + Res_value value; + ResTable_config selected_config; + uint32_t flags; + + ApkAssetsCookie cookie = + assetmanager.GetResource(libclient::R::string::foo_one, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); // Reference comes from libclient. - EXPECT_EQ(2, value->cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(2, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); // Lookup the reference. - value = assetmanager.GetResource(value->data); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(1, value->cookie); - EXPECT_EQ(Res_value::TYPE_STRING, value->type); + cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/, + &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(1, cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); EXPECT_EQ(std::string("Foo from lib_one"), - GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data)); + GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data)); - value = assetmanager.GetResource(libclient::R::string::foo_two); - ASSERT_TRUE(value.has_value()); + cookie = assetmanager.GetResource(libclient::R::string::foo_two, false /*may_be_bag*/, + 0 /*density_override*/, &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); // Reference comes from libclient. - EXPECT_EQ(2, value->cookie); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + EXPECT_EQ(2, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); // Lookup the reference. - value = assetmanager.GetResource(value->data); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(0, value->cookie); - EXPECT_EQ(Res_value::TYPE_STRING, value->type); + cookie = assetmanager.GetResource(value.data, false /* may_be_bag */, 0 /* density_override*/, + &value, &selected_config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(0, cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); EXPECT_EQ(std::string("Foo from lib_two"), - GetStringFromPool(assetmanager.GetStringPoolForCookie(value->cookie), value->data)); + GetStringFromPool(assetmanager.GetStringPoolForCookie(cookie), value.data)); } TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { @@ -190,10 +211,16 @@ TEST_F(AssetManager2Test, FindsResourceFromAppLoadedAsSharedLibrary) { assetmanager.SetApkAssets({appaslib_assets_.get()}); // The appaslib package will have been assigned the package ID 0x02. - auto value = assetmanager.GetResource(fix_package_id(appaslib::R::integer::number1, 0x02)); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); - EXPECT_EQ(fix_package_id(appaslib::R::array::integerArray1, 0x02), value->data); + + Res_value value; + ResTable_config selected_config; + uint32_t flags; + ApkAssetsCookie cookie = assetmanager.GetResource( + fix_package_id(appaslib::R::integer::number1, 0x02), 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(fix_package_id(appaslib::R::array::integerArray1, 0x02), value.data); } TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { @@ -211,40 +238,40 @@ TEST_F(AssetManager2Test, AssignsOverlayPackageIdLast) { return assetmanager.GetAssignedPackageId(apkAssets->GetLoadedArsc()->GetPackages()[0].get()); }; - ASSERT_EQ(0x7f, get_first_package_id(overlayable_assets_.get())); - ASSERT_EQ(0x03, get_first_package_id(overlay_assets_.get())); - ASSERT_EQ(0x02, get_first_package_id(lib_one_assets_.get())); + ASSERT_EQ(get_first_package_id(overlayable_assets_.get()), 0x7f); + ASSERT_EQ(get_first_package_id(overlay_assets_.get()), 0x03); + ASSERT_EQ(get_first_package_id(lib_one_assets_.get()), 0x02); } TEST_F(AssetManager2Test, GetSharedLibraryResourceName) { AssetManager2 assetmanager; assetmanager.SetApkAssets({lib_one_assets_.get()}); - auto name = assetmanager.GetResourceName(lib_one::R::string::foo); - ASSERT_TRUE(name.has_value()); - ASSERT_EQ("com.android.lib_one:string/foo", ToFormattedResourceString(*name)); + AssetManager2::ResourceName name; + ASSERT_TRUE(assetmanager.GetResourceName(lib_one::R::string::foo, &name)); + std::string formatted_name = ToFormattedResourceString(&name); + ASSERT_EQ(formatted_name, "com.android.lib_one:string/foo"); } TEST_F(AssetManager2Test, FindsBagResourceFromSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - auto bag = assetmanager.GetBag(basic::R::array::integerArray1); - ASSERT_TRUE(bag.has_value()); - - ASSERT_EQ(3u, (*bag)->entry_count); + const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1); + ASSERT_NE(nullptr, bag); + ASSERT_EQ(3u, bag->entry_count); - EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[0].value.dataType); - EXPECT_EQ(1u, (*bag)->entries[0].value.data); - EXPECT_EQ(0, (*bag)->entries[0].cookie); + EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[0].value.dataType); + EXPECT_EQ(1u, bag->entries[0].value.data); + EXPECT_EQ(0, bag->entries[0].cookie); - EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[1].value.dataType); - EXPECT_EQ(2u, (*bag)->entries[1].value.data); - EXPECT_EQ(0, (*bag)->entries[1].cookie); + EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[1].value.dataType); + EXPECT_EQ(2u, bag->entries[1].value.data); + EXPECT_EQ(0, bag->entries[1].cookie); - EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), (*bag)->entries[2].value.dataType); - EXPECT_EQ(3u, (*bag)->entries[2].value.data); - EXPECT_EQ(0, (*bag)->entries[2].cookie); + EXPECT_EQ(static_cast<uint8_t>(Res_value::TYPE_INT_DEC), bag->entries[2].value.dataType); + EXPECT_EQ(3u, bag->entries[2].value.data); + EXPECT_EQ(0, bag->entries[2].cookie); } TEST_F(AssetManager2Test, FindsBagResourceFromMultipleApkAssets) {} @@ -257,16 +284,15 @@ TEST_F(AssetManager2Test, FindsBagResourceFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - auto bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); - ASSERT_TRUE(bag.has_value()); - - ASSERT_GE((*bag)->entry_count, 2u); + const ResolvedBag* bag = assetmanager.GetBag(fix_package_id(lib_one::R::style::Theme, 0x03)); + ASSERT_NE(nullptr, bag); + ASSERT_GE(bag->entry_count, 2u); // First two attributes come from lib_one. - EXPECT_EQ(1, (*bag)->entries[0].cookie); - EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key)); - EXPECT_EQ(1, (*bag)->entries[1].cookie); - EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key)); + EXPECT_EQ(1, bag->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); + EXPECT_EQ(1, bag->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); } TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) { @@ -277,17 +303,17 @@ TEST_F(AssetManager2Test, FindsBagResourceFromMultipleSharedLibraries) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - auto bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib); - ASSERT_TRUE(bag.has_value()); - ASSERT_EQ((*bag)->entry_count, 2u); + const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::ThemeMultiLib); + ASSERT_NE(nullptr, bag); + ASSERT_EQ(bag->entry_count, 2u); // First attribute comes from lib_two. - EXPECT_EQ(2, (*bag)->entries[0].cookie); - EXPECT_EQ(0x02, get_package_id((*bag)->entries[0].key)); + EXPECT_EQ(2, bag->entries[0].cookie); + EXPECT_EQ(0x02, get_package_id(bag->entries[0].key)); // The next two attributes come from lib_one. - EXPECT_EQ(2, (*bag)->entries[1].cookie); - EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key)); + EXPECT_EQ(2, bag->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); } TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { @@ -298,79 +324,79 @@ TEST_F(AssetManager2Test, FindsStyleResourceWithParentFromSharedLibrary) { assetmanager.SetApkAssets( {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); - auto bag = assetmanager.GetBag(libclient::R::style::Theme); - ASSERT_TRUE(bag.has_value()); - ASSERT_GE((*bag)->entry_count, 2u); + const ResolvedBag* bag = assetmanager.GetBag(libclient::R::style::Theme); + ASSERT_NE(nullptr, bag); + ASSERT_GE(bag->entry_count, 2u); // First two attributes come from lib_one. - EXPECT_EQ(1, (*bag)->entries[0].cookie); - EXPECT_EQ(0x03, get_package_id((*bag)->entries[0].key)); - EXPECT_EQ(1, (*bag)->entries[1].cookie); - EXPECT_EQ(0x03, get_package_id((*bag)->entries[1].key)); + EXPECT_EQ(1, bag->entries[0].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[0].key)); + EXPECT_EQ(1, bag->entries[1].cookie); + EXPECT_EQ(0x03, get_package_id(bag->entries[1].key)); } TEST_F(AssetManager2Test, MergesStylesWithParentFromSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetApkAssets({style_assets_.get()}); - auto bag_one = assetmanager.GetBag(app::R::style::StyleOne); - ASSERT_TRUE(bag_one.has_value()); - ASSERT_EQ(2u, (*bag_one)->entry_count); + const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleOne); + ASSERT_NE(nullptr, bag_one); + ASSERT_EQ(2u, bag_one->entry_count); - EXPECT_EQ(app::R::attr::attr_one, (*bag_one)->entries[0].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[0].value.dataType); - EXPECT_EQ(1u, (*bag_one)->entries[0].value.data); - EXPECT_EQ(0, (*bag_one)->entries[0].cookie); + EXPECT_EQ(app::R::attr::attr_one, bag_one->entries[0].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[0].value.dataType); + EXPECT_EQ(1u, bag_one->entries[0].value.data); + EXPECT_EQ(0, bag_one->entries[0].cookie); - EXPECT_EQ(app::R::attr::attr_two, (*bag_one)->entries[1].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_one)->entries[1].value.dataType); - EXPECT_EQ(2u, (*bag_one)->entries[1].value.data); - EXPECT_EQ(0, (*bag_one)->entries[1].cookie); + EXPECT_EQ(app::R::attr::attr_two, bag_one->entries[1].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_one->entries[1].value.dataType); + EXPECT_EQ(2u, bag_one->entries[1].value.data); + EXPECT_EQ(0, bag_one->entries[1].cookie); - auto bag_two = assetmanager.GetBag(app::R::style::StyleTwo); - ASSERT_TRUE(bag_two.has_value()); - ASSERT_EQ(6u, (*bag_two)->entry_count); + const ResolvedBag* bag_two = assetmanager.GetBag(app::R::style::StyleTwo); + ASSERT_NE(nullptr, bag_two); + ASSERT_EQ(6u, bag_two->entry_count); // attr_one is inherited from StyleOne. - EXPECT_EQ(app::R::attr::attr_one, (*bag_two)->entries[0].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[0].value.dataType); - EXPECT_EQ(1u, (*bag_two)->entries[0].value.data); - EXPECT_EQ(0, (*bag_two)->entries[0].cookie); - EXPECT_EQ(app::R::style::StyleOne, (*bag_two)->entries[0].style); + EXPECT_EQ(app::R::attr::attr_one, bag_two->entries[0].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[0].value.dataType); + EXPECT_EQ(1u, bag_two->entries[0].value.data); + EXPECT_EQ(0, bag_two->entries[0].cookie); + EXPECT_EQ(app::R::style::StyleOne, bag_two->entries[0].style); // attr_two should be overridden from StyleOne by StyleTwo. - EXPECT_EQ(app::R::attr::attr_two, (*bag_two)->entries[1].key); - EXPECT_EQ(Res_value::TYPE_STRING, (*bag_two)->entries[1].value.dataType); - EXPECT_EQ(0, (*bag_two)->entries[1].cookie); - EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[1].style); + EXPECT_EQ(app::R::attr::attr_two, bag_two->entries[1].key); + EXPECT_EQ(Res_value::TYPE_STRING, bag_two->entries[1].value.dataType); + EXPECT_EQ(0, bag_two->entries[1].cookie); + EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[1].style); EXPECT_EQ(std::string("string"), GetStringFromPool(assetmanager.GetStringPoolForCookie(0), - (*bag_two)->entries[1].value.data)); + bag_two->entries[1].value.data)); // The rest are new attributes. - EXPECT_EQ(app::R::attr::attr_three, (*bag_two)->entries[2].key); - EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, (*bag_two)->entries[2].value.dataType); - EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[2].value.data); - EXPECT_EQ(0, (*bag_two)->entries[2].cookie); - EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[2].style); - - EXPECT_EQ(app::R::attr::attr_five, (*bag_two)->entries[3].key); - EXPECT_EQ(Res_value::TYPE_REFERENCE, (*bag_two)->entries[3].value.dataType); - EXPECT_EQ(app::R::string::string_one, (*bag_two)->entries[3].value.data); - EXPECT_EQ(0, (*bag_two)->entries[3].cookie); - EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[3].style); - - EXPECT_EQ(app::R::attr::attr_indirect, (*bag_two)->entries[4].key); - EXPECT_EQ(Res_value::TYPE_INT_DEC, (*bag_two)->entries[4].value.dataType); - EXPECT_EQ(3u, (*bag_two)->entries[4].value.data); - EXPECT_EQ(0, (*bag_two)->entries[4].cookie); - EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[4].style); - - EXPECT_EQ(app::R::attr::attr_empty, (*bag_two)->entries[5].key); - EXPECT_EQ(Res_value::TYPE_NULL, (*bag_two)->entries[5].value.dataType); - EXPECT_EQ(Res_value::DATA_NULL_EMPTY, (*bag_two)->entries[5].value.data); - EXPECT_EQ(0, (*bag_two)->entries[5].cookie); - EXPECT_EQ(app::R::style::StyleTwo, (*bag_two)->entries[5].style); + EXPECT_EQ(app::R::attr::attr_three, bag_two->entries[2].key); + EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, bag_two->entries[2].value.dataType); + EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[2].value.data); + EXPECT_EQ(0, bag_two->entries[2].cookie); + EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[2].style); + + EXPECT_EQ(app::R::attr::attr_five, bag_two->entries[3].key); + EXPECT_EQ(Res_value::TYPE_REFERENCE, bag_two->entries[3].value.dataType); + EXPECT_EQ(app::R::string::string_one, bag_two->entries[3].value.data); + EXPECT_EQ(0, bag_two->entries[3].cookie); + EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[3].style); + + EXPECT_EQ(app::R::attr::attr_indirect, bag_two->entries[4].key); + EXPECT_EQ(Res_value::TYPE_INT_DEC, bag_two->entries[4].value.dataType); + EXPECT_EQ(3u, bag_two->entries[4].value.data); + EXPECT_EQ(0, bag_two->entries[4].cookie); + EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[4].style); + + EXPECT_EQ(app::R::attr::attr_empty, bag_two->entries[5].key); + EXPECT_EQ(Res_value::TYPE_NULL, bag_two->entries[5].value.dataType); + EXPECT_EQ(Res_value::DATA_NULL_EMPTY, bag_two->entries[5].value.data); + EXPECT_EQ(0, bag_two->entries[5].cookie); + EXPECT_EQ(app::R::style::StyleTwo, bag_two->entries[5].style); } TEST_F(AssetManager2Test, MergeStylesCircularDependency) { @@ -379,41 +405,55 @@ TEST_F(AssetManager2Test, MergeStylesCircularDependency) { // GetBag should stop traversing the parents of styles when a circular // dependency is detected - auto bag = assetmanager.GetBag(app::R::style::StyleFour); - ASSERT_TRUE(bag.has_value()); - ASSERT_EQ(3u, (*bag)->entry_count); + const ResolvedBag* bag_one = assetmanager.GetBag(app::R::style::StyleFour); + ASSERT_NE(nullptr, bag_one); + ASSERT_EQ(3u, bag_one->entry_count); } TEST_F(AssetManager2Test, ResolveReferenceToResource) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - auto value = assetmanager.GetResource(basic::R::integer::ref1); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); - EXPECT_EQ(basic::R::integer::ref2, value->data); - - auto result = assetmanager.ResolveReference(*value); - ASSERT_TRUE(result.has_value()); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(12000u, value->data); - EXPECT_EQ(basic::R::integer::ref2, value->resid); + 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); + + uint32_t last_ref = 0u; + 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); } TEST_F(AssetManager2Test, ResolveReferenceToBag) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - auto value = assetmanager.GetResource(basic::R::integer::number2, true /*may_be_bag*/); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); - EXPECT_EQ(basic::R::array::integerArray1, value->data); - - auto result = assetmanager.ResolveReference(*value); - ASSERT_TRUE(result.has_value()); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); - EXPECT_EQ(basic::R::array::integerArray1, value->data); - EXPECT_EQ(basic::R::array::integerArray1, value->resid); + 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); + + uint32_t last_ref = 0u; + 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); } TEST_F(AssetManager2Test, ResolveDeepIdReference) { @@ -421,25 +461,31 @@ TEST_F(AssetManager2Test, ResolveDeepIdReference) { assetmanager.SetApkAssets({basic_assets_.get()}); // Set up the resource ids - auto high_ref = assetmanager.GetResourceId("@id/high_ref", "values", "com.android.basic"); - ASSERT_TRUE(high_ref.has_value()); - - auto middle_ref = assetmanager.GetResourceId("@id/middle_ref", "values", "com.android.basic"); - ASSERT_TRUE(middle_ref.has_value()); - - auto low_ref = assetmanager.GetResourceId("@id/low_ref", "values", "com.android.basic"); - ASSERT_TRUE(low_ref.has_value()); + const uint32_t high_ref = assetmanager + .GetResourceId("@id/high_ref", "values", "com.android.basic"); + ASSERT_NE(high_ref, 0u); + const uint32_t middle_ref = assetmanager + .GetResourceId("@id/middle_ref", "values", "com.android.basic"); + ASSERT_NE(middle_ref, 0u); + const uint32_t low_ref = assetmanager + .GetResourceId("@id/low_ref", "values", "com.android.basic"); + ASSERT_NE(low_ref, 0u); // Retrieve the most shallow resource - auto value = assetmanager.GetResource(*high_ref); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); - EXPECT_EQ(*middle_ref, value->data);; + Res_value value; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = assetmanager.GetResource(high_ref, false /*may_be_bag*/, + 0 /*density_override*/, + &value, &config, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(middle_ref, value.data); // Check that resolving the reference resolves to the deepest id - auto result = assetmanager.ResolveReference(*value); - ASSERT_TRUE(result.has_value()); - EXPECT_EQ(*low_ref, value->resid); + uint32_t last_ref = high_ref; + assetmanager.ResolveReference(cookie, &value, &config, &flags, &last_ref); + EXPECT_EQ(last_ref, low_ref); } TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) { @@ -449,16 +495,16 @@ TEST_F(AssetManager2Test, KeepLastReferenceIdUnmodifiedIfNoReferenceIsResolved) ResTable_config selected_config; memset(&selected_config, 0, sizeof(selected_config)); - // Create some kind of value that is NOT a reference. - AssetManager2::SelectedValue value{}; - value.cookie = 1; - value.type = Res_value::TYPE_STRING; - value.resid = basic::R::string::test1; + uint32_t flags = 0u; - auto result = assetmanager.ResolveReference(value); - ASSERT_TRUE(result.has_value()); - EXPECT_EQ(1, value.cookie); - EXPECT_EQ(basic::R::string::test1, value.resid); + // Create some kind of Res_value that is NOT a reference. + Res_value value; + value.dataType = Res_value::TYPE_STRING; + value.data = 0; + + uint32_t last_ref = basic::R::string::test1; + EXPECT_EQ(1, assetmanager.ResolveReference(1, &value, &selected_config, &flags, &last_ref)); + EXPECT_EQ(basic::R::string::test1, last_ref); } static bool IsConfigurationPresent(const std::set<ResTable_config>& configurations, @@ -470,45 +516,43 @@ TEST_F(AssetManager2Test, GetResourceConfigurations) { AssetManager2 assetmanager; assetmanager.SetApkAssets({system_assets_.get(), basic_de_fr_assets_.get()}); - auto configurations = assetmanager.GetResourceConfigurations(); - ASSERT_TRUE(configurations.has_value()); + 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()); + 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)); + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); expected_config.language[0] = 'd'; expected_config.language[1] = 'e'; - EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); expected_config.language[0] = 'f'; expected_config.language[1] = 'r'; - EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); // Take out the system assets. configurations = assetmanager.GetResourceConfigurations(true /* exclude_system */); - ASSERT_TRUE(configurations.has_value()); // We expect de and fr from basic_de_fr assets. - EXPECT_EQ(2u, configurations->size()); + EXPECT_EQ(2u, configurations.size()); expected_config.language[0] = 's'; expected_config.language[1] = 'v'; - EXPECT_FALSE(IsConfigurationPresent(*configurations, expected_config)); + EXPECT_FALSE(IsConfigurationPresent(configurations, expected_config)); expected_config.language[0] = 'd'; expected_config.language[1] = 'e'; - EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); expected_config.language[0] = 'f'; expected_config.language[1] = 'r'; - EXPECT_TRUE(IsConfigurationPresent(*configurations, expected_config)); + EXPECT_TRUE(IsConfigurationPresent(configurations, expected_config)); } TEST_F(AssetManager2Test, GetResourceLocales) { @@ -534,17 +578,12 @@ TEST_F(AssetManager2Test, GetResourceId) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_.get()}); - auto resid = assetmanager.GetResourceId("com.android.basic:layout/main", "", ""); - ASSERT_TRUE(resid.has_value()); - EXPECT_EQ(basic::R::layout::main, *resid); - - resid = assetmanager.GetResourceId("layout/main", "", "com.android.basic"); - ASSERT_TRUE(resid.has_value()); - EXPECT_EQ(basic::R::layout::main, *resid); - - resid = assetmanager.GetResourceId("main", "layout", "com.android.basic"); - ASSERT_TRUE(resid.has_value()); - EXPECT_EQ(basic::R::layout::main, *resid); + 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) { @@ -619,8 +658,14 @@ TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) { assetmanager.SetApkAssets({basic_assets_.get()}); assetmanager.SetResourceResolutionLoggingEnabled(false); - auto value = assetmanager.GetResource(basic::R::string::test1); - ASSERT_TRUE(value.has_value()); + 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); @@ -648,12 +693,17 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get()}); - auto value = assetmanager.GetResource(basic::R::string::test1); - ASSERT_TRUE(value.has_value()); + 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); + 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) { @@ -667,14 +717,17 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get(), basic_de_fr_assets_.get()}); - auto value = assetmanager.GetResource(basic::R::string::test1); - ASSERT_TRUE(value.has_value()); + 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); + 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) { @@ -686,8 +739,14 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_.get()}); - auto value = assetmanager.GetResource(basic::R::string::test1); - ASSERT_TRUE(value.has_value()); + 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); diff --git a/libs/androidfw/tests/AttributeResolution_bench.cpp b/libs/androidfw/tests/AttributeResolution_bench.cpp index ddd8ab820cb1..fa300c50218a 100644 --- a/libs/androidfw/tests/AttributeResolution_bench.cpp +++ b/libs/androidfw/tests/AttributeResolution_bench.cpp @@ -108,20 +108,27 @@ static void BM_ApplyStyleFramework(benchmark::State& state) { device_config.screenHeightDp = 1024; device_config.sdkVersion = 27; - auto value = assetmanager.GetResource(basic::R::layout::layoutt); - if (!value.has_value()) { + Res_value value; + ResTable_config config; + uint32_t flags = 0u; + ApkAssetsCookie cookie = + assetmanager.GetResource(basic::R::layout::layoutt, false /*may_be_bag*/, + 0u /*density_override*/, &value, &config, &flags); + if (cookie == kInvalidCookie) { state.SkipWithError("failed to find R.layout.layout"); return; } - auto layout_path = assetmanager.GetStringPoolForCookie(value->cookie)->string8At(value->data); - if (!layout_path.has_value()) { + size_t len = 0u; + const char* layout_path = + assetmanager.GetStringPoolForCookie(cookie)->string8At(value.data, &len); + if (layout_path == nullptr || len == 0u) { state.SkipWithError("failed to lookup layout path"); return; } - std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset(layout_path->to_string(), value->cookie, - Asset::ACCESS_BUFFER); + std::unique_ptr<Asset> asset = assetmanager.OpenNonAsset( + StringPiece(layout_path, len).to_string(), cookie, Asset::ACCESS_BUFFER); if (asset == nullptr) { state.SkipWithError("failed to load layout"); return; diff --git a/libs/androidfw/tests/AttributeResolution_test.cpp b/libs/androidfw/tests/AttributeResolution_test.cpp index bb9129ad01c8..24361b5817f4 100644 --- a/libs/androidfw/tests/AttributeResolution_test.cpp +++ b/libs/androidfw/tests/AttributeResolution_test.cpp @@ -77,9 +77,9 @@ TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { {fix_package_id(R::attr::attr_one, 0x02), fix_package_id(R::attr::attr_two, 0x02)}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; std::array<uint32_t, attrs.size() + 1> indices; - ASSERT_TRUE(ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, - fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), - values.data(), indices.data()).has_value()); + ApplyStyle(theme.get(), nullptr /*xml_parser*/, 0u /*def_style_attr*/, + fix_package_id(R::style::StyleOne, 0x02), attrs.data(), attrs.size(), values.data(), + indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; @@ -102,7 +102,7 @@ TEST(AttributeResolutionLibraryTest, ApplyStyleWithDefaultStyleResId) { TEST_F(AttributeResolutionTest, Theme) { std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value()); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array<uint32_t, 5> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_empty}}; @@ -110,7 +110,7 @@ TEST_F(AttributeResolutionTest, Theme) { ASSERT_TRUE(ResolveAttrs(theme.get(), 0u /*def_style_attr*/, 0u /*def_style_res*/, nullptr /*src_values*/, 0 /*src_values_length*/, attrs.data(), - attrs.size(), values.data(), nullptr /*out_indices*/).has_value()); + attrs.size(), values.data(), nullptr /*out_indices*/)); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; @@ -162,7 +162,7 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; ASSERT_TRUE(RetrieveAttributes(&assetmanager_, &xml_parser_, attrs.data(), attrs.size(), - values.data(), nullptr /*out_indices*/).has_value()); + values.data(), nullptr /*out_indices*/)); uint32_t* values_cursor = values.data(); EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]); @@ -207,15 +207,15 @@ TEST_F(AttributeResolutionXmlTest, XmlParser) { TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) { std::unique_ptr<Theme> theme = assetmanager_.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo).has_value()); + ASSERT_TRUE(theme->ApplyStyle(R::style::StyleTwo)); std::array<uint32_t, 6> attrs{{R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four, R::attr::attr_five, R::attr::attr_empty}}; std::array<uint32_t, attrs.size() * STYLE_NUM_ENTRIES> values; std::array<uint32_t, attrs.size() + 1> indices; - ASSERT_TRUE(ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, - attrs.data(), attrs.size(), values.data(), indices.data()).has_value()); + ApplyStyle(theme.get(), &xml_parser_, 0u /*def_style_attr*/, 0u /*def_style_res*/, attrs.data(), + attrs.size(), values.data(), indices.data()); const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC; diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 0fa0573bcbb8..faddfe599af4 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -71,9 +71,15 @@ void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_ assetmanager.SetConfiguration(*config); } + Res_value value; + ResTable_config selected_config; + uint32_t flags; + uint32_t last_id = 0u; + while (state.KeepRunning()) { - auto value = assetmanager.GetResource(resid); - assetmanager.ResolveReference(*value); + ApkAssetsCookie cookie = assetmanager.GetResource( + resid, false /* may_be_bag */, 0u /* density_override */, &value, &selected_config, &flags); + assetmanager.ResolveReference(cookie, &value, &selected_config, &flags, &last_id); } } diff --git a/libs/androidfw/tests/CommonHelpers.cpp b/libs/androidfw/tests/CommonHelpers.cpp index 3396729536a4..faa5350f9ecc 100644 --- a/libs/androidfw/tests/CommonHelpers.cpp +++ b/libs/androidfw/tests/CommonHelpers.cpp @@ -58,9 +58,8 @@ const std::string& GetTestDataPath() { } std::string GetStringFromPool(const ResStringPool* pool, uint32_t idx) { - auto str = pool->string8ObjectAt(idx); - CHECK(str.has_value()) << "failed to find string entry"; - return std::string(str->string(), str->length()); + String8 str = pool->string8ObjectAt(idx); + return std::string(str.string(), str.length()); } } // namespace android diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 3f0c7cbc8ffc..7aa0dbbafab3 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -62,10 +62,10 @@ class IdmapTest : public ::testing::Test { std::unique_ptr<const ApkAssets> overlayable_assets_; }; -std::string GetStringFromApkAssets(const AssetManager2& asset_manager, - const AssetManager2::SelectedValue& value) { +std::string GetStringFromApkAssets(const AssetManager2& asset_manager, const Res_value& value, + ApkAssetsCookie cookie) { auto assets = asset_manager.GetApkAssets(); - const ResStringPool* string_pool = assets[value.cookie]->GetLoadedArsc()->GetStringPool(); + const ResStringPool* string_pool = assets[cookie]->GetLoadedArsc()->GetStringPool(); return GetStringFromPool(string_pool, value.data); } @@ -75,88 +75,117 @@ TEST_F(IdmapTest, OverlayOverridesResourceValue) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - - auto value = asset_manager.GetResource(overlayable::R::string::overlayable5); - ASSERT_TRUE(value.has_value()); - ASSERT_EQ(value->cookie, 2U); - ASSERT_EQ(value->type, Res_value::TYPE_STRING); - ASSERT_EQ("Overlay One", GetStringFromApkAssets(asset_manager, *value)); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable5, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Overlay One"); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingDifferentPackage) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - - auto value = asset_manager.GetResource(overlayable::R::string::overlayable10); - ASSERT_TRUE(value.has_value()); - ASSERT_EQ(value->cookie, 0U); - ASSERT_EQ(value->type, Res_value::TYPE_STRING); - ASSERT_EQ("yes", GetStringFromApkAssets(asset_manager, *value)); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable10, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 0U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "yes"); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInternalResource) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - - auto value = asset_manager.GetResource(overlayable::R::string::overlayable8); - ASSERT_TRUE(value.has_value()); - ASSERT_EQ(value->cookie, 2U); - ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE); - ASSERT_EQ(value->data, (overlay::R::string::internal & 0x00ffffffU) | (0x02U << 24)); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable8, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE); + ASSERT_EQ(val.data, (overlay::R::string::internal & 0x00ffffff) | (0x02 << 24)); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineInteger) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - - auto value = asset_manager.GetResource(overlayable::R::integer::config_integer); - ASSERT_TRUE(value.has_value()); - ASSERT_EQ(value->cookie, 2U); - ASSERT_EQ(value->type, Res_value::TYPE_INT_DEC); - ASSERT_EQ(value->data, 42); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::integer::config_integer, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_INT_DEC); + ASSERT_EQ(val.data, 42); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingInlineString) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - - auto value = asset_manager.GetResource(overlayable::R::string::overlayable11); - ASSERT_TRUE(value.has_value()); - ASSERT_EQ(value->cookie, 2U); - ASSERT_EQ(value->type, Res_value::TYPE_STRING); - ASSERT_EQ("Hardcoded string", GetStringFromApkAssets(asset_manager, *value)); + Res_value val; + ResTable_config config; + uint32_t flags; + + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "Hardcoded string"); } TEST_F(IdmapTest, OverlayOverridesResourceValueUsingOverlayingResource) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - - auto value = asset_manager.GetResource(overlayable::R::string::overlayable9); - ASSERT_TRUE(value.has_value()); - ASSERT_EQ(value->cookie, 2U); - ASSERT_EQ(value->type, Res_value::TYPE_REFERENCE); - ASSERT_EQ(value->data, overlayable::R::string::overlayable7); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable9, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_REFERENCE); + ASSERT_EQ(val.data, overlayable::R::string::overlayable7); } TEST_F(IdmapTest, OverlayOverridesXmlParser) { AssetManager2 asset_manager; asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - - auto value = asset_manager.GetResource(overlayable::R::layout::hello_view); - ASSERT_TRUE(value.has_value()); - ASSERT_EQ(value->cookie, 2U); - ASSERT_EQ(value->type, Res_value::TYPE_STRING); - ASSERT_EQ("res/layout/hello_view.xml", GetStringFromApkAssets(asset_manager, *value)); - - auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", value->cookie, + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::layout::hello_view, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + ASSERT_EQ(cookie, 2U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "res/layout/hello_view.xml"); + + auto asset = asset_manager.OpenNonAsset("res/layout/hello_view.xml", cookie, Asset::ACCESS_RANDOM); - auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(value->cookie); + auto dynamic_ref_table = asset_manager.GetDynamicRefTableForCookie(cookie); auto xml_tree = util::make_unique<ResXMLTree>(std::move(dynamic_ref_table)); status_t err = xml_tree->setTo(asset->getBuffer(true), asset->getLength(), false); ASSERT_EQ(err, NO_ERROR); @@ -187,24 +216,32 @@ TEST_F(IdmapTest, OverlaidResourceHasSameName) { asset_manager.SetApkAssets({system_assets_.get(), overlayable_assets_.get(), overlay_assets_.get()}); - auto name = asset_manager.GetResourceName(overlayable::R::string::overlayable9); - ASSERT_TRUE(name.has_value()); - ASSERT_EQ("com.android.overlayable", std::string(name->package)); - ASSERT_EQ(std::u16string(u"string"), std::u16string(name->type16)); - ASSERT_EQ("overlayable9", std::string(name->entry)); + AssetManager2::ResourceName name; + ASSERT_TRUE(asset_manager.GetResourceName(overlayable::R::string::overlayable9, &name)); + ASSERT_EQ(std::string(name.package), "com.android.overlayable"); + ASSERT_EQ(String16(name.type16), u"string"); + ASSERT_EQ(std::string(name.entry), "overlayable9"); } TEST_F(IdmapTest, OverlayLoaderInterop) { + std::string contents; auto loader_assets = ApkAssets::LoadTable("loader/resources.arsc", PROPERTY_LOADER); + AssetManager2 asset_manager; asset_manager.SetApkAssets({overlayable_assets_.get(), loader_assets.get(), overlay_assets_.get()}); - auto value = asset_manager.GetResource(overlayable::R::string::overlayable11); - ASSERT_TRUE(value.has_value()); - ASSERT_EQ(1U, value->cookie); - ASSERT_EQ(Res_value::TYPE_STRING, value->type); - ASSERT_EQ("loader", GetStringFromApkAssets(asset_manager, *value)); + Res_value val; + ResTable_config config; + uint32_t flags; + ApkAssetsCookie cookie = asset_manager.GetResource(overlayable::R::string::overlayable11, + false /* may_be_bag */, + 0 /* density_override */, &val, &config, + &flags); + std::cout << asset_manager.GetLastResourceResolution(); + ASSERT_EQ(cookie, 1U); + ASSERT_EQ(val.dataType, Res_value::TYPE_STRING); + ASSERT_EQ(GetStringFromApkAssets(asset_manager, val, cookie), "loader"); } TEST_F(IdmapTest, OverlayAssetsIsUpToDate) { diff --git a/libs/androidfw/tests/LoadedArsc_test.cpp b/libs/androidfw/tests/LoadedArsc_test.cpp index 63574110a817..2d69dfe4f429 100644 --- a/libs/androidfw/tests/LoadedArsc_test.cpp +++ b/libs/androidfw/tests/LoadedArsc_test.cpp @@ -50,8 +50,7 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/styles/styles.apk", "resources.arsc", &contents)); - auto loaded_arsc = LoadedArsc::Load(reinterpret_cast<const void*>(contents.data()), - contents.length()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -67,8 +66,9 @@ TEST(LoadedArscTest, LoadSinglePackageArsc) { ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - auto type = type_spec->types[0]; - ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value()); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSparseEntryApp) { @@ -76,8 +76,7 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/sparse/sparse.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -91,8 +90,9 @@ TEST(LoadedArscTest, LoadSparseEntryApp) { ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - auto type = type_spec->types[0]; - ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value()); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } TEST(LoadedArscTest, LoadSharedLibrary) { @@ -100,8 +100,7 @@ TEST(LoadedArscTest, LoadSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/lib_one/lib_one.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -121,8 +120,7 @@ TEST(LoadedArscTest, LoadAppLinkedAgainstSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/libclient/libclient.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -147,10 +145,8 @@ TEST(LoadedArscTest, LoadAppAsSharedLibrary) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/appaslib/appaslib.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length(), - nullptr /* loaded_idmap */, - PROPERTY_DYNAMIC); + std::unique_ptr<const LoadedArsc> loaded_arsc = + LoadedArsc::Load(StringPiece(contents), nullptr /* loaded_idmap */, PROPERTY_DYNAMIC); ASSERT_THAT(loaded_arsc, NotNull()); const auto& packages = loaded_arsc->GetPackages(); @@ -163,8 +159,7 @@ TEST(LoadedArscTest, LoadFeatureSplit) { std::string contents; ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/feature/feature.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -177,12 +172,15 @@ TEST(LoadedArscTest, LoadFeatureSplit) { const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(type_index); ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); - auto type_name16 = package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1); - ASSERT_TRUE(type_name16.has_value()); - EXPECT_THAT(util::Utf16ToUtf8(*type_name16), StrEq("string")); + size_t len; + const char16_t* type_name16 = + package->GetTypeStringPool()->stringAt(type_spec->type_spec->id - 1, &len); + ASSERT_THAT(type_name16, NotNull()); + EXPECT_THAT(util::Utf16ToUtf8(StringPiece16(type_name16, len)), StrEq("string")); - ASSERT_TRUE(LoadedPackage::GetEntry(type_spec->types[0], entry_index).has_value()); + ASSERT_THAT(LoadedPackage::GetEntry(type_spec->types[0], entry_index), NotNull()); } // AAPT(2) generates resource tables with chunks in a certain order. The rule is that @@ -207,8 +205,7 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { ReadFileFromZipToString(GetTestDataPath() + "/out_of_order_types/out_of_order_types.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); ASSERT_THAT(loaded_arsc->GetPackages(), SizeIs(1u)); @@ -218,10 +215,12 @@ TEST(LoadedArscTest, LoadOutOfOrderTypeSpecs) { const TypeSpec* type_spec = package->GetTypeSpecByTypeIndex(0); ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); type_spec = package->GetTypeSpecByTypeIndex(1); ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); + ASSERT_THAT(type_spec->types[0], NotNull()); } TEST(LoadedArscTest, LoadOverlayable) { @@ -229,8 +228,7 @@ TEST(LoadedArscTest, LoadOverlayable) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = loaded_arsc->GetPackageById( @@ -274,8 +272,7 @@ TEST(LoadedArscTest, ResourceIdentifierIterator) { ASSERT_TRUE( ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_NE(nullptr, loaded_arsc); const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); @@ -323,8 +320,7 @@ TEST(LoadedArscTest, GetOverlayableMap) { ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlayable/overlayable.apk", "resources.arsc", &contents)); - std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(contents.data(), - contents.length()); + std::unique_ptr<const LoadedArsc> loaded_arsc = LoadedArsc::Load(StringPiece(contents)); ASSERT_NE(nullptr, loaded_arsc); const std::vector<std::unique_ptr<const LoadedPackage>>& packages = loaded_arsc->GetPackages(); @@ -349,7 +345,7 @@ TEST(LoadedArscTest, LoadCustomLoader) { asset->getLength()); std::unique_ptr<const LoadedArsc> loaded_arsc = - LoadedArsc::Load(data.data(), data.length(), nullptr, PROPERTY_LOADER); + LoadedArsc::Load(data, nullptr, PROPERTY_LOADER); ASSERT_THAT(loaded_arsc, NotNull()); const LoadedPackage* package = @@ -365,8 +361,9 @@ TEST(LoadedArscTest, LoadCustomLoader) { ASSERT_THAT(type_spec, NotNull()); ASSERT_THAT(type_spec->type_count, Ge(1u)); - auto type = type_spec->types[0]; - ASSERT_TRUE(LoadedPackage::GetEntry(type, entry_index).has_value()); + const ResTable_type* type = type_spec->types[0]; + ASSERT_THAT(type, NotNull()); + ASSERT_THAT(LoadedPackage::GetEntry(type, entry_index), NotNull()); } // structs with size fields (like Res_value, ResTable_entry) should be diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp index 9aeb00c47e63..326474e16e5d 100644 --- a/libs/androidfw/tests/ResTable_test.cpp +++ b/libs/androidfw/tests/ResTable_test.cpp @@ -442,22 +442,22 @@ TEST(ResTableTest, TruncatedEncodeLength) { ASSERT_LT(val.data, pool->size()); // Make sure a string with a truncated length is read to its correct length - auto target_str8 = pool->string8At(val.data); - ASSERT_TRUE(target_str8.has_value()); - ASSERT_EQ(size_t(40076), String8(target_str8->data(), target_str8->size()).size()); - ASSERT_EQ(target_str8->data()[40075], ']'); + size_t str_len; + const char* target_str8 = pool->string8At(val.data, &str_len); + ASSERT_TRUE(target_str8 != NULL); + ASSERT_EQ(size_t(40076), String8(target_str8, str_len).size()); + ASSERT_EQ(target_str8[40075], ']'); - auto target_str16 = pool->stringAt(val.data); - ASSERT_TRUE(target_str16.has_value()); - ASSERT_EQ(size_t(40076), String16(target_str16->data(), target_str16->size()).size()); - ASSERT_EQ(target_str8->data()[40075], (char16_t) ']'); + const char16_t* target_str16 = pool->stringAt(val.data, &str_len); + ASSERT_TRUE(target_str16 != NULL); + ASSERT_EQ(size_t(40076), String16(target_str16, str_len).size()); + ASSERT_EQ(target_str8[40075], (char16_t) ']'); // Load an edited apk with the null terminator removed from the end of the // string std::string invalid_contents; - ASSERT_TRUE(ReadFileFromZipToString( - GetTestDataPath() + "/length_decode/length_decode_invalid.apk", "resources.arsc", - &invalid_contents)); + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/length_decode/length_decode_invalid.apk", + "resources.arsc", &invalid_contents)); ResTable invalid_table; ASSERT_EQ(NO_ERROR, invalid_table.add(invalid_contents.data(), invalid_contents.size())); @@ -472,8 +472,8 @@ TEST(ResTableTest, TruncatedEncodeLength) { // Make sure a string with a truncated length that is not null terminated errors // and does not return the string - ASSERT_FALSE(invalid_pool->string8At(invalid_val.data).has_value()); - ASSERT_FALSE(invalid_pool->stringAt(invalid_val.data).has_value()); + ASSERT_TRUE(invalid_pool->string8At(invalid_val.data, &str_len) == NULL); + ASSERT_TRUE(invalid_pool->stringAt(invalid_val.data, &str_len) == NULL); } } // namespace android diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp index 10c0a4fc8316..a81bb6ffab06 100644 --- a/libs/androidfw/tests/TestHelpers.cpp +++ b/libs/androidfw/tests/TestHelpers.cpp @@ -73,15 +73,11 @@ AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id, return AssertionFailure() << "table has no string pool for block " << block; } - auto actual_str = pool->string8ObjectAt(val.data); - if (!actual_str.has_value()) { - return AssertionFailure() << "could not find string entry"; + const String8 actual_str = pool->string8ObjectAt(val.data); + if (String8(expected_str) != actual_str) { + return AssertionFailure() << actual_str.string(); } - - if (String8(expected_str) != *actual_str) { - return AssertionFailure() << actual_str->string(); - } - return AssertionSuccess() << actual_str->string(); + return AssertionSuccess() << actual_str.string(); } } // namespace android diff --git a/libs/androidfw/tests/Theme_bench.cpp b/libs/androidfw/tests/Theme_bench.cpp index f3d60bbe4f15..594c39eb682f 100644 --- a/libs/androidfw/tests/Theme_bench.cpp +++ b/libs/androidfw/tests/Theme_bench.cpp @@ -70,8 +70,11 @@ static void BM_ThemeGetAttribute(benchmark::State& state) { auto theme = assets.NewTheme(); theme->ApplyStyle(kStyleId, false /* force */); + Res_value value; + uint32_t flags; + while (state.KeepRunning()) { - theme->GetAttribute(kAttrId); + theme->GetAttribute(kAttrId, &value, &flags); } } BENCHMARK(BM_ThemeGetAttribute); diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index f658735da515..16b9c75982fb 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -67,7 +67,10 @@ TEST_F(ThemeTest, EmptyTheme) { std::unique_ptr<Theme> theme = assetmanager.NewTheme(); EXPECT_EQ(0u, theme->GetChangingConfigurations()); EXPECT_EQ(&assetmanager, theme->GetAssetManager()); - EXPECT_FALSE(theme->GetAttribute(app::R::attr::attr_one).has_value()); + + Res_value value; + uint32_t flags; + EXPECT_EQ(kInvalidCookie, theme->GetAttribute(app::R::attr::attr_one, &value, &flags)); } TEST_F(ThemeTest, SingleThemeNoParent) { @@ -75,19 +78,23 @@ TEST_F(ThemeTest, SingleThemeNoParent) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr<Theme> theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne).has_value()); - - auto value = theme->GetAttribute(app::R::attr::attr_one); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(1u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); - - value = theme->GetAttribute(app::R::attr::attr_two); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(2u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; + + cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(2u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } TEST_F(ThemeTest, SingleThemeWithParent) { @@ -95,28 +102,32 @@ TEST_F(ThemeTest, SingleThemeWithParent) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr<Theme> theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); - - auto value = theme->GetAttribute(app::R::attr::attr_one); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(1u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); - - value = theme->GetAttribute(app::R::attr::attr_two); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_STRING, value->type); - EXPECT_EQ(0, value->cookie); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; + + cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); + + cookie = theme->GetAttribute(app::R::attr::attr_two, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_STRING, value.dataType); + EXPECT_EQ(0, cookie); EXPECT_EQ(std::string("string"), - GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value->data)); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + GetStringFromPool(assetmanager.GetStringPoolForCookie(0), value.data)); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); // This attribute should point to an attr_indirect, so the result should be 3. - value = theme->GetAttribute(app::R::attr::attr_three); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(3u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme->GetAttribute(app::R::attr::attr_three, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(3u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } TEST_F(ThemeTest, TryToUseBadResourceId) { @@ -124,8 +135,11 @@ TEST_F(ThemeTest, TryToUseBadResourceId) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr<Theme> theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); - ASSERT_FALSE(theme->GetAttribute(0x7f000001)); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); + + Res_value value; + uint32_t flags; + ASSERT_EQ(kInvalidCookie, theme->GetAttribute(0x7f000001, &value, &flags)); } TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) { @@ -133,29 +147,33 @@ TEST_F(ThemeTest, MultipleThemesOverlaidNotForce) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr<Theme> theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree).has_value()); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; // attr_one is still here from the base. - auto value = theme->GetAttribute(app::R::attr::attr_one); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(1u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); // check for the new attr_six - value = theme->GetAttribute(app::R::attr::attr_six); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(6u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(6u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); // check for the old attr_five (force=true was not used). - value = theme->GetAttribute(app::R::attr::attr_five); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); - EXPECT_EQ(app::R::string::string_one, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); + EXPECT_EQ(app::R::string::string_one, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } TEST_F(ThemeTest, MultipleThemesOverlaidForced) { @@ -163,29 +181,33 @@ TEST_F(ThemeTest, MultipleThemesOverlaidForced) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr<Theme> theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo).has_value()); - ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */).has_value()); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo)); + ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleThree, true /* force */)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; // attr_one is still here from the base. - auto value = theme->GetAttribute(app::R::attr::attr_one); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(1u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); // check for the new attr_six - value = theme->GetAttribute(app::R::attr::attr_six); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(6u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme->GetAttribute(app::R::attr::attr_six, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(6u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); // check for the new attr_five (force=true was used). - value = theme->GetAttribute(app::R::attr::attr_five); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(5u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme->GetAttribute(app::R::attr::attr_five, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(5u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) { @@ -194,24 +216,28 @@ TEST_F(ThemeTest, ResolveDynamicAttributesAndReferencesToSharedLibrary) { {lib_two_assets_.get(), lib_one_assets_.get(), libclient_assets_.get()}); std::unique_ptr<Theme> theme = assetmanager.NewTheme(); - ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/).has_value()); + ASSERT_TRUE(theme->ApplyStyle(libclient::R::style::Theme, false /*force*/)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; // The attribute should be resolved to the final value. - auto value = theme->GetAttribute(libclient::R::attr::foo); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(700u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme->GetAttribute(libclient::R::attr::foo, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(700u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); // The reference should be resolved to a TYPE_REFERENCE. - value = theme->GetAttribute(libclient::R::attr::bar); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_REFERENCE, value->type); + cookie = theme->GetAttribute(libclient::R::attr::bar, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_REFERENCE, value.dataType); // lib_one is assigned package ID 0x03. - EXPECT_EQ(3u, get_package_id(value->data)); - EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value->data)); - EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value->data)); + EXPECT_EQ(3u, get_package_id(value.data)); + EXPECT_EQ(get_type_id(lib_one::R::string::foo), get_type_id(value.data)); + EXPECT_EQ(get_entry_id(lib_one::R::string::foo), get_entry_id(value.data)); } TEST_F(ThemeTest, CopyThemeSameAssetManager) { @@ -219,20 +245,24 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { assetmanager.SetApkAssets({style_assets_.get()}); std::unique_ptr<Theme> theme_one = assetmanager.NewTheme(); - ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne).has_value()); + ASSERT_TRUE(theme_one->ApplyStyle(app::R::style::StyleOne)); + + Res_value value; + uint32_t flags; + ApkAssetsCookie cookie; // attr_one is still here from the base. - auto value = theme_one->GetAttribute(app::R::attr::attr_one); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(1u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(1u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); // attr_six is not here. - ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_six).has_value()); + EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags)); std::unique_ptr<Theme> theme_two = assetmanager.NewTheme(); - ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree).has_value()); + ASSERT_TRUE(theme_two->ApplyStyle(app::R::style::StyleThree)); // Copy the theme to theme_one. theme_one->SetTo(*theme_two); @@ -241,14 +271,14 @@ TEST_F(ThemeTest, CopyThemeSameAssetManager) { theme_two->Clear(); // attr_one is now not here. - ASSERT_FALSE(theme_one->GetAttribute(app::R::attr::attr_one).has_value()); + EXPECT_EQ(kInvalidCookie, theme_one->GetAttribute(app::R::attr::attr_one, &value, &flags)); // attr_six is now here because it was copied. - value = theme_one->GetAttribute(app::R::attr::attr_six); - ASSERT_TRUE(value); - EXPECT_EQ(Res_value::TYPE_INT_DEC, value->type); - EXPECT_EQ(6u, value->data); - EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), value->flags); + cookie = theme_one->GetAttribute(app::R::attr::attr_six, &value, &flags); + ASSERT_NE(kInvalidCookie, cookie); + EXPECT_EQ(Res_value::TYPE_INT_DEC, value.dataType); + EXPECT_EQ(6u, value.data); + EXPECT_EQ(static_cast<uint32_t>(ResTable_typeSpec::SPEC_PUBLIC), flags); } TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) { @@ -261,43 +291,39 @@ TEST_F(ThemeTest, OnlyCopySameAssetsThemeWhenAssetManagersDiffer) { style_assets_.get()}); auto theme_dst = assetmanager_dst.NewTheme(); - ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne).has_value()); + ASSERT_TRUE(theme_dst->ApplyStyle(app::R::style::StyleOne)); auto theme_src = assetmanager_src.NewTheme(); - ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One).has_value()); - ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo).has_value()); + ASSERT_TRUE(theme_src->ApplyStyle(R::style::Theme_One)); + ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleTwo)); ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_one::R::style::Theme, 0x03), - false /*force*/).has_value()); + false /*force*/)); ASSERT_TRUE(theme_src->ApplyStyle(fix_package_id(lib_two::R::style::Theme, 0x02), - false /*force*/).has_value()); + false /*force*/)); theme_dst->SetTo(*theme_src); + Res_value value; + uint32_t flags; + // System resources (present in destination asset manager). - auto value = theme_dst->GetAttribute(R::attr::foreground); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(0, value->cookie); + EXPECT_EQ(0, theme_dst->GetAttribute(R::attr::foreground, &value, &flags)); // The cookie of the style asset is 3 in the source and 2 in the destination. // Check that the cookie has been rewritten to the destination values. - value = theme_dst->GetAttribute(app::R::attr::attr_one); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(2, value->cookie); + EXPECT_EQ(2, theme_dst->GetAttribute(app::R::attr::attr_one, &value, &flags)); // The cookie of the lib_one asset is 2 in the source and 1 in the destination. // The package id of the lib_one package is 0x03 in the source and 0x02 in the destination // Check that the cookie and packages have been rewritten to the destination values. - value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02)); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(1, value->cookie); - - value = theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02)); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(1, value->cookie); + EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr1, 0x02), &value, + &flags)); + EXPECT_EQ(1, theme_dst->GetAttribute(fix_package_id(lib_one::R::attr::attr2, 0x02), &value, + &flags)); // attr2 references an attribute in lib_one. Check that the resolution of the attribute value is // correct after the value of attr2 had its package id rewritten to the destination package id. - EXPECT_EQ(700, value->data); + EXPECT_EQ(700, value.data); } TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) { @@ -309,32 +335,28 @@ TEST_F(ThemeTest, CopyNonReferencesWhenPackagesDiffer) { auto theme_dst = assetmanager_dst.NewTheme(); auto theme_src = assetmanager_src.NewTheme(); - ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven).has_value()); + ASSERT_TRUE(theme_src->ApplyStyle(app::R::style::StyleSeven)); theme_dst->SetTo(*theme_src); + Res_value value; + uint32_t flags; + // Allow inline resource values to be copied even if the source apk asset is not present in the // destination. - auto value = theme_dst->GetAttribute(0x0101021b /* android:versionCode */); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(0, value->cookie); + EXPECT_EQ(0, theme_dst->GetAttribute(0x0101021b /* android:versionCode */, &value, &flags)); // Do not copy strings since the data is an index into the values string pool of the source apk // asset. - EXPECT_FALSE(theme_dst->GetAttribute(0x01010001 /* android:label */).has_value()); + EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010001 /* android:label */, &value, &flags)); // Do not copy values that reference another resource if the resource is not present in the // destination. - EXPECT_FALSE(theme_dst->GetAttribute(0x01010002 /* android:icon */).has_value()); - EXPECT_FALSE(theme_dst->GetAttribute(0x010100d1 /* android:tag */).has_value()); + EXPECT_EQ(-1, theme_dst->GetAttribute(0x01010002 /* android:icon */, &value, &flags)); + EXPECT_EQ(-1, theme_dst->GetAttribute(0x010100d1 /* android:tag */, &value, &flags)); // Allow @empty to and @null to be copied. - value = theme_dst->GetAttribute(0x010100d0 /* android:id */); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(0, value->cookie); - - value = theme_dst->GetAttribute(0x01010000 /* android:theme */); - ASSERT_TRUE(value.has_value()); - EXPECT_EQ(0, value->cookie); + EXPECT_EQ(0, theme_dst->GetAttribute(0x010100d0 /* android:id */, &value, &flags)); + EXPECT_EQ(0, theme_dst->GetAttribute(0x01010000 /* android:theme */, &value, &flags)); } } // namespace android diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index dd3ebdbdea09..ab6dced5b67d 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -519,7 +519,7 @@ static int validateAttr(const String8& path, const ResTable& table, String8(parser.getElementName(&len)).string(), attr); return ATTR_NOT_FOUND; } - if ((str = UnpackOptionalString(pool->stringAt(value.data), &len)) == NULL) { + if ((str=pool->stringAt(value.data, &len)) == NULL) { fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n", path.string(), parser.getLineNumber(), String8(parser.getElementName(&len)).string(), attr); diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 257e96b6e51a..d02f44edaa4c 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -3066,7 +3066,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& for (size_t ti=0; ti<N; ti++) { // Retrieve them in the same order as the type string block. size_t len; - String16 typeName(UnpackOptionalString(p->getTypeStrings().stringAt(ti), &len)); + String16 typeName(p->getTypeStrings().stringAt(ti, &len)); sp<Type> t = p->getTypes().valueFor(typeName); LOG_ALWAYS_FATAL_IF(t == NULL && typeName != String16("<empty>"), "Type name %s not found", @@ -4169,7 +4169,7 @@ status_t ResourceTable::Package::setStrings(const sp<AaptFile>& data, const size_t N = strings->size(); for (size_t i=0; i<N; i++) { size_t len; - mappings->add(String16(UnpackOptionalString(strings->stringAt(i), &len)), i); + mappings->add(String16(strings->stringAt(i, &len)), i); } } return err; diff --git a/tools/aapt/StringPool.cpp b/tools/aapt/StringPool.cpp index 6cacd32eb91d..37b61bfdffbd 100644 --- a/tools/aapt/StringPool.cpp +++ b/tools/aapt/StringPool.cpp @@ -52,9 +52,9 @@ void printStringPool(const ResStringPool* pool) for (size_t i=0; i<N; i++) { size_t len; if (pool->isUTF8()) { - uniqueStrings.add(UnpackOptionalString(pool->string8At(i), &len)); + uniqueStrings.add(pool->string8At(i, &len)); } else { - uniqueStrings.add(UnpackOptionalString(pool->stringAt(i), &len)); + uniqueStrings.add(pool->stringAt(i, &len)); } } @@ -66,8 +66,8 @@ void printStringPool(const ResStringPool* pool) const size_t NS = pool->size(); for (size_t s=0; s<NS; s++) { - auto str = pool->string8ObjectAt(s); - printf("String #" ZD ": %s\n", (ZD_TYPE) s, (str.has_value() ? str->string() : "")); + String8 str = pool->string8ObjectAt(s); + printf("String #" ZD ": %s\n", (ZD_TYPE) s, str.string()); } } diff --git a/tools/aapt2/Debug.cpp b/tools/aapt2/Debug.cpp index 82da24959521..439f231193df 100644 --- a/tools/aapt2/Debug.cpp +++ b/tools/aapt2/Debug.cpp @@ -436,9 +436,9 @@ void Debug::DumpResStringPool(const android::ResStringPool* pool, text::Printer* for (size_t i=0; i<N; i++) { size_t len; if (pool->isUTF8()) { - uniqueStrings.add(UnpackOptionalString(pool->string8At(i), &len)); + uniqueStrings.add(pool->string8At(i, &len)); } else { - uniqueStrings.add(UnpackOptionalString(pool->stringAt(i), &len)); + uniqueStrings.add(pool->stringAt(i, &len)); } } @@ -450,8 +450,8 @@ void Debug::DumpResStringPool(const android::ResStringPool* pool, text::Printer* const size_t NS = pool->size(); for (size_t s=0; s<NS; s++) { - auto str = pool->string8ObjectAt(s); - printer->Print(StringPrintf("String #%zd : %s\n", s, str.has_value() ? str->string() : "")); + String8 str = pool->string8ObjectAt(s); + printer->Print(StringPrintf("String #%zd : %s\n", s, str.string())); } } diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 5b43df6f0935..7dfc983b54ba 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -751,12 +751,10 @@ std::unique_ptr<Item> ParseBinaryResValue(const ResourceType& type, const Config switch (res_value.dataType) { case android::Res_value::TYPE_STRING: { const std::string str = util::GetString(src_pool, data); - auto spans_result = src_pool.styleAt(data); + const android::ResStringPool_span* spans = src_pool.styleAt(data); // Check if the string has a valid style associated with it. - if (spans_result.has_value() && - (*spans_result)->name.index != android::ResStringPool_span::END) { - const android::ResStringPool_span* spans = spans_result->unsafe_ptr(); + if (spans != nullptr && spans->name.index != android::ResStringPool_span::END) { StyleString style_str = {str}; while (spans->name.index != android::ResStringPool_span::END) { style_str.spans.push_back(Span{util::GetString(src_pool, spans->name.index), diff --git a/tools/aapt2/StringPool_test.cpp b/tools/aapt2/StringPool_test.cpp index 6e5200bca44c..9a7238b584ba 100644 --- a/tools/aapt2/StringPool_test.cpp +++ b/tools/aapt2/StringPool_test.cpp @@ -223,11 +223,11 @@ TEST(StringPoolTest, FlattenOddCharactersUtf16) { std::unique_ptr<uint8_t[]> data = util::Copy(buffer); ResStringPool test; ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); - auto str = test.stringAt(0); - ASSERT_TRUE(str.has_value()); - EXPECT_THAT(str->size(), Eq(1u)); - EXPECT_THAT(str->data(), Pointee(Eq(u'\u093f'))); - EXPECT_THAT(str->data()[1], Eq(0u)); + size_t len = 0; + const char16_t* str = test.stringAt(0, &len); + EXPECT_THAT(len, Eq(1u)); + EXPECT_THAT(str, Pointee(Eq(u'\u093f'))); + EXPECT_THAT(str[1], Eq(0u)); } constexpr const char* sLongString = @@ -278,15 +278,14 @@ TEST(StringPoolTest, Flatten) { EXPECT_THAT(util::GetString(test, 3), Eq(sLongString)); EXPECT_THAT(util::GetString16(test, 3), Eq(util::Utf8ToUtf16(sLongString))); - EXPECT_TRUE(test.stringAt(4).has_value() || test.string8At(4).has_value()); + size_t len; + EXPECT_TRUE(test.stringAt(4, &len) != nullptr || test.string8At(4, &len) != nullptr); EXPECT_THAT(util::GetString(test, 0), Eq("style")); EXPECT_THAT(util::GetString16(test, 0), Eq(u"style")); - auto span_result = test.styleAt(0); - ASSERT_TRUE(span_result.has_value()); - - const ResStringPool_span* span = span_result->unsafe_ptr(); + const ResStringPool_span* span = test.styleAt(0); + ASSERT_THAT(span, NotNull()); EXPECT_THAT(util::GetString(test, span->name.index), Eq("b")); EXPECT_THAT(util::GetString16(test, span->name.index), Eq(u"b")); EXPECT_THAT(span->firstChar, Eq(0u)); @@ -319,17 +318,16 @@ TEST(StringPoolTest, ModifiedUTF8) { // Check that the codepoints are encoded using two three-byte surrogate pairs ResStringPool test; ASSERT_EQ(test.setTo(data.get(), buffer.size()), NO_ERROR); - auto str = test.string8At(0); - ASSERT_TRUE(str.has_value()); - EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80")); - - str = test.string8At(1); - ASSERT_TRUE(str.has_value()); - EXPECT_THAT(str->to_string(), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar")); - - str = test.string8At(2); - ASSERT_TRUE(str.has_value()); - EXPECT_THAT(str->to_string(), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7")); + size_t len; + const char* str = test.string8At(0, &len); + ASSERT_THAT(str, NotNull()); + EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80")); + str = test.string8At(1, &len); + ASSERT_THAT(str, NotNull()); + EXPECT_THAT(std::string(str, len), Eq("foo \xED\xA0\x81\xED\xB0\xB7 bar")); + str = test.string8At(2, &len); + ASSERT_THAT(str, NotNull()); + EXPECT_THAT(std::string(str, len), Eq("\xED\xA0\x81\xED\xB0\x80\xED\xA0\x81\xED\xB0\xB7")); // Check that retrieving the strings returns the original UTF-8 character bytes EXPECT_THAT(util::GetString(test, 0), Eq("\xF0\x90\x90\x80")); diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp index 8cbd998f2ae2..0aab94d3299f 100644 --- a/tools/aapt2/cmd/Compile_test.cpp +++ b/tools/aapt2/cmd/Compile_test.cpp @@ -314,10 +314,8 @@ TEST_F(CompilerTest, RelativePathTest) { ASSERT_NE(content_values.find(relative_path_values_colors), -1); ASSERT_EQ(content_values.find(path_values_colors), -1); - ASSERT_TRUE(Link({"-o", apk_path, - "--manifest", GetDefaultManifest(), - "--proto-format"}, - compiled_files_dir, &diag)); + Link({"-o", apk_path, "--manifest", GetDefaultManifest(), "--proto-format"}, + compiled_files_dir, &diag); std::unique_ptr<LoadedApk> apk = LoadedApk::LoadApkFromPath(apk_path, &diag); ResourceTable* resource_table = apk.get()->GetResourceTable(); diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index f92c602fd761..118a76ff73a8 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -732,6 +732,22 @@ static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path, return true; } +static android::ApkAssetsCookie FindFrameworkAssetManagerCookie( + const android::AssetManager2& assets) { + using namespace android; + + // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so + // we're looking for the first attribute resource in the system package. + Res_value val{}; + ResTable_config config{}; + uint32_t type_spec_flags; + ApkAssetsCookie idx = assets.GetResource(0x01010000, true /** may_be_bag */, + 0 /** density_override */, &val, &config, + &type_spec_flags); + + return idx; +} + class Linker { public: Linker(LinkContext* context, const LinkOptions& options) @@ -744,12 +760,8 @@ class Linker { void ExtractCompileSdkVersions(android::AssetManager2* assets) { using namespace android; - // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so - // we're looking for the first attribute resource in the system package. - android::ApkAssetsCookie cookie; - if (auto value = assets->GetResource(0x01010000, true /** may_be_bag */); value.has_value()) { - cookie = value->cookie; - } else { + android::ApkAssetsCookie cookie = FindFrameworkAssetManagerCookie(*assets); + if (cookie == android::kInvalidCookie) { // No Framework assets loaded. Not a failure. return; } diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index f8b8a1c4e35b..6932baf76c75 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -189,16 +189,16 @@ TEST_F(TableFlattenerTest, FlattenFullyLinkedTable) { ResTable_config::CONFIG_VERSION)); std::u16string foo_str = u"foo"; - auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); - ASSERT_TRUE(idx.has_value()); + ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); + ASSERT_GE(idx, 0); EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {}, - Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); + Res_value::TYPE_STRING, (uint32_t)idx, 0u)); std::u16string bar_path = u"res/layout/bar.xml"; idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); - ASSERT_TRUE(idx.has_value()); + ASSERT_GE(idx, 0); EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/bar", ResourceId(0x7f050000), {}, - Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); + Res_value::TYPE_STRING, (uint32_t)idx, 0u)); } TEST_F(TableFlattenerTest, FlattenEntriesWithGapsInIds) { @@ -603,16 +603,16 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesNoNameCollapseExemptionsSucce 2u, ResTable_config::CONFIG_VERSION)); std::u16string foo_str = u"foo"; - auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); - ASSERT_TRUE(idx.has_value()); + ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); + ASSERT_GE(idx, 0); EXPECT_TRUE(Exists(&res_table, "com.app.test:string/0_resource_name_obfuscated", - ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); + ResourceId(0x7f040000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); std::u16string bar_path = u"res/layout/bar.xml"; idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); - ASSERT_TRUE(idx.has_value()); + ASSERT_GE(idx, 0); EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated", - ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); + ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); } TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSucceeds) { @@ -659,16 +659,16 @@ TEST_F(TableFlattenerTest, ObfuscatingResourceNamesWithNameCollapseExemptionsSuc 2u, ResTable_config::CONFIG_VERSION)); std::u16string foo_str = u"foo"; - auto idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); - ASSERT_TRUE(idx.has_value()); + ssize_t idx = res_table.getTableStringBlock(0)->indexOfString(foo_str.data(), foo_str.size()); + ASSERT_GE(idx, 0); EXPECT_TRUE(Exists(&res_table, "com.app.test:string/test", ResourceId(0x7f040000), {}, - Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); + Res_value::TYPE_STRING, (uint32_t)idx, 0u)); std::u16string bar_path = u"res/layout/bar.xml"; idx = res_table.getTableStringBlock(0)->indexOfString(bar_path.data(), bar_path.size()); - ASSERT_TRUE(idx.has_value()); + ASSERT_GE(idx, 0); EXPECT_TRUE(Exists(&res_table, "com.app.test:layout/0_resource_name_obfuscated", - ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)*idx, 0u)); + ResourceId(0x7f050000), {}, Res_value::TYPE_STRING, (uint32_t)idx, 0u)); } TEST_F(TableFlattenerTest, FlattenOverlayable) { diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index ad716c78d65d..897fa80ffedb 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -265,22 +265,21 @@ bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId, static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable( android::AssetManager2& am, ResourceId id) { - using namespace android; if (am.GetApkAssets().empty()) { return {}; } - auto bag_result = am.GetBag(id.id); - if (!bag_result.has_value()) { + const android::ResolvedBag* bag = am.GetBag(id.id); + if (bag == nullptr) { return nullptr; } // We found a resource. std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id); - const ResolvedBag* bag = *bag_result; + const size_t count = bag->entry_count; for (uint32_t i = 0; i < count; i++) { - if (bag->entries[i].key == ResTable_map::ATTR_TYPE) { + if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) { s->attribute = std::make_shared<Attribute>(bag->entries[i].value.data); break; } @@ -288,25 +287,25 @@ static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable( if (s->attribute) { for (size_t i = 0; i < count; i++) { - const ResolvedBag::Entry& map_entry = bag->entries[i]; + const android::ResolvedBag::Entry& map_entry = bag->entries[i]; if (Res_INTERNALID(map_entry.key)) { switch (map_entry.key) { - case ResTable_map::ATTR_MIN: + case android::ResTable_map::ATTR_MIN: s->attribute->min_int = static_cast<int32_t>(map_entry.value.data); break; - case ResTable_map::ATTR_MAX: + case android::ResTable_map::ATTR_MAX: s->attribute->max_int = static_cast<int32_t>(map_entry.value.data); break; } continue; } - auto name = am.GetResourceName(map_entry.key); - if (!name.has_value()) { + android::AssetManager2::ResourceName name; + if (!am.GetResourceName(map_entry.key, &name)) { return nullptr; } - Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(*name); + Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(name); if (!parsed_name) { return nullptr; } @@ -329,7 +328,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( bool found = false; ResourceId res_id = 0; - uint32_t type_spec_flags = 0; + uint32_t type_spec_flags; ResourceName real_name; // There can be mangled resources embedded within other packages. Here we will @@ -341,19 +340,8 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( real_name.package = package_name; } - auto real_res_id = asset_manager_.GetResourceId(real_name.to_string()); - if (!real_res_id.has_value()) { - return true; - } - - res_id.id = *real_res_id; - if (!res_id.is_valid_static()) { - return true; - } - - auto value = asset_manager_.GetResource(res_id.id, true /* may_be_bag */); - if (value.has_value()) { - type_spec_flags = value->flags; + res_id = asset_manager_.GetResourceId(real_name.to_string()); + if (res_id.is_valid_static() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) { found = true; return false; } @@ -383,11 +371,11 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( static Maybe<ResourceName> GetResourceName(android::AssetManager2& am, ResourceId id) { - auto name = am.GetResourceName(id.id); - if (!name.has_value()) { + android::AssetManager2::ResourceName name; + if (!am.GetResourceName(id.id, &name)) { return {}; } - return ResourceUtils::ToResourceName(*name); + return ResourceUtils::ToResourceName(name); } std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById( @@ -406,8 +394,9 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById( return {}; } - auto value = asset_manager_.GetResource(id.id, true /* may_be_bag */); - if (!value.has_value()) { + + uint32_t type_spec_flags = 0; + if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) { return {}; } @@ -422,7 +411,7 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById( } if (s) { - s->is_public = (value->flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; + s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0; return s; } return {}; diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp index ef33c3463a81..37ce65e4fe5b 100644 --- a/tools/aapt2/util/Util.cpp +++ b/tools/aapt2/util/Util.cpp @@ -531,15 +531,19 @@ bool ExtractResFilePathParts(const StringPiece& path, StringPiece* out_prefix, } StringPiece16 GetString16(const android::ResStringPool& pool, size_t idx) { - if (auto str = pool.stringAt(idx)) { - return *str; + size_t len; + const char16_t* str = pool.stringAt(idx, &len); + if (str != nullptr) { + return StringPiece16(str, len); } return StringPiece16(); } std::string GetString(const android::ResStringPool& pool, size_t idx) { - if (auto str = pool.string8At(idx)) { - return ModifiedUtf8ToUtf8(str->to_string()); + size_t len; + const char* str = pool.string8At(idx, &len); + if (str != nullptr) { + return ModifiedUtf8ToUtf8(std::string(str, len)); } return Utf16ToUtf8(GetString16(pool, idx)); } diff --git a/tools/split-select/Main.cpp b/tools/split-select/Main.cpp index e6966db0aa00..d3eb012a80e9 100644 --- a/tools/split-select/Main.cpp +++ b/tools/split-select/Main.cpp @@ -182,18 +182,14 @@ static bool getAppInfo(const String8& path, AppInfo& outInfo) { if (type >= Res_value::TYPE_FIRST_INT && type <= Res_value::TYPE_LAST_INT) { outInfo.minSdkVersion = xml.getAttributeData(idx); } else if (type == Res_value::TYPE_STRING) { - auto minSdk8 = xml.getStrings().string8ObjectAt(idx); - if (!minSdk8.has_value()) { - fprintf(stderr, "warning: failed to retrieve android:minSdkVersion.\n"); + String8 minSdk8(xml.getStrings().string8ObjectAt(idx)); + char* endPtr; + int minSdk = strtol(minSdk8.string(), &endPtr, 10); + if (endPtr != minSdk8.string() + minSdk8.size()) { + fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n", + minSdk8.string()); } else { - char *endPtr; - int minSdk = strtol(minSdk8->string(), &endPtr, 10); - if (endPtr != minSdk8->string() + minSdk8->size()) { - fprintf(stderr, "warning: failed to parse android:minSdkVersion '%s'\n", - minSdk8->string()); - } else { - outInfo.minSdkVersion = minSdk; - } + outInfo.minSdkVersion = minSdk; } } else { fprintf(stderr, "warning: unrecognized value for android:minSdkVersion.\n"); |