blob: 982419059ead0d671b4800ededdee7ca1a91a3fb [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
Yurii Zubrytskyie7c1f002024-01-30 13:50:57 -080021#include "android-base/file.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070022#include "android-base/logging.h"
23#include "android-base/stringprintf.h"
Yurii Zubrytskyie7c1f002024-01-30 13:50:57 -080024#include "android-base/utf8.h"
Ryan Mitchella9093052020-03-26 17:15:01 -070025#include "androidfw/misc.h"
Ryan Mitchell8a891d82019-07-01 09:48:23 -070026#include "androidfw/ResourceTypes.h"
27#include "androidfw/Util.h"
Adam Lesinski970bd8d2017-09-25 13:21:55 -070028#include "utils/ByteOrder.h"
29#include "utils/Trace.h"
30
31#ifdef _WIN32
32#ifdef ERROR
33#undef ERROR
34#endif
35#endif
36
Adam Lesinski970bd8d2017-09-25 13:21:55 -070037using ::android::base::StringPrintf;
38
39namespace android {
40
Ryan Mitchell0699f1d2020-12-03 15:41:42 -080041// See frameworks/base/cmds/idmap2/include/idmap2/Idmap.h for full idmap file format specification.
42struct Idmap_header {
43 // Always 0x504D4449 ('IDMP')
44 uint32_t magic;
45 uint32_t version;
Adam Lesinski970bd8d2017-09-25 13:21:55 -070046
Ryan Mitchell0699f1d2020-12-03 15:41:42 -080047 uint32_t target_crc32;
48 uint32_t overlay_crc32;
49
50 uint32_t fulfilled_policies;
51 uint32_t enforce_overlayable;
52
53 // overlay_path, target_path, and other string values encoded in the idmap header and read and
54 // stored in separate structures. This allows the idmap header data to be casted to this struct
55 // without having to read/store each header entry separately.
56};
57
58struct Idmap_data_header {
Ryan Mitchell0699f1d2020-12-03 15:41:42 -080059 uint32_t target_entry_count;
60 uint32_t target_inline_entry_count;
Jeremy Meyerbe2b7792022-08-23 17:42:50 +000061 uint32_t target_inline_entry_value_count;
62 uint32_t configuration_count;
Ryan Mitchell0699f1d2020-12-03 15:41:42 -080063 uint32_t overlay_entry_count;
64
65 uint32_t string_pool_index_offset;
66};
67
68struct Idmap_target_entry {
69 uint32_t target_id;
70 uint32_t overlay_id;
71};
72
73struct Idmap_target_entry_inline {
74 uint32_t target_id;
Jeremy Meyerbe2b7792022-08-23 17:42:50 +000075 uint32_t start_value_index;
76 uint32_t value_count;
77};
78
79struct Idmap_target_entry_inline_value {
80 uint32_t config_index;
Ryan Mitchell0699f1d2020-12-03 15:41:42 -080081 Res_value value;
82};
83
84struct Idmap_overlay_entry {
85 uint32_t overlay_id;
86 uint32_t target_id;
87};
MÃ¥rten Kongstadd7e8a532019-10-11 08:32:04 +020088
Ryan Mitchell8a891d82019-07-01 09:48:23 -070089OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
Ryan Mitchell73bfe412019-11-12 16:22:04 -080090 : data_header_(loaded_idmap->data_header_),
91 idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
Ryan Mitchell8a891d82019-07-01 09:48:23 -070092
93OverlayStringPool::~OverlayStringPool() {
94 uninit();
95}
96
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +000097base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const {
Ryan Mitchell8a891d82019-07-01 09:48:23 -070098 const size_t offset = dtohl(data_header_->string_pool_index_offset);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080099 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000100 return idmap_string_pool_->stringAt(idx - offset);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700101 }
102
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000103 return ResStringPool::stringAt(idx);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700104}
105
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000106base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700107 const size_t offset = dtohl(data_header_->string_pool_index_offset);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -0800108 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000109 return idmap_string_pool_->string8At(idx - offset);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700110 }
111
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000112 return ResStringPool::string8At(idx);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700113}
114
Ryan Mitchelldf9e7322019-12-12 10:23:54 -0800115size_t OverlayStringPool::size() const {
116 return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
117}
118
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700119OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
120 const Idmap_overlay_entry* entries,
121 uint8_t target_assigned_package_id)
122 : data_header_(data_header),
123 entries_(entries),
124 target_assigned_package_id_(target_assigned_package_id) { };
125
126status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
127 const Idmap_overlay_entry* first_entry = entries_;
128 const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700129 auto entry = std::lower_bound(first_entry, end_entry, *resId,
130 [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
131 return dtohl(e1.overlay_id) < overlay_id;
132 });
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700133
134 if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
135 // A mapping for the target resource id could not be found.
136 return DynamicRefTable::lookupResourceId(resId);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700137 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700138
139 *resId = (0x00FFFFFFU & dtohl(entry->target_id))
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700140 | (((uint32_t) target_assigned_package_id_) << 24U);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700141 return NO_ERROR;
142}
143
144status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
145 return DynamicRefTable::lookupResourceId(resId);
146}
147
148IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
149 const Idmap_target_entry* entries,
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700150 const Idmap_target_entry_inline* inline_entries,
Jeremy Meyerbe2b7792022-08-23 17:42:50 +0000151 const Idmap_target_entry_inline_value* inline_entry_values,
152 const ConfigDescription* configs,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700153 uint8_t target_assigned_package_id,
154 const OverlayDynamicRefTable* overlay_ref_table)
155 : data_header_(data_header),
156 entries_(entries),
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700157 inline_entries_(inline_entries),
Jeremy Meyerbe2b7792022-08-23 17:42:50 +0000158 inline_entry_values_(inline_entry_values),
159 configurations_(configs),
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700160 target_assigned_package_id_(target_assigned_package_id),
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700161 overlay_ref_table_(overlay_ref_table) { }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700162
163IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700164 if ((target_res_id >> 24U) != target_assigned_package_id_) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700165 // The resource id must have the same package id as the target package.
166 return {};
167 }
168
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800169 // The resource ids encoded within the idmap are build-time resource ids so do not consider the
170 // package id when determining if the resource in the target package is overlaid.
171 target_res_id &= 0x00FFFFFFU;
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700172
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700173 // Check if the target resource is mapped to an overlay resource.
174 auto first_entry = entries_;
175 auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
176 auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800177 [](const Idmap_target_entry& e, const uint32_t target_id) {
178 return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700179 });
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700180
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800181 if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) {
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700182 uint32_t overlay_resource_id = dtohl(entry->overlay_id);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700183 // Lookup the resource without rewriting the overlay resource id back to the target resource id
184 // being looked up.
185 overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
186 return Result(overlay_resource_id);
187 }
188
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700189 // Check if the target resources is mapped to an inline table entry.
190 auto first_inline_entry = inline_entries_;
191 auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
192 auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800193 [](const Idmap_target_entry_inline& e,
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700194 const uint32_t target_id) {
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800195 return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700196 });
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700197
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800198 if (inline_entry != end_inline_entry &&
199 (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
Jeremy Meyerbe2b7792022-08-23 17:42:50 +0000200 std::map<ConfigDescription, Res_value> values_map;
201 for (int i = 0; i < inline_entry->value_count; i++) {
202 const auto& value = inline_entry_values_[inline_entry->start_value_index + i];
203 const auto& config = configurations_[value.config_index];
204 values_map[config] = value.value;
205 }
Yurii Zubrytskyia5bc9582022-11-30 23:53:59 -0800206 return Result(std::move(values_map));
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700207 }
208 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700209}
210
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800211namespace {
212template <typename T>
213const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const std::string& label,
214 size_t count = 1) {
215 if (!util::IsFourByteAligned(*in_out_data_ptr)) {
216 LOG(ERROR) << "Idmap " << label << " is not word aligned.";
217 return {};
218 }
219 if ((*in_out_size / sizeof(T)) < count) {
220 LOG(ERROR) << "Idmap too small for the number of " << label << " entries ("
221 << count << ").";
222 return nullptr;
223 }
224 auto data_ptr = *in_out_data_ptr;
225 const size_t read_size = sizeof(T) * count;
226 *in_out_data_ptr += read_size;
227 *in_out_size -= read_size;
228 return reinterpret_cast<const T*>(data_ptr);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700229}
230
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800231std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size_t* in_out_size,
232 const std::string& label) {
233 const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label + " length");
234 if (len == nullptr) {
235 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700236 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800237 const auto* data = ReadType<char>(in_out_data_ptr, in_out_size, label, *len);
238 if (data == nullptr) {
239 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700240 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800241 // Strings are padded to the next 4 byte boundary.
242 const uint32_t padding_size = (4U - ((size_t)*in_out_data_ptr & 0x3U)) % 4U;
243 for (uint32_t i = 0; i < padding_size; i++) {
244 if (**in_out_data_ptr != 0) {
245 LOG(ERROR) << " Idmap padding of " << label << " is non-zero.";
246 return {};
247 }
248 *in_out_data_ptr += sizeof(uint8_t);
249 *in_out_size -= sizeof(uint8_t);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700250 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800251 return std::string_view(data, *len);
252}
Ryan Mitchell2ed8bfa2021-01-08 13:34:28 -0800253} // namespace
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700254
Yurii Zubrytskyie7c1f002024-01-30 13:50:57 -0800255// O_PATH is a lightweight way of creating an FD, only exists on Linux
256#ifndef O_PATH
257#define O_PATH (0)
258#endif
259
260LoadedIdmap::LoadedIdmap(const std::string& idmap_path, const Idmap_header* header,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700261 const Idmap_data_header* data_header,
262 const Idmap_target_entry* target_entries,
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700263 const Idmap_target_entry_inline* target_inline_entries,
Jeremy Meyerbe2b7792022-08-23 17:42:50 +0000264 const Idmap_target_entry_inline_value* inline_entry_values,
265 const ConfigDescription* configs,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700266 const Idmap_overlay_entry* overlay_entries,
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800267 std::unique_ptr<ResStringPool>&& string_pool,
Yurii Zubrytskyia5bc9582022-11-30 23:53:59 -0800268 std::string_view overlay_apk_path, std::string_view target_apk_path)
269 : header_(header),
270 data_header_(data_header),
271 target_entries_(target_entries),
272 target_inline_entries_(target_inline_entries),
273 inline_entry_values_(inline_entry_values),
274 configurations_(configs),
275 overlay_entries_(overlay_entries),
276 string_pool_(std::move(string_pool)),
Yurii Zubrytskyie7c1f002024-01-30 13:50:57 -0800277 idmap_fd_(android::base::utf8::open(idmap_path.c_str(), O_RDONLY|O_CLOEXEC|O_BINARY|O_PATH)),
Yurii Zubrytskyia5bc9582022-11-30 23:53:59 -0800278 overlay_apk_path_(overlay_apk_path),
279 target_apk_path_(target_apk_path),
Yurii Zubrytskyie7c1f002024-01-30 13:50:57 -0800280 idmap_last_mod_time_(getFileModDate(idmap_fd_.get())) {}
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700281
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700282std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700283 ATRACE_CALL();
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800284 size_t data_size = idmap_data.size();
285 auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
286
287 // Parse the idmap header
288 auto header = ReadType<Idmap_header>(&data_ptr, &data_size, "header");
289 if (header == nullptr) {
290 return {};
291 }
292 if (dtohl(header->magic) != kIdmapMagic) {
293 LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
294 dtohl(header->magic), kIdmapMagic);
295 return {};
296 }
297 if (dtohl(header->version) != kIdmapCurrentVersion) {
298 // We are strict about versions because files with this format are generated at runtime and
299 // don't need backwards compatibility.
300 LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
301 dtohl(header->version), kIdmapCurrentVersion);
302 return {};
303 }
Brandon Liu9e93ae42023-03-23 00:16:19 +0000304 std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
305 if (!target_path) {
306 return {};
307 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800308 std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path");
309 if (!overlay_path) {
310 return {};
311 }
Ryan Mitchell30dc2e02020-12-02 11:43:18 -0800312 if (!ReadString(&data_ptr, &data_size, "target name") ||
313 !ReadString(&data_ptr, &data_size, "debug info")) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700314 return {};
315 }
316
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800317 // Parse the idmap data blocks. Currently idmap2 can only generate one data block.
318 auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header");
319 if (data_header == nullptr) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700320 return {};
321 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800322 auto target_entries = ReadType<Idmap_target_entry>(&data_ptr, &data_size, "target",
323 dtohl(data_header->target_entry_count));
324 if (target_entries == nullptr) {
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700325 return {};
326 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800327 auto target_inline_entries = ReadType<Idmap_target_entry_inline>(
328 &data_ptr, &data_size, "target inline", dtohl(data_header->target_inline_entry_count));
329 if (target_inline_entries == nullptr) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700330 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700331 }
Jeremy Meyerbe2b7792022-08-23 17:42:50 +0000332
333 auto target_inline_entry_values = ReadType<Idmap_target_entry_inline_value>(
334 &data_ptr, &data_size, "target inline values",
335 dtohl(data_header->target_inline_entry_value_count));
336 if (target_inline_entry_values == nullptr) {
337 return {};
338 }
339
340 auto configurations = ReadType<ConfigDescription>(
341 &data_ptr, &data_size, "configurations",
342 dtohl(data_header->configuration_count));
343 if (configurations == nullptr) {
344 return {};
345 }
346
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800347 auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
348 dtohl(data_header->overlay_entry_count));
349 if (overlay_entries == nullptr) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700350 return {};
351 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800352 std::optional<std::string_view> string_pool = ReadString(&data_ptr, &data_size, "string pool");
353 if (!string_pool) {
354 return {};
355 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700356 auto idmap_string_pool = util::make_unique<ResStringPool>();
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800357 if (!string_pool->empty()) {
358 const status_t err = idmap_string_pool->setTo(string_pool->data(), string_pool->size());
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700359 if (err != NO_ERROR) {
360 LOG(ERROR) << "idmap string pool corrupt.";
361 return {};
362 }
363 }
364
Ryan Mitchell831b0722021-01-13 10:11:18 -0800365 if (data_size != 0) {
366 LOG(ERROR) << "idmap parsed with " << data_size << "bytes remaining";
367 return {};
368 }
369
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800370 // Can't use make_unique because LoadedIdmap constructor is private.
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800371 return std::unique_ptr<LoadedIdmap>(
Yurii Zubrytskyia5775142022-11-02 17:49:49 -0700372 new LoadedIdmap(std::string(idmap_path), header, data_header, target_entries,
Jeremy Meyerbe2b7792022-08-23 17:42:50 +0000373 target_inline_entries, target_inline_entry_values, configurations,
Brandon Liu9e93ae42023-03-23 00:16:19 +0000374 overlay_entries, std::move(idmap_string_pool), *overlay_path, *target_path));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700375}
376
Ryan Mitchella9093052020-03-26 17:15:01 -0700377bool LoadedIdmap::IsUpToDate() const {
Yurii Zubrytskyie7c1f002024-01-30 13:50:57 -0800378 return idmap_last_mod_time_ == getFileModDate(idmap_fd_.get());
Ryan Mitchella9093052020-03-26 17:15:01 -0700379}
380
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700381} // namespace android