Merge changes I1d3e5e66,I86b869af,Iab4d3902,I645ee722 am: 72864d2930 am: 968582e45f

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1461087

Change-Id: Ice6233ff1de7e7e3112c4f4ba6f01c02468ab5bd
This commit is contained in:
Treehugger Robot
2020-10-19 19:11:43 +00:00
committed by Automerger Merge Worker
32 changed files with 771 additions and 628 deletions

View File

@@ -20,6 +20,8 @@
#include <android-base/unique_fd.h> #include <android-base/unique_fd.h>
#include <binder/BinderService.h> #include <binder/BinderService.h>
#include <string>
#include "android/os/BnIdmap2.h" #include "android/os/BnIdmap2.h"
namespace android::os { namespace android::os {

View File

@@ -36,12 +36,11 @@ class BinaryStreamVisitor : public Visitor {
void visit(const IdmapData::Header& header) override; void visit(const IdmapData::Header& header) override;
private: private:
void Write(const void* value, size_t length);
void Write8(uint8_t value); void Write8(uint8_t value);
void Write16(uint16_t value); void Write16(uint16_t value);
void Write32(uint32_t value); void Write32(uint32_t value);
void WriteString256(const StringPiece& value); void WriteString256(const StringPiece& value);
void WriteString(const std::string& value); void WriteString(const StringPiece& value);
std::ostream& stream_; std::ostream& stream_;
}; };

View File

@@ -17,48 +17,45 @@
/* /*
* # idmap file format (current version) * # idmap file format (current version)
* *
* idmap := header data* * idmap := header data*
* header := magic version target_crc overlay_crc target_path overlay_path debug_info * header := magic version target_crc overlay_crc fulfilled_policies
* data := data_header data_block* * enforce_overlayable target_path overlay_path debug_info
* data_header := target_package_id types_count * data := data_header target_entry* target_inline_entry* overlay_entry*
* data_block := target_type overlay_type entry_count entry_offset entry* * string_pool
* overlay_path := string256 * data_header := target_package_id overlay_package_id padding(2) target_entry_count
* target_path := string256 * target_inline_entry_count overlay_entry_count string_pool_index
* debug_info := string * target_entry := target_id overlay_id
* string := <uint32_t> <uint8_t>+ '\0'+ * target_inline_entry := target_id Res_value::size padding(1) Res_value::type
* entry := <uint32_t> * Res_value::value
* entry_count := <uint16_t> * overlay_entry := overlay_id target_id
* entry_offset := <uint16_t>
* magic := <uint32_t>
* overlay_crc := <uint32_t>
* overlay_type := <uint16_t>
* string256 := <uint8_t>[256]
* target_crc := <uint32_t>
* target_package_id := <uint16_t>
* target_type := <uint16_t>
* types_count := <uint16_t>
* version := <uint32_t>
* *
* * debug_info := string
* # idmap file format changelog * enforce_overlayable := <uint32_t>
* ## v1 * fulfilled_policies := <uint32_t>
* - Identical to idmap v1. * magic := <uint32_t>
* * overlay_crc := <uint32_t>
* ## v2 * overlay_entry_count := <uint32_t>
* - Entries are no longer separated by type into type specific data blocks. * overlay_id := <uint32_t>
* - Added overlay-indexed target resource id lookup capabilities. * overlay_package_id := <uint8_t>
* - Target and overlay entries are stored as a sparse array in the data block. The target entries * overlay_path := string256
* array maps from target resource id to overlay data type and value and the array is sorted by * padding(n) := <uint8_t>[n]
* target resource id. The overlay entries array maps from overlay resource id to target resource * Res_value::size := <uint16_t>
* id and the array is sorted by overlay resource id. It is important for both arrays to be sorted * Res_value::type := <uint8_t>
* to allow for O(log(number_of_overlaid_resources)) performance when looking up resource * Res_value::value := <uint32_t>
* mappings at runtime. * string := <uint32_t> <uint8_t>+ padding(n)
* - Idmap can now encode a type and value to override a resource without needing a table entry. * string256 := <uint8_t>[256]
* - A string pool block is included to retrieve the value of strings that do not have a resource * string_pool := string
* table entry. * string_pool_index := <uint32_t>
* * string_pool_length := <uint32_t>
* ## v3 * target_crc := <uint32_t>
* - Add 'debug' block to IdmapHeader. * target_entry_count := <uint32_t>
* target_inline_entry_count := <uint32_t>
* target_id := <uint32_t>
* target_package_id := <uint8_t>
* target_path := string256
* value_type := <uint8_t>
* value_data := <uint32_t>
* version := <uint32_t>
*/ */
#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_ #ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -183,6 +180,10 @@ class IdmapData {
return target_entry_count; return target_entry_count;
} }
inline uint32_t GetTargetInlineEntryCount() const {
return target_entry_inline_count;
}
inline uint32_t GetOverlayEntryCount() const { inline uint32_t GetOverlayEntryCount() const {
return overlay_entry_count; return overlay_entry_count;
} }
@@ -191,19 +192,15 @@ class IdmapData {
return string_pool_index_offset; return string_pool_index_offset;
} }
inline uint32_t GetStringPoolLength() const {
return string_pool_len;
}
void accept(Visitor* v) const; void accept(Visitor* v) const;
private: private:
PackageId target_package_id_; PackageId target_package_id_;
PackageId overlay_package_id_; PackageId overlay_package_id_;
uint32_t target_entry_count; uint32_t target_entry_count;
uint32_t target_entry_inline_count;
uint32_t overlay_entry_count; uint32_t overlay_entry_count;
uint32_t string_pool_index_offset; uint32_t string_pool_index_offset;
uint32_t string_pool_len;
Header() = default; Header() = default;
friend Idmap; friend Idmap;
@@ -213,8 +210,12 @@ class IdmapData {
struct TargetEntry { struct TargetEntry {
ResourceId target_id; ResourceId target_id;
TargetValue::DataType data_type; ResourceId overlay_id;
TargetValue::DataValue data_value; };
struct TargetInlineEntry {
ResourceId target_id;
TargetValue value;
}; };
struct OverlayEntry { struct OverlayEntry {
@@ -227,20 +228,24 @@ class IdmapData {
static Result<std::unique_ptr<const IdmapData>> FromResourceMapping( static Result<std::unique_ptr<const IdmapData>> FromResourceMapping(
const ResourceMapping& resource_mapping); const ResourceMapping& resource_mapping);
inline const std::unique_ptr<const Header>& GetHeader() const { const std::unique_ptr<const Header>& GetHeader() const {
return header_; return header_;
} }
inline const std::vector<TargetEntry>& GetTargetEntries() const { const std::vector<TargetEntry>& GetTargetEntries() const {
return target_entries_; return target_entries_;
} }
inline const std::vector<OverlayEntry>& GetOverlayEntries() const { const std::vector<TargetInlineEntry>& GetTargetInlineEntries() const {
return target_inline_entries_;
}
const std::vector<OverlayEntry>& GetOverlayEntries() const {
return overlay_entries_; return overlay_entries_;
} }
inline const void* GetStringPoolData() const { const std::string& GetStringPoolData() const {
return string_pool_.get(); return string_pool_data_;
} }
void accept(Visitor* v) const; void accept(Visitor* v) const;
@@ -251,8 +256,9 @@ class IdmapData {
std::unique_ptr<const Header> header_; std::unique_ptr<const Header> header_;
std::vector<TargetEntry> target_entries_; std::vector<TargetEntry> target_entries_;
std::vector<TargetInlineEntry> target_inline_entries_;
std::vector<OverlayEntry> overlay_entries_; std::vector<OverlayEntry> overlay_entries_;
std::unique_ptr<uint8_t[]> string_pool_; std::string string_pool_data_;
friend Idmap; friend Idmap;
DISALLOW_COPY_AND_ASSIGN(IdmapData); DISALLOW_COPY_AND_ASSIGN(IdmapData);
@@ -304,6 +310,10 @@ class Visitor {
virtual void visit(const IdmapData::Header& header) = 0; virtual void visit(const IdmapData::Header& header) = 0;
}; };
inline size_t CalculatePadding(size_t data_length) {
return (4 - (data_length % 4)) % 4;
}
} // namespace android::idmap2 } // namespace android::idmap2
#endif // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_ #endif // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_

View File

@@ -41,8 +41,9 @@ class PrettyPrintVisitor : public Visitor {
private: private:
std::ostream& stream_; std::ostream& stream_;
std::unique_ptr<const ApkAssets> target_apk_;
AssetManager2 target_am_; AssetManager2 target_am_;
AssetManager2 overlay_am_;
std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
}; };
} // namespace idmap2 } // namespace idmap2

View File

@@ -45,11 +45,9 @@ class RawPrintVisitor : public Visitor {
void print(uint16_t value, const char* fmt, ...); void print(uint16_t value, const char* fmt, ...);
void print(uint32_t value, const char* fmt, ...); void print(uint32_t value, const char* fmt, ...);
void print(const std::string& value, size_t encoded_size, const char* fmt, ...); void print(const std::string& value, size_t encoded_size, const char* fmt, ...);
void print_raw(uint32_t length, const char* fmt, ...);
std::ostream& stream_; std::ostream& stream_;
std::unique_ptr<const ApkAssets> target_apk_; std::vector<std::unique_ptr<const ApkAssets>> apk_assets_;
std::unique_ptr<const ApkAssets> overlay_apk_;
AssetManager2 target_am_; AssetManager2 target_am_;
AssetManager2 overlay_am_; AssetManager2 overlay_am_;
size_t offset_; size_t offset_;

View File

@@ -41,7 +41,7 @@ struct TargetValue {
DataValue data_value; DataValue data_value;
}; };
using TargetResourceMap = std::map<ResourceId, TargetValue>; using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>;
using OverlayResourceMap = std::map<ResourceId, ResourceId>; using OverlayResourceMap = std::map<ResourceId, ResourceId>;
class ResourceMapping { class ResourceMapping {
@@ -56,7 +56,7 @@ class ResourceMapping {
bool enforce_overlayable, LogInfo& log_info); bool enforce_overlayable, LogInfo& log_info);
// Retrieves the mapping of target resource id to overlay value. // Retrieves the mapping of target resource id to overlay value.
inline TargetResourceMap GetTargetToOverlayMap() const { inline const TargetResourceMap& GetTargetToOverlayMap() const {
return target_map_; return target_map_;
} }
@@ -81,19 +81,24 @@ class ResourceMapping {
} }
// Retrieves the raw string pool data from the xml referenced in android:resourcesMap. // Retrieves the raw string pool data from the xml referenced in android:resourcesMap.
inline const std::pair<const uint8_t*, uint32_t> GetStringPoolData() const { inline const StringPiece GetStringPoolData() const {
return std::make_pair(string_pool_data_.get(), string_pool_data_length_); return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()),
string_pool_data_length_);
} }
private: private:
ResourceMapping() = default; ResourceMapping() = default;
// Apps a mapping of target resource id to the type and value of the data that overlays the // Maps a target resource id to an overlay resource id.
// target resource. The data_type is the runtime format of the data value (see // If rewrite_overlay_reference is `true` then references to the overlay
// Res_value::dataType). If rewrite_overlay_reference is `true` then references to an overlay
// resource should appear as a reference to its corresponding target resource at runtime. // resource should appear as a reference to its corresponding target resource at runtime.
Result<Unit> AddMapping(ResourceId target_resource, ResourceId overlay_resource,
bool rewrite_overlay_reference);
// Maps a target resource id to a data type and value combination.
// The `data_type` is the runtime format of the data value (see Res_value::dataType).
Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type, Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type,
TargetValue::DataValue data_value, bool rewrite_overlay_reference); TargetValue::DataValue data_value);
// Removes the overlay value mapping for the target resource. // Removes the overlay value mapping for the target resource.
void RemoveMapping(ResourceId target_resource); void RemoveMapping(ResourceId target_resource);

View File

