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