blob: 0b2fd9ec982dd4cea3b0aabd8e0035c072e06052 [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);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080060 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -070061 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);
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080069 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -070070 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
Ryan Mitchelldf9e7322019-12-12 10:23:54 -080076size_t OverlayStringPool::size() const {
77 return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
78}
79
Ryan Mitchell8a891d82019-07-01 09:48:23 -070080OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
81 const Idmap_overlay_entry* entries,
82 uint8_t target_assigned_package_id)
83 : data_header_(data_header),
84 entries_(entries),
85 target_assigned_package_id_(target_assigned_package_id) { };
86
87status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
88 const Idmap_overlay_entry* first_entry = entries_;
89 const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
90 auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
91
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))
98 | (((uint32_t) target_assigned_package_id_) << 24);
99 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,
108 uint8_t target_assigned_package_id,
109 const OverlayDynamicRefTable* overlay_ref_table)
110 : data_header_(data_header),
111 entries_(entries),
112 target_assigned_package_id_(target_assigned_package_id),
113 overlay_ref_table_(overlay_ref_table) { };
114
115IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
116 if ((target_res_id >> 24) != target_assigned_package_id_) {
117 // The resource id must have the same package id as the target package.
118 return {};
119 }
120
121 // The resource ids encoded within the idmap are build-time resource ids.
122 target_res_id = (0x00FFFFFFU & target_res_id)
123 | (((uint32_t) data_header_->target_package_id) << 24);
124
125 const Idmap_target_entry* first_entry = entries_;
126 const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
127 auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
128
129 if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
130 // A mapping for the target resource id could not be found.
131 return {};
132 }
133
134 // A reference should be treated as an alias of the resource. Instead of returning the table
135 // entry, return the alias resource id to look up. The alias resource might not reside within the
136 // overlay package, so the resource id must be fixed with the dynamic reference table of the
137 // overlay before returning.
138 if (entry->type == Res_value::TYPE_REFERENCE
139 || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
140 uint32_t overlay_resource_id = dtohl(entry->value);
141
142 // Lookup the resource without rewriting the overlay resource id back to the target resource id
143 // being looked up.
144 overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
145 return Result(overlay_resource_id);
146 }
147
148 // Copy the type and value into the ResTable_entry structure needed by asset manager.
149 uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
150 auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
151 memset(table_entry, 0, malloc_size);
152 table_entry->size = htods(sizeof(ResTable_entry));
153
154 auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
155 + sizeof(ResTable_entry));
156 table_value->dataType = entry->type;
157 table_value->data = entry->value;
158
159 return Result(ResTable_entry_handle::managed(table_entry));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700160}
161
162static bool is_word_aligned(const void* data) {
163 return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0;
164}
165
166static bool IsValidIdmapHeader(const StringPiece& data) {
167 if (!is_word_aligned(data.data())) {
168 LOG(ERROR) << "Idmap header is not word aligned.";
169 return false;
170 }
171
172 if (data.size() < sizeof(Idmap_header)) {
173 LOG(ERROR) << "Idmap header is too small.";
174 return false;
175 }
176
177 const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data());
178 if (dtohl(header->magic) != kIdmapMagic) {
179 LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
180 dtohl(header->magic), kIdmapMagic);
181 return false;
182 }
183
184 if (dtohl(header->version) != kIdmapCurrentVersion) {
185 // We are strict about versions because files with this format are auto-generated and don't need
186 // backwards compatibility.
187 LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
188 dtohl(header->version), kIdmapCurrentVersion);
189 return false;
190 }
191
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700192 return true;
193}
194
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700195LoadedIdmap::LoadedIdmap(const Idmap_header* header,
196 const Idmap_data_header* data_header,
197 const Idmap_target_entry* target_entries,
198 const Idmap_overlay_entry* overlay_entries,
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800199 ResStringPool* string_pool)
200 : header_(header),
201 data_header_(data_header),
202 target_entries_(target_entries),
203 overlay_entries_(overlay_entries),
204 string_pool_(string_pool) {
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700205
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700206 size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
207 arraysize(header_->overlay_path));
208 overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700209
210 length = strnlen(reinterpret_cast<const char*>(header_->target_path),
211 arraysize(header_->target_path));
212 target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700213}
214
215std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) {
216 ATRACE_CALL();
217 if (!IsValidIdmapHeader(idmap_data)) {
218 return {};
219 }
220
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700221 auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
Mårten Kongstadd7e8a532019-10-11 08:32:04 +0200222 const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + header->Size();
223 size_t data_size = idmap_data.size() - header->Size();
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700224
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700225 // Currently idmap2 can only generate one data block.
226 auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
227 data_ptr += sizeof(*data_header);
228 data_size -= sizeof(*data_header);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700229
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700230 // Make sure there is enough space for the target entries declared in the header.
231 const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
232 if (data_size / sizeof(Idmap_target_entry) <
233 static_cast<size_t>(dtohl(data_header->target_entry_count))) {
234 LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
235 (int)dtohl(data_header->target_entry_count));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700236 return {};
237 }
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700238
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700239 // Advance the data pointer past the target entries.
240 const size_t target_entry_size_bytes =
241 (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
242 data_ptr += target_entry_size_bytes;
243 data_size -= target_entry_size_bytes;
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700244
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700245 // Make sure there is enough space for the overlay entries declared in the header.
246 const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
247 if (data_size / sizeof(Idmap_overlay_entry) <
248 static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
249 LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
250 (int)dtohl(data_header->overlay_entry_count));
251 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700252 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700253
254 // Advance the data pointer past the target entries.
255 const size_t overlay_entry_size_bytes =
256 (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
257 data_ptr += overlay_entry_size_bytes;
258 data_size -= overlay_entry_size_bytes;
259
260 // Read the idmap string pool that holds the value of inline string entries.
261 if (data_size < dtohl(data_header->string_pool_length)) {
262 LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
263 (int)dtohl(data_header->string_pool_length));
264 return {};
265 }
266
267 auto idmap_string_pool = util::make_unique<ResStringPool>();
268 if (dtohl(data_header->string_pool_length) > 0) {
269 status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
270 if (err != NO_ERROR) {
271 LOG(ERROR) << "idmap string pool corrupt.";
272 return {};
273 }
274 }
275
Ryan Mitchell73bfe412019-11-12 16:22:04 -0800276 // Can't use make_unique because LoadedIdmap constructor is private.
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700277 std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
278 new LoadedIdmap(header, data_header, target_entries, overlay_entries,
279 idmap_string_pool.release()));
280
281 return std::move(loaded_idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700282}
283
284} // namespace android