Remove malloc/free for inline overlay values
Remove malloc/free of android::ResTable_entry for inline overlay
values.
Add `target_entry_inline` to the idmap format to encode inline overlay
values separate from direct mapping of target resource to overlay
resource. This reduces the number of bytes needed to represent a direct
mapping of target resource to overlay resource from 9 bytes to 8 bytes
per entry.
Fixed all idmap alignment issues that required the framework to use
"#pragma pack(push, 1)" when loading idmaps.
Bug: 170341022
Test: idmap2_tests and libandroidfw_tests
Change-Id: Iab4d3902508f02773464724913e0ee966e3689e4
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index b9765ea..99dd313 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -39,10 +39,8 @@
namespace android {
struct FindEntryResult {
- // A pointer to the resource table entry for this resource.
- // If the size of the entry is > sizeof(ResTable_entry), it can be cast to
- // a ResTable_map_entry and processed as a bag/map.
- ResTable_entry_handle entry;
+ // A pointer to the value of the resource table entry.
+ std::variant<Res_value, const ResTable_map_entry*> entry;
// The configuration for which the resulting entry was defined. This is already swapped to host
// endianness.
@@ -554,11 +552,9 @@
if (!overlay_entry) {
// No id map entry exists for this target resource.
continue;
- }
-
- if (overlay_entry.IsTableEntry()) {
+ } else if (overlay_entry.IsInlineValue()) {
// 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();
cookie = id_map.cookie;
continue;
@@ -580,7 +576,7 @@
}
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->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
if (resource_resolution_logging_enabled_) {
@@ -761,7 +757,19 @@
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->type_flags = type_flags;
out_entry->package_name = &best_package->GetPackageName();
@@ -905,8 +913,8 @@
return kInvalidCookie;
}
- const ResTable_entry* table_entry = *entry.entry;
- if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) {
+ auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
+ if (result_map_entry != nullptr) {
if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
return kInvalidCookie;
@@ -920,11 +928,8 @@
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.
+ *out_value = std::get<Res_value>(entry.entry);
entry.dynamic_ref_table->lookupResourceValue(out_value);
*out_selected_config = entry.config;
@@ -1004,19 +1009,15 @@
return nullptr;
}
- // Check that the size of the entry header is at least as big as
- // the desired ResTable_map_entry. Also verify that the entry
- // 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) {
+ auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry);
+ if (result_map_entry == nullptr) {
// Not a bag, nothing to do.
return nullptr;
}
- const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry);
- const ResTable_map* map_entry =
- reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size);
+ auto map = reinterpret_cast<const ResTable_map_entry*>(*result_map_entry);
+ auto map_entry = reinterpret_cast<const ResTable_map*>(
+ reinterpret_cast<const uint8_t*>(map) + map->size);
const ResTable_map* const map_entry_end = map_entry + dtohl(map->count);
// Keep track of ids that have already been seen to prevent infinite loops caused by circular
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 5f231ff..4e03ce5 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -36,16 +36,12 @@
namespace android {
-static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
- return dtohl(e1.target_id) < target_id;
-}
-
-static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
- return dtohl(e1.overlay_id) < overlay_id;
+uint32_t round_to_4_bytes(uint32_t size) {
+ return size + (4U - (size % 4U)) % 4U;
}
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)
@@ -88,7 +84,10 @@
status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
const Idmap_overlay_entry* first_entry = entries_;
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) {
// A mapping for the target resource id could not be found.
@@ -96,7 +95,7 @@
}
*resId = (0x00FFFFFFU & dtohl(entry->target_id))
- | (((uint32_t) target_assigned_package_id_) << 24);
+ | (((uint32_t) target_assigned_package_id_) << 24U);
return NO_ERROR;
}
@@ -106,62 +105,58 @@
IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries,
+ const Idmap_target_entry_inline* inline_entries,
uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table)
: data_header_(data_header),
entries_(entries),
+ inline_entries_(inline_entries),
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 {
- 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.
return {};
}
// The resource ids encoded within the idmap are build-time resource ids.
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_;
- const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
- auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
+ // Check if the target resource is mapped to an overlay resource.
+ auto first_entry = entries_;
+ auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
+ auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
+ [](const Idmap_target_entry &e, const uint32_t target_id) {
+ return dtohl(e.target_id) < target_id;
+ });
- if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
- // A mapping for the target resource id could not be found.
- 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
// being looked up.
overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
return Result(overlay_resource_id);
}
- // Copy the type and value into the ResTable_entry structure needed by asset manager.
- uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
- auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
- memset(table_entry, 0, malloc_size);
- table_entry->size = htods(sizeof(ResTable_entry));
+ // Check if the target resources is mapped to an inline table entry.
+ auto first_inline_entry = inline_entries_;
+ auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
+ auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
+ [](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)
- + sizeof(ResTable_entry));
- table_value->dataType = entry->type;
- table_value->data = entry->value;
-
- return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); }));
+ if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) {
+ return Result(inline_entry->value);
+ }
+ return {};
}
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) {
@@ -175,7 +170,7 @@
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) {
LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
dtohl(header->magic), kIdmapMagic);
@@ -198,11 +193,13 @@
const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
+ const Idmap_target_entry_inline* target_inline_entries,
const Idmap_overlay_entry* overlay_entries,
ResStringPool* string_pool)
: header_(header),
data_header_(data_header),
target_entries_(target_entries),
+ target_inline_entries_(target_inline_entries),
overlay_entries_(overlay_entries),
string_pool_(string_pool),
idmap_path_(std::move(idmap_path)),
@@ -233,7 +230,7 @@
data_ptr += 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);
if (data_size / sizeof(Idmap_target_entry) <
static_cast<size_t>(dtohl(data_header->target_entry_count))) {
@@ -248,6 +245,21 @@
data_ptr += 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.
const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
if (data_size / sizeof(Idmap_overlay_entry) <
@@ -257,22 +269,26 @@
return {};
}
- // Advance the data pointer past the target entries.
+ // Advance the data pointer past the overlay entries.
const size_t overlay_entry_size_bytes =
(dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
data_ptr += overlay_entry_size_bytes;
data_size -= overlay_entry_size_bytes;
// 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)",
- (int)dtohl(data_header->string_pool_length));
+ (int)string_pool_size);
return {};
}
auto idmap_string_pool = util::make_unique<ResStringPool>();
- if (dtohl(data_header->string_pool_length) > 0) {
- status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
+ if (string_pool_size > 0) {
+ status_t err = idmap_string_pool->setTo(data_ptr, string_pool_size);
if (err != NO_ERROR) {
LOG(ERROR) << "idmap string pool corrupt.";
return {};
@@ -280,9 +296,10 @@
}
// 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,
- 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);
}
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ecc1ce6..ab0f47f 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -77,40 +77,40 @@
// A mapping of target resource ids to a values or resource ids that should overlay the target.
class IdmapResMap {
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
// alias 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.
class Result {
public:
- Result() : data_(nullptr) {};
+ Result() = default;
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.
- inline explicit operator bool() const {
- return !std::get_if<nullptr_t>(&data_);
+ explicit operator bool() const {
+ return std::get_if<std::monostate>(&data_) == nullptr;
}
- inline bool IsResourceId() const {
- return std::get_if<uint32_t>(&data_);
+ bool IsResourceId() const {
+ return std::get_if<uint32_t>(&data_) != nullptr;
}
- inline uint32_t GetResourceId() const {
- return *std::get_if<uint32_t>(&data_);
+ uint32_t GetResourceId() const {
+ return std::get<uint32_t>(data_);
}
- inline bool IsTableEntry() const {
- return std::get_if<ResTable_entry_handle>(&data_);
+ bool IsInlineValue() const {
+ return std::get_if<Res_value>(&data_) != nullptr;
}
- inline const ResTable_entry_handle& GetTableEntry() const {
- return *std::get_if<ResTable_entry_handle>(&data_);
+ const Res_value& GetInlineValue() const {
+ return std::get<Res_value>(data_);
}
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.
@@ -123,11 +123,13 @@
private:
explicit IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries,
+ const Idmap_target_entry_inline* inline_entries,
uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table);
const Idmap_data_header* data_header_;
const Idmap_target_entry* entries_;
+ const Idmap_target_entry_inline* inline_entries_;
const uint8_t target_assigned_package_id_;
const OverlayDynamicRefTable* overlay_ref_table_;
@@ -163,8 +165,8 @@
// Returns a mapping from target resource ids to overlay values.
inline const IdmapResMap GetTargetResourcesMap(
uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const {
- return IdmapResMap(data_header_, target_entries_, target_assigned_package_id,
- overlay_ref_table);
+ return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
+ target_assigned_package_id, overlay_ref_table);
}
// Returns a dynamic reference table for a loaded overlay package.
@@ -184,6 +186,7 @@
const Idmap_header* header_;
const Idmap_data_header* data_header_;
const Idmap_target_entry* target_entries_;
+ const Idmap_target_entry_inline* target_inline_entries_;
const Idmap_overlay_entry* overlay_entries_;
const std::unique_ptr<ResStringPool> string_pool_;
@@ -200,6 +203,7 @@
const Idmap_header* header,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
+ const Idmap_target_entry_inline* target_inline_entries,
const Idmap_overlay_entry* overlay_entries,
ResStringPool* string_pool);
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index e10a7f3..04ba78b 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -41,7 +41,7 @@
namespace android {
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
@@ -1476,7 +1476,7 @@
// If set, this is a weak resource and may be overriden by strong
// resources of the same name/type. This is only useful during
// linking with other resource tables.
- FLAG_WEAK = 0x0004
+ FLAG_WEAK = 0x0004,
};
uint16_t flags;
@@ -1586,50 +1586,6 @@
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
* in this resource table. The package-id's encoded in this resource
@@ -1740,7 +1696,6 @@
return first;
}
-#pragma pack(push, 1)
struct Idmap_header {
// Always 0x504D4449 ('IDMP')
uint32_t magic;
@@ -1751,7 +1706,7 @@
uint32_t overlay_crc32;
uint32_t fulfilled_policies;
- uint8_t enforce_overlayable;
+ uint32_t enforce_overlayable;
uint8_t target_path[256];
uint8_t overlay_path[256];
@@ -1765,23 +1720,31 @@
struct Idmap_data_header {
uint8_t target_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_inline_entry_count;
uint32_t overlay_entry_count;
+
uint32_t string_pool_index_offset;
- uint32_t string_pool_length;
};
struct Idmap_target_entry {
uint32_t target_id;
- uint8_t type;
- uint32_t value;
+ uint32_t overlay_id;
+};
+
+struct Idmap_target_entry_inline {
+ uint32_t target_id;
+ Res_value value;
};
struct Idmap_overlay_entry {
uint32_t overlay_id;
uint32_t target_id;
};
-#pragma pack(pop)
class AssetManager2;
diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk
index f1ed592..c9bf252 100644
--- a/libs/androidfw/tests/data/overlay/overlay.apk
+++ b/libs/androidfw/tests/data/overlay/overlay.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 29c5eb6..3ab244e 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