AttributeResolution.cpp revision 32e7501a27f0f19bccdf9e91f9b87869c093f695
1e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk/*
2e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk * Copyright (C) 2016 The Android Open Source Project
3e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk *
4e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk * Licensed under the Apache License, Version 2.0 (the "License");
5e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk * you may not use this file except in compliance with the License.
6e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk * You may obtain a copy of the License at
7e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk *
8e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk *      http://www.apache.org/licenses/LICENSE-2.0
9e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk *
10e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk * Unless required by applicable law or agreed to in writing, software
11e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk * distributed under the License is distributed on an "AS IS" BASIS,
12e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk * See the License for the specific language governing permissions and
14e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk * limitations under the License.
15e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk */
16e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk
17e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk#include "androidfw/AttributeResolution.h"
18e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk
19e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk#include <cstdint>
20e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk
21e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk#include <log/log.h>
22e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk
23e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk#include "androidfw/AttributeFinder.h"
24e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk#include "androidfw/ResourceTypes.h"
25e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk
26e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burkconstexpr bool kDebugStyles = false;
27e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk
28e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burknamespace android {
29e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk
30e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burkclass XmlAttributeFinder
31e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk    : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
32e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk public:
33e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk  explicit XmlAttributeFinder(const ResXMLParser* parser)
34e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk      : BackTrackingAttributeFinder(
35e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk            0, parser != nullptr ? parser->getAttributeCount() : 0),
36e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk        parser_(parser) {}
37e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk
38e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk  inline uint32_t GetAttribute(size_t index) const {
39e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk    return parser_->getAttributeNameResID(index);
40e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk  }
41e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk
42e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk private:
43e4d7bb418df0fdc4c708c334ba3601f5ed8d89b3Phil Burk  const ResXMLParser* parser_;
44};
45
46class BagAttributeFinder
47    : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
48 public:
49  BagAttributeFinder(const ResTable::bag_entry* start,
50                     const ResTable::bag_entry* end)
51      : BackTrackingAttributeFinder(start, end) {}
52
53  inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const {
54    return entry->map.name.ident;
55  }
56};
57
58bool 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
206void 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
411bool 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