@@ -24,10 +24,6 @@
namespace android::idmap2 { namespace android::idmap2 {
void BinaryStreamVisitor::Write(const void* value, size_t length) {
stream_.write(reinterpret_cast<const char*>(value), length);
}
void BinaryStreamVisitor::Write8(uint8_t value) { void BinaryStreamVisitor::Write8(uint8_t value) {
stream_.write(reinterpret_cast<char*>(&value), sizeof(uint8_t)); stream_.write(reinterpret_cast<char*>(&value), sizeof(uint8_t));
} }
@@ -49,11 +45,11 @@ void BinaryStreamVisitor::WriteString256(const StringPiece& value) {
stream_.write(buf, sizeof(buf)); stream_.write(buf, sizeof(buf));
} }
void BinaryStreamVisitor::WriteString(const std::string& value) { void BinaryStreamVisitor::WriteString(const StringPiece& value) {
// pad with null to nearest word boundary; include at least one terminating null // pad with null to nearest word boundary;
size_t padding_size = 4 - (value.size() % 4); size_t padding_size = CalculatePadding(value.size());
Write32(value.size() + padding_size); Write32(value.size());
stream_.write(value.c_str(), value.size()); stream_.write(value.data(), value.size());
stream_.write("\0\0\0\0", padding_size); stream_.write("\0\0\0\0", padding_size);
} }
@@ -67,7 +63,7 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) {
Write32(header.GetTargetCrc()); Write32(header.GetTargetCrc());
Write32(header.GetOverlayCrc()); Write32(header.GetOverlayCrc());
Write32(header.GetFulfilledPolicies()); Write32(header.GetFulfilledPolicies());
Write8(static_cast<uint8_t>(header.GetEnforceOverlayable())); Write32(static_cast<uint8_t>(header.GetEnforceOverlayable()));
WriteString256(header.GetTargetPath()); WriteString256(header.GetTargetPath());
WriteString256(header.GetOverlayPath()); WriteString256(header.GetOverlayPath());
WriteString(header.GetDebugInfo()); WriteString(header.GetDebugInfo());
@@ -76,8 +72,16 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) {
void BinaryStreamVisitor::visit(const IdmapData& data) { void BinaryStreamVisitor::visit(const IdmapData& data) {
for (const auto& target_entry : data.GetTargetEntries()) { for (const auto& target_entry : data.GetTargetEntries()) {
Write32(target_entry.target_id); Write32(target_entry.target_id);
Write8(target_entry.data_type); Write32(target_entry.overlay_id);
Write32(target_entry.data_value); }
static constexpr uint16_t kValueSize = 8U;
for (const auto& target_entry : data.GetTargetInlineEntries()) {
Write32(target_entry.target_id);
Write16(kValueSize);
Write8(0U); // padding
Write8(target_entry.value.data_type);
Write32(target_entry.value.data_value);
} }
for (const auto& overlay_entry : data.GetOverlayEntries()) { for (const auto& overlay_entry : data.GetOverlayEntries()) {
@@ -85,16 +89,18 @@ void BinaryStreamVisitor::visit(const IdmapData& data) {
Write32(overlay_entry.target_id); Write32(overlay_entry.target_id);
} }
Write(data.GetStringPoolData(), data.GetHeader()->GetStringPoolLength()); WriteString(data.GetStringPoolData());
} }
void BinaryStreamVisitor::visit(const IdmapData::Header& header) { void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
Write8(header.GetTargetPackageId()); Write8(header.GetTargetPackageId());
Write8(header.GetOverlayPackageId()); Write8(header.GetOverlayPackageId());
Write8(0U); // padding
Write8(0U); // padding
Write32(header.GetTargetEntryCount()); Write32(header.GetTargetEntryCount());
Write32(header.GetTargetInlineEntryCount());
Write32(header.GetOverlayEntryCount()); Write32(header.GetOverlayEntryCount());
Write32(header.GetStringPoolIndexOffset()); Write32(header.GetStringPoolIndexOffset());
Write32(header.GetStringPoolLength());
} }
} // namespace android::idmap2 } // namespace android::idmap2

View File

@@ -51,19 +51,19 @@ bool WARN_UNUSED Read8(std::istream& stream, uint8_t* out) {
return false; return false;
} }
bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) { bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) {
uint32_t value; uint16_t value;
if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) { if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) {
*out = dtohl(value); *out = dtohs(value);
return true; return true;
} }
return false; return false;
} }
bool WARN_UNUSED ReadBuffer(std::istream& stream, std::unique_ptr<uint8_t[]>* out, size_t length) { bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) {
auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[length]); uint32_t value;
if (stream.read(reinterpret_cast<char*>(buffer.get()), length)) { if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) {
*out = std::move(buffer); *out = dtohl(value);
return true; return true;
} }
return false; return false;
@@ -95,8 +95,11 @@ Result<std::string> ReadString(std::istream& stream) {
if (!stream.read(buf.data(), size)) { if (!stream.read(buf.data(), size)) {
return Error("failed to read string of size %u", size); return Error("failed to read string of size %u", size);
} }
// buf is guaranteed to be null terminated (with enough nulls to end on a word boundary) uint32_t padding_size = CalculatePadding(size);
buf.resize(strlen(buf.c_str())); std::string padding(padding_size, '\0');
if (!stream.read(padding.data(), padding_size)) {
return Error("failed to read string padding of size %u", padding_size);
}
return buf; return buf;
} }
@@ -112,16 +115,16 @@ Result<uint32_t> GetPackageCrc(const ZipFile& zip) {
std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) { std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader()); std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader());
uint8_t enforce_overlayable; uint32_t enforce_overlayable;
if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) || if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) ||
!Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) || !Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) ||
!Read32(stream, &idmap_header->fulfilled_policies_) || !Read8(stream, &enforce_overlayable) || !Read32(stream, &idmap_header->fulfilled_policies_) ||
!ReadString256(stream, idmap_header->target_path_) || !Read32(stream, &enforce_overlayable) || !ReadString256(stream, idmap_header->target_path_) ||
!ReadString256(stream, idmap_header->overlay_path_)) { !ReadString256(stream, idmap_header->overlay_path_)) {
return nullptr; return nullptr;
} }
idmap_header->enforce_overlayable_ = static_cast<bool>(enforce_overlayable); idmap_header->enforce_overlayable_ = enforce_overlayable != 0U;
auto debug_str = ReadString(stream); auto debug_str = ReadString(stream);
if (!debug_str) { if (!debug_str) {
@@ -207,12 +210,13 @@ Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overla
std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) { std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) {
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header()); std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
uint8_t padding;
if (!Read8(stream, &idmap_data_header->target_package_id_) || if (!Read8(stream, &idmap_data_header->target_package_id_) ||
!Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &padding) ||
!Read32(stream, &idmap_data_header->target_entry_count) || !Read8(stream, &padding) || !Read32(stream, &idmap_data_header->target_entry_count) ||
!Read32(stream, &idmap_data_header->target_entry_inline_count) ||
!Read32(stream, &idmap_data_header->overlay_entry_count) || !Read32(stream, &idmap_data_header->overlay_entry_count) ||
!Read32(stream, &idmap_data_header->string_pool_index_offset) || !Read32(stream, &idmap_data_header->string_pool_index_offset)) {
!Read32(stream, &idmap_data_header->string_pool_len)) {
return nullptr; return nullptr;
} }
@@ -225,14 +229,27 @@ std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& strea
if (!data->header_) { if (!data->header_) {
return nullptr; return nullptr;
} }
// Read the mapping of target resource id to overlay resource value. // Read the mapping of target resource id to overlay resource value.
for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) { for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) {
TargetEntry target_entry{}; TargetEntry target_entry{};
if (!Read32(stream, &target_entry.target_id) || !Read8(stream, &target_entry.data_type) || if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &target_entry.overlay_id)) {
!Read32(stream, &target_entry.data_value)) {
return nullptr; return nullptr;
} }
data->target_entries_.emplace_back(target_entry); data->target_entries_.push_back(target_entry);
}
// Read the mapping of target resource id to inline overlay values.
uint8_t unused1;
uint16_t unused2;
for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) {
TargetInlineEntry target_entry{};
if (!Read32(stream, &target_entry.target_id) || !Read16(stream, &unused2) ||
!Read8(stream, &unused1) || !Read8(stream, &target_entry.value.data_type) ||
!Read32(stream, &target_entry.value.data_value)) {
return nullptr;
}
data->target_inline_entries_.push_back(target_entry);
} }
// Read the mapping of overlay resource id to target resource id. // Read the mapping of overlay resource id to target resource id.
@@ -245,9 +262,11 @@ std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& strea
} }
// Read raw string pool bytes. // Read raw string pool bytes.
if (!ReadBuffer(stream, &data->string_pool_, data->header_->string_pool_len)) { auto string_pool_data = ReadString(stream);
if (!string_pool_data) {
return nullptr; return nullptr;
} }
data->string_pool_data_ = std::move(*string_pool_data);
return std::move(data); return std::move(data);
} }
@@ -290,27 +309,28 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
} }
std::unique_ptr<IdmapData> data(new IdmapData()); std::unique_ptr<IdmapData> data(new IdmapData());
for (const auto& mappings : resource_mapping.GetTargetToOverlayMap()) { data->string_pool_data_ = resource_mapping.GetStringPoolData().to_string();
data->target_entries_.emplace_back(IdmapData::TargetEntry{ for (const auto& mapping : resource_mapping.GetTargetToOverlayMap()) {
mappings.first, mappings.second.data_type, mappings.second.data_value}); if (auto overlay_resource = std::get_if<ResourceId>(&mapping.second)) {
data->target_entries_.push_back({mapping.first, *overlay_resource});
} else {
data->target_inline_entries_.push_back(
{mapping.first, std::get<TargetValue>(mapping.second)});
}
} }
for (const auto& mappings : resource_mapping.GetOverlayToTargetMap()) { for (const auto& mapping : resource_mapping.GetOverlayToTargetMap()) {
data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mappings.first, mappings.second}); data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mapping.first, mapping.second});
} }
std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header()); std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header());
data_header->target_package_id_ = resource_mapping.GetTargetPackageId(); data_header->target_package_id_ = resource_mapping.GetTargetPackageId();
data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId(); data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId();
data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size()); data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
data_header->target_entry_inline_count =
static_cast<uint32_t>(data->target_inline_entries_.size());
data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size()); data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size());
data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset(); data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset();
const auto string_pool_data = resource_mapping.GetStringPoolData();
data_header->string_pool_len = string_pool_data.second;
data->string_pool_ = std::unique_ptr<uint8_t[]>(new uint8_t[data_header->string_pool_len]);
memcpy(data->string_pool_.get(), string_pool_data.first, data_header->string_pool_len);
data->header_ = std::move(data_header); data->header_ = std::move(data_header);
return {std::move(data)}; return {std::move(data)};
} }

View File

@@ -38,6 +38,7 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) {
stream_ << "Paths:" << std::endl stream_ << "Paths:" << std::endl
<< TAB "target apk path : " << header.GetTargetPath() << std::endl << TAB "target apk path : " << header.GetTargetPath() << std::endl
<< TAB "overlay apk path : " << header.GetOverlayPath() << std::endl; << TAB "overlay apk path : " << header.GetOverlayPath() << std::endl;
const std::string& debug = header.GetDebugInfo(); const std::string& debug = header.GetDebugInfo();
if (!debug.empty()) { if (!debug.empty()) {
std::istringstream debug_stream(debug); std::istringstream debug_stream(debug);
@@ -48,10 +49,16 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) {
} }
} }
target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string()); if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string())) {
if (target_apk_) {
target_am_.SetApkAssets({target_apk_.get()}); target_am_.SetApkAssets({target_apk_.get()});
apk_assets_.push_back(std::move(target_apk_));
} }
if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath().to_string())) {
overlay_am_.SetApkAssets({overlay_apk.get()});
apk_assets_.push_back(std::move(overlay_apk));
}
stream_ << "Mapping:" << std::endl; stream_ << "Mapping:" << std::endl;
} }
@@ -59,34 +66,56 @@ void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED)
} }
void PrettyPrintVisitor::visit(const IdmapData& data) { void PrettyPrintVisitor::visit(const IdmapData& data) {
static constexpr const char* kUnknownResourceName = "???";
const bool target_package_loaded = !target_am_.GetApkAssets().empty(); const bool target_package_loaded = !target_am_.GetApkAssets().empty();
const ResStringPool string_pool(data.GetStringPoolData(), const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty();
data.GetHeader()->GetStringPoolLength());
const ResStringPool string_pool(data.GetStringPoolData().data(), data.GetStringPoolData().size());
const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset(); const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset();
for (auto& target_entry : data.GetTargetEntries()) { for (const auto& target_entry : data.GetTargetEntries()) {
stream_ << TAB << base::StringPrintf("0x%08x ->", target_entry.target_id); std::string target_name = kUnknownResourceName;
if (target_entry.data_type != Res_value::TYPE_REFERENCE &&
target_entry.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) {
stream_ << " " << utils::DataTypeToString(target_entry.data_type);
}
if (target_entry.data_type == Res_value::TYPE_STRING) {
stream_ << " \""
<< string_pool.string8ObjectAt(target_entry.data_value - string_pool_offset).c_str()
<< "\"";
} else {
stream_ << " " << base::StringPrintf("0x%08x", target_entry.data_value);
}
if (target_package_loaded) { if (target_package_loaded) {
Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_entry.target_id); if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
if (name) { target_name = *name;
stream_ << " " << *name;
} }
} }
stream_ << std::endl;
std::string overlay_name = kUnknownResourceName;
if (overlay_package_loaded) {
if (auto name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id)) {
overlay_name = *name;
}
}
stream_ << TAB
<< base::StringPrintf("0x%08x -> 0x%08x (%s -> %s)", target_entry.target_id,
target_entry.overlay_id, target_name.c_str(),
overlay_name.c_str())
<< std::endl;
}
for (auto& target_entry : data.GetTargetInlineEntries()) {
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, &unused);
stream_ << " \"" << StringPiece16(str) << "\"";
} else {
stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value);
}
std::string target_name = kUnknownResourceName;
if (target_package_loaded) {
if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) {
target_name = *name;
}
}
stream_ << " (" << target_name << ")" << std::endl;
} }
} }

View File

