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