blob: 4e03ce5d95843f4ec0392194d52c2a9a32f7fe47 [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 Mitchella9093052020-03-26 17:15:01 -070023#include "androidfw/misc.h"
Ryan Mitchell8a891d82019-07-01 09:48:23 -070024#include "androidfw/ResourceTypes.h"
25#include "androidfw/Util.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070026#include "utils/ByteOrder.h"
27#include "utils/Trace.h"
28
29#ifdef _WIN32
30#ifdef ERROR
31#undef ERROR
32#endif
33#endif
34
Adam Lesinski970bd8d2017-09-25 13:21:55 -070035using ::android::base::StringPrintf;
36
37namespace android {
38
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -070039uint32_t round_to_4_bytes(uint32_t size) {
40 return size + (4U - (size % 4U)) % 4U;
Adam Lesinski970bd8d2017-09-25 13:21:55 -070041}
42
Mårten Kongstadd7e8a532019-10-11 08:32:04 +020043size_t Idmap_header::Size() const {
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -070044 return sizeof(Idmap_header) + sizeof(uint8_t) * round_to_4_bytes(dtohl(debug_info_size));
Mårten Kongstadd7e8a532019-10-11 08:32:04 +020045}
46
Ryan Mitchell8a891d82019-07-01 09:48:23 -070047OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
Ryan Mitchell73bfe412019-11-12 16:22:04 -080048 : data_header_(loaded_idmap->data_header_),
49 idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
Ryan Mitchell8a891d82019-07-01 09:48:23 -070050
51OverlayStringPool::~OverlayStringPool() {
52 uninit();
53}
54
55const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
56 const size_t offset = dtohl(data_header_->string_pool_index_offset);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080057 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -070058 return idmap_string_pool_->stringAt(idx - offset, outLen);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070059 }
60
Ryan Mitchell8a891d82019-07-01 09:48:23 -070061 return ResStringPool::stringAt(idx, outLen);
62}
63
64const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
65 const size_t offset = dtohl(data_header_->string_pool_index_offset);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080066 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -070067 return idmap_string_pool_->string8At(idx - offset, outLen);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070068 }
69
Ryan Mitchell8a891d82019-07-01 09:48:23 -070070 return ResStringPool::string8At(idx, outLen);
71}
72
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080073size_t OverlayStringPool::size() const {
74 return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
75}
76
Ryan Mitchell8a891d82019-07-01 09:48:23 -070077OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
78 const Idmap_overlay_entry* entries,
79 uint8_t target_assigned_package_id)
80 : data_header_(data_header),
81 entries_(entries),
82 target_assigned_package_id_(target_assigned_package_id) { };
83
84status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
85 const Idmap_overlay_entry* first_entry = entries_;
86 const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -070087 auto entry = std::lower_bound(first_entry, end_entry, *resId,
88 [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
89 return dtohl(e1.overlay_id) < overlay_id;
90 });
Ryan Mitchell8a891d82019-07-01 09:48:23 -070091
92 if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
93 // A mapping for the target resource id could not be found.
94 return DynamicRefTable::lookupResourceId(resId);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070095 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -070096
97 *resId = (0x00FFFFFFU & dtohl(entry->target_id))
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -070098 | (((uint32_t) target_assigned_package_id_) << 24U);
Ryan Mitchell8a891d82019-07-01 09:48:23 -070099 return NO_ERROR;
100}
101
102status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
103 return DynamicRefTable::lookupResourceId(resId);
104}
105
106IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
107 const Idmap_target_entry* entries,
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700108 const Idmap_target_entry_inline* inline_entries,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700109 uint8_t target_assigned_package_id,
110 const OverlayDynamicRefTable* overlay_ref_table)
111 : data_header_(data_header),
112 entries_(entries),
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700113 inline_entries_(inline_entries),
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700114 target_assigned_package_id_(target_assigned_package_id),
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700115 overlay_ref_table_(overlay_ref_table) { }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700116
117IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700118 if ((target_res_id >> 24U) != target_assigned_package_id_) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700119 // The resource id must have the same package id as the target package.
120 return {};
121 }
122
123 // The resource ids encoded within the idmap are build-time resource ids.
124 target_res_id = (0x00FFFFFFU & target_res_id)
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700125 | (((uint32_t) data_header_->target_package_id) << 24U);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700126
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700127 // Check if the target resource is mapped to an overlay resource.
128 auto first_entry = entries_;
129 auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
130 auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
131 [](const Idmap_target_entry &e, const uint32_t target_id) {
132 return dtohl(e.target_id) < target_id;
133 });
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700134
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700135 if (entry != end_entry && dtohl(entry->target_id) == target_res_id) {
136 uint32_t overlay_resource_id = dtohl(entry->overlay_id);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700137 // Lookup the resource without rewriting the overlay resource id back to the target resource id
138 // being looked up.
139 overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
140 return Result(overlay_resource_id);
141 }
142
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700143 // Check if the target resources is mapped to an inline table entry.
144 auto first_inline_entry = inline_entries_;
145 auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
146 auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
147 [](const Idmap_target_entry_inline &e,
148 const uint32_t target_id) {
149 return dtohl(e.target_id) < target_id;
150 });
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700151
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700152 if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) {
153 return Result(inline_entry->value);
154 }
155 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700156}
157
158static bool is_word_aligned(const void* data) {
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700159 return (reinterpret_cast<uintptr_t>(data) & 0x03U) == 0U;
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700160}
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
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700173 auto header = reinterpret_cast<const Idmap_header*>(data.data());
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700174 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 Mitchella9093052020-03-26 17:15:01 -0700191LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
192 const time_t last_mod_time,
193 const Idmap_header* header,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700194 const Idmap_data_header* data_header,
195 const Idmap_target_entry* target_entries,
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700196 const Idmap_target_entry_inline* target_inline_entries,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700197 const Idmap_overlay_entry* overlay_entries,
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800198 ResStringPool* string_pool)
199 : header_(header),
200 data_header_(data_header),
201 target_entries_(target_entries),
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700202 target_inline_entries_(target_inline_entries),
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800203 overlay_entries_(overlay_entries),
Ryan Mitchella9093052020-03-26 17:15:01 -0700204 string_pool_(string_pool),
205 idmap_path_(std::move(idmap_path)),
206 idmap_last_mod_time_(last_mod_time) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700207
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700208 size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
209 arraysize(header_->overlay_path));
210 overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700211
212 length = strnlen(reinterpret_cast<const char*>(header_->target_path),
213 arraysize(header_->target_path));
214 target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700215}
216
Ryan Mitchella9093052020-03-26 17:15:01 -0700217std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
218 const StringPiece& idmap_data) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700219 ATRACE_CALL();
220 if (!IsValidIdmapHeader(idmap_data)) {
221 return {};
222 }
223
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700224 auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
Mårten Kongstadd7e8a532019-10-11 08:32:04 +0200225 const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + header->Size();
226 size_t data_size = idmap_data.size() - header->Size();
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700227
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700228 // Currently idmap2 can only generate one data block.
229 auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
230 data_ptr += sizeof(*data_header);
231 data_size -= sizeof(*data_header);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700232
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700233 // Make sure there is enough space for the target entries declared in the header
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700234 const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
235 if (data_size / sizeof(Idmap_target_entry) <
236 static_cast<size_t>(dtohl(data_header->target_entry_count))) {
237 LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
238 (int)dtohl(data_header->target_entry_count));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700239 return {};
240 }
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700241
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700242 // Advance the data pointer past the target entries.
243 const size_t target_entry_size_bytes =
244 (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
245 data_ptr += target_entry_size_bytes;
246 data_size -= target_entry_size_bytes;
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700247
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700248 // Make sure there is enough space for the target entries declared in the header.
249 const auto target_inline_entries = reinterpret_cast<const Idmap_target_entry_inline*>(data_ptr);
250 if (data_size / sizeof(Idmap_target_entry_inline) <
251 static_cast<size_t>(dtohl(data_header->target_inline_entry_count))) {
252 LOG(ERROR) << StringPrintf("Idmap too small for the number of target inline entries (%d)",
253 (int)dtohl(data_header->target_inline_entry_count));
254 return {};
255 }
256
257 // Advance the data pointer past the target entries.
258 const size_t target_inline_entry_size_bytes =
259 (dtohl(data_header->target_inline_entry_count) * sizeof(Idmap_target_entry_inline));
260 data_ptr += target_inline_entry_size_bytes;
261 data_size -= target_inline_entry_size_bytes;
262
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700263 // Make sure there is enough space for the overlay entries declared in the header.
264 const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
265 if (data_size / sizeof(Idmap_overlay_entry) <
266 static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
267 LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
268 (int)dtohl(data_header->overlay_entry_count));
269 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700270 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700271
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700272 // Advance the data pointer past the overlay entries.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700273 const size_t overlay_entry_size_bytes =
274 (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
275 data_ptr += overlay_entry_size_bytes;
276 data_size -= overlay_entry_size_bytes;
277
278 // Read the idmap string pool that holds the value of inline string entries.
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700279 uint32_t string_pool_size = dtohl(*reinterpret_cast<const uint32_t*>(data_ptr));
280 data_ptr += sizeof(uint32_t);
281 data_size -= sizeof(uint32_t);
282
283 if (data_size < string_pool_size) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700284 LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700285 (int)string_pool_size);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700286 return {};
287 }
288
289 auto idmap_string_pool = util::make_unique<ResStringPool>();
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700290 if (string_pool_size > 0) {
291 status_t err = idmap_string_pool->setTo(data_ptr, string_pool_size);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700292 if (err != NO_ERROR) {
293 LOG(ERROR) << "idmap string pool corrupt.";
294 return {};
295 }
296 }
297
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800298 // Can't use make_unique because LoadedIdmap constructor is private.
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700299 auto loaded_idmap = std::unique_ptr<LoadedIdmap>(
Ryan Mitchella9093052020-03-26 17:15:01 -0700300 new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header,
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700301 data_header, target_entries, target_inline_entries, overlay_entries,
302 idmap_string_pool.release()));
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700303
304 return std::move(loaded_idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700305}
306
Ryan Mitchella9093052020-03-26 17:15:01 -0700307bool LoadedIdmap::IsUpToDate() const {
308 return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str());
309}
310
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700311} // namespace android