@@ -30,15 +30,6 @@
using android::ApkAssets; using android::ApkAssets;
using android::idmap2::policy::PoliciesToDebugString; using android::idmap2::policy::PoliciesToDebugString;
namespace {
size_t StringSizeWhenEncoded(const std::string& s) {
size_t null_bytes = 4 - (s.size() % 4);
return sizeof(uint32_t) + s.size() + null_bytes;
}
} // namespace
namespace android::idmap2 { namespace android::idmap2 {
void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) { void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) {
@@ -51,19 +42,24 @@ void RawPrintVisitor::visit(const IdmapHeader& header) {
print(header.GetOverlayCrc(), "overlay crc"); print(header.GetOverlayCrc(), "overlay crc");
print(header.GetFulfilledPolicies(), "fulfilled policies: %s", print(header.GetFulfilledPolicies(), "fulfilled policies: %s",
PoliciesToDebugString(header.GetFulfilledPolicies()).c_str()); PoliciesToDebugString(header.GetFulfilledPolicies()).c_str());
print(static_cast<uint8_t>(header.GetEnforceOverlayable()), "enforce overlayable"); print(static_cast<uint32_t>(header.GetEnforceOverlayable()), "enforce overlayable");
print(header.GetTargetPath().to_string(), kIdmapStringLength, "target path"); print(header.GetTargetPath().to_string(), kIdmapStringLength, "target path");
print(header.GetOverlayPath().to_string(), kIdmapStringLength, "overlay path"); print(header.GetOverlayPath().to_string(), kIdmapStringLength, "overlay path");
print("...", StringSizeWhenEncoded(header.GetDebugInfo()), "debug info");
target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string()); uint32_t debug_info_size = header.GetDebugInfo().size();
print(debug_info_size, "debug info size");
print("...", debug_info_size + CalculatePadding(debug_info_size), "debug info");
auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string());
if (target_apk_) { if (target_apk_) {
target_am_.SetApkAssets({target_apk_.get()}); target_am_.SetApkAssets({target_apk_.get()});
apk_assets_.push_back(std::move(target_apk_));
} }
overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string()); auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string());
if (overlay_apk_) { if (overlay_apk_) {
overlay_am_.SetApkAssets({overlay_apk_.get()}); overlay_am_.SetApkAssets({overlay_apk_.get()});
apk_assets_.push_back(std::move(overlay_apk_));
} }
} }
@@ -82,18 +78,44 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
print(target_entry.target_id, "target id"); print(target_entry.target_id, "target id");
} }
print(target_entry.data_type, "type: %s",
utils::DataTypeToString(target_entry.data_type).data());
Result<std::string> overlay_name(Error("")); Result<std::string> overlay_name(Error(""));
if (overlay_package_loaded && (target_entry.data_type == Res_value::TYPE_REFERENCE || if (overlay_package_loaded) {
target_entry.data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) { overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id);
overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.data_value);
} }
if (overlay_name) { if (overlay_name) {
print(target_entry.data_value, "value: %s", overlay_name->c_str()); print(target_entry.overlay_id, "overlay id: %s", overlay_name->c_str());
} else { } else {
print(target_entry.data_value, "value"); print(target_entry.overlay_id, "overlay id");
}
}
for (auto& target_entry : data.GetTargetInlineEntries()) {
Result<std::string> target_name(Error(""));
if (target_package_loaded) {
target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id);
}
if (target_name) {
print(target_entry.target_id, "target id: %s", target_name->c_str());
} else {
print(target_entry.target_id, "target id");
}
print("...", sizeof(Res_value::size) + sizeof(Res_value::res0), "padding");
print(target_entry.value.data_type, "type: %s",
utils::DataTypeToString(target_entry.value.data_type).data());
Result<std::string> overlay_name(Error(""));
if (overlay_package_loaded &&
(target_entry.value.data_value == Res_value::TYPE_REFERENCE ||
target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.value.data_value);
}
if (overlay_name) {
print(target_entry.value.data_value, "data: %s", overlay_name->c_str());
} else {
print(target_entry.value.data_value, "data");
} }
} }
@@ -121,19 +143,19 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
} }
} }
const size_t string_pool_length = data.GetHeader()->GetStringPoolLength(); uint32_t string_pool_size = data.GetStringPoolData().size();
if (string_pool_length > 0) { print(string_pool_size, "string pool size");
print_raw(string_pool_length, "%zu raw string pool bytes", string_pool_length); print("...", string_pool_size + CalculatePadding(string_pool_size), "string pool");
}
} }
void RawPrintVisitor::visit(const IdmapData::Header& header) { void RawPrintVisitor::visit(const IdmapData::Header& header) {
print(header.GetTargetPackageId(), "target package id"); print(header.GetTargetPackageId(), "target package id");
print(header.GetOverlayPackageId(), "overlay package id"); print(header.GetOverlayPackageId(), "overlay package id");
print("...", sizeof(Idmap_data_header::p0), "padding");
print(header.GetTargetEntryCount(), "target entry count"); print(header.GetTargetEntryCount(), "target entry count");
print(header.GetTargetInlineEntryCount(), "target inline entry count");
print(header.GetOverlayEntryCount(), "overlay entry count"); print(header.GetOverlayEntryCount(), "overlay entry count");
print(header.GetStringPoolIndexOffset(), "string pool index offset"); print(header.GetStringPoolIndexOffset(), "string pool index offset");
print(header.GetStringPoolLength(), "string pool byte length");
} }
// NOLINTNEXTLINE(cert-dcl50-cpp) // NOLINTNEXTLINE(cert-dcl50-cpp)
@@ -190,17 +212,4 @@ void RawPrintVisitor::print(const std::string& value, size_t encoded_size, const
offset_ += encoded_size; offset_ += encoded_size;
} }
// NOLINTNEXTLINE(cert-dcl50-cpp)
void RawPrintVisitor::print_raw(uint32_t length, const char* fmt, ...) {
va_list ap;
va_start(ap, fmt);
std::string comment;
base::StringAppendV(&comment, fmt, ap);
va_end(ap);
stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << std::endl;
offset_ += length;
}
} // namespace android::idmap2 } // namespace android::idmap2

View File

@@ -71,9 +71,9 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
if (!target_package.DefinesOverlayable()) { if (!target_package.DefinesOverlayable()) {
return (sDefaultPolicies & fulfilled_policies) != 0 return (sDefaultPolicies & fulfilled_policies) != 0
? Result<Unit>({}) ? Result<Unit>({})
: Error( : Error("overlay must be preinstalled, signed with the same signature as the target,"
"overlay must be preinstalled or signed with the same signature as the " " or signed with the same signature as the package referenced through"
"target"); " <overlay-config-signature>.");
} }
const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource); const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource);
@@ -205,19 +205,14 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManage
overlay_resource->data += string_pool_offset; overlay_resource->data += string_pool_offset;
} }
// Only rewrite resources defined within the overlay package to their corresponding target if (IsReference(overlay_resource->dataType)) {
// resource ids at runtime. // Only rewrite resources defined within the overlay package to their corresponding target
bool rewrite_overlay_reference = // resource ids at runtime.
IsReference(overlay_resource->dataType) bool rewrite_reference = overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data);
? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data) resource_mapping.AddMapping(target_id, overlay_resource->data, rewrite_reference);
: false; } else {
resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data);
if (rewrite_overlay_reference) {
overlay_resource->dataType = Res_value::TYPE_DYNAMIC_REFERENCE;
} }
resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data,
rewrite_overlay_reference);
} }
return resource_mapping; return resource_mapping;
@@ -246,9 +241,8 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy(
// Retrieve the compile-time resource id of the target resource. // Retrieve the compile-time resource id of the target resource.
target_resource = REWRITE_PACKAGE(target_resource, target_package_id); target_resource = REWRITE_PACKAGE(target_resource, target_package_id);
resource_mapping.AddMapping(target_resource, overlay_resid,
resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid, false /* rewrite_overlay_reference */);
/* rewrite_overlay_reference */ false);
} }
return resource_mapping; return resource_mapping;
@@ -396,9 +390,7 @@ OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const {
return map; return map;
} }
Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource,
TargetValue::DataType data_type,
TargetValue::DataValue data_value,
bool rewrite_overlay_reference) { bool rewrite_overlay_reference) {
if (target_map_.find(target_resource) != target_map_.end()) { if (target_map_.find(target_resource) != target_map_.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource); return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
@@ -407,13 +399,26 @@ Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved. // runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value})); target_map_.insert(std::make_pair(target_resource, overlay_resource));
if (rewrite_overlay_reference && IsReference(data_type)) { if (rewrite_overlay_reference) {
overlay_map_.insert(std::make_pair(data_value, target_resource)); overlay_map_.insert(std::make_pair(overlay_resource, target_resource));
}
return Unit{};
}
Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource,
TargetValue::DataType data_type,
TargetValue::DataValue data_value) {
if (target_map_.find(target_resource) != target_map_.end()) {
return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
} }
return Result<Unit>({}); // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value}));
return Unit{};
} }
void ResourceMapping::RemoveMapping(ResourceId target_resource) { void ResourceMapping::RemoveMapping(ResourceId target_resource) {
@@ -422,14 +427,15 @@ void ResourceMapping::RemoveMapping(ResourceId target_resource) {
return; return;
} }
const TargetValue value = target_iter->second; const auto value = target_iter->second;
target_map_.erase(target_iter); target_map_.erase(target_iter);
if (!IsReference(value.data_type)) { const ResourceId* overlay_resource = std::get_if<ResourceId>(&value);
if (overlay_resource == nullptr) {
return; return;
} }
auto overlay_iter = overlay_map_.equal_range(value.data_value); auto overlay_iter = overlay_map_.equal_range(*overlay_resource);
for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) { for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) {
if (i->second == target_resource) { if (i->second == target_resource) {
overlay_map_.erase(i); overlay_map_.erase(i);

View File

@@ -72,13 +72,20 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
const auto& target_entries2 = data2->GetTargetEntries(); const auto& target_entries2 = data2->GetTargetEntries();
ASSERT_EQ(target_entries1.size(), target_entries2.size()); ASSERT_EQ(target_entries1.size(), target_entries2.size());
ASSERT_EQ(target_entries1[0].target_id, target_entries2[0].target_id); ASSERT_EQ(target_entries1[0].target_id, target_entries2[0].target_id);
ASSERT_EQ(target_entries1[0].data_value, target_entries2[0].data_value); ASSERT_EQ(target_entries1[0].overlay_id, target_entries2[0].overlay_id);
ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id); ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id);
ASSERT_EQ(target_entries1[1].data_value, target_entries2[1].data_value); ASSERT_EQ(target_entries1[1].overlay_id, target_entries2[1].overlay_id);
ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id); ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id);
ASSERT_EQ(target_entries1[2].data_value, target_entries2[2].data_value); ASSERT_EQ(target_entries1[2].overlay_id, target_entries2[2].overlay_id);
const auto& target_inline_entries1 = data1->GetTargetInlineEntries();
const auto& target_inline_entries2 = data2->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries1.size(), target_inline_entries2.size());
ASSERT_EQ(target_inline_entries1[0].target_id, target_inline_entries2[0].target_id);
ASSERT_EQ(target_inline_entries1[0].value.data_type, target_inline_entries2[0].value.data_type);
ASSERT_EQ(target_inline_entries1[0].value.data_value, target_inline_entries2[0].value.data_value);
const auto& overlay_entries1 = data1->GetOverlayEntries(); const auto& overlay_entries1 = data1->GetOverlayEntries();
const auto& overlay_entries2 = data2->GetOverlayEntries(); const auto& overlay_entries2 = data2->GetOverlayEntries();

View File

@@ -128,13 +128,13 @@ TEST_F(Idmap2BinaryTests, Dump) {
// clang-format on // clang-format on
ASSERT_THAT(result, NotNull()); ASSERT_THAT(result, NotNull());
ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1"), ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000"),
std::string::npos); std::string::npos);
ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000 string/str1"), ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000"),
std::string::npos); std::string::npos);
ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001 string/str3"), ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001"),
std::string::npos); std::string::npos);
ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002 string/str4"), ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002"),
std::string::npos); std::string::npos);
// clang-format off // clang-format off

View File

