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