blob: ce3bffff0aa5e027ab5ae42139a2cd0bb0e059f9 [file] [log] [blame]
Adam Lesinski970bd8d2017-09-25 13:21:55 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define ATRACE_TAG ATRACE_TAG_RESOURCES
18
19#include "androidfw/Idmap.h"
20
21#include "android-base/logging.h"
22#include "android-base/stringprintf.h"
Ryan Mitchell8a891d82019-07-01 09:48:23 -070023#include "androidfw/ResourceTypes.h"
24#include "androidfw/Util.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070025#include "utils/ByteOrder.h"
26#include "utils/Trace.h"
27
28#ifdef _WIN32
29#ifdef ERROR
30#undef ERROR
31#endif
32#endif
33
Adam Lesinski970bd8d2017-09-25 13:21:55 -070034using ::android::base::StringPrintf;
35
36namespace android {
37
Ryan Mitchell8a891d82019-07-01 09:48:23 -070038static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
39 return dtohl(e1.target_id) < target_id;
Adam Lesinski970bd8d2017-09-25 13:21:55 -070040}
41
Ryan Mitchell8a891d82019-07-01 09:48:23 -070042static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
43 return dtohl(e1.overlay_id) < overlay_id;
Adam Lesinski970bd8d2017-09-25 13:21:55 -070044}
45
Mårten Kongstadd7e8a532019-10-11 08:32:04 +020046size_t Idmap_header::Size() const {
47 return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size);
48}
49
Ryan Mitchell8a891d82019-07-01 09:48:23 -070050OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
Ryan Mitchell73bfe412019-11-12 16:22:04 -080051 : data_header_(loaded_idmap->data_header_),
52 idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
Ryan Mitchell8a891d82019-07-01 09:48:23 -070053
54OverlayStringPool::~OverlayStringPool() {
55 uninit();
56}
57
58const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
59 const size_t offset = dtohl(data_header_->string_pool_index_offset);
60 if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
61 return idmap_string_pool_->stringAt(idx - offset, outLen);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070062 }
63
Ryan Mitchell8a891d82019-07-01 09:48:23 -070064 return ResStringPool::stringAt(idx, outLen);
65}
66
67const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
68 const size_t offset = dtohl(data_header_->string_pool_index_offset);
69 if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
70 return idmap_string_pool_->string8At(idx - offset, outLen);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070071 }
72
Ryan Mitchell8a891d82019-07-01 09:48:23 -070073 return ResStringPool::string8At(idx, outLen);
74}
75
76OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
77 const Idmap_overlay_entry* entries,
78 uint8_t target_assigned_package_id)
79 : data_header_(data_header),
80 entries_(entries),
81 target_assigned_package_id_(target_assigned_package_id) { };
82
83status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
84 const Idmap_overlay_entry* first_entry = entries_;
85 const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
86 auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
87
88 if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
89 // A mapping for the target resource id could not be found.
90 return DynamicRefTable::lookupResourceId(resId);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070091 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -070092
93 *resId = (0x00FFFFFFU & dtohl(entry->target_id))
94 | (((uint32_t) target_assigned_package_id_) << 24);
95 return NO_ERROR;
96}
97
98status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
99 return DynamicRefTable::lookupResourceId(resId);
100}
101
102IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
103 const Idmap_target_entry* entries,
104 uint8_t target_assigned_package_id,
105 const OverlayDynamicRefTable* overlay_ref_table)
106 : data_header_(data_header),
107 entries_(entries),
108 target_assigned_package_id_(target_assigned_package_id),
109 overlay_ref_table_(overlay_ref_table) { };
110
111IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
112 if ((target_res_id >> 24) != target_assigned_package_id_) {
113 // The resource id must have the same package id as the target package.
114 return {};
115 }
116
117 // The resource ids encoded within the idmap are build-time resource ids.
118 target_res_id = (0x00FFFFFFU & target_res_id)
119 | (((uint32_t) data_header_->target_package_id) << 24);
120
121 const Idmap_target_entry* first_entry = entries_;
122 const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
123 auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
124
125 if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
126 // A mapping for the target resource id could not be found.
127 return {};
128 }
129
130 // A reference should be treated as an alias of the resource. Instead of returning the table
131 // entry, return the alias resource id to look up. The alias resource might not reside within the
132 // overlay package, so the resource id must be fixed with the dynamic reference table of the
133 // overlay before returning.
134 if (entry->type == Res_value::TYPE_REFERENCE
135 || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
136 uint32_t overlay_resource_id = dtohl(entry->value);
137
138 // Lookup the resource without rewriting the overlay resource id back to the target resource id
139 // being looked up.
140 overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
141 return Result(overlay_resource_id);
142 }
143
144 // Copy the type and value into the ResTable_entry structure needed by asset manager.
145 uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
146 auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
147 memset(table_entry, 0, malloc_size);
148 table_entry->size = htods(sizeof(ResTable_entry));
149
150 auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
151 + sizeof(ResTable_entry));
152 table_value->dataType = entry->type;
153 table_value->data = entry->value;
154
155 return Result(ResTable_entry_handle::managed(table_entry));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700156}
157
158static bool is_word_aligned(const void* data) {
159 return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0;
160}
161
162static bool IsValidIdmapHeader(const StringPiece& data) {
163 if (!is_word_aligned(data.data())) {
164 LOG(ERROR) << "Idmap header is not word aligned.";
165 return false;
166 }
167
168 if (data.size() < sizeof(Idmap_header)) {
169 LOG(ERROR) << "Idmap header is too small.";
170 return false;
171 }
172
173 const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data());
174 if (dtohl(header->magic) != kIdmapMagic) {
175 LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
176 dtohl(header->magic), kIdmapMagic);
177 return false;
178 }
179
180 if (dtohl(header->version) != kIdmapCurrentVersion) {
181 // We are strict about versions because files with this format are auto-generated and don't need
182 // backwards compatibility.
183 LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
184 dtohl(header->version), kIdmapCurrentVersion);
185 return false;
186 }
187
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700188 return true;
189}
190
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700191LoadedIdmap::LoadedIdmap(const Idmap_header* header,
192 const Idmap_data_header* data_header,
193 const Idmap_target_entry* target_entries,
194 const Idmap_overlay_entry* overlay_entries,
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800195 ResStringPool* string_pool)
196 : header_(header),
197 data_header_(data_header),
198 target_entries_(target_entries),
199 overlay_entries_(overlay_entries),
200 string_pool_(string_pool) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700201
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700202 size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
203 arraysize(header_->overlay_path));
204 overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700205
206 length = strnlen(reinterpret_cast<const char*>(header_->target_path),
207 arraysize(header_->target_path));
208 target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700209}
210
211std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) {
212 ATRACE_CALL();
213 if (!IsValidIdmapHeader(idmap_data)) {
214 return {};
215 }
216
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700217 auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
Mårten Kongstadd7e8a532019-10-11 08:32:04 +0200218 const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + header->Size();
219 size_t data_size = idmap_data.size() - header->Size();
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700220
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700221 // Currently idmap2 can only generate one data block.
222 auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
223 data_ptr += sizeof(*data_header);
224 data_size -= sizeof(*data_header);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700225
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700226 // Make sure there is enough space for the target entries declared in the header.
227 const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
228 if (data_size / sizeof(Idmap_target_entry) <
229 static_cast<size_t>(dtohl(data_header->target_entry_count))) {
230 LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
231 (int)dtohl(data_header->target_entry_count));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700232 return {};
233 }
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700234
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700235 // Advance the data pointer past the target entries.
236 const size_t target_entry_size_bytes =
237 (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
238 data_ptr += target_entry_size_bytes;
239 data_size -= target_entry_size_bytes;
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700240
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700241 // Make sure there is enough space for the overlay entries declared in the header.
242 const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
243 if (data_size / sizeof(Idmap_overlay_entry) <
244 static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
245 LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
246 (int)dtohl(data_header->overlay_entry_count));
247 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700248 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700249
250 // Advance the data pointer past the target entries.
251 const size_t overlay_entry_size_bytes =
252 (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
253 data_ptr += overlay_entry_size_bytes;
254 data_size -= overlay_entry_size_bytes;
255
256 // Read the idmap string pool that holds the value of inline string entries.
257 if (data_size < dtohl(data_header->string_pool_length)) {
258 LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
259 (int)dtohl(data_header->string_pool_length));
260 return {};
261 }
262
263 auto idmap_string_pool = util::make_unique<ResStringPool>();
264 if (dtohl(data_header->string_pool_length) > 0) {
265 status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
266 if (err != NO_ERROR) {
267 LOG(ERROR) << "idmap string pool corrupt.";
268 return {};
269 }
270 }
271
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800272 // Can't use make_unique because LoadedIdmap constructor is private.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700273 std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
274 new LoadedIdmap(header, data_header, target_entries, overlay_entries,
275 idmap_string_pool.release()));
276
277 return std::move(loaded_idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700278}
279
280} // namespace android