@@ -42,14 +42,18 @@ using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
namespace android::idmap2 { namespace android::idmap2 {
#define ASSERT_TARGET_ENTRY(entry, target_resid, type, value) \ #define ASSERT_TARGET_ENTRY(entry, target_resid, overlay_resid) \
ASSERT_EQ(entry.target_id, target_resid); \ ASSERT_EQ((entry).target_id, (target_resid)); \
ASSERT_EQ(entry.data_type, type); \ ASSERT_EQ((entry).overlay_id, (overlay_resid))
ASSERT_EQ(entry.data_value, value)
#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, expected_type, expected_value) \
ASSERT_EQ((entry).target_id, target_resid); \
ASSERT_EQ((entry).value.data_type, (expected_type)); \
ASSERT_EQ((entry).value.data_value, (expected_value))
#define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \ #define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \
ASSERT_EQ(entry.overlay_id, overlay_resid); \ ASSERT_EQ((entry).overlay_id, (overlay_resid)); \
ASSERT_EQ(entry.target_id, target_resid) ASSERT_EQ((entry).target_id, (target_resid))
TEST(IdmapTests, TestCanonicalIdmapPathFor) { TEST(IdmapTests, TestCanonicalIdmapPathFor) {
ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"), ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"),
@@ -62,7 +66,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream); std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull()); ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U); ASSERT_EQ(header->GetMagic(), 0x504d4449U);
ASSERT_EQ(header->GetVersion(), 0x04U); ASSERT_EQ(header->GetVersion(), 0x05U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U); ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U); ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(header->GetFulfilledPolicies(), 0x11); ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -75,7 +79,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) { TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len); std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len);
// overwrite the target path string, including the terminating null, with '.' // overwrite the target path string, including the terminating null, with '.'
for (size_t i = 0x15; i < 0x115; i++) { for (size_t i = 0x18; i < 0x118; i++) {
raw[i] = '.'; raw[i] = '.';
} }
std::istringstream stream(raw); std::istringstream stream(raw);
@@ -84,7 +88,7 @@ TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) {
} }
TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
const size_t offset = 0x221; const size_t offset = 0x224;
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset), std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
idmap_raw_data_len - offset); idmap_raw_data_len - offset);
std::istringstream stream(raw); std::istringstream stream(raw);
@@ -96,7 +100,7 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) {
} }
TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
const size_t offset = 0x221; const size_t offset = 0x224;
std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset), std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset),
idmap_raw_data_len - offset); idmap_raw_data_len - offset);
std::istringstream stream(raw); std::istringstream stream(raw);
@@ -106,12 +110,14 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
const auto& target_entries = data->GetTargetEntries(); const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 3U); ASSERT_EQ(target_entries.size(), 3U);
ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x01 /* Res_value::TYPE_REFERENCE */, ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x7f020000);
0x7f020000); ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x7f030000);
ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x01 /* Res_value::TYPE_REFERENCE */, ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x7f030001);
0x7f030000);
ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x01 /* Res_value::TYPE_REFERENCE */, const auto& target_inline_entries = data->GetTargetInlineEntries();
0x7f030001); ASSERT_EQ(target_inline_entries.size(), 1U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
0x12345678);
const auto& overlay_entries = data->GetOverlayEntries(); const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 3U); ASSERT_EQ(target_entries.size(), 3U);
@@ -130,7 +136,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U); ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), 0x11); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), 0x11);
@@ -146,9 +152,14 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
const auto& target_entries = data->GetTargetEntries(); const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 3U); ASSERT_EQ(target_entries.size(), 3U);
ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, Res_value::TYPE_REFERENCE, 0x7f020000); ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x7f020000);
ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, Res_value::TYPE_REFERENCE, 0x7f030000); ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x7f030000);
ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, Res_value::TYPE_REFERENCE, 0x7f030001); ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x7f030001);
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 1U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
0x12345678);
const auto& overlay_entries = data->GetOverlayEntries(); const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 3U); ASSERT_EQ(target_entries.size(), 3U);
@@ -184,7 +195,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U); ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
@@ -244,14 +255,13 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) {
const auto& target_entries = data->GetTargetEntries(); const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 4U); ASSERT_EQ(target_entries.size(), 4U);
ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, R::overlay::integer::int1);
Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay::integer::int1); ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, R::overlay::string::str1);
ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE, ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, R::overlay::string::str3);
R::overlay::string::str1); ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, R::overlay::string::str4);
ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
R::overlay::string::str3); const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE, ASSERT_EQ(target_inline_entries.size(), 0U);
R::overlay::string::str4);
const auto& overlay_entries = data->GetOverlayEntries(); const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 4U); ASSERT_EQ(target_entries.size(), 4U);
@@ -286,13 +296,13 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
const auto& target_entries = data->GetTargetEntries(); const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 4U); ASSERT_EQ(target_entries.size(), 4U);
ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay_shared::integer::int1); R::overlay_shared::integer::int1);
ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE, ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, R::overlay_shared::string::str1);
R::overlay_shared::string::str1); ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, R::overlay_shared::string::str3);
ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE, ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, R::overlay_shared::string::str4);
R::overlay_shared::string::str3);
ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE, const auto& target_inline_entries = data->GetTargetInlineEntries();
R::overlay_shared::string::str4); ASSERT_EQ(target_inline_entries.size(), 0U);
const auto& overlay_entries = data->GetOverlayEntries(); const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 4U); ASSERT_EQ(target_entries.size(), 4U);
@@ -320,10 +330,12 @@ TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
const auto& target_entries = data->GetTargetEntries(); const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 2U); ASSERT_EQ(target_entries.size(), 2U);
ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1, Res_value::TYPE_REFERENCE, ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1,
0x0104000a); // -> android:string/ok 0x0104000a); // -> android:string/ok
ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE, ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, R::overlay::string::str3);
R::overlay::string::str3);
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 0U);
const auto& overlay_entries = data->GetOverlayEntries(); const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(overlay_entries.size(), 1U); ASSERT_EQ(overlay_entries.size(), 1U);
@@ -342,13 +354,17 @@ TEST(IdmapTests, CreateIdmapDataInlineResources) {
ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage(); ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage();
auto& data = *idmap_data; auto& data = *idmap_data;
constexpr size_t overlay_string_pool_size = 8U;
const auto& target_entries = data->GetTargetEntries(); const auto& target_entries = data->GetTargetEntries();
ASSERT_EQ(target_entries.size(), 2U); ASSERT_EQ(target_entries.size(), 0U);
ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, Res_value::TYPE_INT_DEC,
73U); // -> 73 constexpr size_t overlay_string_pool_size = 8U;
ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_STRING, const auto& target_inline_entries = data->GetTargetInlineEntries();
overlay_string_pool_size + 0U); // -> "Hello World" ASSERT_EQ(target_inline_entries.size(), 2U);
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
Res_value::TYPE_INT_DEC, 73U); // -> 73
ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
Res_value::TYPE_STRING,
overlay_string_pool_size + 0U); // -> "Hello World"
const auto& overlay_entries = data->GetOverlayEntries(); const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(overlay_entries.size(), 0U); ASSERT_EQ(overlay_entries.size(), 0U);
@@ -479,9 +495,9 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// target path: bytes (0x15, 0x114) // target path: bytes (0x18, 0x117)
std::string bad_target_path_string(stream.str()); std::string bad_target_path_string(stream.str());
bad_target_path_string[0x15] = '\0'; bad_target_path_string[0x18] = '\0';
std::stringstream bad_target_path_stream(bad_target_path_string); std::stringstream bad_target_path_stream(bad_target_path_string);
std::unique_ptr<const IdmapHeader> bad_target_path_header = std::unique_ptr<const IdmapHeader> bad_target_path_header =
IdmapHeader::FromBinaryStream(bad_target_path_stream); IdmapHeader::FromBinaryStream(bad_target_path_stream);
@@ -490,9 +506,9 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) {
ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(),
PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); PolicyFlags::PUBLIC, /* enforce_overlayable */ true));
// overlay path: bytes (0x115, 0x214) // overlay path: bytes (0x118, 0x217)
std::string bad_overlay_path_string(stream.str()); std::string bad_overlay_path_string(stream.str());
bad_overlay_path_string[0x115] = '\0'; bad_overlay_path_string[0x118] = '\0';
std::stringstream bad_overlay_path_stream(bad_overlay_path_string); std::stringstream bad_overlay_path_stream(bad_overlay_path_string);
std::unique_ptr<const IdmapHeader> bad_overlay_path_header = std::unique_ptr<const IdmapHeader> bad_overlay_path_header =
IdmapHeader::FromBinaryStream(bad_overlay_path_stream); IdmapHeader::FromBinaryStream(bad_overlay_path_stream);

View File

@@ -56,7 +56,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) {
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos); ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos); ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
ASSERT_NE(stream.str().find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1\n"), ASSERT_NE(stream.str().find(R::target::integer::literal::int1 +
" -> 0x7f010000 (integer/int1 -> integer/int1)\n"),
std::string::npos); std::string::npos);
} }
@@ -75,7 +76,7 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) {
ASSERT_NE(stream.str().find("target apk path : "), std::string::npos); ASSERT_NE(stream.str().find("target apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos); ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos);
ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000\n"), std::string::npos); ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000 (\?\?\? -> \?\?\?)\n"), std::string::npos);
} }
} // namespace android::idmap2 } // namespace android::idmap2

View File

@@ -65,7 +65,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
(*idmap)->accept(&visitor); (*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000005 version\n", stream.str());
ASSERT_CONTAINS_REGEX( ASSERT_CONTAINS_REGEX(
StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING), StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
stream.str()); stream.str());
@@ -73,19 +73,19 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
StringPrintf(ADDRESS "%s overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING), StringPrintf(ADDRESS "%s overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING),
stream.str()); stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000008 string pool index offset\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000008 string pool index offset\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool byte length\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 07 type: reference \\(dynamic\\)\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 value: integer/int1\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool size\n", stream.str());
ASSERT_CONTAINS_REGEX("000002bc: ........ string pool: ...\n", stream.str());
} }
TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
@@ -102,22 +102,26 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
(*idmap)->accept(&visitor); (*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000005 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool index offset\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool index offset\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool byte length\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 01 type: reference\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 value\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 11 type: integer\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "12345678 data\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str());
ASSERT_CONTAINS_REGEX("00000278: ........ string pool: ...\n", stream.str());
} }
} // namespace android::idmap2 } // namespace android::idmap2

View File

@@ -77,30 +77,61 @@ Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local
fulfilled_policies, enforce_overlayable); fulfilled_policies, enforce_overlayable);
} }
Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource, Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource,
const uint8_t type, const uint32_t value, bool rewrite) { ResourceId overlay_resource, bool rewrite) {
auto target_map = mapping.GetTargetToOverlayMap(); auto target_map = mapping.GetTargetToOverlayMap();
auto entry_map = target_map.find(target_resource); auto entry_map = target_map.find(target_resource);
if (entry_map == target_map.end()) { if (entry_map == target_map.end()) {
return Error("Failed to find mapping for target resource"); return Error("Failed to find mapping for target resource");
} }
if (entry_map->second.data_type != type) { auto actual_overlay_resource = std::get_if<ResourceId>(&entry_map->second);
return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type, if (actual_overlay_resource == nullptr) {
entry_map->second.data_type); return Error("Target resource is not mapped to an overlay resource id");
} }
if (entry_map->second.data_value != value) { if (*actual_overlay_resource != overlay_resource) {
return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type, return Error(R"(Expected id: "0x%02x" Actual id: "0x%02x")", overlay_resource,
entry_map->second.data_value); *actual_overlay_resource);
} }
auto overlay_map = mapping.GetOverlayToTargetMap(); auto overlay_map = mapping.GetOverlayToTargetMap();
auto overlay_iter = overlay_map.find(entry_map->second.data_value); auto overlay_iter = overlay_map.find(overlay_resource);
if ((overlay_iter != overlay_map.end()) != rewrite) { if ((overlay_iter != overlay_map.end()) != rewrite) {
return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false"); return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false");
} }
if (rewrite && overlay_iter->second != target_resource) {
return Error(R"(Expected rewrite id: "0x%02x" Actual id: "0x%02x")", target_resource,
overlay_iter->second);
}
return Result<Unit>({});
}
Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource,
const uint8_t type, const uint32_t value) {
auto target_map = mapping.GetTargetToOverlayMap();
auto entry_map = target_map.find(target_resource);
if (entry_map == target_map.end()) {
return Error("Failed to find mapping for target resource");
}
auto actual_overlay_value = std::get_if<TargetValue>(&entry_map->second);
if (actual_overlay_value == nullptr) {
return Error("Target resource is not mapped to an inline value");
}
if (actual_overlay_value->data_type != type) {
return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
actual_overlay_value->data_type);
}
if (actual_overlay_value->data_value != value) {
return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
actual_overlay_value->data_value);
}
return Result<Unit>({}); return Result<Unit>({});
} }
@@ -116,14 +147,14 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) {
ASSERT_TRUE(resources) << resources.GetErrorMessage(); ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources; auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U); ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE, ASSERT_RESULT(
R::overlay::integer::int1, false /* rewrite */)); MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, ASSERT_RESULT(
R::overlay::string::str1, false /* rewrite */)); MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE, ASSERT_RESULT(
R::overlay::string::str3, false /* rewrite */)); MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE, ASSERT_RESULT(
R::overlay::string::str4, false /* rewrite */)); MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */));
} }
TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) { TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
@@ -138,12 +169,12 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) {
ASSERT_TRUE(resources) << resources.GetErrorMessage(); ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources; auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE, ASSERT_RESULT(
R::overlay::string::str4, true /* rewrite */)); MappingExists(res, R::target::string::str1, R::overlay::string::str4, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE, ASSERT_RESULT(
R::overlay::string::str1, true /* rewrite */)); MappingExists(res, R::target::string::str3, R::overlay::string::str1, true /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE, ASSERT_RESULT(
R::overlay::string::str3, true /* rewrite */)); MappingExists(res, R::target::string::str4, R::overlay::string::str3, true /* rewrite */));
} }
TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) { TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
@@ -159,10 +190,9 @@ TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) {
auto& res = *resources; auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U); ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U); ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U);
ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x0104000a, ASSERT_RESULT(MappingExists(res, R::target::string::str1, 0x0104000a,
false /* rewrite */)); // -> android:string/ok false /* rewrite */)); // -> android:string/ok
ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::str3, 0x7f020001, true /* rewrite */));
0x7f020001, true /* rewrite */));
} }
TEST(ResourceMappingTests, InlineResources) { TEST(ResourceMappingTests, InlineResources) {
@@ -180,10 +210,8 @@ TEST(ResourceMappingTests, InlineResources) {
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U); ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U);
ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U); ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U);
ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_STRING, ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_STRING,
overlay_string_pool_size + 0U, overlay_string_pool_size + 0U)); // -> "Hello World"
false /* rewrite */)); // -> "Hello World" ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U));
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U,
false /* rewrite */)); // -> 73
} }
TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) { TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
@@ -195,13 +223,13 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) {
ASSERT_TRUE(resources) << resources.GetErrorMessage(); ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources; auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
R::system_overlay::string::policy_public, false /* rewrite */)); R::system_overlay::string::policy_public, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
R::system_overlay::string::policy_system, false /* rewrite */)); R::system_overlay::string::policy_system, false /* rewrite */));
ASSERT_RESULT( ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE, R::system_overlay::string::policy_system_vendor,
R::system_overlay::string::policy_system_vendor, false /* rewrite */)); false /* rewrite */));
} }
// Resources that are not declared as overlayable and resources that a protected by policies the // Resources that are not declared as overlayable and resources that a protected by policies the
@@ -215,15 +243,15 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) {
ASSERT_TRUE(resources) << resources.GetErrorMessage(); ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources; auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U);
ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
R::system_overlay_invalid::string::policy_public, R::system_overlay_invalid::string::policy_public,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
R::system_overlay_invalid::string::policy_system, R::system_overlay_invalid::string::policy_system,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT( ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE, R::system_overlay_invalid::string::policy_system_vendor,
R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */)); false /* rewrite */));
} }
// Resources that are not declared as overlayable and resources that a protected by policies the // Resources that are not declared as overlayable and resources that a protected by policies the
@@ -238,37 +266,36 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnore
ASSERT_TRUE(resources) << resources.GetErrorMessage(); ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources; auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 11U); ASSERT_EQ(res.GetTargetToOverlayMap().size(), 11U);
ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
R::system_overlay_invalid::string::not_overlayable, R::system_overlay_invalid::string::not_overlayable,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::other,
R::system_overlay_invalid::string::other, false /* rewrite */)); R::system_overlay_invalid::string::other, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
R::system_overlay_invalid::string::policy_actor, R::system_overlay_invalid::string::policy_actor,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm,
R::system_overlay_invalid::string::policy_odm, false /* rewrite */)); R::system_overlay_invalid::string::policy_odm, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem,
R::system_overlay_invalid::string::policy_oem, false /* rewrite */)); R::system_overlay_invalid::string::policy_oem, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
R::system_overlay_invalid::string::policy_product, R::system_overlay_invalid::string::policy_product,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
R::system_overlay_invalid::string::policy_public, R::system_overlay_invalid::string::policy_public,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature, ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
Res_value::TYPE_REFERENCE,
R::system_overlay_invalid::string::policy_config_signature, R::system_overlay_invalid::string::policy_config_signature,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
R::system_overlay_invalid::string::policy_signature, R::system_overlay_invalid::string::policy_signature,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
R::system_overlay_invalid::string::policy_system, R::system_overlay_invalid::string::policy_system,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT( ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE, R::system_overlay_invalid::string::policy_system_vendor,
R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */)); false /* rewrite */));
} }
// Overlays that do not target an <overlayable> tag can overlay resources defined within any // Overlays that do not target an <overlayable> tag can overlay resources defined within any
@@ -281,14 +308,14 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTarget
ASSERT_TRUE(resources) << resources.GetErrorMessage(); ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources; auto& res = *resources;
ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U); ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U);
ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE, ASSERT_RESULT(
R::overlay::integer::int1, false /* rewrite */)); MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, ASSERT_RESULT(
R::overlay::string::str1, false /* rewrite */)); MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE, ASSERT_RESULT(
R::overlay::string::str3, false /* rewrite */)); MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE, ASSERT_RESULT(
R::overlay::string::str4, false /* rewrite */)); MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */));
} }
// Overlays that are neither pre-installed nor signed with the same signature as the target cannot // Overlays that are neither pre-installed nor signed with the same signature as the target cannot
@@ -302,9 +329,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U); ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
} }
// Overlays that are pre-installed or are signed with the same signature as the target or are signed // Overlays that are pre-installed or are signed with the same signature as the target or are
// with the same signature as the reference package can overlay packages that have not defined // signed with the same signature as the reference package can overlay packages that have not
// overlayable resources. // defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) { TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void { auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk", auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
@@ -315,39 +342,38 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
ASSERT_TRUE(resources) << resources.GetErrorMessage(); ASSERT_TRUE(resources) << resources.GetErrorMessage();
auto& res = *resources; auto& res = *resources;
ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 11U); ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 11U);
ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable,
R::system_overlay_invalid::string::not_overlayable, R::system_overlay_invalid::string::not_overlayable,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::other,
R::system_overlay_invalid::string::other, false /* rewrite */)); R::system_overlay_invalid::string::other, false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor,
R::system_overlay_invalid::string::policy_actor, R::system_overlay_invalid::string::policy_actor,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm,
R::system_overlay_invalid::string::policy_odm, R::system_overlay_invalid::string::policy_odm,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem,
R::system_overlay_invalid::string::policy_oem, R::system_overlay_invalid::string::policy_oem,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_product,
R::system_overlay_invalid::string::policy_product, R::system_overlay_invalid::string::policy_product,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_public,
R::system_overlay_invalid::string::policy_public, R::system_overlay_invalid::string::policy_public,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature, ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature,
Res_value::TYPE_REFERENCE,
R::system_overlay_invalid::string::policy_config_signature, R::system_overlay_invalid::string::policy_config_signature,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature,
R::system_overlay_invalid::string::policy_signature, R::system_overlay_invalid::string::policy_signature,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE, ASSERT_RESULT(MappingExists(res, R::target::string::policy_system,
R::system_overlay_invalid::string::policy_system, R::system_overlay_invalid::string::policy_system,
false /* rewrite */)); false /* rewrite */));
ASSERT_RESULT(MappingExists( ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor,
res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE, R::system_overlay_invalid::string::policy_system_vendor,
R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */)); false /* rewrite */));
}; };
CheckEntries(PolicyFlags::SIGNATURE); CheckEntries(PolicyFlags::SIGNATURE);

