1 /*
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"
23 #include "androidfw/misc.h"
24 #include "androidfw/ResourceTypes.h"
25 #include "androidfw/Util.h"
26 #include "utils/ByteOrder.h"
27 #include "utils/Trace.h"
28
29 #ifdef _WIN32
30 #ifdef ERROR
31 #undef ERROR
32 #endif
33 #endif
34
35 using ::android::base::StringPrintf;
36
37 namespace android {
38
39 // See frameworks/base/cmds/idmap2/include/idmap2/Idmap.h for full idmap file format specification.
40 struct Idmap_header {
41 // Always 0x504D4449 ('IDMP')
42 uint32_t magic;
43 uint32_t version;
44
45 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
56 struct Idmap_data_header {
57 uint32_t target_entry_count;
58 uint32_t target_inline_entry_count;
59 uint32_t target_inline_entry_value_count;
60 uint32_t configuration_count;
61 uint32_t overlay_entry_count;
62
63 uint32_t string_pool_index_offset;
64 };
65
66 struct Idmap_target_entry {
67 uint32_t target_id;
68 uint32_t overlay_id;
69 };
70
71 struct Idmap_target_entry_inline {
72 uint32_t target_id;
73 uint32_t start_value_index;
74 uint32_t value_count;
75 };
76
77 struct Idmap_target_entry_inline_value {
78 uint32_t config_index;
79 Res_value value;
80 };
81
82 struct Idmap_overlay_entry {
83 uint32_t overlay_id;
84 uint32_t target_id;
85 };
86
OverlayStringPool(const LoadedIdmap * loaded_idmap)87 OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap)
88 : data_header_(loaded_idmap->data_header_),
89 idmap_string_pool_(loaded_idmap->string_pool_.get()) { };
90
~OverlayStringPool()91 OverlayStringPool::~OverlayStringPool() {
92 uninit();
93 }
94
stringAt(size_t idx) const95 base::expected<StringPiece16, NullOrIOError> OverlayStringPool::stringAt(size_t idx) const {
96 const size_t offset = dtohl(data_header_->string_pool_index_offset);
97 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
98 return idmap_string_pool_->stringAt(idx - offset);
99 }
100
101 return ResStringPool::stringAt(idx);
102 }
103
string8At(size_t idx) const104 base::expected<StringPiece, NullOrIOError> OverlayStringPool::string8At(size_t idx) const {
105 const size_t offset = dtohl(data_header_->string_pool_index_offset);
106 if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
107 return idmap_string_pool_->string8At(idx - offset);
108 }
109
110 return ResStringPool::string8At(idx);
111 }
112
size() const113 size_t OverlayStringPool::size() const {
114 return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
115 }
116
OverlayDynamicRefTable(const Idmap_data_header * data_header,const Idmap_overlay_entry * entries,uint8_t target_assigned_package_id)117 OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
118 const Idmap_overlay_entry* entries,
119 uint8_t target_assigned_package_id)
120 : data_header_(data_header),
121 entries_(entries),
122 target_assigned_package_id_(target_assigned_package_id) { };
123
lookupResourceId(uint32_t * resId) const124 status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const {
125 const Idmap_overlay_entry* first_entry = entries_;
126 const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count);
127 auto entry = std::lower_bound(first_entry, end_entry, *resId,
128 [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) {
129 return dtohl(e1.overlay_id) < overlay_id;
130 });
131
132 if (entry == end_entry || dtohl(entry->overlay_id) != *resId) {
133 // A mapping for the target resource id could not be found.
134 return DynamicRefTable::lookupResourceId(resId);
135 }
136
137 *resId = (0x00FFFFFFU & dtohl(entry->target_id))
138 | (((uint32_t) target_assigned_package_id_) << 24U);
139 return NO_ERROR;
140 }
141
lookupResourceIdNoRewrite(uint32_t * resId) const142 status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) const {
143 return DynamicRefTable::lookupResourceId(resId);
144 }
145
IdmapResMap(const Idmap_data_header * data_header,const Idmap_target_entry * entries,const Idmap_target_entry_inline * inline_entries,const Idmap_target_entry_inline_value * inline_entry_values,const ConfigDescription * configs,uint8_t target_assigned_package_id,const OverlayDynamicRefTable * overlay_ref_table)146 IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
147 const Idmap_target_entry* entries,
148 const Idmap_target_entry_inline* inline_entries,
149 const Idmap_target_entry_inline_value* inline_entry_values,
150 const ConfigDescription* configs,
151 uint8_t target_assigned_package_id,
152 const OverlayDynamicRefTable* overlay_ref_table)
153 : data_header_(data_header),
154 entries_(entries),
155 inline_entries_(inline_entries),
156 inline_entry_values_(inline_entry_values),
157 configurations_(configs),
158 target_assigned_package_id_(target_assigned_package_id),
159 overlay_ref_table_(overlay_ref_table) { }
160
Lookup(uint32_t target_res_id) const161 IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
162 if ((target_res_id >> 24U) != target_assigned_package_id_) {
163 // The resource id must have the same package id as the target package.
164 return {};
165 }
166
167 // The resource ids encoded within the idmap are build-time resource ids so do not consider the
168 // package id when determining if the resource in the target package is overlaid.
169 target_res_id &= 0x00FFFFFFU;
170
171 // Check if the target resource is mapped to an overlay resource.
172 auto first_entry = entries_;
173 auto end_entry = entries_ + dtohl(data_header_->target_entry_count);
174 auto entry = std::lower_bound(first_entry, end_entry, target_res_id,
175 [](const Idmap_target_entry& e, const uint32_t target_id) {
176 return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
177 });
178
179 if (entry != end_entry && (0x00FFFFFFU & dtohl(entry->target_id)) == target_res_id) {
180 uint32_t overlay_resource_id = dtohl(entry->overlay_id);
181 // Lookup the resource without rewriting the overlay resource id back to the target resource id
182 // being looked up.
183 overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id);
184 return Result(overlay_resource_id);
185 }
186
187 // Check if the target resources is mapped to an inline table entry.
188 auto first_inline_entry = inline_entries_;
189 auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count);
190 auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id,
191 [](const Idmap_target_entry_inline& e,
192 const uint32_t target_id) {
193 return (0x00FFFFFFU & dtohl(e.target_id)) < target_id;
194 });
195
196 if (inline_entry != end_inline_entry &&
197 (0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
198 std::map<ConfigDescription, Res_value> values_map;
199 for (int i = 0; i < inline_entry->value_count; i++) {
200 const auto& value = inline_entry_values_[inline_entry->start_value_index + i];
201 const auto& config = configurations_[value.config_index];
202 values_map[config] = value.value;
203 }
204 return Result(std::move(values_map));
205 }
206 return {};
207 }
208
209 namespace {
210 template <typename T>
ReadType(const uint8_t ** in_out_data_ptr,size_t * in_out_size,const std::string & label,size_t count=1)211 const T* ReadType(const uint8_t** in_out_data_ptr, size_t* in_out_size, const std::string& label,
212 size_t count = 1) {
213 if (!util::IsFourByteAligned(*in_out_data_ptr)) {
214 LOG(ERROR) << "Idmap " << label << " is not word aligned.";
215 return {};
216 }
217 if ((*in_out_size / sizeof(T)) < count) {
218 LOG(ERROR) << "Idmap too small for the number of " << label << " entries ("
219 << count << ").";
220 return nullptr;
221 }
222 auto data_ptr = *in_out_data_ptr;
223 const size_t read_size = sizeof(T) * count;
224 *in_out_data_ptr += read_size;
225 *in_out_size -= read_size;
226 return reinterpret_cast<const T*>(data_ptr);
227 }
228
ReadString(const uint8_t ** in_out_data_ptr,size_t * in_out_size,const std::string & label)229 std::optional<std::string_view> ReadString(const uint8_t** in_out_data_ptr, size_t* in_out_size,
230 const std::string& label) {
231 const auto* len = ReadType<uint32_t>(in_out_data_ptr, in_out_size, label + " length");
232 if (len == nullptr) {
233 return {};
234 }
235 const auto* data = ReadType<char>(in_out_data_ptr, in_out_size, label, *len);
236 if (data == nullptr) {
237 return {};
238 }
239 // Strings are padded to the next 4 byte boundary.
240 const uint32_t padding_size = (4U - ((size_t)*in_out_data_ptr & 0x3U)) % 4U;
241 for (uint32_t i = 0; i < padding_size; i++) {
242 if (**in_out_data_ptr != 0) {
243 LOG(ERROR) << " Idmap padding of " << label << " is non-zero.";
244 return {};
245 }
246 *in_out_data_ptr += sizeof(uint8_t);
247 *in_out_size -= sizeof(uint8_t);
248 }
249 return std::string_view(data, *len);
250 }
251 } // namespace
252
LoadedIdmap(std::string && idmap_path,const Idmap_header * header,const Idmap_data_header * data_header,const Idmap_target_entry * target_entries,const Idmap_target_entry_inline * target_inline_entries,const Idmap_target_entry_inline_value * inline_entry_values,const ConfigDescription * configs,const Idmap_overlay_entry * overlay_entries,std::unique_ptr<ResStringPool> && string_pool,std::string_view overlay_apk_path,std::string_view target_apk_path)253 LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header,
254 const Idmap_data_header* data_header,
255 const Idmap_target_entry* target_entries,
256 const Idmap_target_entry_inline* target_inline_entries,
257 const Idmap_target_entry_inline_value* inline_entry_values,
258 const ConfigDescription* configs,
259 const Idmap_overlay_entry* overlay_entries,
260 std::unique_ptr<ResStringPool>&& string_pool,
261 std::string_view overlay_apk_path, std::string_view target_apk_path)
262 : header_(header),
263 data_header_(data_header),
264 target_entries_(target_entries),
265 target_inline_entries_(target_inline_entries),
266 inline_entry_values_(inline_entry_values),
267 configurations_(configs),
268 overlay_entries_(overlay_entries),
269 string_pool_(std::move(string_pool)),
270 idmap_path_(std::move(idmap_path)),
271 overlay_apk_path_(overlay_apk_path),
272 target_apk_path_(target_apk_path),
273 idmap_last_mod_time_(getFileModDate(idmap_path_.data())) {}
274
Load(StringPiece idmap_path,StringPiece idmap_data)275 std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
276 ATRACE_CALL();
277 size_t data_size = idmap_data.size();
278 auto data_ptr = reinterpret_cast<const uint8_t*>(idmap_data.data());
279
280 // Parse the idmap header
281 auto header = ReadType<Idmap_header>(&data_ptr, &data_size, "header");
282 if (header == nullptr) {
283 return {};
284 }
285 if (dtohl(header->magic) != kIdmapMagic) {
286 LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)",
287 dtohl(header->magic), kIdmapMagic);
288 return {};
289 }
290 if (dtohl(header->version) != kIdmapCurrentVersion) {
291 // We are strict about versions because files with this format are generated at runtime and
292 // don't need backwards compatibility.
293 LOG(ERROR) << StringPrintf("Version mismatch in Idmap (was 0x%08x, expected 0x%08x)",
294 dtohl(header->version), kIdmapCurrentVersion);
295 return {};
296 }
297 std::optional<std::string_view> overlay_path = ReadString(&data_ptr, &data_size, "overlay path");
298 if (!overlay_path) {
299 return {};
300 }
301 std::optional<std::string_view> target_path = ReadString(&data_ptr, &data_size, "target path");
302 if (!target_path) {
303 return {};
304 }
305 if (!ReadString(&data_ptr, &data_size, "target name") ||
306 !ReadString(&data_ptr, &data_size, "debug info")) {
307 return {};
308 }
309
310 // Parse the idmap data blocks. Currently idmap2 can only generate one data block.
311 auto data_header = ReadType<Idmap_data_header>(&data_ptr, &data_size, "data header");
312 if (data_header == nullptr) {
313 return {};
314 }
315 auto target_entries = ReadType<Idmap_target_entry>(&data_ptr, &data_size, "target",
316 dtohl(data_header->target_entry_count));
317 if (target_entries == nullptr) {
318 return {};
319 }
320 auto target_inline_entries = ReadType<Idmap_target_entry_inline>(
321 &data_ptr, &data_size, "target inline", dtohl(data_header->target_inline_entry_count));
322 if (target_inline_entries == nullptr) {
323 return {};
324 }
325
326 auto target_inline_entry_values = ReadType<Idmap_target_entry_inline_value>(
327 &data_ptr, &data_size, "target inline values",
328 dtohl(data_header->target_inline_entry_value_count));
329 if (target_inline_entry_values == nullptr) {
330 return {};
331 }
332
333 auto configurations = ReadType<ConfigDescription>(
334 &data_ptr, &data_size, "configurations",
335 dtohl(data_header->configuration_count));
336 if (configurations == nullptr) {
337 return {};
338 }
339
340 auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
341 dtohl(data_header->overlay_entry_count));
342 if (overlay_entries == nullptr) {
343 return {};
344 }
345 std::optional<std::string_view> string_pool = ReadString(&data_ptr, &data_size, "string pool");
346 if (!string_pool) {
347 return {};
348 }
349 auto idmap_string_pool = util::make_unique<ResStringPool>();
350 if (!string_pool->empty()) {
351 const status_t err = idmap_string_pool->setTo(string_pool->data(), string_pool->size());
352 if (err != NO_ERROR) {
353 LOG(ERROR) << "idmap string pool corrupt.";
354 return {};
355 }
356 }
357
358 if (data_size != 0) {
359 LOG(ERROR) << "idmap parsed with " << data_size << "bytes remaining";
360 return {};
361 }
362
363 // Can't use make_unique because LoadedIdmap constructor is private.
364 return std::unique_ptr<LoadedIdmap>(
365 new LoadedIdmap(std::string(idmap_path), header, data_header, target_entries,
366 target_inline_entries, target_inline_entry_values, configurations,
367 overlay_entries, std::move(idmap_string_pool), *target_path, *overlay_path));
368 }
369
IsUpToDate() const370 bool LoadedIdmap::IsUpToDate() const {
371 return idmap_last_mod_time_ == getFileModDate(idmap_path_.c_str());
372 }
373
374 } // namespace android
375