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   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 
    290     // Try to find a value for this attribute...  we prioritize values
    291     // coming from, first XML attributes, then XML style, then default
    292     // style, and finally the theme.
    293 
    294     // Walk through the xml attributes looking for the requested attribute.
    295     const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
    296     if (xml_attr_idx != xml_attr_finder.end()) {
    297       // We found the attribute we were looking for.
    298       xml_parser->getAttributeValue(xml_attr_idx, &value);
    299       if (kDebugStyles) {
    300         ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
    301       }
    302     }
    303 
    304     if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
    305       // Walk through the style class values looking for the requested attribute.
    306       const ResolvedBag::Entry* entry = xml_style_attr_finder.Find(cur_ident);
    307       if (entry != xml_style_attr_finder.end()) {
    308         // We found the attribute we were looking for.
    309         cookie = entry->cookie;
    310         type_set_flags = style_flags;
    311         value = entry->value;
    312         if (kDebugStyles) {
    313           ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
    314         }
    315       }
    316     }
    317 
    318     if (value.dataType == Res_value::TYPE_NULL && value.data != Res_value::DATA_NULL_EMPTY) {
    319       // Walk through the default style values looking for the requested attribute.
    320       const ResolvedBag::Entry* entry = def_style_attr_finder.Find(cur_ident);
    321       if (entry != def_style_attr_finder.end()) {
    322         // We found the attribute we were looking for.
    323         cookie = entry->cookie;
    324         type_set_flags = def_style_flags;
    325         value = entry->value;
    326         if (kDebugStyles) {
    327           ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
    328         }
    329       }
    330     }
    331 
    332     uint32_t resid = 0u;
    333     if (value.dataType != Res_value::TYPE_NULL) {
    334       // Take care of resolving the found resource to its final value.
    335       ApkAssetsCookie new_cookie =
    336           theme->ResolveAttributeReference(cookie, &value, &config, &type_set_flags, &resid);
    337       if (new_cookie != kInvalidCookie) {
    338         cookie = new_cookie;
    339       }
    340 
    341       if (kDebugStyles) {
    342         ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
    343       }
    344     } else if (value.data != Res_value::DATA_NULL_EMPTY) {
    345       // If we still don't have a value for this attribute, try to find it in the theme!
    346       ApkAssetsCookie new_cookie = theme->GetAttribute(cur_ident, &value, &type_set_flags);
    347       if (new_cookie != kInvalidCookie) {
    348         if (kDebugStyles) {
    349           ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
    350         }
    351         new_cookie =
    352             assetmanager->ResolveReference(new_cookie, &value, &config, &type_set_flags, &resid);
    353         if (new_cookie != kInvalidCookie) {
    354           cookie = new_cookie;
    355         }
    356 
    357         if (kDebugStyles) {
    358           ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
    359         }
    360       }
    361     }
    362 
    363     // Deal with the special @null value -- it turns back to TYPE_NULL.
    364     if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
    365       if (kDebugStyles) {
    366         ALOGI("-> Setting to @null!");
    367       }
    368       value.dataType = Res_value::TYPE_NULL;
    369       value.data = Res_value::DATA_NULL_UNDEFINED;
    370       cookie = kInvalidCookie;
    371     }
    372 
    373     if (kDebugStyles) {
    374       ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
    375     }
    376 
    377     // Write the final value back to Java.
    378     out_values[STYLE_TYPE] = value.dataType;
    379     out_values[STYLE_DATA] = value.data;
    380     out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
    381     out_values[STYLE_RESOURCE_ID] = resid;
    382     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
    383     out_values[STYLE_DENSITY] = config.density;
    384 
    385     if (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY) {
    386       indices_idx++;
    387 
    388       // out_indices must NOT be nullptr.
    389       out_indices[indices_idx] = ii;
    390     }
    391 
    392     out_values += STYLE_NUM_ENTRIES;
    393   }
    394 
    395   // out_indices must NOT be nullptr.
    396   out_indices[0] = indices_idx;
    397 }
    398 
    399 bool RetrieveAttributes(AssetManager2* assetmanager, ResXMLParser* xml_parser, uint32_t* attrs,
    400                         size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
    401   ResTable_config config;
    402   Res_value value;
    403 
    404   int indices_idx = 0;
    405 
    406   // Retrieve the XML attributes, if requested.
    407   const size_t xml_attr_count = xml_parser->getAttributeCount();
    408   size_t ix = 0;
    409   uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
    410 
    411   // Now iterate through all of the attributes that the client has requested,
    412   // filling in each with whatever data we can find.
    413   for (size_t ii = 0; ii < attrs_length; ii++) {
    414     const uint32_t cur_ident = attrs[ii];
    415     ApkAssetsCookie cookie = kInvalidCookie;
    416     uint32_t type_set_flags = 0u;
    417 
    418     value.dataType = Res_value::TYPE_NULL;
    419     value.data = Res_value::DATA_NULL_UNDEFINED;
    420     config.density = 0;
    421 
    422     // Try to find a value for this attribute...
    423     // Skip through XML attributes until the end or the next possible match.
    424     while (ix < xml_attr_count && cur_ident > cur_xml_attr) {
    425       ix++;
    426       cur_xml_attr = xml_parser->getAttributeNameResID(ix);
    427     }
    428     // Retrieve the current XML attribute if it matches, and step to next.
    429     if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
    430       xml_parser->getAttributeValue(ix, &value);
    431       ix++;
    432       cur_xml_attr = xml_parser->getAttributeNameResID(ix);
    433     }
    434 
    435     uint32_t resid = 0u;
    436     if (value.dataType != Res_value::TYPE_NULL) {
    437       // Take care of resolving the found resource to its final value.
    438       ApkAssetsCookie new_cookie =
    439           assetmanager->ResolveReference(cookie, &value, &config, &type_set_flags, &resid);
    440       if (new_cookie != kInvalidCookie) {
    441         cookie = new_cookie;
    442       }
    443     }
    444 
    445     // Deal with the special @null value -- it turns back to TYPE_NULL.
    446     if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
    447       value.dataType = Res_value::TYPE_NULL;
    448       value.data = Res_value::DATA_NULL_UNDEFINED;
    449       cookie = kInvalidCookie;
    450     }
    451 
    452     // Write the final value back to Java.
    453     out_values[STYLE_TYPE] = value.dataType;
    454     out_values[STYLE_DATA] = value.data;
    455     out_values[STYLE_ASSET_COOKIE] = ApkAssetsCookieToJavaCookie(cookie);
    456     out_values[STYLE_RESOURCE_ID] = resid;
    457     out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
    458     out_values[STYLE_DENSITY] = config.density;
    459 
    460     if (out_indices != nullptr &&
    461         (value.dataType != Res_value::TYPE_NULL || value.data == Res_value::DATA_NULL_EMPTY)) {
    462       indices_idx++;
    463       out_indices[indices_idx] = ii;
    464     }
    465 
    466     out_values += STYLE_NUM_ENTRIES;
    467   }
    468 
    469   if (out_indices != nullptr) {
    470     out_indices[0] = indices_idx;
    471   }
    472   return true;
    473 }
    474 
    475 }  // namespace android
    476