View File

@@ -19,7 +19,7 @@
namespace android::idmap2::TestConstants { namespace android::idmap2::TestConstants {
constexpr const auto TARGET_CRC = 0x7c2d4719; constexpr const auto TARGET_CRC = 0x7c2d4719;
constexpr const auto TARGET_CRC_STRING = "7c2d4719"; constexpr const auto TARGET_CRC_STRING = "7c2d4719";
constexpr const auto OVERLAY_CRC = 0x5afff726; constexpr const auto OVERLAY_CRC = 0x5afff726;

View File

@@ -30,7 +30,7 @@ const unsigned char idmap_raw_data[] = {
0x49, 0x44, 0x4d, 0x50, 0x49, 0x44, 0x4d, 0x50,
// 0x4: version // 0x4: version
0x04, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
// 0x8: target crc // 0x8: target crc
0x34, 0x12, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00,
@@ -42,9 +42,9 @@ const unsigned char idmap_raw_data[] = {
0x11, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00,
// 0x14: enforce overlayable // 0x14: enforce overlayable
0x01, 0x01, 0x00, 0x00, 0x00,
// 0x15: target path "targetX.apk" // 0x18: target path "targetX.apk"
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -62,7 +62,7 @@ const unsigned char idmap_raw_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x115: overlay path "overlayX.apk" // 0x118: overlay path "overlayX.apk"
0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -80,71 +80,89 @@ const unsigned char idmap_raw_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x215: debug string // 0x218: debug string
// string length, including terminating null // string length,
0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
// string contents "debug\0\0\0" (padded to word alignment) // 0x21c string contents "debug\0\0\0" (padded to word alignment)
0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00, 0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00,
// DATA HEADER // DATA HEADER
// 0x221: target_package_id // 0x224: target_package_id
0x7f, 0x7f,
// 0x222: overlay_package_id // 0x225: overlay_package_id
0x7f, 0x7f,
// 0x223: target_entry_count // 0x226: padding
0x00, 0x00,
// 0x228: target_entry_count
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
// 0x227: overlay_entry_count // 0x22c: target_inline_entry_count
0x01, 0x00, 0x00, 0x00,
// 0x230: overlay_entry_count
0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
// 0x22b: string_pool_offset // 0x234: string_pool_offset
0x00, 0x00, 0x00, 0x00,
// 0x22f: string_pool_byte_length
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// TARGET ENTRIES // TARGET ENTRIES
// 0x233: 0x7f020000 // 0x238: target id (0x7f020000)
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
// 0x237: TYPE_REFERENCE // 0x23c: overlay_id (0x7f020000)
0x01,
// 0x238: 0x7f020000
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
// 0x23c: 0x7f030000 // 0x240: target id (0x7f030000)
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
// 0x240: TYPE_REFERENCE // 0x244: overlay_id (0x7f030000)
0x01,
// 0x241: 0x7f030000
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
// 0x245: 0x7f030002 // 0x248: target id (0x7f030002)
0x02, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
// 0x249: TYPE_REFERENCE // 0x24c: overlay_id (0x7f030001)
0x01,
// 0x24a: 0x7f030001
0x01, 0x00, 0x03, 0x7f, 0x01, 0x00, 0x03, 0x7f,
// INLINE TARGET ENTRIES
// 0x250: target_id
0x00, 0x00, 0x04, 0x7f,
// 0x254: Res_value::size (value ignored by idmap)
0x08, 0x00,
// 0x256: Res_value::res0 (value ignored by idmap)
0x00,
// 0x257: Res_value::dataType (TYPE_INT_HEX)
0x11,
// 0x258: Res_value::data
0x78, 0x56, 0x34, 0x12,
// OVERLAY ENTRIES // OVERLAY ENTRIES
// 0x24e: 0x7f020000 -> 0x7f020000 // 0x25c: 0x7f020000 -> 0x7f020000
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
// 0x256: 0x7f030000 -> 0x7f030000 // 0x264: 0x7f030000 -> 0x7f030000
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
// 0x25e: 0x7f030001 -> 0x7f030002 // 0x26c: 0x7f030001 -> 0x7f030002
0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f}; 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
const unsigned int idmap_raw_data_len = 0x266; // 0x274: string pool
// string length,
0x04, 0x00, 0x00, 0x00,
// 0x278 string contents "test" (padded to word alignment)
0x74, 0x65, 0x73, 0x74};
const unsigned int idmap_raw_data_len = 0x27c;
std::string GetTestDataPath(); std::string GetTestDataPath();

View File

@@ -243,7 +243,7 @@ public class SystemConfig {
// partition that is used to verify if an overlay package fulfills // partition that is used to verify if an overlay package fulfills
// the 'config_signature' policy by comparing their signatures: // the 'config_signature' policy by comparing their signatures:
// if the overlay package is signed with the same certificate as // if the overlay package is signed with the same certificate as
// the package declared in 'config-signature' tag, then the // the package declared in 'overlay-config-signature' tag, then the
// overlay package fulfills the 'config_signature' policy. // overlay package fulfills the 'config_signature' policy.
private String mOverlayConfigSignaturePackage; private String mOverlayConfigSignaturePackage;

View File

@@ -39,10 +39,8 @@
namespace android { namespace android {
struct FindEntryResult { struct FindEntryResult {
// A pointer to the resource table entry for this resource. // A pointer to the value of the resource table entry.
// If the size of the entry is > sizeof(ResTable_entry), it can be cast to std::variant<Res_value, const ResTable_map_entry*> entry;
// a ResTable_map_entry and processed as a bag/map.
ResTable_entry_handle entry;
// The configuration for which the resulting entry was defined. This is already swapped to host // The configuration for which the resulting entry was defined. This is already swapped to host
// endianness. // endianness.
@@ -554,11 +552,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
if (!overlay_entry) { if (!overlay_entry) {
// No id map entry exists for this target resource. // No id map entry exists for this target resource.
continue; continue;
} } else if (overlay_entry.IsInlineValue()) {
if (overlay_entry.IsTableEntry()) {
// The target resource is overlaid by an inline value not represented by a resource. // The target resource is overlaid by an inline value not represented by a resource.
out_entry->entry = overlay_entry.GetTableEntry(); out_entry->entry = overlay_entry.GetInlineValue();
out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
cookie = id_map.cookie; cookie = id_map.cookie;
continue; continue;
@@ -580,7 +576,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri
} }
cookie = overlay_cookie; cookie = overlay_cookie;
out_entry->entry = std::move(overlay_result.entry); out_entry->entry = overlay_result.entry;
out_entry->config = overlay_result.config; out_entry->config = overlay_result.config;
out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
if (resource_resolution_logging_enabled_) { if (resource_resolution_logging_enabled_) {
@@ -761,7 +757,19 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro
return kInvalidCookie; return kInvalidCookie;
} }
out_entry->entry = ResTable_entry_handle::unmanaged(best_entry); const uint16_t entry_size = dtohs(best_entry->size);
if (entry_size >= sizeof(ResTable_map_entry) &&
(dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) {
// The entry represents a bag/map.
out_entry->entry = reinterpret_cast<const ResTable_map_entry*>(best_entry);
} else {
// The entry represents a value.
Res_value value;
value.copyFrom_dtoh(*reinterpret_cast<const Res_value*>(
reinterpret_cast<const uint8_t*>(best_entry) + entry_size));
out_entry->entry = value;
}
out_entry->config = *best_config; out_entry->config = *best_config;
out_entry->type_flags = type_flags; out_entry->type_flags = type_flags;
out_entry->package_name = &best_package->GetPackageName(); out_entry->package_name = &best_package->GetPackageName();
@@ -905,8 +913,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
return kInvalidCookie; return kInvalidCookie;
} }
const ResTable_entry* table_entry = *entry.entry; auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) { if (result_map_entry != nullptr) {
if (!may_be_bag) { if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
return kInvalidCookie; return kInvalidCookie;
@@ -920,11 +928,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
return cookie; return cookie;
} }
const Res_value* device_value = reinterpret_cast<const Res_value*>(
reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size));
out_value->copyFrom_dtoh(*device_value);
// Convert the package ID to the runtime assigned package ID. // Convert the package ID to the runtime assigned package ID.
*out_value = std::get<Res_value>(entry.entry);
entry.dynamic_ref_table->lookupResourceValue(out_value); entry.dynamic_ref_table->lookupResourceValue(out_value);
*out_selected_config = entry.config; *out_selected_config = entry.config;
@@ -1004,19 +1009,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>&
return nullptr; return nullptr;
} }
// Check that the size of the entry header is at least as big as auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
// the desired ResTable_map_entry. Also verify that the entry if (result_map_entry == nullptr) {
// was intended to be a map.
const ResTable_entry* table_entry = *entry.entry;
if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) ||
(dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) {
// Not a bag, nothing to do. // Not a bag, nothing to do.
return nullptr; return nullptr;
} }
const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry); auto map = reinterpret_cast<const ResTable_map_entry*>(*result_map_entry);
const ResTable_map* map_entry = auto map_entry = reinterpret_cast<const ResTable_map*>(
reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size); reinterpret_cast<const uint8_t*>(map) + map->size);
const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
// Keep track of ids that have already been seen to prevent infinite loops caused by circular // Keep track of ids that have already been seen to prevent infinite loops caused by circular

View File

