blob: adb383f95d40b567bb2dca189e479184af156111 [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 Mitchell0699f1d2020-12-03 15:41:42 -080039// See frameworks/base/cmds/idmap2/include/idmap2/Idmap.h for full idmap file format specification.
40struct Idmap_header {
41 // Always 0x504D4449 ('IDMP')
42 uint32_t magic;
43 uint32_t version;
Adam Lesinski970bd8d2017-09-25 13:21:55 -070044
Ryan Mitchell0699f1d2020-12-03 15:41:42 -080045 uint32_t target_crc32;
46 uint32_t overlay_crc32;
47
48 uint32_t fulfilled_policies;
49 uint32_t enforce_overlayable;
50
51 // overlay_path, target_path, and other string values encoded in the idmap header and read and
52 // stored in separate structures. This allows the idmap header data to be casted to this struct
53 // without having to read/store each header entry separately.
54};
55
56struct Idmap_data_header {
57 uint8_t target_package_id;
58 uint8_t overlay_package_id;
59
60 // Padding to ensure 4 byte alignment for target_entry_count
61 uint16_t p0;
62
63 uint32_t target_entry_count;
64 uint32_t target_inline_entry_count;
65 uint32_t overlay_entry_count;
66
67 uint32_t string_pool_index_offset;
68};
69
70struct Idmap_target_entry {
71 uint32_t target_id;
72 uint32_t overlay_id;
73};
74
75struct Idmap_target_entry_inline {
76 uint32_t target_id;
77 Res_value value;
78};
79
80struct Idmap_overlay_entry {
81 uint32_t overlay_id;
82 uint32_t target_id;
83};
MÃ¥rten Kongstadd7e8a532019-10-11 08:32:04 +020084
Ryan Mitchell8a891d82019-07-01 09:48:23 -070085OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
Ryan Mitchell73bfe412019-11-12 16:22:04 -080086 : data_header_(loaded_idmap->data_header_),
87 idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
Ryan Mitchell8a891d82019-07-01 09:48:23 -070088
89OverlayStringPool::~OverlayStringPool() {
90 uninit();
91}
92
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +000093base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const {
Ryan Mitchell8a891d82019-07-01 09:48:23 -070094 const size_t offset = dtohl(data_header_->string_pool_index_offset);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080095 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +000096 return idmap_string_pool_->stringAt(idx - offset);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070097 }
98
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +000099 return ResStringPool::stringAt(idx);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700100}
101
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000102base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700103 const size_t offset = dtohl(data_header_->string_pool_index_offset);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -0800104 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000105 return idmap_string_pool_->string8At(idx - offset);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700106 }
107
Ryan Mitchelldb21f09a2020-11-16 23:08:18 +0000108 return ResStringPool::string8At(idx);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700109}
110
Ryan Mitchelldf9e7322019-12-12 10:23:54 -0800111size_t OverlayStringPool::size() const {
112 return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
113}
114
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700115OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
116 const Idmap_overlay_entry* entries,
117 uint8_t target_assigned_package_id)
118 : data_header_(data_header),
119 entries_(entries),
120 target_assigned_package_id_(target_assigned_package_id) { };
121
122status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
123 const Idmap_overlay_entry* first_entry = entries_;
124 const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700125 auto entry = std::lower_bound(first_entry, end_entry, *resId,
126 [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
127 return dtohl(e1.overlay_id) < overlay_id;
128 });
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700129
130 if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
131 // A mapping for the target resource id could not be found.
132 return DynamicRefTable::lookupResourceId(resId);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700133 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700134
135 *resId = (0x00FFFFFFU & dtohl(entry->target_id))
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700136 | (((uint32_t) target_assigned_package_id_) << 24U);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700137 return NO_ERROR;
138}
139
140status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
141 return DynamicRefTable::lookupResourceId(resId);
142}
143
144IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
145 const Idmap_target_entry* entries,
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700146 const Idmap_target_entry_inline* inline_entries,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700147 uint8_t target_assigned_package_id,
148 const OverlayDynamicRefTable* overlay_ref_table)
149 : data_header_(data_header),
150 entries_(entries),
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700151 inline_entries_(inline_entries),
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700152 target_assigned_package_id_(target_assigned_package_id),
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700153 overlay_ref_table_(overlay_ref_table) { }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700154
155IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700156 if ((target_res_id >> 24U) != target_assigned_package_id_) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700157 // The resource id must have the same package id as the target package.
158 return {};
159 }
160
161 // The resource ids encoded within the idmap are build-time resource ids.
162 target_res_id = (0x00FFFFFFU & target_res_id)
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700163 | (((uint32_t) data_header_->target_package_id) << 24U);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700164
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700165 // Check if the target resource is mapped to an overlay resource.
166 auto first_entry = entries_;
167 auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
168 auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
169 [](const Idmap_target_entry &e, const uint32_t target_id) {
170 return dtohl(e.target_id) < target_id;
171 });
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700172
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700173 if (entry != end_entry && dtohl(entry->target_id) == target_res_id) {
174 uint32_t overlay_resource_id = dtohl(entry->overlay_id);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700175 // Lookup the resource without rewriting the overlay resource id back to the target resource id
176 // being looked up.
177 overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
178 return Result(overlay_resource_id);
179 }
180
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700181 // Check if the target resources is mapped to an inline table entry.
182 auto first_inline_entry = inline_entries_;
183 auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
184 auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
185 [](const Idmap_target_entry_inline &e,
186 const uint32_t target_id) {
187 return dtohl(e.target_id) < target_id;
188 });
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700189
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700190 if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) {
191 return Result(inline_entry->value);
192 }
193 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700194}
195
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800196namespace {
197template <typename T>
198const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const std::string& label,
199 size_t count = 1) {
200 if (!util::IsFourByteAligned(*in_out_data_ptr)) {
201 LOG(ERROR) << "Idmap " << label << " is not word aligned.";
202 return {};
203 }
204 if ((*in_out_size / sizeof(T)) < count) {
205 LOG(ERROR) << "Idmap too small for the number of " << label << " entries ("
206 << count << ").";
207 return nullptr;
208 }
209 auto data_ptr = *in_out_data_ptr;
210 const size_t read_size = sizeof(T) * count;
211 *in_out_data_ptr += read_size;
212 *in_out_size -= read_size;
213 return reinterpret_cast<const T*>(data_ptr);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700214}
215
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800216std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size_t* in_out_size,
217 const std::string& label) {
218 const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label + " length");
219 if (len == nullptr) {
220 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700221 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800222 const auto* data = ReadType<char>(in_out_data_ptr, in_out_size, label, *len);
223 if (data == nullptr) {
224 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700225 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800226 // Strings are padded to the next 4 byte boundary.
227 const uint32_t padding_size = (4U - ((size_t)*in_out_data_ptr & 0x3U)) % 4U;
228 for (uint32_t i = 0; i < padding_size; i++) {
229 if (**in_out_data_ptr != 0) {
230 LOG(ERROR) << " Idmap padding of " << label << " is non-zero.";
231 return {};
232 }
233 *in_out_data_ptr += sizeof(uint8_t);
234 *in_out_size -= sizeof(uint8_t);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700235 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800236 return std::string_view(data, *len);
237}
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700238}
239
Ryan Mitchella9093052020-03-26 17:15:01 -0700240LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
Ryan Mitchella9093052020-03-26 17:15:01 -0700241 const Idmap_header* header,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700242 const Idmap_data_header* data_header,
243 const Idmap_target_entry* target_entries,
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700244 const Idmap_target_entry_inline* target_inline_entries,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700245 const Idmap_overlay_entry* overlay_entries,
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800246 std::unique_ptr<ResStringPool>&& string_pool,
247 std::string_view overlay_apk_path,
248 std::string_view target_apk_path)
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800249 : header_(header),
250 data_header_(data_header),
251 target_entries_(target_entries),
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700252 target_inline_entries_(target_inline_entries),
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800253 overlay_entries_(overlay_entries),
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800254 string_pool_(std::move(string_pool)),
Ryan Mitchella9093052020-03-26 17:15:01 -0700255 idmap_path_(std::move(idmap_path)),
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800256 overlay_apk_path_(overlay_apk_path),
257 target_apk_path_(target_apk_path),
258 idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700259
Ryan Mitchella9093052020-03-26 17:15:01 -0700260std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
261 const StringPiece& idmap_data) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700262 ATRACE_CALL();
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800263 size_t data_size = idmap_data.size();
264 auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
265
266 // Parse the idmap header
267 auto header = ReadType<Idmap_header>(&data_ptr, &data_size, "header");
268 if (header == nullptr) {
269 return {};
270 }
271 if (dtohl(header->magic) != kIdmapMagic) {
272 LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
273 dtohl(header->magic), kIdmapMagic);
274 return {};
275 }
276 if (dtohl(header->version) != kIdmapCurrentVersion) {
277 // We are strict about versions because files with this format are generated at runtime and
278 // don't need backwards compatibility.
279 LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
280 dtohl(header->version), kIdmapCurrentVersion);
281 return {};
282 }
283 std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path");
284 if (!overlay_path) {
285 return {};
286 }
287 std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
288 if (!target_path) {
289 return {};
290 }
Ryan Mitchell30dc2e02020-12-02 11:43:18 -0800291 if (!ReadString(&data_ptr, &data_size, "target name") ||
292 !ReadString(&data_ptr, &data_size, "debug info")) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700293 return {};
294 }
295
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800296 // Parse the idmap data blocks. Currently idmap2 can only generate one data block.
297 auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header");
298 if (data_header == nullptr) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700299 return {};
300 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800301 auto target_entries = ReadType<Idmap_target_entry>(&data_ptr, &data_size, "target",
302 dtohl(data_header->target_entry_count));
303 if (target_entries == nullptr) {
Ryan Mitchellbf1f45b2020-09-29 17:22:52 -0700304 return {};
305 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800306 auto target_inline_entries = ReadType<Idmap_target_entry_inline>(
307 &data_ptr, &data_size, "target inline", dtohl(data_header->target_inline_entry_count));
308 if (target_inline_entries == nullptr) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700309 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700310 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800311 auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
312 dtohl(data_header->overlay_entry_count));
313 if (overlay_entries == nullptr) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700314 return {};
315 }
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800316 std::optional<std::string_view> string_pool = ReadString(&data_ptr, &data_size, "string pool");
317 if (!string_pool) {
318 return {};
319 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700320 auto idmap_string_pool = util::make_unique<ResStringPool>();
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800321 if (!string_pool->empty()) {
322 const status_t err = idmap_string_pool->setTo(string_pool->data(), string_pool->size());
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700323 if (err != NO_ERROR) {
324 LOG(ERROR) << "idmap string pool corrupt.";
325 return {};
326 }
327 }
328
Ryan Mitchell831b0722021-01-13 10:11:18 -0800329 if (data_size != 0) {
330 LOG(ERROR) << "idmap parsed with " << data_size << "bytes remaining";
331 return {};
332 }
333
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800334 // Can't use make_unique because LoadedIdmap constructor is private.
Ryan Mitchell0699f1d2020-12-03 15:41:42 -0800335 return std::unique_ptr<LoadedIdmap>(
336 new LoadedIdmap(idmap_path.to_string(), header, data_header, target_entries,
337 target_inline_entries, overlay_entries, std::move(idmap_string_pool),
338 *target_path, *overlay_path));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700339}
340
Ryan Mitchella9093052020-03-26 17:15:01 -0700341bool LoadedIdmap::IsUpToDate() const {
342 return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str());
343}
344
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700345} // namespace android