Revert "libandroidfw hardening for IncFs"
Revert "Move map_ptr to incfs namspace" Revert submission 12787270 Reason for revert: b/173250495 Reverted Changes: I5cd1bc8a2:libandroidfw hardening for IncFs Ice5dbcfb2:Move map_ptr to incfs namspace I29ccdc8ed:Do not cache bag parent stack until requested I1e9e9acaa:Cache resolved theme values Change-Id: Ib90ef68339710086df41e9abe0833a542d03a74f
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
if (entry == rend) {
|
||||
// Keep searching (the old implementation did that).
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
auto result = theme->GetAssetManager()->ResolveReference(*value);
|
||||
if (!result.has_value()) {
|
||||
ThrowIfIOError(env, result);
|
||||
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,
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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: [
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"liblog",
|
||||
"libutils",
|
||||
"libziparchive",
|
||||
"libbase",
|
||||
"liblog",
|
||||
"libcutils",
|
||||
"libutils",
|
||||
],
|
||||
shared_libs: [
|
||||
"libz",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
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_ << "'";
|
||||
} 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 {};
|
||||
}
|
||||
}
|
||||
|
||||
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 {};
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
|
||||
status_t result = pAsset->openChunk(std::move(dataMap), std::move(fd));
|
||||
pAsset = new _FileAsset;
|
||||
result = pAsset->openChunk(dataMap, base::unique_fd(-1));
|
||||
if (result != NO_ERROR) {
|
||||
delete pAsset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pAsset->mAccessMode = mode;
|
||||
return pAsset;
|
||||
}
|
||||
|
||||
/*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;
|
||||
|
||||
status_t result = pAsset->openChunk(std::move(dataMap), uncompressedLen);
|
||||
pAsset = new _CompressedAsset;
|
||||
result = pAsset->openChunk(dataMap, uncompressedLen);
|
||||
if (result != NO_ERROR) {
|
||||
delete pAsset;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
@@ -64,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;
|
||||
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);
|
||||
}
|
||||
|
||||
AssetManager2* assetmanager = theme->GetAssetManager();
|
||||
ResTable_config config;
|
||||
Res_value value;
|
||||
|
||||
int indices_idx = 0;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to loading the style from the resource id if specified.
|
||||
if (fallback_resid != 0U) {
|
||||
return theme->GetAssetManager()->GetBag(fallback_resid);
|
||||
}
|
||||
|
||||
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.
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (value.dataType == Res_value::TYPE_REFERENCE) {
|
||||
return theme->GetAssetManager()->GetBag(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()));
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
@@ -215,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BagAttributeFinder def_style_attr_finder(default_style_bag.value_or(nullptr));
|
||||
BagAttributeFinder xml_style_attr_finder(xml_style_bag.value_or(nullptr));
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
@@ -265,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);
|
||||
|
||||
auto result = assetmanager->ResolveReference(value);
|
||||
if (UNLIKELY(IsIOError(result))) {
|
||||
return base::unexpected(GetIOError(result.error()));
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -419,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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
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) {
|
||||
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 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 type_name = type_string_pool_.string8At(type_idx);
|
||||
if (UNLIKELY(IsIOError(type_name))) {
|
||||
return base::unexpected(GetIOError(type_name.error()));
|
||||
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);
|
||||
}
|
||||
if (type_name.has_value()) {
|
||||
if (strncmp(type_name->data(), "mipmap", type_name->size()) == 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);
|
||||
}
|
||||
}
|
||||
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()) {
|
||||
|
||||
@@ -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;
|
||||
if ((len & 0x8000) != 0) {
|
||||
(*str)++;
|
||||
len = ((len & 0x7FFF) << 16) | **str;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
++(*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 (str[*u16len] != 0x0000) {
|
||||
ALOGW("Bad string block: string #%d is not null-terminated",
|
||||
(int)idx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
@@ -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(),
|
||||
};
|
||||
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();
|
||||
|
||||
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());
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -232,29 +232,6 @@ FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
|
||||
return newMap;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 std::unique_ptr<Asset> createFromUncompressedMap(incfs::IncFsFileMap&& dataMap,
|
||||
AccessMode mode,
|
||||
base::unique_fd fd = {});
|
||||
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(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
|
||||
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
|
||||
|
||||
class StreamingZipInflater* mZipInflater; // for streaming large compressed assets
|
||||
unsigned char* mBuf; // for getBuffer()
|
||||
std::optional<incfs::IncFsFileMap> mMap; // for memory-mapped input
|
||||
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?
|
||||
|
||||
@@ -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,106 +194,53 @@ 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;
|
||||
// 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;
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
|
||||
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) {};
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
// 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`.
|
||||
//
|
||||
// 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) const;
|
||||
|
||||
// Retrieves the best matching bag/map resource with ID `resid`.
|
||||
//
|
||||
// This method will resolve all parent references for this bag and merge keys with the child.
|
||||
// To iterate over the keys, use the following idiom:
|
||||
//
|
||||
// base::expected<const ResolvedBag*, NullOrIOError> bag = asset_manager->GetBag(id);
|
||||
// if (bag.has_value()) {
|
||||
// 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;
|
||||
// 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;
|
||||
|
||||
// Resets the resource resolution structures in preparation for the next resource retrieval.
|
||||
void ResetResourceResolution() const;
|
||||
@@ -307,6 +252,20 @@ class AssetManager2 {
|
||||
// resolved yet.
|
||||
std::string GetLastResourceResolution() 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:
|
||||
//
|
||||
// const AssetManager2::ResolvedBag* bag = asset_manager->GetBag(id);
|
||||
// if (bag != nullptr) {
|
||||
// for (auto iter = begin(bag); iter != end(bag); ++iter) {
|
||||
// ...
|
||||
// }
|
||||
// }
|
||||
const ResolvedBag* GetBag(uint32_t resid);
|
||||
|
||||
// Creates a new Theme from this AssetManager.
|
||||
std::unique_ptr<Theme> NewTheme();
|
||||
|
||||
@@ -327,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.
|
||||
@@ -376,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.
|
||||
//
|
||||
@@ -390,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.
|
||||
@@ -417,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.
|
||||
@@ -440,11 +394,11 @@ 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_;
|
||||
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;
|
||||
@@ -501,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;
|
||||
void Dump() 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;
|
||||
|
||||
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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
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
|
||||
|
||||
base::expected<StringPiece, NullOrIOError> stringDecodeAt(
|
||||
size_t idx, incfs::map_ptr<uint8_t> str, size_t encLen) const;
|
||||
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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,25 +136,13 @@ 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.
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
const ResolvedBag* bag = assetmanager.GetBag(basic::R::array::integerArray1);
|
||||
ASSERT_NE(nullptr, bag);
|
||||
ASSERT_EQ(3u, bag->entry_count);
|
||||
|
||||
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_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_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_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_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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()});
|
||||
Res_value val;
|
||||
ResTable_config config;
|
||||
uint32_t flags;
|
||||
|
||||
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));
|
||||
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()});
|
||||
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 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,
|
||||
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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleOne));
|
||||
|
||||
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);
|
||||
Res_value value;
|
||||
uint32_t flags;
|
||||
ApkAssetsCookie cookie;
|
||||
|
||||
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);
|
||||
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());
|
||||
ASSERT_TRUE(theme->ApplyStyle(app::R::style::StyleTwo));
|
||||
|
||||
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);
|
||||
Res_value value;
|
||||
uint32_t flags;
|
||||
ApkAssetsCookie cookie;
|
||||
|
||||
value = theme->GetAttribute(app::R::attr::attr_two);
|
||||
ASSERT_TRUE(value);
|
||||
EXPECT_EQ(Res_value::TYPE_STRING, value->type);
|
||||
EXPECT_EQ(0, value->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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {};
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user