Home | History | Annotate | Download | only in androidfw
      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:
     33   explicit XmlAttributeFinder(const ResXMLParser* parser)
     34       : BackTrackingAttributeFinder(
     35             0, parser != nullptr ? parser->getAttributeCount() : 0),
     36         parser_(parser) {}
     37 
     38   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:
     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 
     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 
    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 
    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