1 /*
2 * Copyright (C) 2016 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 #include "androidfw/AttributeResolution.h"
18
19 #include <cstdint>
20
21 #include <log/log.h>
22
23 #include "androidfw/AttributeFinder.h"
24 #include "androidfw/ResourceTypes.h"
25
26 constexpr bool kDebugStyles = false;
27
28 namespace android {
29
30 class XmlAttributeFinder
31 : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
32 public:
XmlAttributeFinder(const ResXMLParser * parser)33 explicit XmlAttributeFinder(const ResXMLParser* parser)
34 : BackTrackingAttributeFinder(
35 0, parser != nullptr ? parser->getAttributeCount() : 0),
36 parser_(parser) {}
37
GetAttribute(size_t index) const38 inline uint32_t GetAttribute(size_t index) const {
39 return parser_->getAttributeNameResID(index);
40 }
41
42 private:
43 const ResXMLParser* parser_;
44 };
45
46 class BagAttributeFinder
47 : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
48 public:
BagAttributeFinder(const ResTable::bag_entry * start,const ResTable::bag_entry * end)49 BagAttributeFinder(const ResTable::bag_entry* start,
50 const ResTable::bag_entry* end)
51 : BackTrackingAttributeFinder(start, end) {}
52
GetAttribute(const ResTable::bag_entry * entry) const53 inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const {
54 return entry->map.name.ident;
55 }
56 };
57
ResolveAttrs(ResTable::Theme * theme,uint32_t def_style_attr,uint32_t def_style_res,uint32_t * src_values,size_t src_values_length,uint32_t * attrs,size_t attrs_length,uint32_t * out_values,uint32_t * out_indices)58 bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr,
59 uint32_t def_style_res, uint32_t* src_values,
60 size_t src_values_length, uint32_t* attrs,
61 size_t attrs_length, uint32_t* out_values,
62 uint32_t* out_indices) {
63 if (kDebugStyles) {
64 ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme,
65 def_style_attr, def_style_res);
66 }
67
68 const ResTable& res = theme->getResTable();
69 ResTable_config config;
70 Res_value value;
71
72 int indices_idx = 0;
73
74 // Load default style from attribute, if specified...
75 uint32_t def_style_bag_type_set_flags = 0;
76 if (def_style_attr != 0) {
77 Res_value value;
78 if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
79 if (value.dataType == Res_value::TYPE_REFERENCE) {
80 def_style_res = value.data;
81 }
82 }
83 }
84
85 // Now lock down the resource object and start pulling stuff from it.
86 res.lock();
87
88 // Retrieve the default style bag, if requested.
89 const ResTable::bag_entry* def_style_start = nullptr;
90 uint32_t def_style_type_set_flags = 0;
91 ssize_t bag_off = def_style_res != 0
92 ? res.getBagLocked(def_style_res, &def_style_start,
93 &def_style_type_set_flags)
94 : -1;
95 def_style_type_set_flags |= def_style_bag_type_set_flags;
96 const ResTable::bag_entry* const def_style_end =
97 def_style_start + (bag_off >= 0 ? bag_off : 0);
98 BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end);
99
100 // Now iterate through all of the attributes that the client has requested,
101 // filling in each with whatever data we can find.
102 for (size_t ii = 0; ii < attrs_length; ii++) {
103 const uint32_t cur_ident = attrs[ii];
104
105 if (kDebugStyles) {
106 ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
107 }
108
109 ssize_t block = -1;
110 uint32_t type_set_flags = 0;
111
112 value.dataType = Res_value::TYPE_NULL;
113 value.data = Res_value::DATA_NULL_UNDEFINED;
114 config.density = 0;
115
116 // Try to find a value for this attribute... we prioritize values
117 // coming from, first XML attributes, then XML style, then default
118 // style, and finally the theme.
119
120 // Retrieve the current input value if available.
121 if (src_values_length > 0 && src_values[ii] != 0) {
122 value.dataType = Res_value::TYPE_ATTRIBUTE;
123 value.data = src_values[ii];
124 if (kDebugStyles) {
125 ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType,
126 value.data);
127 }
128 } else {
129 const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident);
130 if (def_style_entry != def_style_end) {
131 block = def_style_entry->stringBlock;
132 type_set_flags = def_style_type_set_flags;
133 value = def_style_entry->map.value;
134 if (kDebugStyles) {
135 ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
136 }
137 }
138 }
139
140 uint32_t resid = 0;
141 if (value.dataType != Res_value::TYPE_NULL) {
142 // Take care of resolving the found resource to its final value.
143 ssize_t new_block =
144 theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
145 if (new_block >= 0) block = new_block;
146 if (kDebugStyles) {
147 ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
148 }
149 } else if (value.data != Res_value::DATA_NULL_EMPTY) {
150 // If we still don't have a value for this attribute, try to find
151 // it in the theme!
152 ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
153 if (new_block >= 0) {
154 if (kDebugStyles) {
155 ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
156 }
157 new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
158 if (new_block >= 0) block = new_block;
159 if (kDebugStyles) {
160 ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
161 }
162 }
163 }
164
165 // Deal with the special @null value -- it turns back to TYPE_NULL.
166 if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
167 if (kDebugStyles) {
168 ALOGI("-> Setting to @null!");
169 }
170 value.dataType = Res_value::TYPE_NULL;
171 value.data = Res_value::DATA_NULL_UNDEFINED;
172 block = -1;
173 }
174
175 if (kDebugStyles) {
176 ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
177 }
178
179 // Write the final value back to Java.
180 out_values[STYLE_TYPE] = value.dataType;
181 out_values[STYLE_DATA] = value.data;
182 out_values[STYLE_ASSET_COOKIE] =
183 block != -1 ? static_cast<uint32_t>(res.getTableCookie(block))
184 : static_cast<uint32_t>(-1);
185 out_values[STYLE_RESOURCE_ID] = resid;
186 out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
187 out_values[STYLE_DENSITY] = config.density;
188
189 if (out_indices != nullptr &&
190 (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
191 indices_idx++;
192 out_indices[indices_idx] = ii;
193 }
194
195 out_values += STYLE_NUM_ENTRIES;
196 }
197
198 res.unlock();
199
200 if (out_indices != nullptr) {
201 out_indices[0] = indices_idx;
202 }
203 return true;
204 }
205
ApplyStyle(ResTable::Theme * theme,ResXMLParser * xml_parser,uint32_t def_style_attr,uint32_t def_style_res,const uint32_t * attrs,size_t attrs_length,uint32_t * out_values,uint32_t * out_indices)206 void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
207 uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length,
208 uint32_t* out_values, uint32_t* out_indices) {
209 if (kDebugStyles) {
210 ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p",
211 theme, def_style_attr, def_style_res, xml_parser);
212 }
213
214 const ResTable& res = theme->getResTable();
215 ResTable_config config;
216 Res_value value;
217
218 int indices_idx = 0;
219
220 // Load default style from attribute, if specified...
221 uint32_t def_style_bag_type_set_flags = 0;
222 if (def_style_attr != 0) {
223 Res_value value;
224 if (theme->getAttribute(def_style_attr, &value,
225 &def_style_bag_type_set_flags) >= 0) {
226 if (value.dataType == Res_value::TYPE_REFERENCE) {
227 def_style_res = value.data;
228 }
229 }
230 }
231
232 // Retrieve the style class associated with the current XML tag.
233 int style = 0;
234 uint32_t style_bag_type_set_flags = 0;
235 if (xml_parser != nullptr) {
236 ssize_t idx = xml_parser->indexOfStyle();
237 if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
238 if (value.dataType == value.TYPE_ATTRIBUTE) {
239 if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 0) {
240 value.dataType = Res_value::TYPE_NULL;
241 }
242 }
243 if (value.dataType == value.TYPE_REFERENCE) {
244 style = value.data;
245 }
246 }
247 }
248
249 // Now lock down the resource object and start pulling stuff from it.
250 res.lock();
251
252 // Retrieve the default style bag, if requested.
253 const ResTable::bag_entry* def_style_attr_start = nullptr;
254 uint32_t def_style_type_set_flags = 0;
255 ssize_t bag_off = def_style_res != 0
256 ? res.getBagLocked(def_style_res, &def_style_attr_start,
257 &def_style_type_set_flags)
258 : -1;
259 def_style_type_set_flags |= def_style_bag_type_set_flags;
260 const ResTable::bag_entry* const def_style_attr_end =
261 def_style_attr_start + (bag_off >= 0 ? bag_off : 0);
262 BagAttributeFinder def_style_attr_finder(def_style_attr_start,
263 def_style_attr_end);
264
265 // Retrieve the style class bag, if requested.
266 const ResTable::bag_entry* style_attr_start = nullptr;
267 uint32_t style_type_set_flags = 0;
268 bag_off =
269 style != 0
270 ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags)
271 : -1;
272 style_type_set_flags |= style_bag_type_set_flags;
273 const ResTable::bag_entry* const style_attr_end =
274 style_attr_start + (bag_off >= 0 ? bag_off : 0);
275 BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end);
276
277 // Retrieve the XML attributes, if requested.
278 static const ssize_t kXmlBlock = 0x10000000;
279 XmlAttributeFinder xml_attr_finder(xml_parser);
280 const size_t xml_attr_end =
281 xml_parser != nullptr ? xml_parser->getAttributeCount() : 0;
282
283 // Now iterate through all of the attributes that the client has requested,
284 // filling in each with whatever data we can find.
285 for (size_t ii = 0; ii < attrs_length; ii++) {
286 const uint32_t cur_ident = attrs[ii];
287
288 if (kDebugStyles) {
289 ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
290 }
291
292 ssize_t block = kXmlBlock;
293 uint32_t type_set_flags = 0;
294
295 value.dataType = Res_value::TYPE_NULL;
296 value.data = Res_value::DATA_NULL_UNDEFINED;
297 config.density = 0;
298
299 // Try to find a value for this attribute... we prioritize values
300 // coming from, first XML attributes, then XML style, then default
301 // style, and finally the theme.
302
303 // Walk through the xml attributes looking for the requested attribute.
304 const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
305 if (xml_attr_idx != xml_attr_end) {
306 // We found the attribute we were looking for.
307 xml_parser->getAttributeValue(xml_attr_idx, &value);
308 if (kDebugStyles) {
309 ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
310 }
311 }
312
313 if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
314 // Walk through the style class values looking for the requested attribute.
315 const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident);
316 if (style_attr_entry != style_attr_end) {
317 // We found the attribute we were looking for.
318 block = style_attr_entry->stringBlock;
319 type_set_flags = style_type_set_flags;
320 value = style_attr_entry->map.value;
321 if (kDebugStyles) {
322 ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
323 }
324 }
325 }
326
327 if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
328 // Walk through the default style values looking for the requested attribute.
329 const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident);
330 if (def_style_attr_entry != def_style_attr_end) {
331 // We found the attribute we were looking for.
332 block = def_style_attr_entry->stringBlock;
333 type_set_flags = style_type_set_flags;
334 value = def_style_attr_entry->map.value;
335 if (kDebugStyles) {
336 ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
337 }
338 }
339 }
340
341 uint32_t resid = 0;
342 if (value.dataType != Res_value::TYPE_NULL) {
343 // Take care of resolving the found resource to its final value.
344 ssize_t new_block =
345 theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
346 if (new_block >= 0) {
347 block = new_block;
348 }
349
350 if (kDebugStyles) {
351 ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
352 }
353 } else if (value.data != Res_value::DATA_NULL_EMPTY) {
354 // If we still don't have a value for this attribute, try to find it in the theme!
355 ssize_t new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
356 if (new_block >= 0) {
357 if (kDebugStyles) {
358 ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
359 }
360 new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
361 if (new_block >= 0) {
362 block = new_block;
363 }
364
365 if (kDebugStyles) {
366 ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
367 }
368 }
369 }
370
371 // Deal with the special @null value -- it turns back to TYPE_NULL.
372 if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
373 if (kDebugStyles) {
374 ALOGI("-> Setting to @null!");
375 }
376 value.dataType = Res_value::TYPE_NULL;
377 value.data = Res_value::DATA_NULL_UNDEFINED;
378 block = kXmlBlock;
379 }
380
381 if (kDebugStyles) {
382 ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
383 }
384
385 // Write the final value back to Java.
386 out_values[STYLE_TYPE] = value.dataType;
387 out_values[STYLE_DATA] = value.data;
388 out_values[STYLE_ASSET_COOKIE] =
389 block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block))
390 : static_cast<uint32_t>(-1);
391 out_values[STYLE_RESOURCE_ID] = resid;
392 out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
393 out_values[STYLE_DENSITY] = config.density;
394
395 if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
396 indices_idx++;
397
398 // out_indices must NOT be nullptr.
399 out_indices[indices_idx] = ii;
400 }
401
402 out_values += STYLE_NUM_ENTRIES;
403 }
404
405 res.unlock();
406
407 // out_indices must NOT be nullptr.
408 out_indices[0] = indices_idx;
409 }
410
RetrieveAttributes(const ResTable * res,ResXMLParser * xml_parser,uint32_t * attrs,size_t attrs_length,uint32_t * out_values,uint32_t * out_indices)411 bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser,
412 uint32_t* attrs, size_t attrs_length,
413 uint32_t* out_values, uint32_t* out_indices) {
414 ResTable_config config;
415 Res_value value;
416
417 int indices_idx = 0;
418
419 // Now lock down the resource object and start pulling stuff from it.
420 res->lock();
421
422 // Retrieve the XML attributes, if requested.
423 const size_t xml_attr_count = xml_parser->getAttributeCount();
424 size_t ix = 0;
425 uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
426
427 static const ssize_t kXmlBlock = 0x10000000;
428
429 // Now iterate through all of the attributes that the client has requested,
430 // filling in each with whatever data we can find.
431 for (size_t ii = 0; ii < attrs_length; ii++) {
432 const uint32_t cur_ident = attrs[ii];
433 ssize_t block = kXmlBlock;
434 uint32_t type_set_flags = 0;
435
436 value.dataType = Res_value::TYPE_NULL;
437 value.data = Res_value::DATA_NULL_UNDEFINED;
438 config.density = 0;
439
440 // Try to find a value for this attribute...
441 // Skip through XML attributes until the end or the next possible match.
442 while (ix < xml_attr_count && cur_ident > cur_xml_attr) {
443 ix++;
444 cur_xml_attr = xml_parser->getAttributeNameResID(ix);
445 }
446 // Retrieve the current XML attribute if it matches, and step to next.
447 if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
448 xml_parser->getAttributeValue(ix, &value);
449 ix++;
450 cur_xml_attr = xml_parser->getAttributeNameResID(ix);
451 }
452
453 uint32_t resid = 0;
454 if (value.dataType != Res_value::TYPE_NULL) {
455 // Take care of resolving the found resource to its final value.
456 // printf("Resolving attribute reference\n");
457 ssize_t new_block = res->resolveReference(&value, block, &resid,
458 &type_set_flags, &config);
459 if (new_block >= 0) block = new_block;
460 }
461
462 // Deal with the special @null value -- it turns back to TYPE_NULL.
463 if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
464 value.dataType = Res_value::TYPE_NULL;
465 value.data = Res_value::DATA_NULL_UNDEFINED;
466 block = kXmlBlock;
467 }
468
469 // Write the final value back to Java.
470 out_values[STYLE_TYPE] = value.dataType;
471 out_values[STYLE_DATA] = value.data;
472 out_values[STYLE_ASSET_COOKIE] =
473 block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block))
474 : static_cast<uint32_t>(-1);
475 out_values[STYLE_RESOURCE_ID] = resid;
476 out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
477 out_values[STYLE_DENSITY] = config.density;
478
479 if (out_indices != nullptr &&
480 (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
481 indices_idx++;
482 out_indices[indices_idx] = ii;
483 }
484
485 out_values += STYLE_NUM_ENTRIES;
486 }
487
488 res->unlock();
489
490 if (out_indices != nullptr) {
491 out_indices[0] = indices_idx;
492 }
493 return true;
494 }
495
496 } // namespace android
497