blob: 5f231ffe4786276122d14c818e88bb552b1dc832 [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 Mitchell8a891d82019-07-01 09:48:23 -070039static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) {
40 return dtohl(e1.target_id) < target_id;
Adam Lesinski970bd8d2017-09-25 13:21:55 -070041}
42
Ryan Mitchell8a891d82019-07-01 09:48:23 -070043static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
44 return dtohl(e1.overlay_id) < overlay_id;
Adam Lesinski970bd8d2017-09-25 13:21:55 -070045}
46
Mårten Kongstadd7e8a532019-10-11 08:32:04 +020047size_t Idmap_header::Size() const {
48 return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size);
49}
50
Ryan Mitchell8a891d82019-07-01 09:48:23 -070051OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
Ryan Mitchell73bfe412019-11-12 16:22:04 -080052 : data_header_(loaded_idmap->data_header_),
53 idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
Ryan Mitchell8a891d82019-07-01 09:48:23 -070054
55OverlayStringPool::~OverlayStringPool() {
56 uninit();
57}
58
59const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
60 const size_t offset = dtohl(data_header_->string_pool_index_offset);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080061 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -070062 return idmap_string_pool_->stringAt(idx - offset, outLen);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070063 }
64
Ryan Mitchell8a891d82019-07-01 09:48:23 -070065 return ResStringPool::stringAt(idx, outLen);
66}
67
68const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
69 const size_t offset = dtohl(data_header_->string_pool_index_offset);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080070 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -070071 return idmap_string_pool_->string8At(idx - offset, outLen);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070072 }
73
Ryan Mitchell8a891d82019-07-01 09:48:23 -070074 return ResStringPool::string8At(idx, outLen);
75}
76
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080077size_t OverlayStringPool::size() const {
78 return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
79}
80
Ryan Mitchell8a891d82019-07-01 09:48:23 -070081OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
82 const Idmap_overlay_entry* entries,
83 uint8_t target_assigned_package_id)
84 : data_header_(data_header),
85 entries_(entries),
86 target_assigned_package_id_(target_assigned_package_id) { };
87
88status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
89 const Idmap_overlay_entry* first_entry = entries_;
90 const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
91 auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
92
93 if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
94 // A mapping for the target resource id could not be found.
95 return DynamicRefTable::lookupResourceId(resId);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070096 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -070097
98 *resId = (0x00FFFFFFU & dtohl(entry->target_id))
99 | (((uint32_t) target_assigned_package_id_) << 24);
100 return NO_ERROR;
101}
102
103status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
104 return DynamicRefTable::lookupResourceId(resId);
105}
106
107IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
108 const Idmap_target_entry* entries,
109 uint8_t target_assigned_package_id,
110 const OverlayDynamicRefTable* overlay_ref_table)
111 : data_header_(data_header),
112 entries_(entries),
113 target_assigned_package_id_(target_assigned_package_id),
114 overlay_ref_table_(overlay_ref_table) { };
115
116IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
117 if ((target_res_id >> 24) != target_assigned_package_id_) {
118 // The resource id must have the same package id as the target package.
119 return {};
120 }
121
122 // The resource ids encoded within the idmap are build-time resource ids.
123 target_res_id = (0x00FFFFFFU & target_res_id)
124 | (((uint32_t) data_header_->target_package_id) << 24);
125
126 const Idmap_target_entry* first_entry = entries_;
127 const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
128 auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
129
130 if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
131 // A mapping for the target resource id could not be found.
132 return {};
133 }
134
135 // A reference should be treated as an alias of the resource. Instead of returning the table
136 // entry, return the alias resource id to look up. The alias resource might not reside within the
137 // overlay package, so the resource id must be fixed with the dynamic reference table of the
138 // overlay before returning.
139 if (entry->type == Res_value::TYPE_REFERENCE
140 || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
141 uint32_t overlay_resource_id = dtohl(entry->value);
142
143 // Lookup the resource without rewriting the overlay resource id back to the target resource id
144 // being looked up.
145 overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
146 return Result(overlay_resource_id);
147 }
148
149 // Copy the type and value into the ResTable_entry structure needed by asset manager.
150 uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
151 auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
152 memset(table_entry, 0, malloc_size);
153 table_entry->size = htods(sizeof(ResTable_entry));
154
155 auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
156 + sizeof(ResTable_entry));
157 table_value->dataType = entry->type;
158 table_value->data = entry->value;
159
Ryan Mitchellc4142d92020-06-22 13:00:58 -0700160 return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); }));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700161}
162
163static bool is_word_aligned(const void* data) {
164 return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0;
165}
166
167static bool IsValidIdmapHeader(const StringPiece& data) {
168 if (!is_word_aligned(data.data())) {
169 LOG(ERROR) << "Idmap header is not word aligned.";
170 return false;
171 }
172
173 if (data.size() < sizeof(Idmap_header)) {
174 LOG(ERROR) << "Idmap header is too small.";
175 return false;
176 }
177
178 const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data());
179 if (dtohl(header->magic) != kIdmapMagic) {
180 LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
181 dtohl(header->magic), kIdmapMagic);
182 return false;
183 }
184
185 if (dtohl(header->version) != kIdmapCurrentVersion) {
186 // We are strict about versions because files with this format are auto-generated and don't need
187 // backwards compatibility.
188 LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
189 dtohl(header->version), kIdmapCurrentVersion);
190 return false;
191 }
192
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700193 return true;
194}
195
Ryan Mitchella9093052020-03-26 17:15:01 -0700196LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
197 const time_t last_mod_time,
198 const Idmap_header* header,
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700199 const Idmap_data_header* data_header,
200 const Idmap_target_entry* target_entries,
201 const Idmap_overlay_entry* overlay_entries,
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800202 ResStringPool* string_pool)
203 : header_(header),
204 data_header_(data_header),
205 target_entries_(target_entries),
206 overlay_entries_(overlay_entries),
Ryan Mitchella9093052020-03-26 17:15:01 -0700207 string_pool_(string_pool),
208 idmap_path_(std::move(idmap_path)),
209 idmap_last_mod_time_(last_mod_time) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700210
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700211 size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
212 arraysize(header_->overlay_path));
213 overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700214
215 length = strnlen(reinterpret_cast<const char*>(header_->target_path),
216 arraysize(header_->target_path));
217 target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700218}
219
Ryan Mitchella9093052020-03-26 17:15:01 -0700220std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
221 const StringPiece& idmap_data) {
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700222 ATRACE_CALL();
223 if (!IsValidIdmapHeader(idmap_data)) {
224 return {};
225 }
226
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700227 auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
Mårten Kongstadd7e8a532019-10-11 08:32:04 +0200228 const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + header->Size();
229 size_t data_size = idmap_data.size() - header->Size();
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700230
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700231 // Currently idmap2 can only generate one data block.
232 auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
233 data_ptr += sizeof(*data_header);
234 data_size -= sizeof(*data_header);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700235
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700236 // Make sure there is enough space for the target entries declared in the header.
237 const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
238 if (data_size / sizeof(Idmap_target_entry) <
239 static_cast<size_t>(dtohl(data_header->target_entry_count))) {
240 LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
241 (int)dtohl(data_header->target_entry_count));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700242 return {};
243 }
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700244
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700245 // Advance the data pointer past the target entries.
246 const size_t target_entry_size_bytes =
247 (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
248 data_ptr += target_entry_size_bytes;
249 data_size -= target_entry_size_bytes;
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700250
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700251 // Make sure there is enough space for the overlay entries declared in the header.
252 const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
253 if (data_size / sizeof(Idmap_overlay_entry) <
254 static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
255 LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
256 (int)dtohl(data_header->overlay_entry_count));
257 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700258 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700259
260 // Advance the data pointer past the target entries.
261 const size_t overlay_entry_size_bytes =
262 (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
263 data_ptr += overlay_entry_size_bytes;
264 data_size -= overlay_entry_size_bytes;
265
266 // Read the idmap string pool that holds the value of inline string entries.
267 if (data_size < dtohl(data_header->string_pool_length)) {
268 LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
269 (int)dtohl(data_header->string_pool_length));
270 return {};
271 }
272
273 auto idmap_string_pool = util::make_unique<ResStringPool>();
274 if (dtohl(data_header->string_pool_length) > 0) {
275 status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
276 if (err != NO_ERROR) {
277 LOG(ERROR) << "idmap string pool corrupt.";
278 return {};
279 }
280 }
281
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800282 // Can't use make_unique because LoadedIdmap constructor is private.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700283 std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
Ryan Mitchella9093052020-03-26 17:15:01 -0700284 new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header,
285 data_header, target_entries, overlay_entries, idmap_string_pool.release()));
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700286
287 return std::move(loaded_idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700288}
289
Ryan Mitchella9093052020-03-26 17:15:01 -0700290bool LoadedIdmap::IsUpToDate() const {
291 return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str());
292}
293
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700294} // namespace android