blob: 2b69c923597f9eb7ecde7585a9814fca7de5dc74 [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
Ryan Mitchell8a891d82019-07-01 09:48:23 -070046OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
47 : data_header_(loaded_idmap->data_header_),
48 idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
49
50OverlayStringPool::~OverlayStringPool() {
51 uninit();
52}
53
54const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
55 const size_t offset = dtohl(data_header_->string_pool_index_offset);
56 if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
57 return idmap_string_pool_->stringAt(idx - offset, outLen);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070058 }
59
Ryan Mitchell8a891d82019-07-01 09:48:23 -070060 return ResStringPool::stringAt(idx, outLen);
61}
62
63const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
64 const size_t offset = dtohl(data_header_->string_pool_index_offset);
65 if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
66 return idmap_string_pool_->string8At(idx - offset, outLen);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070067 }
68
Ryan Mitchell8a891d82019-07-01 09:48:23 -070069 return ResStringPool::string8At(idx, outLen);
70}
71
72OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
73 const Idmap_overlay_entry* entries,
74 uint8_t target_assigned_package_id)
75 : data_header_(data_header),
76 entries_(entries),
77 target_assigned_package_id_(target_assigned_package_id) { };
78
79status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
80 const Idmap_overlay_entry* first_entry = entries_;
81 const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
82 auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries);
83
84 if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
85 // A mapping for the target resource id could not be found.
86 return DynamicRefTable::lookupResourceId(resId);
Adam Lesinski970bd8d2017-09-25 13:21:55 -070087 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -070088
89 *resId = (0x00FFFFFFU & dtohl(entry->target_id))
90 | (((uint32_t) target_assigned_package_id_) << 24);
91 return NO_ERROR;
92}
93
94status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
95 return DynamicRefTable::lookupResourceId(resId);
96}
97
98IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
99 const Idmap_target_entry* entries,
100 uint8_t target_assigned_package_id,
101 const OverlayDynamicRefTable* overlay_ref_table)
102 : data_header_(data_header),
103 entries_(entries),
104 target_assigned_package_id_(target_assigned_package_id),
105 overlay_ref_table_(overlay_ref_table) { };
106
107IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
108 if ((target_res_id >> 24) != target_assigned_package_id_) {
109 // The resource id must have the same package id as the target package.
110 return {};
111 }
112
113 // The resource ids encoded within the idmap are build-time resource ids.
114 target_res_id = (0x00FFFFFFU & target_res_id)
115 | (((uint32_t) data_header_->target_package_id) << 24);
116
117 const Idmap_target_entry* first_entry = entries_;
118 const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count);
119 auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries);
120
121 if (entry == end_entry || dtohl(entry->target_id) != target_res_id) {
122 // A mapping for the target resource id could not be found.
123 return {};
124 }
125
126 // A reference should be treated as an alias of the resource. Instead of returning the table
127 // entry, return the alias resource id to look up. The alias resource might not reside within the
128 // overlay package, so the resource id must be fixed with the dynamic reference table of the
129 // overlay before returning.
130 if (entry->type == Res_value::TYPE_REFERENCE
131 || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) {
132 uint32_t overlay_resource_id = dtohl(entry->value);
133
134 // Lookup the resource without rewriting the overlay resource id back to the target resource id
135 // being looked up.
136 overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
137 return Result(overlay_resource_id);
138 }
139
140 // Copy the type and value into the ResTable_entry structure needed by asset manager.
141 uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value);
142 auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size));
143 memset(table_entry, 0, malloc_size);
144 table_entry->size = htods(sizeof(ResTable_entry));
145
146 auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry)
147 + sizeof(ResTable_entry));
148 table_value->dataType = entry->type;
149 table_value->data = entry->value;
150
151 return Result(ResTable_entry_handle::managed(table_entry));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700152}
153
154static bool is_word_aligned(const void* data) {
155 return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0;
156}
157
158static bool IsValidIdmapHeader(const StringPiece& data) {
159 if (!is_word_aligned(data.data())) {
160 LOG(ERROR) << "Idmap header is not word aligned.";
161 return false;
162 }
163
164 if (data.size() < sizeof(Idmap_header)) {
165 LOG(ERROR) << "Idmap header is too small.";
166 return false;
167 }
168
169 const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data());
170 if (dtohl(header->magic) != kIdmapMagic) {
171 LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
172 dtohl(header->magic), kIdmapMagic);
173 return false;
174 }
175
176 if (dtohl(header->version) != kIdmapCurrentVersion) {
177 // We are strict about versions because files with this format are auto-generated and don't need
178 // backwards compatibility.
179 LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
180 dtohl(header->version), kIdmapCurrentVersion);
181 return false;
182 }
183
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700184 return true;
185}
186
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700187LoadedIdmap::LoadedIdmap(const Idmap_header* header,
188 const Idmap_data_header* data_header,
189 const Idmap_target_entry* target_entries,
190 const Idmap_overlay_entry* overlay_entries,
191 ResStringPool* string_pool) : header_(header),
192 data_header_(data_header),
193 target_entries_(target_entries),
194 overlay_entries_(overlay_entries),
195 string_pool_(string_pool) {
196
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700197 size_t length = strnlen(reinterpret_cast<const char*>(header_->overlay_path),
198 arraysize(header_->overlay_path));
199 overlay_apk_path_.assign(reinterpret_cast<const char*>(header_->overlay_path), length);
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700200
201 length = strnlen(reinterpret_cast<const char*>(header_->target_path),
202 arraysize(header_->target_path));
203 target_apk_path_.assign(reinterpret_cast<const char*>(header_->target_path), length);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700204}
205
206std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_data) {
207 ATRACE_CALL();
208 if (!IsValidIdmapHeader(idmap_data)) {
209 return {};
210 }
211
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700212 auto header = reinterpret_cast<const Idmap_header*>(idmap_data.data());
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700213 const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data()) + sizeof(*header);
214 size_t data_size = idmap_data.size() - sizeof(*header);
215
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700216 // Currently idmap2 can only generate one data block.
217 auto data_header = reinterpret_cast<const Idmap_data_header*>(data_ptr);
218 data_ptr += sizeof(*data_header);
219 data_size -= sizeof(*data_header);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700220
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700221 // Make sure there is enough space for the target entries declared in the header.
222 const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr);
223 if (data_size / sizeof(Idmap_target_entry) <
224 static_cast<size_t>(dtohl(data_header->target_entry_count))) {
225 LOG(ERROR) << StringPrintf("Idmap too small for the number of target entries (%d)",
226 (int)dtohl(data_header->target_entry_count));
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700227 return {};
228 }
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700229
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700230 // Advance the data pointer past the target entries.
231 const size_t target_entry_size_bytes =
232 (dtohl(data_header->target_entry_count) * sizeof(Idmap_target_entry));
233 data_ptr += target_entry_size_bytes;
234 data_size -= target_entry_size_bytes;
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700235
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700236 // Make sure there is enough space for the overlay entries declared in the header.
237 const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr);
238 if (data_size / sizeof(Idmap_overlay_entry) <
239 static_cast<size_t>(dtohl(data_header->overlay_entry_count))) {
240 LOG(ERROR) << StringPrintf("Idmap too small for the number of overlay entries (%d)",
241 (int)dtohl(data_header->overlay_entry_count));
242 return {};
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700243 }
Ryan Mitchell8a891d82019-07-01 09:48:23 -0700244
245 // Advance the data pointer past the target entries.
246 const size_t overlay_entry_size_bytes =
247 (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry));
248 data_ptr += overlay_entry_size_bytes;
249 data_size -= overlay_entry_size_bytes;
250
251 // Read the idmap string pool that holds the value of inline string entries.
252 if (data_size < dtohl(data_header->string_pool_length)) {
253 LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)",
254 (int)dtohl(data_header->string_pool_length));
255 return {};
256 }
257
258 auto idmap_string_pool = util::make_unique<ResStringPool>();
259 if (dtohl(data_header->string_pool_length) > 0) {
260 status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length));
261 if (err != NO_ERROR) {
262 LOG(ERROR) << "idmap string pool corrupt.";
263 return {};
264 }
265 }
266
267 // Can't use make_unique because LoadedImpl constructor is private.
268 std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>(
269 new LoadedIdmap(header, data_header, target_entries, overlay_entries,
270 idmap_string_pool.release()));
271
272 return std::move(loaded_idmap);
Adam Lesinski970bd8d2017-09-25 13:21:55 -0700273}
274
275} // namespace android