@@ -36,16 +36,12 @@ using ::android::base::StringPrintf;
namespace android { namespace android {
static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) { uint32_t round_to_4_bytes(uint32_t size) {
return dtohl(e1.target_id) < target_id; return size + (4U - (size % 4U)) % 4U;
}
static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
return dtohl(e1.overlay_id) < overlay_id;
} }
size_t Idmap_header::Size() const { size_t Idmap_header::Size() const {
return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size); return sizeof(Idmap_header) + sizeof(uint8_t) * round_to_4_bytes(dtohl(debug_info_size));
} }
OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap) OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
@@ -88,7 +84,10 @@ OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_hea
status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const { status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
const Idmap_overlay_entry* first_entry = entries_; const Idmap_overlay_entry* first_entry = entries_;
const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count); const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries); auto entry = std::lower_bound(first_entry, end_entry, *resId,
[](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
return dtohl(e1.overlay_id) < overlay_id;
});
if (entry == end_entry || dtohl(entry->overlay_id) != *resId) { if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
// A mapping for the target resource id could not be found. // A mapping for the target resource id could not be found.
@@ -96,7 +95,7 @@ status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
} }
*resId = (0x00FFFFFFU & dtohl(entry->target_id)) *resId = (0x00FFFFFFU & dtohl(entry->target_id))
| (((uint32_t) target_assigned_package_id_) << 24); | (((uint32_t) target_assigned_package_id_) << 24U);
return NO_ERROR; return NO_ERROR;
} }
@@ -106,62 +105,58 @@ status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) cons
IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries, const Idmap_target_entry* entries,
const Idmap_target_entry_inline* inline_entries,
uint8_t target_assigned_package_id, uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table) const OverlayDynamicRefTable* overlay_ref_table)
: data_header_(data_header), : data_header_(data_header),
entries_(entries), entries_(entries),
inline_entries_(inline_entries),
target_assigned_package_id_(target_assigned_package_id), target_assigned_package_id_(target_assigned_package_id),
overlay_ref_table_(overlay_ref_table) { }; overlay_ref_table_(overlay_ref_table) { }
IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
if ((target_res_id >> 24) != target_assigned_package_id_) { if ((target_res_id >> 24U) != target_assigned_package_id_) {
// The resource id must have the same package id as the target package. // The resource id must have the same package id as the target package.
return {}; return {};
} }
// The resource ids encoded within the idmap are build-time resource ids. // The resource ids encoded within the idmap are build-time resource ids.
target_res_id = (0x00FFFFFFU & target_res_id) target_res_id = (0x00FFFFFFU & target_res_id)
| (((uint32_t) data_header_->target_package_id) << 24); | (((uint32_t) data_header_->target_package_id) << 24U);
const Idmap_target_entry* first_entry = entries_; // Check if the target resource is mapped to an overlay resource.
const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count); auto first_entry = entries_;
auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries); auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
if (entry == end_entry || dtohl(entry->target_id) != target_res_id) { [](const Idmap_target_entry &e, const uint32_t target_id) {
// A mapping for the target resource id could not be found. return dtohl(e.target_id) < target_id;
return {}; });
}
// A reference should be treated as an alias of the resource. Instead of returning the table
// entry, return the alias resource id to look up. The alias resource might not reside within the
// overlay package, so the resource id must be fixed with the dynamic reference table of the
// overlay before returning.
if (entry->type == Res_value::TYPE_REFERENCE
|| entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
uint32_t overlay_resource_id = dtohl(entry->value);
if (entry != end_entry && dtohl(entry->target_id) == target_res_id) {
uint32_t overlay_resource_id = dtohl(entry->overlay_id);
// Lookup the resource without rewriting the overlay resource id back to the target resource id // Lookup the resource without rewriting the overlay resource id back to the target resource id
// being looked up. // being looked up.
overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id); overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
return Result(overlay_resource_id); return Result(overlay_resource_id);
} }
// Copy the type and value into the ResTable_entry structure needed by asset manager. // Check if the target resources is mapped to an inline table entry.
uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value); auto first_inline_entry = inline_entries_;
auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size)); auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
memset(table_entry, 0, malloc_size); auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
table_entry->size = htods(sizeof(ResTable_entry)); [](const Idmap_target_entry_inline &e,
const uint32_t target_id) {
return dtohl(e.target_id) < target_id;
});
auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry) if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) {
+ sizeof(ResTable_entry)); return Result(inline_entry->value);
table_value->dataType = entry->type; }
table_value->data = entry->value; return {};
return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); }));
} }
static bool is_word_aligned(const void* data) { static bool is_word_aligned(const void* data) {
return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0; return (reinterpret_cast<uintptr_t>(data) & 0x03U) == 0U;
} }
static bool IsValidIdmapHeader(const StringPiece& data) { static bool IsValidIdmapHeader(const StringPiece& data) {
@@ -175,7 +170,7 @@ static bool IsValidIdmapHeader(const StringPiece& data) {
return false; return false;
} }
const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data()); auto header = reinterpret_cast<const Idmap_header*>(data.data());
if (dtohl(header->magic) != kIdmapMagic) { if (dtohl(header->magic) != kIdmapMagic) {
LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)", LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
dtohl(header->magic), kIdmapMagic); dtohl(header->magic), kIdmapMagic);
@@ -198,11 +193,13 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
const Idmap_header* header, const Idmap_header* header,
const Idmap_data_header* data_header, const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries, const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
const Idmap_overlay_entry* overlay_entries, const Idmap_overlay_entry* overlay_entries,
ResStringPool* string_pool) ResStringPool* string_pool)
: header_(header), : header_(header),
data_header_(data_header), data_header_(data_header),
target_entries_(target_entries), target_entries_(target_entries),
target_inline_entries_(target_inline_entries),
overlay_entries_(overlay_entries), overlay_entries_(overlay_entries),
string_pool_(string_pool), string_pool_(string_pool),
idmap_path_(std::move(idmap_path)), idmap_path_(std::move(idmap_path)),
@@ -233,7 +230,7 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa
data_ptr += sizeof(*data_header); data_ptr += sizeof(*data_header);
data_size -= sizeof(*data_header); data_size -= sizeof(*data_header);
// Make sure there is enough space for the target entries declared in the header. // Make sure there is enough space for the target entries declared in the header
const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr); const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
if (data_size / sizeof(Idmap_target_entry) < if (data_size / sizeof(Idmap_target_entry) <
static_cast<size_t>(dtohl(data_header->target_entry_count))) { static_cast<size_t>(dtohl(data_header->target_entry_count))) {
@@ -248,6 +245,21 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa
data_ptr += target_entry_size_bytes; data_ptr += target_entry_size_bytes;
data_size -= target_entry_size_bytes; data_size -= target_entry_size_bytes;
// Make sure there is enough space for the target entries declared in the header.
const auto target_inline_entries = reinterpret_cast<const Idmap_target_entry_inline*>(data_ptr);
if (data_size / sizeof(Idmap_target_entry_inline) <
static_cast<size_t>(dtohl(data_header->target_inline_entry_count))) {
LOG(ERROR) << StringPrintf("Idmap too small for the number of target inline entries (%d)",
(int)dtohl(data_header->target_inline_entry_count));
return {};
}
// Advance the data pointer past the target entries.
const size_t target_inline_entry_size_bytes =
(dtohl(data_header->target_inline_entry_count) * sizeof(Idmap_target_entry_inline));
data_ptr += target_inline_entry_size_bytes;
data_size -= target_inline_entry_size_bytes;
// Make sure there is enough space for the overlay entries declared in the header. // Make sure there is enough space for the overlay entries declared in the header.
const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr); const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
if (data_size / sizeof(Idmap_overlay_entry) < if (data_size / sizeof(Idmap_overlay_entry) <
@@ -257,22 +269,26 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa
return {}; return {};
} }
// Advance the data pointer past the target entries. // Advance the data pointer past the overlay entries.
const size_t overlay_entry_size_bytes = const size_t overlay_entry_size_bytes =
(dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry)); (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
data_ptr += overlay_entry_size_bytes; data_ptr += overlay_entry_size_bytes;
data_size -= overlay_entry_size_bytes; data_size -= overlay_entry_size_bytes;
// Read the idmap string pool that holds the value of inline string entries. // Read the idmap string pool that holds the value of inline string entries.
if (data_size < dtohl(data_header->string_pool_length)) { uint32_t string_pool_size = dtohl(*reinterpret_cast<const uint32_t*>(data_ptr));
data_ptr += sizeof(uint32_t);
data_size -= sizeof(uint32_t);
if (data_size < string_pool_size) {
LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)", LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
(int)dtohl(data_header->string_pool_length)); (int)string_pool_size);
return {}; return {};
} }
auto idmap_string_pool = util::make_unique<ResStringPool>(); auto idmap_string_pool = util::make_unique<ResStringPool>();
if (dtohl(data_header->string_pool_length) > 0) { if (string_pool_size > 0) {
status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length)); status_t err = idmap_string_pool->setTo(data_ptr, string_pool_size);
if (err != NO_ERROR) { if (err != NO_ERROR) {
LOG(ERROR) << "idmap string pool corrupt."; LOG(ERROR) << "idmap string pool corrupt.";
return {}; return {};
@@ -280,9 +296,10 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa
} }
// Can't use make_unique because LoadedIdmap constructor is private. // Can't use make_unique because LoadedIdmap constructor is private.
std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>( auto loaded_idmap = std::unique_ptr<LoadedIdmap>(
new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header, new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header,
data_header, target_entries, overlay_entries, idmap_string_pool.release())); data_header, target_entries, target_inline_entries, overlay_entries,
idmap_string_pool.release()));
return std::move(loaded_idmap); return std::move(loaded_idmap);
} }

View File

