AttributeResolution.cpp revision 06d3e8fec7e2b29f99d755bee849023d88957953
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 <android/log.h> 22 23#include "androidfw/AttributeFinder.h" 24#include "androidfw/ResourceTypes.h" 25 26constexpr bool kDebugStyles = false; 27 28namespace android { 29 30class 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 46class BagAttributeFinder 47 : public BackTrackingAttributeFinder<BagAttributeFinder, 48 const ResTable::bag_entry*> { 49 public: 50 BagAttributeFinder(const ResTable::bag_entry* start, 51 const ResTable::bag_entry* end) 52 : BackTrackingAttributeFinder(start, end) {} 53 54 inline uint32_t GetAttribute(const ResTable::bag_entry* entry) const { 55 return entry->map.name.ident; 56 } 57}; 58 59bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, 60 uint32_t def_style_res, uint32_t* src_values, 61 size_t src_values_length, uint32_t* attrs, 62 size_t attrs_length, uint32_t* out_values, 63 uint32_t* out_indices) { 64 if (kDebugStyles) { 65 ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, 66 def_style_attr, def_style_res); 67 } 68 69 const ResTable& res = theme->getResTable(); 70 ResTable_config config; 71 Res_value value; 72 73 int indices_idx = 0; 74 75 // Load default style from attribute, if specified... 76 uint32_t def_style_bag_type_set_flags = 0; 77 if (def_style_attr != 0) { 78 Res_value value; 79 if (theme->getAttribute(def_style_attr, &value, 80 &def_style_bag_type_set_flags) >= 0) { 81 if (value.dataType == Res_value::TYPE_REFERENCE) { 82 def_style_res = value.data; 83 } 84 } 85 } 86 87 // Now lock down the resource object and start pulling stuff from it. 88 res.lock(); 89 90 // Retrieve the default style bag, if requested. 91 const ResTable::bag_entry* def_style_start = nullptr; 92 uint32_t def_style_type_set_flags = 0; 93 ssize_t bag_off = def_style_res != 0 94 ? res.getBagLocked(def_style_res, &def_style_start, 95 &def_style_type_set_flags) 96 : -1; 97 def_style_type_set_flags |= def_style_bag_type_set_flags; 98 const ResTable::bag_entry* const def_style_end = 99 def_style_start + (bag_off >= 0 ? bag_off : 0); 100 BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end); 101 102 // Now iterate through all of the attributes that the client has requested, 103 // filling in each with whatever data we can find. 104 for (size_t ii = 0; ii < attrs_length; ii++) { 105 const uint32_t cur_ident = attrs[ii]; 106 107 if (kDebugStyles) { 108 ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); 109 } 110 111 ssize_t block = -1; 112 uint32_t type_set_flags = 0; 113 114 value.dataType = Res_value::TYPE_NULL; 115 value.data = Res_value::DATA_NULL_UNDEFINED; 116 config.density = 0; 117 118 // Try to find a value for this attribute... we prioritize values 119 // coming from, first XML attributes, then XML style, then default 120 // style, and finally the theme. 121 122 // Retrieve the current input value if available. 123 if (src_values_length > 0 && src_values[ii] != 0) { 124 value.dataType = Res_value::TYPE_ATTRIBUTE; 125 value.data = src_values[ii]; 126 if (kDebugStyles) { 127 ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, 128 value.data); 129 } 130 } 131 132 if (value.dataType == Res_value::TYPE_NULL) { 133 const ResTable::bag_entry* const def_style_entry = 134 def_style_attr_finder.Find(cur_ident); 135 if (def_style_entry != def_style_end) { 136 block = def_style_entry->stringBlock; 137 type_set_flags = def_style_type_set_flags; 138 value = def_style_entry->map.value; 139 if (kDebugStyles) { 140 ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, 141 value.data); 142 } 143 } 144 } 145 146 uint32_t resid = 0; 147 if (value.dataType != Res_value::TYPE_NULL) { 148 // Take care of resolving the found resource to its final value. 149 ssize_t new_block = theme->resolveAttributeReference( 150 &value, block, &resid, &type_set_flags, &config); 151 if (new_block >= 0) block = new_block; 152 if (kDebugStyles) { 153 ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, 154 value.data); 155 } 156 } else { 157 // If we still don't have a value for this attribute, try to find 158 // it in the theme! 159 ssize_t new_block = 160 theme->getAttribute(cur_ident, &value, &type_set_flags); 161 if (new_block >= 0) { 162 if (kDebugStyles) { 163 ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, 164 value.data); 165 } 166 new_block = res.resolveReference(&value, new_block, &resid, 167 &type_set_flags, &config); 168 if (new_block >= 0) block = new_block; 169 if (kDebugStyles) { 170 ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, 171 value.data); 172 } 173 } 174 } 175 176 // Deal with the special @null value -- it turns back to TYPE_NULL. 177 if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { 178 if (kDebugStyles) { 179 ALOGI("-> Setting to @null!"); 180 } 181 value.dataType = Res_value::TYPE_NULL; 182 value.data = Res_value::DATA_NULL_UNDEFINED; 183 block = -1; 184 } 185 186 if (kDebugStyles) { 187 ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, 188 value.dataType, value.data); 189 } 190 191 // Write the final value back to Java. 192 out_values[STYLE_TYPE] = value.dataType; 193 out_values[STYLE_DATA] = value.data; 194 out_values[STYLE_ASSET_COOKIE] = 195 block != -1 ? static_cast<uint32_t>(res.getTableCookie(block)) 196 : static_cast<uint32_t>(-1); 197 out_values[STYLE_RESOURCE_ID] = resid; 198 out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; 199 out_values[STYLE_DENSITY] = config.density; 200 201 if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) { 202 indices_idx++; 203 out_indices[indices_idx] = ii; 204 } 205 206 out_values += STYLE_NUM_ENTRIES; 207 } 208 209 res.unlock(); 210 211 if (out_indices != nullptr) { 212 out_indices[0] = indices_idx; 213 } 214 return true; 215} 216 217void ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr, 218 uint32_t def_style_res, const uint32_t* attrs, size_t attrs_length, 219 uint32_t* out_values, uint32_t* out_indices) { 220 if (kDebugStyles) { 221 ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", 222 theme, def_style_attr, def_style_res, xml_parser); 223 } 224 225 const ResTable& res = theme->getResTable(); 226 ResTable_config config; 227 Res_value value; 228 229 int indices_idx = 0; 230 231 // Load default style from attribute, if specified... 232 uint32_t def_style_bag_type_set_flags = 0; 233 if (def_style_attr != 0) { 234 Res_value value; 235 if (theme->getAttribute(def_style_attr, &value, 236 &def_style_bag_type_set_flags) >= 0) { 237 if (value.dataType == Res_value::TYPE_REFERENCE) { 238 def_style_res = value.data; 239 } 240 } 241 } 242 243 // Retrieve the style class associated with the current XML tag. 244 int style = 0; 245 uint32_t style_bag_type_set_flags = 0; 246 if (xml_parser != nullptr) { 247 ssize_t idx = xml_parser->indexOfStyle(); 248 if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) { 249 if (value.dataType == value.TYPE_ATTRIBUTE) { 250 if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 251 0) { 252 value.dataType = Res_value::TYPE_NULL; 253 } 254 } 255 if (value.dataType == value.TYPE_REFERENCE) { 256 style = value.data; 257 } 258 } 259 } 260 261 // Now lock down the resource object and start pulling stuff from it. 262 res.lock(); 263 264 // Retrieve the default style bag, if requested. 265 const ResTable::bag_entry* def_style_attr_start = nullptr; 266 uint32_t def_style_type_set_flags = 0; 267 ssize_t bag_off = def_style_res != 0 268 ? res.getBagLocked(def_style_res, &def_style_attr_start, 269 &def_style_type_set_flags) 270 : -1; 271 def_style_type_set_flags |= def_style_bag_type_set_flags; 272 const ResTable::bag_entry* const def_style_attr_end = 273 def_style_attr_start + (bag_off >= 0 ? bag_off : 0); 274 BagAttributeFinder def_style_attr_finder(def_style_attr_start, 275 def_style_attr_end); 276 277 // Retrieve the style class bag, if requested. 278 const ResTable::bag_entry* style_attr_start = nullptr; 279 uint32_t style_type_set_flags = 0; 280 bag_off = 281 style != 0 282 ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) 283 : -1; 284 style_type_set_flags |= style_bag_type_set_flags; 285 const ResTable::bag_entry* const style_attr_end = 286 style_attr_start + (bag_off >= 0 ? bag_off : 0); 287 BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end); 288 289 // Retrieve the XML attributes, if requested. 290 static const ssize_t kXmlBlock = 0x10000000; 291 XmlAttributeFinder xml_attr_finder(xml_parser); 292 const size_t xml_attr_end = 293 xml_parser != nullptr ? xml_parser->getAttributeCount() : 0; 294 295 // Now iterate through all of the attributes that the client has requested, 296 // filling in each with whatever data we can find. 297 for (size_t ii = 0; ii < attrs_length; ii++) { 298 const uint32_t cur_ident = attrs[ii]; 299 300 if (kDebugStyles) { 301 ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident); 302 } 303 304 ssize_t block = kXmlBlock; 305 uint32_t type_set_flags = 0; 306 307 value.dataType = Res_value::TYPE_NULL; 308 value.data = Res_value::DATA_NULL_UNDEFINED; 309 config.density = 0; 310 311 // Try to find a value for this attribute... we prioritize values 312 // coming from, first XML attributes, then XML style, then default 313 // style, and finally the theme. 314 315 // Walk through the xml attributes looking for the requested attribute. 316 const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident); 317 if (xml_attr_idx != xml_attr_end) { 318 // We found the attribute we were looking for. 319 xml_parser->getAttributeValue(xml_attr_idx, &value); 320 if (kDebugStyles) { 321 ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, 322 value.data); 323 } 324 } 325 326 if (value.dataType == Res_value::TYPE_NULL) { 327 // Walk through the style class values looking for the requested 328 // attribute. 329 const ResTable::bag_entry* const style_attr_entry = 330 style_attr_finder.Find(cur_ident); 331 if (style_attr_entry != style_attr_end) { 332 // We found the attribute we were looking for. 333 block = style_attr_entry->stringBlock; 334 type_set_flags = style_type_set_flags; 335 value = style_attr_entry->map.value; 336 if (kDebugStyles) { 337 ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, 338 value.data); 339 } 340 } 341 } 342 343 if (value.dataType == Res_value::TYPE_NULL) { 344 // Walk through the default style values looking for the requested 345 // attribute. 346 const ResTable::bag_entry* const def_style_attr_entry = 347 def_style_attr_finder.Find(cur_ident); 348 if (def_style_attr_entry != def_style_attr_end) { 349 // We found the attribute we were looking for. 350 block = def_style_attr_entry->stringBlock; 351 type_set_flags = style_type_set_flags; 352 value = def_style_attr_entry->map.value; 353 if (kDebugStyles) { 354 ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, 355 value.data); 356 } 357 } 358 } 359 360 uint32_t resid = 0; 361 if (value.dataType != Res_value::TYPE_NULL) { 362 // Take care of resolving the found resource to its final value. 363 ssize_t new_block = theme->resolveAttributeReference( 364 &value, block, &resid, &type_set_flags, &config); 365 if (new_block >= 0) { 366 block = new_block; 367 } 368 369 if (kDebugStyles) { 370 ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, 371 value.data); 372 } 373 } else { 374 // If we still don't have a value for this attribute, try to find 375 // it in the theme! 376 ssize_t new_block = 377 theme->getAttribute(cur_ident, &value, &type_set_flags); 378 if (new_block >= 0) { 379 if (kDebugStyles) { 380 ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, 381 value.data); 382 } 383 new_block = res.resolveReference(&value, new_block, &resid, 384 &type_set_flags, &config); 385 if (new_block >= 0) { 386 block = new_block; 387 } 388 389 if (kDebugStyles) { 390 ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, 391 value.data); 392 } 393 } 394 } 395 396 // Deal with the special @null value -- it turns back to TYPE_NULL. 397 if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { 398 if (kDebugStyles) { 399 ALOGI("-> Setting to @null!"); 400 } 401 value.dataType = Res_value::TYPE_NULL; 402 value.data = Res_value::DATA_NULL_UNDEFINED; 403 block = kXmlBlock; 404 } 405 406 if (kDebugStyles) { 407 ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, 408 value.dataType, value.data); 409 } 410 411 // Write the final value back to Java. 412 out_values[STYLE_TYPE] = value.dataType; 413 out_values[STYLE_DATA] = value.data; 414 out_values[STYLE_ASSET_COOKIE] = 415 block != kXmlBlock ? static_cast<uint32_t>(res.getTableCookie(block)) 416 : static_cast<uint32_t>(-1); 417 out_values[STYLE_RESOURCE_ID] = resid; 418 out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; 419 out_values[STYLE_DENSITY] = config.density; 420 421 if (value.dataType != Res_value::TYPE_NULL) { 422 indices_idx++; 423 424 // out_indices must NOT be nullptr. 425 out_indices[indices_idx] = ii; 426 } 427 428 out_values += STYLE_NUM_ENTRIES; 429 } 430 431 res.unlock(); 432 433 // out_indices must NOT be nullptr. 434 out_indices[0] = indices_idx; 435} 436 437bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, 438 uint32_t* attrs, size_t attrs_length, 439 uint32_t* out_values, uint32_t* out_indices) { 440 ResTable_config config; 441 Res_value value; 442 443 int indices_idx = 0; 444 445 // Now lock down the resource object and start pulling stuff from it. 446 res->lock(); 447 448 // Retrieve the XML attributes, if requested. 449 const size_t xml_attr_count = xml_parser->getAttributeCount(); 450 size_t ix = 0; 451 uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix); 452 453 static const ssize_t kXmlBlock = 0x10000000; 454 455 // Now iterate through all of the attributes that the client has requested, 456 // filling in each with whatever data we can find. 457 for (size_t ii = 0; ii < attrs_length; ii++) { 458 const uint32_t cur_ident = attrs[ii]; 459 ssize_t block = kXmlBlock; 460 uint32_t type_set_flags = 0; 461 462 value.dataType = Res_value::TYPE_NULL; 463 value.data = Res_value::DATA_NULL_UNDEFINED; 464 config.density = 0; 465 466 // Try to find a value for this attribute... 467 // Skip through XML attributes until the end or the next possible match. 468 while (ix < xml_attr_count && cur_ident > cur_xml_attr) { 469 ix++; 470 cur_xml_attr = xml_parser->getAttributeNameResID(ix); 471 } 472 // Retrieve the current XML attribute if it matches, and step to next. 473 if (ix < xml_attr_count && cur_ident == cur_xml_attr) { 474 xml_parser->getAttributeValue(ix, &value); 475 ix++; 476 cur_xml_attr = xml_parser->getAttributeNameResID(ix); 477 } 478 479 uint32_t resid = 0; 480 if (value.dataType != Res_value::TYPE_NULL) { 481 // Take care of resolving the found resource to its final value. 482 // printf("Resolving attribute reference\n"); 483 ssize_t new_block = res->resolveReference(&value, block, &resid, 484 &type_set_flags, &config); 485 if (new_block >= 0) block = new_block; 486 } 487 488 // Deal with the special @null value -- it turns back to TYPE_NULL. 489 if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { 490 value.dataType = Res_value::TYPE_NULL; 491 value.data = Res_value::DATA_NULL_UNDEFINED; 492 block = kXmlBlock; 493 } 494 495 // Write the final value back to Java. 496 out_values[STYLE_TYPE] = value.dataType; 497 out_values[STYLE_DATA] = value.data; 498 out_values[STYLE_ASSET_COOKIE] = 499 block != kXmlBlock ? static_cast<uint32_t>(res->getTableCookie(block)) 500 : static_cast<uint32_t>(-1); 501 out_values[STYLE_RESOURCE_ID] = resid; 502 out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags; 503 out_values[STYLE_DENSITY] = config.density; 504 505 if (out_indices != nullptr && value.dataType != Res_value::TYPE_NULL) { 506 indices_idx++; 507 out_indices[indices_idx] = ii; 508 } 509 510 out_values += STYLE_NUM_ENTRIES; 511 } 512 513 res->unlock(); 514 515 if (out_indices != nullptr) { 516 out_indices[0] = indices_idx; 517 } 518 return true; 519} 520 521} // namespace android 522