HELLO·Android
系统源代码
IT资讯
技术文章
我的收藏
注册
登录
-
我收藏的文章
创建代码块
我的代码块
我的账号
Oreo
|
8.0.0_r4
下载
查看原文件
收藏
根目录
frameworks
base
tools
aapt2
unflatten
BinaryResourceParser.cpp
/* * Copyright (C) 2015 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. */ #include "unflatten/BinaryResourceParser.h" #include
#include
#include
#include "android-base/logging.h" #include "android-base/macros.h" #include "android-base/stringprintf.h" #include "androidfw/ResourceTypes.h" #include "androidfw/TypeWrappers.h" #include "ResourceTable.h" #include "ResourceUtils.h" #include "ResourceValues.h" #include "Source.h" #include "ValueVisitor.h" #include "unflatten/ResChunkPullParser.h" #include "util/Util.h" namespace aapt { using namespace android; using android::base::StringPrintf; namespace { /* * Visitor that converts a reference's resource ID to a resource name, * given a mapping from resource ID to resource name. */ class ReferenceIdToNameVisitor : public ValueVisitor { public: using ValueVisitor::Visit; explicit ReferenceIdToNameVisitor( const std::map
* mapping) : mapping_(mapping) { CHECK(mapping_ != nullptr); } void Visit(Reference* reference) override { if (!reference->id || !reference->id.value().is_valid()) { return; } ResourceId id = reference->id.value(); auto cache_iter = mapping_->find(id); if (cache_iter != mapping_->end()) { reference->name = cache_iter->second; } } private: DISALLOW_COPY_AND_ASSIGN(ReferenceIdToNameVisitor); const std::map
* mapping_; }; } // namespace BinaryResourceParser::BinaryResourceParser(IAaptContext* context, ResourceTable* table, const Source& source, const void* data, size_t len, io::IFileCollection* files) : context_(context), table_(table), source_(source), data_(data), data_len_(len), files_(files) { } bool BinaryResourceParser::Parse() { ResChunkPullParser parser(data_, data_len_); if (!ResChunkPullParser::IsGoodEvent(parser.Next())) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt resources.arsc: " << parser.error()); return false; } if (parser.chunk()->type != android::RES_TABLE_TYPE) { context_->GetDiagnostics()->Error(DiagMessage(source_) << StringPrintf("unknown chunk of type 0x%02x", (int)parser.chunk()->type)); return false; } if (!ParseTable(parser.chunk())) { return false; } if (parser.Next() != ResChunkPullParser::Event::kEndDocument) { if (parser.event() == ResChunkPullParser::Event::kBadDocument) { context_->GetDiagnostics()->Warn( DiagMessage(source_) << "invalid chunk trailing RES_TABLE_TYPE: " << parser.error()); } else { context_->GetDiagnostics()->Warn( DiagMessage(source_) << StringPrintf( "unexpected chunk of type 0x%02x trailing RES_TABLE_TYPE", (int)parser.chunk()->type)); } } return true; } /** * Parses the resource table, which contains all the packages, types, and * entries. */ bool BinaryResourceParser::ParseTable(const ResChunk_header* chunk) { const ResTable_header* table_header = ConvertTo
(chunk); if (!table_header) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_header chunk"); return false; } ResChunkPullParser parser(GetChunkData(&table_header->header), GetChunkDataLen(&table_header->header)); while (ResChunkPullParser::IsGoodEvent(parser.Next())) { switch (util::DeviceToHost16(parser.chunk()->type)) { case android::RES_STRING_POOL_TYPE: if (value_pool_.getError() == NO_INIT) { status_t err = value_pool_.setTo( parser.chunk(), util::DeviceToHost32(parser.chunk()->size)); if (err != NO_ERROR) { context_->GetDiagnostics()->Error( DiagMessage(source_) << "corrupt string pool in ResTable: " << value_pool_.getError()); return false; } // Reserve some space for the strings we are going to add. table_->string_pool.HintWillAdd(value_pool_.size(), value_pool_.styleCount()); } else { context_->GetDiagnostics()->Warn( DiagMessage(source_) << "unexpected string pool in ResTable"); } break; case android::RES_TABLE_PACKAGE_TYPE: if (!ParsePackage(parser.chunk())) { return false; } break; default: context_->GetDiagnostics()->Warn( DiagMessage(source_) << "unexpected chunk type " << (int)util::DeviceToHost16(parser.chunk()->type)); break; } } if (parser.event() == ResChunkPullParser::Event::kBadDocument) { context_->GetDiagnostics()->Error( DiagMessage(source_) << "corrupt resource table: " << parser.error()); return false; } return true; } bool BinaryResourceParser::ParsePackage(const ResChunk_header* chunk) { constexpr size_t kMinPackageSize = sizeof(ResTable_package) - sizeof(ResTable_package::typeIdOffset); const ResTable_package* package_header = ConvertTo
(chunk); if (!package_header) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_package chunk"); return false; } uint32_t package_id = util::DeviceToHost32(package_header->id); if (package_id > std::numeric_limits
::max()) { context_->GetDiagnostics()->Error( DiagMessage(source_) << "package ID is too big (" << package_id << ")"); return false; } // Extract the package name. size_t len = strnlen16((const char16_t*)package_header->name, arraysize(package_header->name)); std::u16string package_name; package_name.resize(len); for (size_t i = 0; i < len; i++) { package_name[i] = util::DeviceToHost16(package_header->name[i]); } ResourceTablePackage* package = table_->CreatePackage( util::Utf16ToUtf8(package_name), static_cast
(package_id)); if (!package) { context_->GetDiagnostics()->Error( DiagMessage(source_) << "incompatible package '" << package_name << "' with ID " << package_id); return false; } // There can be multiple packages in a table, so // clear the type and key pool in case they were set from a previous package. type_pool_.uninit(); key_pool_.uninit(); ResChunkPullParser parser(GetChunkData(&package_header->header), GetChunkDataLen(&package_header->header)); while (ResChunkPullParser::IsGoodEvent(parser.Next())) { switch (util::DeviceToHost16(parser.chunk()->type)) { case android::RES_STRING_POOL_TYPE: if (type_pool_.getError() == NO_INIT) { status_t err = type_pool_.setTo( parser.chunk(), util::DeviceToHost32(parser.chunk()->size)); if (err != NO_ERROR) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt type string pool in " << "ResTable_package: " << type_pool_.getError()); return false; } } else if (key_pool_.getError() == NO_INIT) { status_t err = key_pool_.setTo( parser.chunk(), util::DeviceToHost32(parser.chunk()->size)); if (err != NO_ERROR) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt key string pool in " << "ResTable_package: " << key_pool_.getError()); return false; } } else { context_->GetDiagnostics()->Warn(DiagMessage(source_) << "unexpected string pool"); } break; case android::RES_TABLE_TYPE_SPEC_TYPE: if (!ParseTypeSpec(parser.chunk())) { return false; } break; case android::RES_TABLE_TYPE_TYPE: if (!ParseType(package, parser.chunk())) { return false; } break; case android::RES_TABLE_LIBRARY_TYPE: if (!ParseLibrary(parser.chunk())) { return false; } break; default: context_->GetDiagnostics()->Warn( DiagMessage(source_) << "unexpected chunk type " << (int)util::DeviceToHost16(parser.chunk()->type)); break; } } if (parser.event() == ResChunkPullParser::Event::kBadDocument) { context_->GetDiagnostics()->Error( DiagMessage(source_) << "corrupt ResTable_package: " << parser.error()); return false; } // Now go through the table and change local resource ID references to // symbolic references. ReferenceIdToNameVisitor visitor(&id_index_); VisitAllValuesInTable(table_, &visitor); return true; } bool BinaryResourceParser::ParseTypeSpec(const ResChunk_header* chunk) { if (type_pool_.getError() != NO_ERROR) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool"); return false; } const ResTable_typeSpec* type_spec = ConvertTo
(chunk); if (!type_spec) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_typeSpec chunk"); return false; } if (type_spec->id == 0) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "ResTable_typeSpec has invalid id: " << type_spec->id); return false; } return true; } bool BinaryResourceParser::ParseType(const ResourceTablePackage* package, const ResChunk_header* chunk) { if (type_pool_.getError() != NO_ERROR) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing type string pool"); return false; } if (key_pool_.getError() != NO_ERROR) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "missing key string pool"); return false; } // Specify a manual size, because ResTable_type contains ResTable_config, which changes // a lot and has its own code to handle variable size. const ResTable_type* type = ConvertTo
(chunk); if (!type) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "corrupt ResTable_type chunk"); return false; } if (type->id == 0) { context_->GetDiagnostics()->Error(DiagMessage(source_) << "ResTable_type has invalid id: " << (int)type->id); return false; } ConfigDescription config; config.copyFromDtoH(type->config); const std::string type_str = util::GetString(type_pool_, type->id - 1); const ResourceType* parsed_type = ParseResourceType(type_str); if (!parsed_type) { context_->GetDiagnostics()->Error( DiagMessage(source_) << "invalid type name '" << type_str << "' for type with ID " << (int)type->id); return false; } TypeVariant tv(type); for (auto it = tv.beginEntries(); it != tv.endEntries(); ++it) { const ResTable_entry* entry = *it; if (!entry) { continue; } const ResourceName name( package->name, *parsed_type, util::GetString(key_pool_, util::DeviceToHost32(entry->key.index))); const ResourceId res_id(package->id.value(), type->id, static_cast
(it.index())); std::unique_ptr
resource_value; if (entry->flags & ResTable_entry::FLAG_COMPLEX) { const ResTable_map_entry* mapEntry = static_cast
(entry); // TODO(adamlesinski): Check that the entry count is valid. resource_value = ParseMapEntry(name, config, mapEntry); } else { const Res_value* value = (const Res_value*)((const uint8_t*)entry + util::DeviceToHost32(entry->size)); resource_value = ParseValue(name, config, *value); } if (!resource_value) { context_->GetDiagnostics()->Error( DiagMessage(source_) << "failed to parse value for resource " << name << " (" << res_id << ") with configuration '" << config << "'"); return false; } if (!table_->AddResourceAllowMangled(name, res_id, config, {}, std::move(resource_value), context_->GetDiagnostics())) { return false; } if ((entry->flags & ResTable_entry::FLAG_PUBLIC) != 0) { Symbol symbol; symbol.state = SymbolState::kPublic; symbol.source = source_.WithLine(0); if (!table_->SetSymbolStateAllowMangled(name, res_id, symbol, context_->GetDiagnostics())) { return false; } } // Add this resource name->id mapping to the index so // that we can resolve all ID references to name references. auto cache_iter = id_index_.find(res_id); if (cache_iter == id_index_.end()) { id_index_.insert({res_id, name}); } } return true; } bool BinaryResourceParser::ParseLibrary(const ResChunk_header* chunk) { DynamicRefTable dynamic_ref_table; if (dynamic_ref_table.load(reinterpret_cast
(chunk)) != NO_ERROR) { return false; } const KeyedVector
& entries = dynamic_ref_table.entries(); const size_t count = entries.size(); for (size_t i = 0; i < count; i++) { table_->included_packages_[entries.valueAt(i)] = util::Utf16ToUtf8(StringPiece16(entries.keyAt(i).string())); } return true; } std::unique_ptr
BinaryResourceParser::ParseValue(const ResourceNameRef& name, const ConfigDescription& config, const android::Res_value& value) { std::unique_ptr
item = ResourceUtils::ParseBinaryResValue(name.type, config, value_pool_, value, &table_->string_pool); if (files_ != nullptr && item != nullptr) { FileReference* file_ref = ValueCast
(item.get()); if (file_ref != nullptr) { file_ref->file = files_->FindFile(*file_ref->path); if (file_ref->file == nullptr) { context_->GetDiagnostics()->Warn(DiagMessage() << "resource " << name << " for config '" << config << "' is a file reference to '" << *file_ref->path << "' but no such path exists"); } } } return item; } std::unique_ptr
BinaryResourceParser::ParseMapEntry( const ResourceNameRef& name, const ConfigDescription& config, const ResTable_map_entry* map) { switch (name.type) { case ResourceType::kStyle: return ParseStyle(name, config, map); case ResourceType::kAttrPrivate: // fallthrough case ResourceType::kAttr: return ParseAttr(name, config, map); case ResourceType::kArray: return ParseArray(name, config, map); case ResourceType::kPlurals: return ParsePlural(name, config, map); case ResourceType::kId: // Special case: An ID is not a bag, but some apps have defined the auto-generated // IDs that come from declaring an enum value in an attribute as an empty map... // We can ignore the value here. return util::make_unique
(); default: context_->GetDiagnostics()->Error(DiagMessage() << "illegal map type '" << ToString(name.type) << "' (" << (int)name.type << ")"); break; } return {}; } std::unique_ptr