@@ -77,40 +77,40 @@ class OverlayDynamicRefTable : public DynamicRefTable {
// A mapping of target resource ids to a values or resource ids that should overlay the target. // A mapping of target resource ids to a values or resource ids that should overlay the target.
class IdmapResMap { class IdmapResMap {
public: public:
// Represents the result of a idmap lookup. The result can be one of three possibillities: // Represents the result of a idmap lookup. The result can be one of three possibilities:
// 1) The result is a resource id which represents the overlay resource that should act as an // 1) The result is a resource id which represents the overlay resource that should act as an
// alias of the target resource. // alias of the target resource.
// 2) The result is a table entry which overlays the type and value of the target resource. // 2) The result is a table entry which overlays the type and value of the target resource.
// 3) The result is neither and the target resource is not overlaid. // 3) The result is neither and the target resource is not overlaid.
class Result { class Result {
public: public:
Result() : data_(nullptr) {}; Result() = default;
explicit Result(uint32_t value) : data_(value) {}; explicit Result(uint32_t value) : data_(value) {};
explicit Result(ResTable_entry_handle&& value) : data_(value) { }; explicit Result(const Res_value& value) : data_(value) { };
// Returns `true` if the resource is overlaid. // Returns `true` if the resource is overlaid.
inline explicit operator bool() const { explicit operator bool() const {
return !std::get_if<nullptr_t>(&data_); return std::get_if<std::monostate>(&data_) == nullptr;
} }
inline bool IsResourceId() const { bool IsResourceId() const {
return std::get_if<uint32_t>(&data_); return std::get_if<uint32_t>(&data_) != nullptr;
} }
inline uint32_t GetResourceId() const { uint32_t GetResourceId() const {
return *std::get_if<uint32_t>(&data_); return std::get<uint32_t>(data_);
} }
inline bool IsTableEntry() const { bool IsInlineValue() const {
return std::get_if<ResTable_entry_handle>(&data_); return std::get_if<Res_value>(&data_) != nullptr;
} }
inline const ResTable_entry_handle& GetTableEntry() const { const Res_value& GetInlineValue() const {
return *std::get_if<ResTable_entry_handle>(&data_); return std::get<Res_value>(data_);
} }
private: private:
std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_; std::variant<std::monostate, uint32_t, Res_value> data_;
}; };
// Looks up the value that overlays the target resource id. // Looks up the value that overlays the target resource id.
@@ -123,11 +123,13 @@ class IdmapResMap {
private: private:
explicit IdmapResMap(const Idmap_data_header* data_header, explicit IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries, const Idmap_target_entry* entries,
const Idmap_target_entry_inline* inline_entries,
uint8_t target_assigned_package_id, uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table); const OverlayDynamicRefTable* overlay_ref_table);
const Idmap_data_header* data_header_; const Idmap_data_header* data_header_;
const Idmap_target_entry* entries_; const Idmap_target_entry* entries_;
const Idmap_target_entry_inline* inline_entries_;
const uint8_t target_assigned_package_id_; const uint8_t target_assigned_package_id_;
const OverlayDynamicRefTable* overlay_ref_table_; const OverlayDynamicRefTable* overlay_ref_table_;
@@ -163,8 +165,8 @@ class LoadedIdmap {
// Returns a mapping from target resource ids to overlay values. // Returns a mapping from target resource ids to overlay values.
inline const IdmapResMap GetTargetResourcesMap( inline const IdmapResMap GetTargetResourcesMap(
uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const { uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
return IdmapResMap(data_header_, target_entries_, target_assigned_package_id, return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
overlay_ref_table); target_assigned_package_id, overlay_ref_table);
} }
// Returns a dynamic reference table for a loaded overlay package. // Returns a dynamic reference table for a loaded overlay package.
@@ -184,6 +186,7 @@ class LoadedIdmap {
const Idmap_header* header_; const Idmap_header* header_;
const Idmap_data_header* data_header_; const Idmap_data_header* data_header_;
const Idmap_target_entry* target_entries_; const Idmap_target_entry* target_entries_;
const Idmap_target_entry_inline* target_inline_entries_;
const Idmap_overlay_entry* overlay_entries_; const Idmap_overlay_entry* overlay_entries_;
const std::unique_ptr<ResStringPool> string_pool_; const std::unique_ptr<ResStringPool> string_pool_;
@@ -200,6 +203,7 @@ class LoadedIdmap {
const Idmap_header* header, const Idmap_header* header,
const Idmap_data_header* data_header, const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries, const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
const Idmap_overlay_entry* overlay_entries, const Idmap_overlay_entry* overlay_entries,
ResStringPool* string_pool); ResStringPool* string_pool);

View File

@@ -41,7 +41,7 @@
namespace android { namespace android {
constexpr const static uint32_t kIdmapMagic = 0x504D4449u; constexpr const static uint32_t kIdmapMagic = 0x504D4449u;
constexpr const static uint32_t kIdmapCurrentVersion = 0x00000004u; constexpr const static uint32_t kIdmapCurrentVersion = 0x00000005u;
/** /**
* In C++11, char16_t is defined as *at least* 16 bits. We do a lot of * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of
@@ -1476,7 +1476,7 @@ struct ResTable_entry
// If set, this is a weak resource and may be overriden by strong // If set, this is a weak resource and may be overriden by strong
// resources of the same name/type. This is only useful during // resources of the same name/type. This is only useful during
// linking with other resource tables. // linking with other resource tables.
FLAG_WEAK = 0x0004 FLAG_WEAK = 0x0004,
}; };
uint16_t flags; uint16_t flags;
@@ -1586,50 +1586,6 @@ struct ResTable_map
Res_value value; Res_value value;
}; };
// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or
// holds a ResTable_entry which is tied to the lifetime of the handle.
class ResTable_entry_handle {
public:
ResTable_entry_handle() = default;
ResTable_entry_handle(const ResTable_entry_handle& handle) {
entry_ = handle.entry_;
}
ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept {
entry_ = handle.entry_;
}
inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) {
return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, deleter));
}
inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) {
return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){}));
}
inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept {
entry_ = handle.entry_;
return *this;
}
inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept {
entry_ = handle.entry_;
return *this;
}
inline const ResTable_entry* operator*() & {
return entry_.get();
}
private:
explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry)
: entry_(std::move(entry)) { }
std::shared_ptr<const ResTable_entry> entry_;
};
/** /**
* A package-id to package name mapping for any shared libraries used * A package-id to package name mapping for any shared libraries used
* in this resource table. The package-id's encoded in this resource * in this resource table. The package-id's encoded in this resource
@@ -1740,7 +1696,6 @@ inline ResTable_overlayable_policy_header::PolicyFlags& operator |=(
return first; return first;
} }
#pragma pack(push, 1)
struct Idmap_header { struct Idmap_header {
// Always 0x504D4449 ('IDMP') // Always 0x504D4449 ('IDMP')
uint32_t magic; uint32_t magic;
@@ -1751,7 +1706,7 @@ struct Idmap_header {
uint32_t overlay_crc32; uint32_t overlay_crc32;
uint32_t fulfilled_policies; uint32_t fulfilled_policies;
uint8_t enforce_overlayable; uint32_t enforce_overlayable;
uint8_t target_path[256]; uint8_t target_path[256];
uint8_t overlay_path[256]; uint8_t overlay_path[256];
@@ -1765,23 +1720,31 @@ struct Idmap_header {
struct Idmap_data_header { struct Idmap_data_header {
uint8_t target_package_id; uint8_t target_package_id;
uint8_t overlay_package_id; uint8_t overlay_package_id;
// Padding to ensure 4 byte alignment for target_entry_count
uint16_t p0;
uint32_t target_entry_count; uint32_t target_entry_count;
uint32_t target_inline_entry_count;
uint32_t overlay_entry_count; uint32_t overlay_entry_count;
uint32_t string_pool_index_offset; uint32_t string_pool_index_offset;
uint32_t string_pool_length;
}; };
struct Idmap_target_entry { struct Idmap_target_entry {
uint32_t target_id; uint32_t target_id;
uint8_t type; uint32_t overlay_id;
uint32_t value; };
struct Idmap_target_entry_inline {
uint32_t target_id;
Res_value value;
}; };
struct Idmap_overlay_entry { struct Idmap_overlay_entry {
uint32_t overlay_id; uint32_t overlay_id;
uint32_t target_id; uint32_t target_id;
}; };
#pragma pack(pop)
class AssetManager2; class AssetManager2;

View File

@@ -57,7 +57,7 @@ final class IdmapManager {
private final PackageManagerHelper mPackageManager; private final PackageManagerHelper mPackageManager;
/** /**
* Package name of the reference package defined in 'config-signature' tag of * Package name of the reference package defined in 'overlay-config-signature' tag of
* SystemConfig or empty String if tag not defined. This package is vetted on scan by * SystemConfig or empty String if tag not defined. This package is vetted on scan by
* PackageManagerService that it's a system package and is used to check if overlay matches * PackageManagerService that it's a system package and is used to check if overlay matches
* its signature in order to fulfill the config_signature policy. * its signature in order to fulfill the config_signature policy.
@@ -159,7 +159,7 @@ final class IdmapManager {
fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE; fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
} }
// If SystemConfig defines 'config-signature' package, given that // If SystemConfig defines 'overlay-config-signature' package, given that
// this package is vetted by OverlayManagerService that it's a // this package is vetted by OverlayManagerService that it's a
// preinstalled package, check if overlay matches its signature. // preinstalled package, check if overlay matches its signature.
if (!TextUtils.isEmpty(mConfigSignaturePackage) if (!TextUtils.isEmpty(mConfigSignaturePackage)

View File

@@ -12176,9 +12176,10 @@ public class PackageManagerService extends IPackageManager.Stub
// A non-preloaded overlay package, without <overlay android:targetName>, will // A non-preloaded overlay package, without <overlay android:targetName>, will
// only be used if it is signed with the same certificate as its target OR if // only be used if it is signed with the same certificate as its target OR if
// it is signed with the same certificate as a reference package declared // it is signed with the same certificate as a reference package declared
// in 'config-signature' tag of SystemConfig. // in 'overlay-config-signature' tag of SystemConfig.
// If the target is already installed or 'config-signature' tag in SystemConfig // If the target is already installed or 'overlay-config-signature' tag in
// is set, check this here to augment the last line of defence which is OMS. // SystemConfig is set, check this here to augment the last line of defense
// which is OMS.
if (pkg.getOverlayTargetName() == null) { if (pkg.getOverlayTargetName() == null) {
final PackageSetting targetPkgSetting = final PackageSetting targetPkgSetting =
mSettings.getPackageLPr(pkg.getOverlayTarget()); mSettings.getPackageLPr(pkg.getOverlayTarget());

View File

@@ -35,8 +35,8 @@ import java.util.function.BiConsumer;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase { public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase {
private static final String OVERLAY = "com.dummy.overlay"; private static final String OVERLAY = "com.test.overlay";
private static final String TARGET = "com.dummy.target"; private static final String TARGET = "com.test.target";
private static final int USER = 0; private static final int USER = 0;
private static final String OVERLAY2 = OVERLAY + "2"; private static final String OVERLAY2 = OVERLAY + "2";

View File

@@ -39,8 +39,8 @@ import java.util.Map;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase { public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
private static final String OVERLAY = "com.dummy.overlay"; private static final String OVERLAY = "com.test.overlay";
private static final String TARGET = "com.dummy.target"; private static final String TARGET = "com.test.target";
private static final int USER = 0; private static final int USER = 0;
private static final String OVERLAY2 = OVERLAY + "2"; private static final String OVERLAY2 = OVERLAY + "2";
@@ -50,7 +50,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
private static final String OVERLAY3 = OVERLAY + "3"; private static final String OVERLAY3 = OVERLAY + "3";
private static final int USER3 = USER2 + 1; private static final int USER3 = USER2 + 1;
private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.dummy.ref"; private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.test.ref";
private static final String CERT_CONFIG_OK = "config_certificate_ok"; private static final String CERT_CONFIG_OK = "config_certificate_ok";
private static final String CERT_CONFIG_NOK = "config_certificate_nok"; private static final String CERT_CONFIG_NOK = "config_certificate_nok";
@@ -149,7 +149,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
installNewPackage(overlay(OVERLAY, TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET), USER);
assertState(STATE_MISSING_TARGET, OVERLAY, USER); assertState(STATE_MISSING_TARGET, OVERLAY, USER);
final DummyDeviceState.PackageBuilder target = target(TARGET); final FakeDeviceState.PackageBuilder target = target(TARGET);
installNewPackage(target, USER); installNewPackage(target, USER);
assertState(STATE_DISABLED, OVERLAY, USER); assertState(STATE_DISABLED, OVERLAY, USER);
@@ -169,9 +169,9 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
@Test @Test
public void testOnOverlayPackageUpgraded() { public void testOnOverlayPackageUpgraded() {
final DummyListener listener = getListener(); final FakeListener listener = getListener();
final DummyDeviceState.PackageBuilder target = target(TARGET); final FakeDeviceState.PackageBuilder target = target(TARGET);
final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET); final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
installNewPackage(target, USER); installNewPackage(target, USER);
installNewPackage(overlay, USER); installNewPackage(overlay, USER);
listener.count = 0; listener.count = 0;
@@ -181,7 +181,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
// upgrade to a version where the overlay has changed its target // upgrade to a version where the overlay has changed its target
// expect once for the old target package, once for the new target package // expect once for the old target package, once for the new target package
listener.count = 0; listener.count = 0;
final DummyDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target"); final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
upgradePackage(overlay2, USER); upgradePackage(overlay2, USER);
assertEquals(3, listener.count); assertEquals(3, listener.count);
@@ -193,7 +193,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
@Test @Test
public void testListener() { public void testListener() {
final OverlayManagerServiceImpl impl = getImpl(); final OverlayManagerServiceImpl impl = getImpl();
final DummyListener listener = getListener(); final FakeListener listener = getListener();
installNewPackage(overlay(OVERLAY, TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET), USER);
assertEquals(1, listener.count); assertEquals(1, listener.count);
listener.count = 0; listener.count = 0;
@@ -219,12 +219,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
installNewPackage(target(TARGET), USER); installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER);
final DummyIdmapDaemon idmapd = getIdmapd(); final FakeIdmapDaemon idmapd = getIdmapd();
final DummyDeviceState state = getState(); final FakeDeviceState state = getState();
String overlayPath = state.select(OVERLAY, USER).apkPath; String overlayPath = state.select(OVERLAY, USER).apkPath;
assertTrue(idmapd.idmapExists(overlayPath, USER)); assertTrue(idmapd.idmapExists(overlayPath, USER));
DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
assertTrue((CONFIG_SIGNATURE & idmap.policies) == CONFIG_SIGNATURE); assertTrue((CONFIG_SIGNATURE & idmap.policies) == CONFIG_SIGNATURE);
} }
@@ -237,12 +237,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
installNewPackage(target(TARGET), USER); installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
final DummyIdmapDaemon idmapd = getIdmapd(); final FakeIdmapDaemon idmapd = getIdmapd();
final DummyDeviceState state = getState(); final FakeDeviceState state = getState();
String overlayPath = state.select(OVERLAY, USER).apkPath; String overlayPath = state.select(OVERLAY, USER).apkPath;
assertTrue(idmapd.idmapExists(overlayPath, USER)); assertTrue(idmapd.idmapExists(overlayPath, USER));
DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0); assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
} }
@@ -252,12 +252,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
installNewPackage(target(TARGET), USER); installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
final DummyIdmapDaemon idmapd = getIdmapd(); final FakeIdmapDaemon idmapd = getIdmapd();
final DummyDeviceState state = getState(); final FakeDeviceState state = getState();
String overlayPath = state.select(OVERLAY, USER).apkPath; String overlayPath = state.select(OVERLAY, USER).apkPath;
assertTrue(idmapd.idmapExists(overlayPath, USER)); assertTrue(idmapd.idmapExists(overlayPath, USER));
DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0); assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
} }
@@ -266,12 +266,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
installNewPackage(target(TARGET), USER); installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
final DummyIdmapDaemon idmapd = getIdmapd(); final FakeIdmapDaemon idmapd = getIdmapd();
final DummyDeviceState state = getState(); final FakeDeviceState state = getState();
String overlayPath = state.select(OVERLAY, USER).apkPath; String overlayPath = state.select(OVERLAY, USER).apkPath;
assertTrue(idmapd.idmapExists(overlayPath, USER)); assertTrue(idmapd.idmapExists(overlayPath, USER));
DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0); assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
} }
@@ -284,12 +284,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes
installNewPackage(target(TARGET), USER); installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
final DummyIdmapDaemon idmapd = getIdmapd(); final FakeIdmapDaemon idmapd = getIdmapd();
final DummyDeviceState state = getState(); final FakeDeviceState state = getState();
String overlayPath = state.select(OVERLAY, USER).apkPath; String overlayPath = state.select(OVERLAY, USER).apkPath;
assertTrue(idmapd.idmapExists(overlayPath, USER)); assertTrue(idmapd.idmapExists(overlayPath, USER));
DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath);
assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0); assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0);
} }
} }

View File

@@ -48,19 +48,19 @@ import java.util.stream.Collectors;
/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */ /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
class OverlayManagerServiceImplTestsBase { class OverlayManagerServiceImplTestsBase {
private OverlayManagerServiceImpl mImpl; private OverlayManagerServiceImpl mImpl;
private DummyDeviceState mState; private FakeDeviceState mState;
private DummyListener mListener; private FakeListener mListener;
private DummyPackageManagerHelper mPackageManager; private FakePackageManagerHelper mPackageManager;
private DummyIdmapDaemon mIdmapDaemon; private FakeIdmapDaemon mIdmapDaemon;
private OverlayConfig mOverlayConfig; private OverlayConfig mOverlayConfig;
private String mConfigSignaturePackageName; private String mConfigSignaturePackageName;
@Before @Before
public void setUp() { public void setUp() {
mState = new DummyDeviceState(); mState = new FakeDeviceState();
mListener = new DummyListener(); mListener = new FakeListener();
mPackageManager = new DummyPackageManagerHelper(mState); mPackageManager = new FakePackageManagerHelper(mState);
mIdmapDaemon = new DummyIdmapDaemon(mState); mIdmapDaemon = new FakeIdmapDaemon(mState);
mOverlayConfig = mock(OverlayConfig.class); mOverlayConfig = mock(OverlayConfig.class);
when(mOverlayConfig.getPriority(any())).thenReturn(OverlayConfig.DEFAULT_PRIORITY); when(mOverlayConfig.getPriority(any())).thenReturn(OverlayConfig.DEFAULT_PRIORITY);
when(mOverlayConfig.isEnabled(any())).thenReturn(false); when(mOverlayConfig.isEnabled(any())).thenReturn(false);
@@ -81,15 +81,15 @@ class OverlayManagerServiceImplTestsBase {
return mImpl; return mImpl;
} }
DummyListener getListener() { FakeListener getListener() {
return mListener; return mListener;
} }
DummyIdmapDaemon getIdmapd() { FakeIdmapDaemon getIdmapd() {
return mIdmapDaemon; return mIdmapDaemon;
} }
DummyDeviceState getState() { FakeDeviceState getState() {
return mState; return mState;
} }
@@ -116,27 +116,27 @@ class OverlayManagerServiceImplTestsBase {
assertEquals(expected, actual); assertEquals(expected, actual);
} }
DummyDeviceState.PackageBuilder app(String packageName) { FakeDeviceState.PackageBuilder app(String packageName) {
return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */, return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
null /* targetOverlayableName */, "data"); null /* targetOverlayableName */, "data");
} }
DummyDeviceState.PackageBuilder target(String packageName) { FakeDeviceState.PackageBuilder target(String packageName) {
return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */, return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */,
null /* targetOverlayableName */, ""); null /* targetOverlayableName */, "");
} }
DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) { FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) {
return overlay(packageName, targetPackageName, null /* targetOverlayableName */); return overlay(packageName, targetPackageName, null /* targetOverlayableName */);
} }
DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName, FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName,
String targetOverlayableName) { String targetOverlayableName) {
return new DummyDeviceState.PackageBuilder(packageName, targetPackageName, return new FakeDeviceState.PackageBuilder(packageName, targetPackageName,
targetOverlayableName, ""); targetOverlayableName, "");
} }
void addPackage(DummyDeviceState.PackageBuilder pkg, int userId) { void addPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
mState.add(pkg, userId); mState.add(pkg, userId);
} }
@@ -155,7 +155,7 @@ class OverlayManagerServiceImplTestsBase {
* *
* @throws IllegalStateException if the package is currently installed * @throws IllegalStateException if the package is currently installed
*/ */
void installNewPackage(DummyDeviceState.PackageBuilder pkg, int userId) { void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
if (mState.select(pkg.packageName, userId) != null) { if (mState.select(pkg.packageName, userId) != null) {
throw new IllegalStateException("package " + pkg.packageName + " already installed"); throw new IllegalStateException("package " + pkg.packageName + " already installed");
} }
@@ -178,8 +178,8 @@ class OverlayManagerServiceImplTestsBase {
* *
* @throws IllegalStateException if the package is not currently installed * @throws IllegalStateException if the package is not currently installed
*/ */
void upgradePackage(DummyDeviceState.PackageBuilder pkg, int userId) { void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) {
final DummyDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId); final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
if (replacedPackage == null) { if (replacedPackage == null) {
throw new IllegalStateException("package " + pkg.packageName + " not installed"); throw new IllegalStateException("package " + pkg.packageName + " not installed");
} }
@@ -204,7 +204,7 @@ class OverlayManagerServiceImplTestsBase {
* @throws IllegalStateException if the package is not currently installed * @throws IllegalStateException if the package is not currently installed
*/ */
void uninstallPackage(String packageName, int userId) { void uninstallPackage(String packageName, int userId) {
final DummyDeviceState.Package pkg = mState.select(packageName, userId); final FakeDeviceState.Package pkg = mState.select(packageName, userId);
if (pkg == null) { if (pkg == null) {
throw new IllegalStateException("package " + packageName+ " not installed"); throw new IllegalStateException("package " + packageName+ " not installed");
} }
@@ -217,7 +217,7 @@ class OverlayManagerServiceImplTestsBase {
} }
/** Represents the state of packages installed on a fake device. */ /** Represents the state of packages installed on a fake device. */
static class DummyDeviceState { static class FakeDeviceState {
private ArrayMap<String, Package> mPackages = new ArrayMap<>(); private ArrayMap<String, Package> mPackages = new ArrayMap<>();
void add(PackageBuilder pkgBuilder, int userId) { void add(PackageBuilder pkgBuilder, int userId) {
@@ -333,16 +333,16 @@ class OverlayManagerServiceImplTestsBase {
} }
} }
final class DummyPackageManagerHelper implements PackageManagerHelper { final class FakePackageManagerHelper implements PackageManagerHelper {
private final DummyDeviceState mState; private final FakeDeviceState mState;
private DummyPackageManagerHelper(DummyDeviceState state) { private FakePackageManagerHelper(FakeDeviceState state) {
mState = state; mState = state;
} }
@Override @Override
public PackageInfo getPackageInfo(@NonNull String packageName, int userId) { public PackageInfo getPackageInfo(@NonNull String packageName, int userId) {
final DummyDeviceState.Package pkg = mState.select(packageName, userId); final FakeDeviceState.Package pkg = mState.select(packageName, userId);
if (pkg == null) { if (pkg == null) {
return null; return null;
} }
@@ -353,15 +353,15 @@ class OverlayManagerServiceImplTestsBase {
pi.packageName = pkg.packageName; pi.packageName = pkg.packageName;
pi.overlayTarget = pkg.targetPackageName; pi.overlayTarget = pkg.targetPackageName;
pi.targetOverlayableName = pkg.targetOverlayableName; pi.targetOverlayableName = pkg.targetOverlayableName;
pi.overlayCategory = "dummy-category-" + pkg.targetPackageName; pi.overlayCategory = "Fake-category-" + pkg.targetPackageName;
return pi; return pi;
} }
@Override @Override
public boolean signaturesMatching(@NonNull String packageName1, public boolean signaturesMatching(@NonNull String packageName1,
@NonNull String packageName2, int userId) { @NonNull String packageName2, int userId) {
final DummyDeviceState.Package pkg1 = mState.select(packageName1, userId); final FakeDeviceState.Package pkg1 = mState.select(packageName1, userId);
final DummyDeviceState.Package pkg2 = mState.select(packageName2, userId); final FakeDeviceState.Package pkg2 = mState.select(packageName2, userId);
return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate); return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate);
} }
@@ -382,7 +382,7 @@ class OverlayManagerServiceImplTestsBase {
@Override @Override
public OverlayableInfo getOverlayableForTarget(@NonNull String packageName, public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
@NonNull String targetOverlayableName, int userId) { @NonNull String targetOverlayableName, int userId) {
final DummyDeviceState.Package pkg = mState.select(packageName, userId); final FakeDeviceState.Package pkg = mState.select(packageName, userId);
if (pkg == null || !pkg.overlayableNames.contains(targetOverlayableName)) { if (pkg == null || !pkg.overlayableNames.contains(targetOverlayableName)) {
return null; return null;
} }
@@ -403,7 +403,7 @@ class OverlayManagerServiceImplTestsBase {
@Override @Override
public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) { public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) {
final DummyDeviceState.Package pkg = mState.select(targetPackageName, userId); final FakeDeviceState.Package pkg = mState.select(targetPackageName, userId);
return pkg != null && pkg.overlayableNames.contains(targetPackageName); return pkg != null && pkg.overlayableNames.contains(targetPackageName);
} }
@@ -413,16 +413,16 @@ class OverlayManagerServiceImplTestsBase {
} }
} }
static class DummyIdmapDaemon extends IdmapDaemon { static class FakeIdmapDaemon extends IdmapDaemon {
private final DummyDeviceState mState; private final FakeDeviceState mState;
private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>(); private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>();
DummyIdmapDaemon(DummyDeviceState state) { FakeIdmapDaemon(FakeDeviceState state) {
this.mState = state; this.mState = state;
} }
private int getCrc(@NonNull final String path) { private int getCrc(@NonNull final String path) {
final DummyDeviceState.Package pkg = mState.selectFromPath(path); final FakeDeviceState.Package pkg = mState.selectFromPath(path);
Assert.assertNotNull(pkg); Assert.assertNotNull(pkg);
return pkg.versionCode; return pkg.versionCode;
} }
@@ -486,7 +486,7 @@ class OverlayManagerServiceImplTestsBase {
} }
} }
static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener { static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener {
public int count; public int count;
public void onOverlaysChanged(@NonNull String targetPackage, int userId) { public void onOverlaysChanged(@NonNull String targetPackage, int userId) {

View File

@@ -50,55 +50,55 @@ public class OverlayManagerSettingsTests {
private OverlayManagerSettings mSettings; private OverlayManagerSettings mSettings;
private static final OverlayInfo OVERLAY_A0 = new OverlayInfo( private static final OverlayInfo OVERLAY_A0 = new OverlayInfo(
"com.dummy.overlay_a", "com.test.overlay_a",
"com.dummy.target", "com.test.target",
null, null,
"some-category", "some-category",
"/data/app/com.dummy.overlay_a-1/base.apk", "/data/app/com.test.overlay_a-1/base.apk",
STATE_DISABLED, STATE_DISABLED,
0, 0,
0, 0,
true); true);
private static final OverlayInfo OVERLAY_B0 = new OverlayInfo( private static final OverlayInfo OVERLAY_B0 = new OverlayInfo(
"com.dummy.overlay_b", "com.test.overlay_b",
"com.dummy.target", "com.test.target",
null, null,
"some-category", "some-category",
"/data/app/com.dummy.overlay_b-1/base.apk", "/data/app/com.test.overlay_b-1/base.apk",
STATE_DISABLED, STATE_DISABLED,
0, 0,
0, 0,
true); true);
private static final OverlayInfo OVERLAY_C0 = new OverlayInfo( private static final OverlayInfo OVERLAY_C0 = new OverlayInfo(
"com.dummy.overlay_c", "com.test.overlay_c",
"com.dummy.target", "com.test.target",
null, null,
"some-category", "some-category",
"/data/app/com.dummy.overlay_c-1/base.apk", "/data/app/com.test.overlay_c-1/base.apk",
STATE_DISABLED, STATE_DISABLED,
0, 0,
0, 0,
true); true);
private static final OverlayInfo OVERLAY_A1 = new OverlayInfo( private static final OverlayInfo OVERLAY_A1 = new OverlayInfo(
"com.dummy.overlay_a", "com.test.overlay_a",
"com.dummy.target", "com.test.target",
null, null,
"some-category", "some-category",
"/data/app/com.dummy.overlay_a-1/base.apk", "/data/app/com.test.overlay_a-1/base.apk",
STATE_DISABLED, STATE_DISABLED,
1, 1,
0, 0,
true); true);
private static final OverlayInfo OVERLAY_B1 = new OverlayInfo( private static final OverlayInfo OVERLAY_B1 = new OverlayInfo(
"com.dummy.overlay_b", "com.test.overlay_b",
"com.dummy.target", "com.test.target",
null, null,
"some-category", "some-category",
"/data/app/com.dummy.overlay_b-1/base.apk", "/data/app/com.test.overlay_b-1/base.apk",
STATE_DISABLED, STATE_DISABLED,
1, 1,
0, 0,
@@ -230,11 +230,11 @@ public class OverlayManagerSettingsTests {
assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0); assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
OverlayInfo otherTarget = new OverlayInfo( OverlayInfo otherTarget = new OverlayInfo(
"com.dummy.overlay_other", "com.test.overlay_other",
"com.dummy.some.other.target", "com.test.some.other.target",
null, null,
"some-category", "some-category",
"/data/app/com.dummy.overlay_other-1/base.apk", "/data/app/com.test.overlay_other-1/base.apk",
STATE_DISABLED, STATE_DISABLED,
0, 0,
0, 0,
@@ -350,7 +350,7 @@ public class OverlayManagerSettingsTests {
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
mSettings.restore(is); mSettings.restore(is);
assertDoesNotContain(mSettings, "com.dummy.overlay", 0); assertDoesNotContain(mSettings, "com.test.overlay", 0);
} }
@Test @Test
@@ -359,27 +359,27 @@ public class OverlayManagerSettingsTests {
final String xml = final String xml =
"<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n" "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n"
+ "<overlays version='" + version + "'>\n" + "<overlays version='" + version + "'>\n"
+ "<item packageName='com.dummy.overlay'\n" + "<item packageName='com.test.overlay'\n"
+ " userId='1234'\n" + " userId='1234'\n"
+ " targetPackageName='com.dummy.target'\n" + " targetPackageName='com.test.target'\n"
+ " baseCodePath='/data/app/com.dummy.overlay-1/base.apk'\n" + " baseCodePath='/data/app/com.test.overlay-1/base.apk'\n"
+ " state='" + STATE_DISABLED + "'\n" + " state='" + STATE_DISABLED + "'\n"
+ " isEnabled='false'\n" + " isEnabled='false'\n"
+ " category='dummy-category'\n" + " category='test-category'\n"
+ " isStatic='false'\n" + " isStatic='false'\n"
+ " priority='0' />\n" + " priority='0' />\n"
+ "</overlays>\n"; + "</overlays>\n";
ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
mSettings.restore(is); mSettings.restore(is);
OverlayInfo oi = mSettings.getOverlayInfo("com.dummy.overlay", 1234); OverlayInfo oi = mSettings.getOverlayInfo("com.test.overlay", 1234);
assertNotNull(oi); assertNotNull(oi);
assertEquals("com.dummy.overlay", oi.packageName); assertEquals("com.test.overlay", oi.packageName);
assertEquals("com.dummy.target", oi.targetPackageName); assertEquals("com.test.target", oi.targetPackageName);
assertEquals("/data/app/com.dummy.overlay-1/base.apk", oi.baseCodePath); assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath);
assertEquals(1234, oi.userId); assertEquals(1234, oi.userId);
assertEquals(STATE_DISABLED, oi.state); assertEquals(STATE_DISABLED, oi.state);
assertFalse(mSettings.getEnabled("com.dummy.overlay", 1234)); assertFalse(mSettings.getEnabled("com.test.overlay", 1234));
} }
@Test @Test